From 7f5c8f9721216c496b54eff209b96f6f5f6bd189 Mon Sep 17 00:00:00 2001 From: John Marino Date: Sat, 17 Mar 2012 19:52:17 +0100 Subject: [PATCH] rtld: Add main object initialization and finalization Since DragonFly's inception, crt1 has called _init and _fini functions of the binary rather than leaving this task to the runtime linker. Likely this was done in order to use the same crt code for both statically and dynamically linked binaries. When FreeBSD imported DragonFly's preinit, init, and fini array handling code, they moved the _init and _fini calls to rtld. In order for rtld to maintain compatiblity with binaries created with crt code that call these functions, rtld looks for an ELF note that indicates the binary was built with a crt that does not call _init and _fini. This commit imports this capability as well as synchronizes some of the changes FreeBSD made to the DragonFly code. Many of the differences weren't necessary, but some represented improvements. In any case, it benefits both operating systems to minimize the differences between the runtime linkers in order to continue to collaborate and share new features. Taken from: FreeBSD SVN 232831 (2012-03-11) One key difference between the DragonFly and FreeBSD implementations is that FreeBSD will not properly execute a binary that requires initialization and/or finalization if the PT_NOTE program header is intentionally omitted using a custom linker script and the PHDRS directive. In the same case, DragonFly will look to see if the main binary contains a gnu hash dynamic tag. If it does, it assumes the binary was built with the new crt, which is a very good assumption. Since gnu hash was inserted by default into binaries only a week ago, there's only a window of few days where this isn't true, and PT_NOTE-free binaries are very rare. --- libexec/rtld-elf/Makefile | 1 + libexec/rtld-elf/i386/rtld_machdep.h | 4 +- libexec/rtld-elf/map_object.c | 13 +++ libexec/rtld-elf/rtld.c | 109 ++++++++++++++++++------- libexec/rtld-elf/rtld.h | 10 ++- libexec/rtld-elf/x86_64/rtld_machdep.h | 4 +- 6 files changed, 108 insertions(+), 33 deletions(-) diff --git a/libexec/rtld-elf/Makefile b/libexec/rtld-elf/Makefile index 3527ad1c0b..2017433f97 100644 --- a/libexec/rtld-elf/Makefile +++ b/libexec/rtld-elf/Makefile @@ -13,6 +13,7 @@ WARNS?= 2 CFLAGS+= -DIN_RTLD CFLAGS+= -I${.CURDIR}/${MACHINE_ARCH} -I${.CURDIR} -D__thread= +CFLAGS+= -I${.CURDIR}/../../lib/csu/common LDFLAGS+= -nostdlib -e .rtld_start -Wl,--no-undefined INSTALLFLAGS= -C -b .ifndef NOFSCHG diff --git a/libexec/rtld-elf/i386/rtld_machdep.h b/libexec/rtld-elf/i386/rtld_machdep.h index 987b1102d4..d84251922c 100644 --- a/libexec/rtld-elf/i386/rtld_machdep.h +++ b/libexec/rtld-elf/i386/rtld_machdep.h @@ -60,8 +60,8 @@ reloc_jmpslot(Elf_Addr *where, Elf_Addr target, #define call_initfini_pointer(obj, target) \ (((InitFunc)(target))()) -#define call_array_pointer(target, argc, argv, env) \ - (((InitArrayFunc)(target))(argc, argv, env)) +#define call_init_pointer(obj, target) \ + (((InitArrFunc)(target))(main_argc, main_argv, environ)) #define round(size, align) \ (((size) + (align) - 1) & ~((align) - 1)) diff --git a/libexec/rtld-elf/map_object.c b/libexec/rtld-elf/map_object.c index ffb816a346..215ae0c58f 100644 --- a/libexec/rtld-elf/map_object.c +++ b/libexec/rtld-elf/map_object.c @@ -85,6 +85,8 @@ map_object(int fd, const char *path, const struct stat *sb) Elf_Word stack_flags; Elf_Addr relro_page; size_t relro_size; + Elf_Addr note_start; + Elf_Addr note_end; hdr = get_elf_header(fd, path); if (hdr == NULL) @@ -103,6 +105,8 @@ map_object(int fd, const char *path, const struct stat *sb) phdr_vaddr = 0; relro_page = 0; relro_size = 0; + note_start = 0; + note_end = 0; segs = alloca(sizeof(segs[0]) * hdr->e_phnum); stack_flags = RTLD_DEFAULT_STACK_PF_EXEC | PF_R | PF_W; while (phdr < phlimit) { @@ -142,6 +146,15 @@ map_object(int fd, const char *path, const struct stat *sb) relro_page = phdr->p_vaddr; relro_size = phdr->p_memsz; break; + + case PT_NOTE: + if (phdr->p_offset > PAGE_SIZE || + phdr->p_offset + phdr->p_filesz > PAGE_SIZE) + break; + note_start = (Elf_Addr)(char *)hdr + phdr->p_offset; + note_end = note_start + phdr->p_filesz; + digest_notes(obj, note_start, note_end); + break; } ++phdr; diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c index 9a7723cb32..fa45cae028 100644 --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -65,6 +65,7 @@ #include "rtld.h" #include "libmap.h" #include "rtld_printf.h" +#include "notes.h" #define PATH_RTLD "/usr/libexec/ld-elf.so.2" #define LD_ARY_CACHE 16 @@ -255,11 +256,10 @@ char *__progname; char **environ; /* - * Globals passed as arguments to .init_array and .preinit_array functions + * Used to pass argc, argv to init functions. */ - -int glac; -char **glav; +int main_argc; +char **main_argv; /* * Globals to control TLS allocation. @@ -409,8 +409,8 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) __progname = obj_rtld.path; argv0 = argv[0] != NULL ? argv[0] : "(null)"; environ = env; - glac = argc; - glav = argv; + main_argc = argc; + main_argv = argv; trust = !issetugid(); @@ -566,9 +566,6 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) obj_tail = &obj_main->next; obj_count++; obj_loads++; - /* Make sure we don't call the main program's init and fini functions. */ - obj_main->init = obj_main->fini = (Elf_Addr)NULL; - obj_main->init_array = obj_main->fini_array = (Elf_Addr)NULL; /* Initialize a fake symbol for resolving undefined weak references. */ sym_zero.st_info = ELF_ST_INFO(STB_GLOBAL, STT_NOTYPE); @@ -705,8 +702,18 @@ _rtld_call_init(void) RtldLockState lockstate; Obj_Entry *obj; - preinitialize_main_object(); wlock_acquire(rtld_bind_lock, &lockstate); + if (obj_main->crt_no_init) { + preinitialize_main_object(); + } + else { + /* + * Make sure we don't call the main program's init and fini functions + * for binaries linked with old crt1 which calls _init itself. + */ + obj_main->init = obj_main->fini = (Elf_Addr)NULL; + obj_main->init_array = obj_main->fini_array = (Elf_Addr)NULL; + } objlist_call_init(&initlist, &lockstate); objlist_clear(&initlist); dbg("loading filtees"); @@ -1266,6 +1273,7 @@ digest_phdr(const Elf_Phdr *phdr, int phnum, caddr_t entry, const char *path) Obj_Entry *obj; const Elf_Phdr *phlimit = phdr + phnum; const Elf_Phdr *ph; + Elf_Addr note_start, note_end; int nsegs = 0; obj = obj_new(); @@ -1321,6 +1329,12 @@ digest_phdr(const Elf_Phdr *phdr, int phnum, caddr_t entry, const char *path) obj->relro_page = obj->relocbase + trunc_page(ph->p_vaddr); obj->relro_size = round_page(ph->p_memsz); break; + + case PT_NOTE: + note_start = (Elf_Addr)obj->relocbase + ph->p_vaddr; + note_end = note_start + ph->p_filesz; + digest_notes(obj, note_start, note_end); + break; } } if (nsegs < 1) { @@ -1332,6 +1346,42 @@ digest_phdr(const Elf_Phdr *phdr, int phnum, caddr_t entry, const char *path) return obj; } +void +digest_notes(Obj_Entry *obj, Elf_Addr note_start, Elf_Addr note_end) +{ + const Elf_Note *note; + const char *note_name; + uintptr_t p; + + for (note = (const Elf_Note *)note_start; (Elf_Addr)note < note_end; + note = (const Elf_Note *)((const char *)(note + 1) + + roundup2(note->n_namesz, sizeof(Elf32_Addr)) + + roundup2(note->n_descsz, sizeof(Elf32_Addr)))) { + if (note->n_namesz != sizeof(NOTE_VENDOR) || + note->n_descsz != sizeof(int32_t)) + continue; + if (note->n_type != ABI_NOTETYPE && note->n_type != CRT_NOINIT_NOTETYPE) + continue; + note_name = (const char *)(note + 1); + if (strncmp(NOTE_VENDOR, note_name, sizeof(NOTE_VENDOR)) != 0) + continue; + switch (note->n_type) { + case ABI_NOTETYPE: + /* FreeBSD osrel note */ + p = (uintptr_t)(note + 1); + p += roundup2(note->n_namesz, sizeof(Elf32_Addr)); + obj->osrel = *(const int32_t *)(p); + dbg("note osrel %d", obj->osrel); + break; + case CRT_NOINIT_NOTETYPE: + /* FreeBSD 'crt does not call init' note */ + obj->crt_no_init = true; + dbg("note crt_no_init"); + break; + } + } +} + static Obj_Entry * dlcheck(void *handle) { @@ -1717,7 +1767,8 @@ initlist_add_objects(Obj_Entry *obj, Obj_Entry **tail, Objlist *list) initlist_add_neededs(obj->needed, list); /* Add the object to the init list. */ - if (obj->init != (Elf_Addr)NULL || obj->init_array != (Elf_Addr)NULL) + if (obj->preinit_array != (Elf_Addr)NULL || obj->init != (Elf_Addr)NULL || + obj->init_array != (Elf_Addr)NULL) objlist_push_tail(list, obj); /* Add the object to the global fini list in the reverse order. */ @@ -2052,10 +2103,10 @@ objlist_call_fini(Objlist *list, Obj_Entry *root, RtldLockState *lockstate) * values of 0 or 1, but they need to be ignored. */ fini_addr = (Elf_Addr *)elm->obj->fini_array; - if (fini_addr != (Elf_Addr)NULL) { + if (fini_addr != NULL && elm->obj->fini_array_num > 0) { for (index = elm->obj->fini_array_num - 1; index >= 0; index--) { if (fini_addr[index] != 0 && fini_addr[index] != 1) { - dbg("DSO Array: calling fini function for %s at %p", + dbg("calling fini array function for %s at %p", elm->obj->path, (void *)fini_addr[index]); LD_UTRACE(UTRACE_FINI_CALL, elm->obj, (void *)fini_addr[index], 0, 0, elm->obj->path); @@ -2093,21 +2144,22 @@ objlist_call_fini(Objlist *list, Obj_Entry *root, RtldLockState *lockstate) static void preinitialize_main_object (void) { - Elf_Addr *init_addr; + Elf_Addr *preinit_addr; int index; - init_addr = (Elf_Addr *)obj_main->preinit_array; - if (init_addr == (Elf_Addr)NULL) + preinit_addr = (Elf_Addr *)obj_main->preinit_array; + if (preinit_addr == NULL) return; - for (index = 0; index < obj_main->preinit_array_num; index++) - if (init_addr[index] != 0 && init_addr[index] != 1) { - dbg("Calling preinit array function for %s at %p", - obj_main->path, (void *)init_addr[index]); - LD_UTRACE(UTRACE_INIT_CALL, obj_main, (void *)init_addr[index], + for (index = 0; index < obj_main->preinit_array_num; index++) { + if (preinit_addr[index] != 0 && preinit_addr[index] != 1) { + dbg("calling preinit function for %s at %p", obj_main->path, + (void *)preinit_addr[index]); + LD_UTRACE(UTRACE_INIT_CALL, obj_main, (void *)preinit_addr[index], 0, 0, obj_main->path); - call_array_pointer(init_addr[index], glac, glav, environ); + call_init_pointer(obj_main, preinit_addr[index]); } + } } /* @@ -2156,22 +2208,23 @@ objlist_call_init(Objlist *list, RtldLockState *lockstate) * need to be ignored. */ if (elm->obj->init != (Elf_Addr)NULL) { - dbg("DSO: calling init function for %s at %p", elm->obj->path, + dbg("calling init function for %s at %p", elm->obj->path, (void *)elm->obj->init); LD_UTRACE(UTRACE_INIT_CALL, elm->obj, (void *)elm->obj->init, 0, 0, elm->obj->path); call_initfini_pointer(elm->obj, elm->obj->init); } init_addr = (Elf_Addr *)elm->obj->init_array; - if (init_addr != (Elf_Addr)NULL) { - for (index = 0; index < elm->obj->init_array_num; index++) + if (init_addr != NULL) { + for (index = 0; index < elm->obj->init_array_num; index++) { if (init_addr[index] != 0 && init_addr[index] != 1) { - dbg("DSO Array: calling init function for %s at %p", - elm->obj->path, (void *)init_addr[index]); + dbg("calling init array function for %s at %p", elm->obj->path, + (void *)init_addr[index]); LD_UTRACE(UTRACE_INIT_CALL, elm->obj, (void *)init_addr[index], 0, 0, elm->obj->path); - call_array_pointer(init_addr[index], glac, glav, environ); + call_init_pointer(elm->obj, init_addr[index]); } + } } wlock_acquire(rtld_bind_lock, lockstate); } diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h index b58cea72e2..680c748e2a 100644 --- a/libexec/rtld-elf/rtld.h +++ b/libexec/rtld-elf/rtld.h @@ -59,6 +59,10 @@ extern size_t tls_static_space; extern int tls_dtv_generation; extern int tls_max_index; +extern int main_argc; +extern char **main_argv; +extern char **environ; + struct stat; struct Struct_Obj_Entry; @@ -72,7 +76,7 @@ typedef STAILQ_HEAD(Struct_Objlist, Struct_Objlist_Entry) Objlist; /* Types of init and fini functions */ typedef void (*InitFunc)(void); -typedef void (*InitArrayFunc)(int, char **, char **); +typedef void (*InitArrFunc)(int, char **, char **); /* Lists of shared object dependencies */ typedef struct Struct_Needed_Entry { @@ -218,6 +222,8 @@ typedef struct Struct_Obj_Entry { int init_array_num; /* Number of entries in init_array */ int fini_array_num; /* Number of entries in fini_array */ + int32_t osrel; /* OSREL note value */ + bool mainprog : 1; /* True if this is the main program */ bool rtld : 1; /* True if this is the dynamic linker */ bool textrel : 1; /* True if there are relocations to text seg */ @@ -239,6 +245,7 @@ typedef struct Struct_Obj_Entry { bool filtees_loaded : 1; /* Filtees loaded */ bool irelative : 1; /* Object has R_MACHDEP_IRELATIVE relocs */ bool gnu_ifunc : 1; /* Object has references to STT_GNU_IFUNC */ + bool crt_no_init : 1; /* Object's crt does not call _init/_fini */ bool valid_hash_sysv : 1; /* A valid System V hash hash tag is available */ bool valid_hash_gnu : 1; /* A valid GNU hash tag is available */ @@ -327,6 +334,7 @@ const Elf_Sym *find_symdef(unsigned long, const Obj_Entry *, const Obj_Entry **, int, SymCache *, struct Struct_RtldLockState *); void init_pltgot(Obj_Entry *); void lockdflt_init(void); +void digest_notes(Obj_Entry *, Elf_Addr, Elf_Addr); void obj_free(Obj_Entry *); Obj_Entry *obj_new(void); void _rtld_bind_start(void); diff --git a/libexec/rtld-elf/x86_64/rtld_machdep.h b/libexec/rtld-elf/x86_64/rtld_machdep.h index d1598849dd..05c0e7abc8 100644 --- a/libexec/rtld-elf/x86_64/rtld_machdep.h +++ b/libexec/rtld-elf/x86_64/rtld_machdep.h @@ -60,8 +60,8 @@ reloc_jmpslot(Elf_Addr *where, Elf_Addr target, #define call_initfini_pointer(obj, target) \ (((InitFunc)(target))()) -#define call_array_pointer(target, argc, argv, env) \ - (((InitArrayFunc)(target))(argc, argv, env)) +#define call_init_pointer(obj, target) \ + (((InitArrFunc)(target))(main_argc, main_argv, environ)) #define round(size, align) \ (((size) + (align) - 1) & ~((align) - 1)) -- 2.41.0