diff --git a/lib/csu/Makefile.inc b/lib/csu/Makefile.inc index 078cb9f..11ddc66 100644 --- a/lib/csu/Makefile.inc +++ b/lib/csu/Makefile.inc @@ -2,4 +2,6 @@ SSP_CFLAGS= +SED_FIX_NOTE = -i "" -e '/\.note\.tag/s/progbits/note/' + .include "../Makefile.inc" diff --git a/lib/csu/amd64/Makefile b/lib/csu/amd64/Makefile index aac0c64..b4a60a1 100644 --- a/lib/csu/amd64/Makefile +++ b/lib/csu/amd64/Makefile @@ -19,21 +19,21 @@ CLEANFILES+= crt1.s gcrt1.s Scrt1.s crt1.s: crt1.c ${CC} ${CFLAGS} -S -o ${.TARGET} ${.CURDIR}/crt1.c - sed -i "" -e '/\.note\.ABI-tag/s/progbits/note/' ${.TARGET} + sed ${SED_FIX_NOTE} ${.TARGET} crt1.o: crt1.s ${CC} ${CFLAGS} -c -o ${.TARGET} crt1.s gcrt1.s: crt1.c ${CC} ${CFLAGS} -DGCRT -S -o ${.TARGET} ${.CURDIR}/crt1.c - sed -i "" -e '/\.note\.ABI-tag/s/progbits/note/' ${.TARGET} + sed ${SED_FIX_NOTE} ${.TARGET} gcrt1.o: gcrt1.s ${CC} ${CFLAGS} -c -o ${.TARGET} gcrt1.s Scrt1.s: crt1.c ${CC} ${CFLAGS} -fPIC -DPIC -S -o ${.TARGET} ${.CURDIR}/crt1.c - sed -i "" -e '/\.note\.ABI-tag/s/progbits/note/' ${.TARGET} + sed ${SED_FIX_NOTE} ${.TARGET} Scrt1.o: Scrt1.s ${CC} ${CFLAGS} -c -o ${.TARGET} Scrt1.s diff --git a/lib/csu/amd64/crt1.c b/lib/csu/amd64/crt1.c index 998477a..7f237f9 100644 --- a/lib/csu/amd64/crt1.c +++ b/lib/csu/amd64/crt1.c @@ -37,14 +37,10 @@ __FBSDID("$FreeBSD$"); #include "libc_private.h" #include "crtbrand.c" - -extern int _DYNAMIC; -#pragma weak _DYNAMIC +#include "ignore_init.c" typedef void (*fptr)(void); -extern void _fini(void); -extern void _init(void); extern int main(int, char **, char **); #ifdef GCRT @@ -86,12 +82,10 @@ _start(char **ap, void (*cleanup)(void)) #ifdef GCRT atexit(_mcleanup); -#endif - atexit(_fini); -#ifdef GCRT monstartup(&eprol, &etext); __asm__("eprol:"); #endif - _init(); - exit( main(argc, argv, env) ); + + handle_static_init(argc, argv, env); + exit(main(argc, argv, env)); } diff --git a/lib/csu/arm/Makefile b/lib/csu/arm/Makefile index 095a9ad..1e1d1fa 100644 --- a/lib/csu/arm/Makefile +++ b/lib/csu/arm/Makefile @@ -18,21 +18,21 @@ CLEANFILES+= crt1.s gcrt1.s Scrt1.s crt1.s: crt1.c ${CC} ${CFLAGS} -S -o ${.TARGET} ${.CURDIR}/crt1.c - sed -i "" -e '/\.note\.ABI-tag/s/progbits/note/' ${.TARGET} + sed ${SED_FIX_NOTE} ${.TARGET} crt1.o: crt1.s ${CC} ${CFLAGS} -c -o ${.TARGET} crt1.s gcrt1.s: crt1.c ${CC} ${CFLAGS} -DGCRT -S -o ${.TARGET} ${.CURDIR}/crt1.c - sed -i "" -e '/\.note\.ABI-tag/s/progbits/note/' ${.TARGET} + sed ${SED_FIX_NOTE} ${.TARGET} gcrt1.o: gcrt1.s ${CC} ${CFLAGS} -c -o ${.TARGET} gcrt1.s Scrt1.s: crt1.c ${CC} ${CFLAGS} -fPIC -DPIC -S -o ${.TARGET} ${.CURDIR}/crt1.c - sed -i "" -e '/\.note\.ABI-tag/s/progbits/note/' ${.TARGET} + sed ${SED_FIX_NOTE} ${.TARGET} Scrt1.o: Scrt1.s ${CC} ${CFLAGS} -c -o ${.TARGET} Scrt1.s diff --git a/lib/csu/arm/crt1.c b/lib/csu/arm/crt1.c index f2d4dbf..54f1777 100644 --- a/lib/csu/arm/crt1.c +++ b/lib/csu/arm/crt1.c @@ -52,15 +52,11 @@ __FBSDID("$FreeBSD$"); #include "libc_private.h" #include "crtbrand.c" +#include "ignore_init.c" struct Struct_Obj_Entry; struct ps_strings; -extern int _DYNAMIC; -#pragma weak _DYNAMIC - -extern void _fini(void); -extern void _init(void); extern int main(int, char **, char **); extern void _start(int, char **, char **, const struct Struct_Obj_Entry *, void (*)(void), struct ps_strings *); @@ -124,13 +120,10 @@ __start(int argc, char **argv, char **env, struct ps_strings *ps_strings, _init_tls(); #ifdef GCRT atexit(_mcleanup); -#endif - atexit(_fini); -#ifdef GCRT monstartup(&eprol, &etext); #endif - _init(); - exit( main(argc, argv, env) ); + handle_static_init(argc, argv, env); + exit(main(argc, argv, env)); } #ifdef GCRT diff --git a/lib/csu/common/crtbrand.c b/lib/csu/common/crtbrand.c index 444d7f1..04df686 100644 --- a/lib/csu/common/crtbrand.c +++ b/lib/csu/common/crtbrand.c @@ -27,10 +27,7 @@ __FBSDID("$FreeBSD$"); #include - -#define ABI_VENDOR "FreeBSD" -#define ABI_SECTION ".note.ABI-tag" -#define ABI_NOTETYPE 1 +#include "notes.h" /* * Special ".note" entry specifying the ABI version. See @@ -55,15 +52,15 @@ __FBSDID("$FreeBSD$"); * These steps are done in the invididual Makefiles for each applicable arch. */ static const struct { - int32_t namesz; - int32_t descsz; - int32_t type; - char name[sizeof ABI_VENDOR]; - int32_t desc; -} abitag __attribute__ ((section (ABI_SECTION), aligned(4))) __used = { - sizeof ABI_VENDOR, - sizeof(int32_t), - ABI_NOTETYPE, - ABI_VENDOR, - __FreeBSD_version + int32_t namesz; + int32_t descsz; + int32_t type; + char name[sizeof(NOTE_FREEBSD_VENDOR)]; + int32_t desc; +} abitag __attribute__ ((section (NOTE_SECTION), aligned(4))) __used = { + .namesz = sizeof(NOTE_FREEBSD_VENDOR), + .descsz = sizeof(int32_t), + .type = ABI_NOTETYPE, + .name = NOTE_FREEBSD_VENDOR, + .desc = __FreeBSD_version }; diff --git a/lib/csu/common/ignore_init.c b/lib/csu/common/ignore_init.c new file mode 100644 index 0000000..68f6594 --- /dev/null +++ b/lib/csu/common/ignore_init.c @@ -0,0 +1,98 @@ +/*- + * Copyright 2012 Konstantin Belousov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "notes.h" + +extern void (*__preinit_array_start[])(int, char **, char **) __hidden; +extern void (*__preinit_array_end[])(int, char **, char **) __hidden; +extern void (*__init_array_start[])(int, char **, char **) __hidden; +extern void (*__init_array_end[])(int, char **, char **) __hidden; +extern void (*__fini_array_start[])(void) __hidden; +extern void (*__fini_array_end[])(void) __hidden; +extern void _fini(void) __hidden; +extern void _init(void) __hidden; + +extern int _DYNAMIC; +#pragma weak _DYNAMIC + +static void +finalizer(void) +{ + void (*fn)(void); + size_t array_size, n; + + array_size = __fini_array_end - __fini_array_start; + for (n = array_size; n > 0; n--) { + fn = __fini_array_start[n - 1]; + if ((uintptr_t)fn != 0 && (uintptr_t)fn != 1) + (fn)(); + } + _fini(); +} + +static inline void +handle_static_init(int argc, char **argv, char **env) +{ + void (*fn)(int, char **, char **); + size_t array_size, n; + + if (&_DYNAMIC != NULL) + return; + + atexit(finalizer); + + array_size = __preinit_array_end - __preinit_array_start; + for (n = 0; n < array_size; n++) { + fn = __preinit_array_start[n]; + if ((uintptr_t)fn != 0 && (uintptr_t)fn != 1) + fn(argc, argv, env); + } + _init(); + array_size = __init_array_end - __init_array_start; + for (n = 0; n < array_size; n++) { + fn = __init_array_start[n]; + if ((uintptr_t)fn != 0 && (uintptr_t)fn != 1) + fn(argc, argv, env); + } +} + + +static const struct { + int32_t namesz; + int32_t descsz; + int32_t type; + char name[sizeof(NOTE_FREEBSD_VENDOR)]; + uint32_t desc; +} crt_noinit_tag __attribute__ ((section (NOTE_SECTION), + aligned(4))) __used = { + .namesz = sizeof(NOTE_FREEBSD_VENDOR), + .descsz = sizeof(uint32_t), + .type = CRT_NOINIT_NOTETYPE, + .name = NOTE_FREEBSD_VENDOR, + .desc = 0 +}; diff --git a/lib/csu/common/notes.h b/lib/csu/common/notes.h new file mode 100644 index 0000000..fcc2b66 --- /dev/null +++ b/lib/csu/common/notes.h @@ -0,0 +1,38 @@ +/*- + * Copyright 2012 Konstantin Belousov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef CSU_COMMON_NOTES_H +#define CSU_COMMON_NOTES_H + +#define NOTE_FREEBSD_VENDOR "FreeBSD" + +#define NOTE_SECTION ".note.tag" + +#define ABI_NOTETYPE 1 +#define CRT_NOINIT_NOTETYPE 2 + +#endif diff --git a/lib/csu/i386-elf/Makefile b/lib/csu/i386-elf/Makefile index b100d7a..aef9a5c 100644 --- a/lib/csu/i386-elf/Makefile +++ b/lib/csu/i386-elf/Makefile @@ -18,7 +18,7 @@ CLEANFILES+= crt1_c.s gcrt1_c.s Scrt1_c.s gcrt1_c.s: crt1_c.c ${CC} ${CFLAGS} -DGCRT -S -o ${.TARGET} ${.CURDIR}/crt1_c.c - sed -i "" -e '/\.note\.ABI-tag/s/progbits/note/' ${.TARGET} + sed ${SED_FIX_NOTE} ${.TARGET} gcrt1_c.o: gcrt1_c.s ${CC} ${CFLAGS} -c -o ${.TARGET} gcrt1_c.s @@ -28,7 +28,7 @@ gcrt1.o: gcrt1_c.o crt1_s.o crt1_c.s: crt1_c.c ${CC} ${CFLAGS} -S -o ${.TARGET} ${.CURDIR}/crt1_c.c - sed -i "" -e '/\.note\.ABI-tag/s/progbits/note/' ${.TARGET} + sed ${SED_FIX_NOTE} ${.TARGET} crt1_c.o: crt1_c.s ${CC} ${CFLAGS} -c -o ${.TARGET} crt1_c.s @@ -39,7 +39,7 @@ crt1.o: crt1_c.o crt1_s.o Scrt1_c.s: crt1_c.c ${CC} ${CFLAGS} -fPIC -DPIC -S -o ${.TARGET} ${.CURDIR}/crt1_c.c - sed -i "" -e '/\.note\.ABI-tag/s/progbits/note/' ${.TARGET} + sed ${SED_FIX_NOTE} ${.TARGET} Scrt1_c.o: Scrt1_c.s ${CC} ${CFLAGS} -c -o ${.TARGET} Scrt1_c.s diff --git a/lib/csu/i386-elf/crt1_c.c b/lib/csu/i386-elf/crt1_c.c index 1eadc7c..8e06b5a 100644 --- a/lib/csu/i386-elf/crt1_c.c +++ b/lib/csu/i386-elf/crt1_c.c @@ -39,14 +39,10 @@ __FBSDID("$FreeBSD$"); #include "libc_private.h" #include "crtbrand.c" - -extern int _DYNAMIC; -#pragma weak _DYNAMIC +#include "ignore_init.c" typedef void (*fptr)(void); -extern void _fini(void); -extern void _init(void); extern int main(int, char **, char **); extern void _start(char *, ...); @@ -85,14 +81,12 @@ _start1(fptr cleanup, int argc, char *argv[]) #ifdef GCRT atexit(_mcleanup); -#endif - atexit(_fini); -#ifdef GCRT monstartup(&eprol, &etext); __asm__("eprol:"); #endif - _init(); - exit( main(argc, argv, env) ); + + handle_static_init(argc, argv, env); + exit(main(argc, argv, env)); } __asm(".hidden _start1"); diff --git a/libexec/rtld-elf/Makefile b/libexec/rtld-elf/Makefile index 2e7925e..e00eee1 100644 --- a/libexec/rtld-elf/Makefile +++ b/libexec/rtld-elf/Makefile @@ -10,6 +10,7 @@ SRCS= rtld_start.S \ MAN= rtld.1 CSTD?= gnu99 CFLAGS+= -Wall -DFREEBSD_ELF -DIN_RTLD +CFLAGS+= -I${.CURDIR}/../../lib/csu/common .if exists(${.CURDIR}/${MACHINE_ARCH}) RTLD_ARCH= ${MACHINE_ARCH} .else diff --git a/libexec/rtld-elf/amd64/rtld_machdep.h b/libexec/rtld-elf/amd64/rtld_machdep.h index 48d225f..7b5d4d2 100644 --- a/libexec/rtld-elf/amd64/rtld_machdep.h +++ b/libexec/rtld-elf/amd64/rtld_machdep.h @@ -58,6 +58,9 @@ reloc_jmpslot(Elf_Addr *where, Elf_Addr target, #define call_initfini_pointer(obj, target) \ (((InitFunc)(target))()) +#define call_init_pointer(obj, target) \ + (((InitArrFunc)(target))(main_argc, main_argv, environ)) + #define round(size, align) \ (((size) + (align) - 1) & ~((align) - 1)) #define calculate_first_tls_offset(size, align) \ diff --git a/libexec/rtld-elf/i386/rtld_machdep.h b/libexec/rtld-elf/i386/rtld_machdep.h index 7d121e4..dfbe2e1 100644 --- a/libexec/rtld-elf/i386/rtld_machdep.h +++ b/libexec/rtld-elf/i386/rtld_machdep.h @@ -58,6 +58,9 @@ reloc_jmpslot(Elf_Addr *where, Elf_Addr target, #define call_initfini_pointer(obj, target) \ (((InitFunc)(target))()) +#define call_init_pointer(obj, target) \ + (((InitArrFunc)(target))(main_argc, main_argv, environ)) + #define round(size, align) \ (((size) + (align) - 1) & ~((align) - 1)) #define calculate_first_tls_offset(size, align) \ diff --git a/libexec/rtld-elf/map_object.c b/libexec/rtld-elf/map_object.c index 6a13692..d240575 100644 --- a/libexec/rtld-elf/map_object.c +++ b/libexec/rtld-elf/map_object.c @@ -86,6 +86,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) @@ -104,6 +106,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) { @@ -143,6 +147,12 @@ 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: + note_start = (Elf_Addr)obj->relocbase + 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 568d10b..460d081 100644 --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -61,6 +61,7 @@ #include "libmap.h" #include "rtld_tls.h" #include "rtld_printf.h" +#include "notes.h" #ifndef COMPAT_32BIT #define PATH_RTLD "/libexec/ld-elf.so.1" @@ -139,6 +140,7 @@ static void ref_dag(Obj_Entry *); static int origin_subst_one(char **, const char *, const char *, const char *, char *); static char *origin_subst(const char *, const char *); +static void preinit_main(void); static int rtld_verify_versions(const Objlist *); static int rtld_verify_object_versions(Obj_Entry *); static void object_add_name(Obj_Entry *, const char *); @@ -205,6 +207,12 @@ char *__progname; char **environ; /* + * Used to pass argc, argv to init functions. + */ +int main_argc; +char **main_argv; + +/* * Globals to control TLS allocation. */ size_t tls_last_offset; /* Static TLS offset of last module */ @@ -335,6 +343,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; + main_argc = argc; + main_argv = argv; trust = !issetugid(); @@ -458,8 +468,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; /* Initialize a fake symbol for resolving undefined weak references. */ sym_zero.st_info = ELF_ST_INFO(STB_GLOBAL, STT_NOTYPE); @@ -551,7 +559,20 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) ld_bind_now != NULL && *ld_bind_now != '\0', NULL) == -1) die(); + if (!obj_main->crt_no_init) { + /* + * 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->preinit_array = obj_main->init_array = + obj_main->fini_array = (Elf_Addr)NULL; + } + wlock_acquire(rtld_bind_lock, &lockstate); + if (obj_main->crt_no_init) + preinit_main(); objlist_call_init(&initlist, &lockstate); objlist_clear(&initlist); dbg("loading filtees"); @@ -936,10 +957,34 @@ digest_dynamic1(Obj_Entry *obj, int early, const Elf_Dyn **dyn_rpath, obj->init = (Elf_Addr) (obj->relocbase + dynp->d_un.d_ptr); break; + case DT_PREINIT_ARRAY: + obj->preinit_array = (Elf_Addr)(obj->relocbase + dynp->d_un.d_ptr); + break; + + case DT_PREINIT_ARRAYSZ: + obj->preinit_array_num = dynp->d_un.d_val / sizeof(Elf_Addr); + break; + + case DT_INIT_ARRAY: + obj->init_array = (Elf_Addr)(obj->relocbase + dynp->d_un.d_ptr); + break; + + case DT_INIT_ARRAYSZ: + obj->init_array_num = dynp->d_un.d_val / sizeof(Elf_Addr); + break; + case DT_FINI: obj->fini = (Elf_Addr) (obj->relocbase + dynp->d_un.d_ptr); break; + case DT_FINI_ARRAY: + obj->fini_array = (Elf_Addr)(obj->relocbase + dynp->d_un.d_ptr); + break; + + case DT_FINI_ARRAYSZ: + obj->fini_array_num = dynp->d_un.d_val / sizeof(Elf_Addr); + break; + /* * Don't process DT_DEBUG on MIPS as the dynamic section * is mapped read-only. DT_MIPS_RLD_MAP is used instead. @@ -1065,6 +1110,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(); @@ -1120,6 +1166,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) { @@ -1131,6 +1183,48 @@ 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;) { + if (note->n_namesz == sizeof(NOTE_FREEBSD_VENDOR) && + note->n_descsz == sizeof(int32_t) && + note->n_type == ABI_NOTETYPE) { + note_name = (const char *)(note + 1); + if (strncmp(NOTE_FREEBSD_VENDOR, note_name, + sizeof(NOTE_FREEBSD_VENDOR)) == 0) { + /* 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); + goto nextnote; + } + } + if (note->n_namesz == sizeof(NOTE_FREEBSD_VENDOR) && + note->n_descsz == sizeof(uint32_t) && + note->n_type == CRT_NOINIT_NOTETYPE) { + note_name = (const char *)(note + 1); + if (strncmp(NOTE_FREEBSD_VENDOR, note_name, + sizeof(NOTE_FREEBSD_VENDOR)) == 0) { + /* FreeBSD 'crt does not call init' note */ + obj->crt_no_init = true; + dbg("note crt_no_init"); + goto nextnote; + } + } +nextnote: + note = (const Elf_Note *)((const char *)(note + 1) + + roundup2(note->n_namesz, sizeof(Elf32_Addr)) + + roundup2(note->n_descsz, sizeof(Elf32_Addr))); + } +} + static Obj_Entry * dlcheck(void *handle) { @@ -1504,11 +1598,13 @@ 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) + 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. */ - if (obj->fini != (Elf_Addr)NULL && !obj->on_fini_list) { + if ((obj->fini != (Elf_Addr)NULL || obj->fini_array != (Elf_Addr)NULL) + && !obj->on_fini_list) { objlist_push_head(&list_fini, obj); obj->on_fini_list = true; } @@ -1796,6 +1892,27 @@ obj_from_addr(const void *addr) return NULL; } +static void +preinit_main(void) +{ + Elf_Addr *preinit_addr; + int index; + + preinit_addr = (Elf_Addr *)obj_main->preinit_array; + if (preinit_addr == (Elf_Addr)NULL) + return; + + 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_init_pointer(elm->obj, preinit_addr[index]); + } + } +} + /* * Call the finalization functions for each of the objects in "list" * belonging to the DAG of "root" and referenced once. If NULL "root" @@ -1808,6 +1925,8 @@ objlist_call_fini(Objlist *list, Obj_Entry *root, RtldLockState *lockstate) { Objlist_Entry *elm; char *saved_msg; + Elf_Addr *fini_addr; + int index; assert(root == NULL || root->refcount == 1); @@ -1821,10 +1940,6 @@ objlist_call_fini(Objlist *list, Obj_Entry *root, RtldLockState *lockstate) if (root != NULL && (elm->obj->refcount != 1 || objlist_find(&root->dagmembers, elm->obj) == NULL)) continue; - dbg("calling fini function for %s at %p", elm->obj->path, - (void *)elm->obj->fini); - LD_UTRACE(UTRACE_FINI_CALL, elm->obj, (void *)elm->obj->fini, 0, 0, - elm->obj->path); /* Remove object from fini list to prevent recursive invocation. */ STAILQ_REMOVE(list, elm, Struct_Objlist_Entry, link); /* @@ -1835,7 +1950,31 @@ objlist_call_fini(Objlist *list, Obj_Entry *root, RtldLockState *lockstate) * called. */ lock_release(rtld_bind_lock, lockstate); - call_initfini_pointer(elm->obj, elm->obj->fini); + + /* + * It is legal to have both DT_FINI and DT_FINI_ARRAY defined. + * When this happens, DT_FINI_ARRAY is processed first. + */ + fini_addr = (Elf_Addr *)elm->obj->fini_array; + 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("calling fini 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); + call_initfini_pointer(elm->obj, fini_addr[index]); + } + } + } + if (elm->obj->fini != (Elf_Addr)NULL) { + dbg("calling fini function for %s at %p", elm->obj->path, + (void *)elm->obj->fini); + LD_UTRACE(UTRACE_FINI_CALL, elm->obj, (void *)elm->obj->fini, + 0, 0, elm->obj->path); + call_initfini_pointer(elm->obj, elm->obj->fini); + } wlock_acquire(rtld_bind_lock, lockstate); /* No need to free anything if process is going down. */ if (root != NULL) @@ -1862,6 +2001,8 @@ objlist_call_init(Objlist *list, RtldLockState *lockstate) Objlist_Entry *elm; Obj_Entry *obj; char *saved_msg; + Elf_Addr *init_addr; + int index; /* * Clean init_scanned flag so that objects can be rechecked and @@ -1879,10 +2020,6 @@ objlist_call_init(Objlist *list, RtldLockState *lockstate) STAILQ_FOREACH(elm, list, link) { if (elm->obj->init_done) /* Initialized early. */ continue; - 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); /* * Race: other thread might try to use this object before current * one completes the initilization. Not much can be done here @@ -1890,7 +2027,30 @@ objlist_call_init(Objlist *list, RtldLockState *lockstate) */ elm->obj->init_done = true; lock_release(rtld_bind_lock, lockstate); - call_initfini_pointer(elm->obj, elm->obj->init); + + /* + * It is legal to have both DT_INIT and DT_INIT_ARRAY defined. + * When this happens, DT_INIT is processed first. + */ + if (elm->obj->init != (Elf_Addr)NULL) { + 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[index] != 0 && init_addr[index] != 1) { + dbg("calling init 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_init_pointer(elm->obj, init_addr[index]); + } + } + } wlock_acquire(rtld_bind_lock, lockstate); } errmsg_restore(saved_msg); diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h index d8a3a39..09d3dfc 100644 --- a/libexec/rtld-elf/rtld.h +++ b/libexec/rtld-elf/rtld.h @@ -71,6 +71,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; @@ -84,6 +88,7 @@ typedef STAILQ_HEAD(Struct_Objlist, Struct_Objlist_Entry) Objlist; /* Types of init and fini functions */ typedef void (*InitFunc)(void); +typedef void (*InitArrFunc)(int, char **, char **); /* Lists of shared object dependencies */ typedef struct Struct_Needed_Entry { @@ -213,6 +218,14 @@ typedef struct Struct_Obj_Entry { Elf_Addr init; /* Initialization function to call */ Elf_Addr fini; /* Termination function to call */ + Elf_Addr preinit_array; /* Pre-initialization array of functions */ + Elf_Addr init_array; /* Initialization array of functions */ + Elf_Addr fini_array; /* Termination array of functions */ + int preinit_array_num; /* Number of entries in preinit_array */ + 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 */ @@ -235,6 +248,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' crt does not call _init/_fini */ struct link_map linkmap; /* For GDB and dlinfo() */ Objlist dldags; /* Object belongs to these dlopened DAGs (%) */ @@ -319,6 +333,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/sys/kern/imgact_elf.c b/sys/kern/imgact_elf.c index bde590c..f907526 100644 --- a/sys/kern/imgact_elf.c +++ b/sys/kern/imgact_elf.c @@ -1549,32 +1549,14 @@ __elfN(putnote)(void *dst, size_t *off, const char *name, int type, *off += roundup2(note.n_descsz, sizeof(Elf_Size)); } -/* - * Try to find the appropriate ABI-note section for checknote, - * fetch the osreldate for binary from the ELF OSABI-note. Only the - * first page of the image is searched, the same as for headers. - */ static boolean_t -__elfN(check_note)(struct image_params *imgp, Elf_Brandnote *checknote, - int32_t *osrel) +__elfN(parse_notes)(struct image_params *imgp, Elf_Brandnote *checknote, + int32_t *osrel, const Elf_Phdr *pnote) { const Elf_Note *note, *note0, *note_end; - const Elf_Phdr *phdr, *pnote; - const Elf_Ehdr *hdr; const char *note_name; int i; - pnote = NULL; - hdr = (const Elf_Ehdr *)imgp->image_header; - phdr = (const Elf_Phdr *)(imgp->image_header + hdr->e_phoff); - - for (i = 0; i < hdr->e_phnum; i++) { - if (phdr[i].p_type == PT_NOTE) { - pnote = &phdr[i]; - break; - } - } - if (pnote == NULL || pnote->p_offset >= PAGE_SIZE || pnote->p_offset + pnote->p_filesz >= PAGE_SIZE) return (FALSE); @@ -1613,6 +1595,31 @@ nextnote: } /* + * Try to find the appropriate ABI-note section for checknote, + * fetch the osreldate for binary from the ELF OSABI-note. Only the + * first page of the image is searched, the same as for headers. + */ +static boolean_t +__elfN(check_note)(struct image_params *imgp, Elf_Brandnote *checknote, + int32_t *osrel) +{ + const Elf_Phdr *phdr; + const Elf_Ehdr *hdr; + int i; + + hdr = (const Elf_Ehdr *)imgp->image_header; + phdr = (const Elf_Phdr *)(imgp->image_header + hdr->e_phoff); + + for (i = 0; i < hdr->e_phnum; i++) { + if (phdr[i].p_type == PT_NOTE && + __elfN(parse_notes)(imgp, checknote, osrel, &phdr[i])) + return (TRUE); + } + return (FALSE); + +} + +/* * Tell kern_execve.c about it, with a little help from the linker. */ static struct execsw __elfN(execsw) = {