diff options
author | Rich Felker <dalias@aerifal.cx> | 2015-04-13 02:56:26 -0400 |
---|---|---|
committer | Rich Felker <dalias@aerifal.cx> | 2015-04-13 03:04:42 -0400 |
commit | f3ddd173806fd5c60b3f034528ca24542aecc5b9 (patch) | |
tree | 63cc7432a3c40f011c4818de32ef6257acbf0e73 /src/ldso/dlstart.c | |
parent | 385c01112c083eb383d972da45836d497cc0556d (diff) | |
download | musl-f3ddd173806fd5c60b3f034528ca24542aecc5b9.tar.gz musl-f3ddd173806fd5c60b3f034528ca24542aecc5b9.tar.bz2 musl-f3ddd173806fd5c60b3f034528ca24542aecc5b9.tar.xz musl-f3ddd173806fd5c60b3f034528ca24542aecc5b9.zip |
dynamic linker bootstrap overhaul
this overhaul further reduces the amount of arch-specific code needed
by the dynamic linker and removes a number of assumptions, including:
- that symbolic function references inside libc are bound at link time
via the linker option -Bsymbolic-functions.
- that libc functions used by the dynamic linker do not require
access to data symbols.
- that static/internal function calls and data accesses can be made
without performing any relocations, or that arch-specific startup
code handled any such relocations needed.
removing these assumptions paves the way for allowing libc.so itself
to be built with stack protector (among other things), and is achieved
by a three-stage bootstrap process:
1. relative relocations are processed with a flat function.
2. symbolic relocations are processed with no external calls/data.
3. main program and dependency libs are processed with a
fully-functional libc/ldso.
reduction in arch-specific code is achived through the following:
- crt_arch.h, used for generating crt1.o, now provides the entry point
for the dynamic linker too.
- asm is no longer responsible for skipping the beginning of argv[]
when ldso is invoked as a command.
- the functionality previously provided by __reloc_self for heavily
GOT-dependent RISC archs is now the arch-agnostic stage-1.
- arch-specific relocation type codes are mapped directly as macros
rather than via an inline translation function/switch statement.
Diffstat (limited to 'src/ldso/dlstart.c')
-rw-r--r-- | src/ldso/dlstart.c | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/src/ldso/dlstart.c b/src/ldso/dlstart.c new file mode 100644 index 00000000..5bd2a080 --- /dev/null +++ b/src/ldso/dlstart.c @@ -0,0 +1,107 @@ +#include <stddef.h> +#include "dynlink.h" + +#ifdef SHARED + +#ifndef START +#define START "_dlstart" +#endif + +#include "crt_arch.h" + +void _dlstart_c(size_t *sp, size_t *dynv) +{ + size_t i, aux[AUX_CNT], dyn[DYN_CNT]; + + int argc = *sp; + char **argv = (void *)(sp+1); + + for (i=argc+1; argv[i]; i++); + size_t *auxv = (void *)(argv+i+1); + + for (i=0; i<AUX_CNT; i++) aux[i] = 0; + for (i=0; auxv[i]; i+=2) if (auxv[i]<AUX_CNT) + aux[auxv[i]] = auxv[i+1]; + + for (i=0; i<DYN_CNT; i++) dyn[i] = 0; + for (i=0; dynv[i]; i+=2) if (dynv[i]<DYN_CNT) + dyn[dynv[i]] = dynv[i+1]; + + /* If the dynamic linker is invoked as a command, its load + * address is not available in the aux vector. Instead, compute + * the load address as the difference between &_DYNAMIC and the + * virtual address in the PT_DYNAMIC program header. */ + unsigned char *base = (void *)aux[AT_BASE]; + if (!base) { + size_t phnum = aux[AT_PHNUM]; + size_t phentsize = aux[AT_PHENT]; + Phdr *ph = (void *)aux[AT_PHDR]; + for (i=phnum; i--; ph = (void *)((char *)ph + phentsize)) { + if (ph->p_type == PT_DYNAMIC) { + base = (void *)((size_t)dynv - ph->p_vaddr); + break; + } + } + } + + /* MIPS uses an ugly packed form for GOT relocations. Since we + * can't make function calls yet and the code is tiny anyway, + * it's simply inlined here. */ + if (NEED_MIPS_GOT_RELOCS) { + size_t local_cnt = 0; + size_t *got = (void *)(base + dyn[DT_PLTGOT]); + for (i=0; dynv[i]; i+=2) if (dynv[i]==DT_MIPS_LOCAL_GOTNO) + local_cnt = dynv[i+1]; + for (i=0; i<local_cnt; i++) got[i] += (size_t)base; + } + + /* The use of the reloc_info structure and nested loops is a trick + * to work around the fact that we can't necessarily make function + * calls yet. Each struct in the array serves like the arguments + * to a function call. */ + struct { + void *rel; + size_t size; + size_t stride; + } reloc_info[] = { + { base+dyn[DT_JMPREL], dyn[DT_PLTRELSZ], 2+(dyn[DT_PLTREL]==DT_RELA) }, + { base+dyn[DT_REL], dyn[DT_RELSZ], 2 }, + { base+dyn[DT_RELA], dyn[DT_RELASZ], 3 }, + { 0, 0, 0 } + }; + + for (i=0; reloc_info[i].stride; i++) { + size_t *rel = reloc_info[i].rel; + size_t rel_size = reloc_info[i].size; + size_t stride = reloc_info[i].stride; + for (; rel_size; rel+=stride, rel_size-=stride*sizeof(size_t)) { + if (!IS_RELATIVE(rel[1])) continue; + size_t *rel_addr = (void *)(base + rel[0]); + size_t addend = stride==3 ? rel[2] : *rel_addr; + *rel_addr = (size_t)base + addend; + } + } + + const char *strings = (void *)(base + dyn[DT_STRTAB]); + const Sym *syms = (void *)(base + dyn[DT_SYMTAB]); + + /* Call dynamic linker stage-2, __dls2 */ + for (i=0; ;i++) { + const char *s = strings + syms[i].st_name; + if (s[0]=='_' && s[1]=='_' && s[2]=='d' + && s[3]=='l' && s[4]=='s' && s[5]=='2' && !s[6]) + break; + } + ((stage2_func)(base + syms[i].st_value))(base); + + /* Call dynamic linker stage-3, __dls3 */ + for (i=0; ;i++) { + const char *s = strings + syms[i].st_name; + if (s[0]=='_' && s[1]=='_' && s[2]=='d' + && s[3]=='l' && s[4]=='s' && s[5]=='3' && !s[6]) + break; + } + ((stage3_func)(base + syms[i].st_value))(sp); +} + +#endif |