diff --git a/lib/csu/amd64/crt1.c b/lib/csu/amd64/crt1.c index 998477a..ea80cf8 100644 --- a/lib/csu/amd64/crt1.c +++ b/lib/csu/amd64/crt1.c @@ -37,14 +37,13 @@ __FBSDID("$FreeBSD$"); #include "libc_private.h" #include "crtbrand.c" +#include "ignore_init.c" extern int _DYNAMIC; #pragma weak _DYNAMIC typedef void (*fptr)(void); -extern void _fini(void); -extern void _init(void); extern int main(int, char **, char **); #ifdef GCRT @@ -86,12 +85,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; + exit(main(argc, argv, env)); } diff --git a/lib/csu/common/ignore_init.c b/lib/csu/common/ignore_init.c new file mode 100644 index 0000000..f74e813 --- /dev/null +++ b/lib/csu/common/ignore_init.c @@ -0,0 +1,59 @@ +/*- + * 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$"); + +__asm(".set __crt1_ignores_init, ."); +__asm(".globl __crt1_ignores_init"); +__asm(".type __crt1_ignores_init, @notype"); + +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; + +#define HANDLE_STATIC_INIT do { \ + if (&_DYNAMIC == NULL) { \ + size_t array_size, n; \ + \ + atexit(_fini); \ + array_size = __fini_array_end - __fini_array_start; \ + for (n = 0; n < array_size; n++) \ + atexit(*__fini_array_start[n]); \ + \ + array_size = __preinit_array_end - __preinit_array_start; \ + for (n = 0; n < array_size; n++) \ + (*__preinit_array_start[n])(argc, argv, env); \ + _init(); \ + array_size = __init_array_end - __init_array_start; \ + for (n = 0; n < array_size; n++) \ + (*__init_array_start[n])(argc, argv, env); \ + } \ +} while (0) diff --git a/lib/csu/i386-elf/crt1_c.c b/lib/csu/i386-elf/crt1_c.c index 1eadc7c..2af72f1 100644 --- a/lib/csu/i386-elf/crt1_c.c +++ b/lib/csu/i386-elf/crt1_c.c @@ -39,14 +39,13 @@ __FBSDID("$FreeBSD$"); #include "libc_private.h" #include "crtbrand.c" +#include "ignore_init.c" extern int _DYNAMIC; #pragma weak _DYNAMIC typedef void (*fptr)(void); -extern void _fini(void); -extern void _init(void); extern int main(int, char **, char **); extern void _start(char *, ...); @@ -85,14 +84,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; + exit(main(argc, argv, env)); } __asm(".hidden _start1"); diff --git a/lib/libc/gen/aux.c b/lib/libc/gen/aux.c index b5c079b..4bf8643 100644 --- a/lib/libc/gen/aux.c +++ b/lib/libc/gen/aux.c @@ -1,5 +1,5 @@ /*- - * Copyright 2010 Konstantin Belousov . + * Copyright 2010, 2012 Konstantin Belousov . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -36,10 +36,34 @@ __FBSDID("$FreeBSD$"); #include "un-namespace.h" #include "libc_private.h" -Elf_Auxinfo *__elf_aux_vector; +extern char **environ; +extern int _DYNAMIC; +#pragma weak _DYNAMIC -static pthread_once_t aux_once = PTHREAD_ONCE_INIT; +void *__elf_aux_vector; +static pthread_once_t aux_vector_once = PTHREAD_ONCE_INIT; + +static void +init_aux_vector_once(void) +{ + Elf_Addr *sp; + + sp = (Elf_Addr *)environ; + while (*sp++ != 0) + ; + __elf_aux_vector = (Elf_Auxinfo *)sp; +} +void +__init_elf_aux_vector(void) +{ + + if (&_DYNAMIC != NULL) + return; + _once(&aux_vector_once, init_aux_vector_once); +} + +static pthread_once_t aux_once = PTHREAD_ONCE_INIT; static int pagesize, osreldate, canary_len, ncpus, pagesizes_len; static char *canary, *pagesizes; @@ -86,6 +110,7 @@ _elf_aux_info(int aux, void *buf, int buflen) { int res; + __init_elf_aux_vector(); if (__elf_aux_vector == NULL) return (ENOSYS); _once(&aux_once, init_aux); diff --git a/lib/libc/gen/dl_iterate_phdr.3 b/lib/libc/gen/dl_iterate_phdr.3 new file mode 100644 index 0000000..2d81d68 --- /dev/null +++ b/lib/libc/gen/dl_iterate_phdr.3 @@ -0,0 +1,115 @@ +.\" Copyright (c) 2005 Mark Kettenis +.\" Copyright (c) 2012 Konstantin Belousov +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.\" $OpenBSD: dl_iterate_phdr.3,v 1.3 2007/05/31 19:19:48 jmc Exp $ +.\" $FreeBSD$ +.Dd February 15, 2012 +.Dt DL_ITERATE_PHDR 3 +.Os +.Sh NAME +.Nm dl_iterate_phdr +.Nd iterate over program headers +.Sh LIBRARY +For the dynamically linked binaries, the service is provided by +.Xr ld-elf.so.1 5 +dynamic linker. +Statically linked programs use an implementation of +.Fn dl_iterate_phdr +from libc. +.Sh SYNOPSIS +.Fd #include +.Ft int +.Fn dl_iterate_phdr "int (*callback)(struct dl_phdr_info *, size_t, void *)" "void *data" +.Sh DESCRIPTION +The +.Fn dl_iterate_phdr +function iterates over all ELF objects loaded into a process's +address space, calling +.Fa callback +for each object, passing it information about the object's +program headers and the +.Fa data +argument. +The iteration is aborted when all objects are passed, or when the next +.Fa callback +call returns non-zero value. +The information about the program headers is passed in a structure +that is defined as: +.Bd -literal +struct dl_phdr_info { + Elf_Addr dlpi_addr; + const char *dlpi_name; + const Elf_Phdr *dlpi_phdr; + Elf_Half dlpi_phnum; + unsigned long long int dlpi_adds; + unsigned long long int dlpi_subs; + size_t dlpi_tls_modid; + void *dlpi_tls_data; +}; +.Ed +.Pp +The members of +.Li struct dl_phdr_info +have the following meaning: +.Bl -tag -width dlpi_tls_modid +.It Fa dlpi_addr +The base address at which the object is mapped into the address +space of the calling process. +.It Fa dlpi_name +The name of the ELF object. +.It Fa dlpi_phdr +A pointer to the object's program headers. +.It Fa dlpi_phnum +The number of program headers in the object. +.It Fa dlpi_adds +The counter of the object loads performed by the dynamic linker. +.It Fa dlpi_subs +The counter of the object unloads performed by the dynamic linker. +.It Fa dlpi_tls_modid +The TLS index of the object. +.It Fa dlpi_tls_data +A pointer to the initialization data for the object TLS segment. +.El +.Pp +Future versions of +.Fx +might add more members to this structure. +To make it possible for programs to check whether any new members have +been added, the size of the structure is passed as an second argument to +.Fa callback . +.Pp +The third argument to callback is the +.Fa data +value passed to the call to +.Fn dl_iterate_phdr , +allowing the +.Fa callback +to have a context. +.Sh RETURN VALUES +The +.Fn dl_iterate_phdr +returns the value returned by the last +.Fa callback +call executed. +.Sh SEE ALSO +.Xr ld 1 , +.Xr ld-elf.so 1 , +.Xr dlopen 3 , +.Xr elf 5 +.Sh HISTORY +The +.Nm +function first appeared in +.Fx 7.0 . diff --git a/lib/libc/gen/dlfcn.c b/lib/libc/gen/dlfcn.c index 7be9f87..ad24bb4 100644 --- a/lib/libc/gen/dlfcn.c +++ b/lib/libc/gen/dlfcn.c @@ -34,6 +34,10 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include "namespace.h" +#include +#include "un-namespace.h" +#include "libc_private.h" static char sorry[] = "Service unavailable"; @@ -138,13 +142,58 @@ _rtld_thread_init(void * li) _rtld_error(sorry); } +static pthread_once_t dl_phdr_info_once = PTHREAD_ONCE_INIT; +static struct dl_phdr_info phdr_info; + +static void +dl_init_phdr_info(void) +{ + Elf_Auxinfo *auxp; + size_t phent; + unsigned int i; + + phent = 0; + for (auxp = __elf_aux_vector; auxp->a_type != AT_NULL; auxp++) { + switch (auxp->a_type) { + case AT_BASE: + phdr_info.dlpi_addr = (Elf_Addr)auxp->a_un.a_ptr; + break; + case AT_EXECPATH: + phdr_info.dlpi_name = (const char *)auxp->a_un.a_ptr; + break; + case AT_PHDR: + phdr_info.dlpi_phdr = + (const Elf_Phdr *)auxp->a_un.a_ptr; + break; + case AT_PHENT: + phent = auxp->a_un.a_val; + break; + case AT_PHNUM: + phdr_info.dlpi_phnum = (Elf_Half)auxp->a_un.a_val; + break; + } + } + for (i = 0; i < phdr_info.dlpi_phnum; i++) { + if (phdr_info.dlpi_phdr[i].p_type == PT_TLS) { + phdr_info.dlpi_tls_modid = 1; + phdr_info.dlpi_tls_data = + (void*)phdr_info.dlpi_phdr[i].p_vaddr; + } + } + phdr_info.dlpi_adds = 1; +} + #pragma weak dl_iterate_phdr int dl_iterate_phdr(int (*callback)(struct dl_phdr_info *, size_t, void *), void *data) { - _rtld_error(sorry); - return 0; + + __init_elf_aux_vector(); + if (__elf_aux_vector == NULL) + return (1); + _once(&dl_phdr_info_once, dl_init_phdr_info); + return (callback(&phdr_info, sizeof(phdr_info), data)); } #pragma weak fdlopen diff --git a/lib/libc/include/libc_private.h b/lib/libc/include/libc_private.h index c7284cc..d4b24a7 100644 --- a/lib/libc/include/libc_private.h +++ b/lib/libc/include/libc_private.h @@ -44,6 +44,15 @@ extern int __isthreaded; /* + * Elf_Auxinfo *__elf_aux_vector, the pointer to the ELF aux vector + * provided by kernel. Either set for us by rtld, or found at runtime + * on stack for static binaries. + * + * Type is void to avoid polluting whole libc with ELF types. + */ +extern void *__elf_aux_vector; + +/* * libc should use libc_dlopen internally, which respects a global * flag where loading of new shared objects can be restricted. */ @@ -229,6 +238,7 @@ int _execvpe(const char *, char * const *, char * const *); int _elf_aux_info(int aux, void *buf, int buflen); struct dl_phdr_info; int __elf_phdr_match_addr(struct dl_phdr_info *, void *); +void __init_elf_aux_vector(void); void _pthread_cancel_enter(int); void _pthread_cancel_leave(int); 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/rtld.c b/libexec/rtld-elf/rtld.c index 6980857..cc8aef8 100644 --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -139,6 +139,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 +206,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 */ @@ -303,6 +310,7 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) Obj_Entry **preload_tail; Objlist initlist; RtldLockState lockstate; + bool need_call_init_main; /* * On entry, the dynamic linker itself has not been relocated yet. @@ -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,22 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) ld_bind_now != NULL && *ld_bind_now != '\0', NULL) == -1) die(); + need_call_init_main = get_program_var_addr("__crt1_ignores_init", NULL) + != NULL; + if (!need_call_init_main) { + /* + * 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 (need_call_init_main) + preinit_main(); objlist_call_init(&initlist, &lockstate); objlist_clear(&initlist); dbg("loading filtees"); @@ -936,10 +959,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. @@ -1504,11 +1551,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 +1845,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 +1878,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 +1893,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 +1903,30 @@ 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) { + for (index = 0; index < elm->obj->fini_array_num; 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 +1953,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 +1972,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 +1979,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..1c2b712 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,12 @@ 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 */ bool mainprog : 1; /* True if this is the main program */ bool rtld : 1; /* True if this is the dynamic linker */