From b56b5659b3996e98c2060f168d1cff1474e77d2a Mon Sep 17 00:00:00 2001 From: David 'Digit' Turner Date: Sat, 18 Jul 2009 01:11:10 +0200 Subject: [PATCH] Fix the C library runtime initialization order. This allows libc.so to run the C runtime initializer as soon as the dynamic linker loads the shared library, i.e. before any other initializers (e.g. static C++ constructors in other shared libraries the executable depends on). This also removes the bug where the initializers from the executable itself were run twice: once by the dynamic linker, and another time by __libc_init as defined by libc_init_dynamic.c --- libc/bionic/libc_init_common.c | 81 +++++++++++---------------------- libc/bionic/libc_init_common.h | 6 +-- libc/bionic/libc_init_dynamic.c | 69 ++++++++++++++++++++-------- libc/bionic/libc_init_static.c | 63 +++++++++++++++---------- 4 files changed, 117 insertions(+), 102 deletions(-) diff --git a/libc/bionic/libc_init_common.c b/libc/bionic/libc_init_common.c index de4919d27..c77c162c5 100644 --- a/libc/bionic/libc_init_common.c +++ b/libc/bionic/libc_init_common.c @@ -39,23 +39,6 @@ #include #include -extern void _init(void); -extern void _fini(void); - -static void call_array(void(**list)()) -{ - // First element is -1, list is null-terminated - while (*++list) { - (*list)(); - } -} - -static void __bionic_do_global_dtors(structors_array_t const * const p) -{ - call_array(p->fini_array); - //_fini(); -} - extern unsigned __get_sp(void); extern pid_t gettid(void); @@ -69,23 +52,23 @@ unsigned int __page_shift = PAGE_SHIFT; int __system_properties_init(void); -void __libc_init_common(uintptr_t *elfdata, - void (*onexit)(void), - int (*slingshot)(int, char**, char**), - structors_array_t const * const structors, - void (*pre_ctor_hook)()) -{ - pthread_internal_t thread; - pthread_attr_t thread_attr; - void *tls_area[BIONIC_TLS_SLOTS]; - int argc; - char **argv, **envp, **envend; - struct auxentry *auxentry; - unsigned int page_size = 0, page_shift = 0; +#ifdef MALLOCK_LEAK_CHECK +void malloc_debug_init(void); +#endif - /* The main thread's stack has empirically shown to be 84k */ +void __libc_init_common(uintptr_t *elfdata) +{ + int argc = *elfdata; + char** argv = (char**)(elfdata + 1); + char** envp = argv + argc + 1; + + pthread_attr_t thread_attr; + static pthread_internal_t thread; + static void* tls_area[BIONIC_TLS_SLOTS]; + + /* setup pthread runtime and maint thread descriptor */ unsigned stacktop = (__get_sp() & ~(PAGE_SIZE - 1)) + PAGE_SIZE; - unsigned stacksize = 128 * 1024; //84 * 1024; + unsigned stacksize = 128 * 1024; unsigned stackbottom = stacktop - stacksize; pthread_attr_init(&thread_attr); @@ -93,30 +76,20 @@ void __libc_init_common(uintptr_t *elfdata, _init_thread(&thread, gettid(), &thread_attr, (void*)stackbottom); __init_tls(tls_area, &thread); - argc = (int) *elfdata++; - argv = (char**) elfdata; - envp = argv+(argc+1); - environ = envp; - - __progname = argv[0] ? argv[0] : ""; - + /* clear errno - requires TLS area */ errno = 0; + /* set program name */ + __progname = argv[0] ? argv[0] : ""; + + /* setup environment pointer */ + environ = envp; + + /* setup system properties - requires environment */ __system_properties_init(); - if (pre_ctor_hook) pre_ctor_hook(); - - // XXX: we should execute the .fini_array upon exit - - // pre-init array. - // XXX: I'm not sure what's the different with the init array. - call_array(structors->preinit_array); - - // for compatibility with non-eabi binary, call the .ctors section - call_array(structors->ctors_array); - - // call static constructors - call_array(structors->init_array); - - exit(slingshot(argc, argv, envp)); + /* setup malloc leak checker, requires system properties */ +#if MALLOC_LEAK_CHECK + malloc_debug_init(); +#endif } diff --git a/libc/bionic/libc_init_common.h b/libc/bionic/libc_init_common.h index bbc82e4b7..8663c611b 100644 --- a/libc/bionic/libc_init_common.h +++ b/libc/bionic/libc_init_common.h @@ -38,10 +38,6 @@ typedef struct void (**ctors_array)(void); } structors_array_t; -extern __noreturn void __libc_init_common(uintptr_t *elfdata, - void (*onexit)(void), - int (*slingshot)(int, char**, char**), - structors_array_t const * const structors, - void (*pre_ctor_hook)()); +extern void __libc_init_common(uintptr_t *elfdata); #endif diff --git a/libc/bionic/libc_init_dynamic.c b/libc/bionic/libc_init_dynamic.c index 8cf24b466..b8e10782f 100644 --- a/libc/bionic/libc_init_dynamic.c +++ b/libc/bionic/libc_init_dynamic.c @@ -26,41 +26,74 @@ * SUCH DAMAGE. */ /* - * libc_init_static.c + * libc_init_dynamic.c * - * This function takes the raw data block set up by the ELF loader - * in the kernel and parses it. It is invoked by crt0.S which makes - * any necessary adjustments and passes calls this function using - * the standard C calling convention. + * This source files provides two important functions for dynamic + * executables: * - * The arguments are: - * uintptr_t *elfdata -- The ELF loader data block; usually from the stack. - * Basically a pointer to argc. - * void (*onexit)(void) -- Function to install into onexit + * - a C runtime initializer (__libc_preinit), which is called by + * the dynamic linker when libc.so is loaded. This happens before + * any other initializer (e.g. static C++ constructors in other + * shared libraries the program depends on). + * + * - a program launch function (__libc_init), which is called after + * all dynamic linking has been performed. Technically, it is called + * from arch-$ARCH/bionic/crtbegin_dynamic.S which is itself called + * by the dynamic linker after all libraries have been loaded and + * initialized. */ -/* - * Several Linux ABIs don't pass the onexit pointer, and the ones that - * do never use it. Therefore, unless USE_ONEXIT is defined, we just - * ignore the onexit pointer. - */ -/* #define USE_ONEXIT */ - #include #include #include #include #include -#include "pthread_internal.h" #include "atexit.h" #include "libc_init_common.h" +#include extern void malloc_debug_init(); +/* We flag the __libc_preinit function as a constructor to ensure + * that its address is listed in libc.so's .init_array section. + * This ensures that the function is called by the dynamic linker + * as soon as the shared library is loaded. + */ +void __attribute__((constructor)) __libc_prenit(void); + +void __libc_prenit(void) +{ + /* Read the ELF data pointer form a special slot of the + * TLS area, then call __libc_init_common with it. + * + * Note that: + * - we clear the slot so no other initializer sees its value. + * - __libc_init_common() will change the TLS area so the old one + * won't be accessible anyway. + */ + void** tls_area = (void**)__get_tls(); + unsigned* elfdata = tls_area[TLS_SLOT_BIONIC_PREINIT]; + + tls_area[TLS_SLOT_BIONIC_PREINIT] = NULL; + + __libc_init_common(elfdata); +} + __noreturn void __libc_init(uintptr_t *elfdata, void (*onexit)(void), int (*slingshot)(int, char**, char**), structors_array_t const * const structors) { - __libc_init_common(elfdata, onexit, slingshot, structors, malloc_debug_init); + /* When we reach this point, all initializers have been already + * run by the dynamic linker, so ignore 'structors'. + */ + int argc = (int)*elfdata; + char** argv = (char**)(elfdata + 1); + char** envp = argv + argc + 1; + + /* Several Linux ABIs don't pass the onexit pointer, and the ones that + * do never use it. Therefore, we ignore it. + */ + + exit(slingshot(argc, argv, envp)); } diff --git a/libc/bionic/libc_init_static.c b/libc/bionic/libc_init_static.c index ec463f7ec..d097b6baf 100644 --- a/libc/bionic/libc_init_static.c +++ b/libc/bionic/libc_init_static.c @@ -28,24 +28,15 @@ /* * libc_init_static.c * - * This function takes the raw data block set up by the ELF loader - * in the kernel and parses it. It is invoked by crt0.S which makes - * any necessary adjustments and passes calls this function using - * the standard C calling convention. + * The program startup function __libc_init() defined here is + * used for static executables only (i.e. those that don't depend + * on shared libraries). It is called from arch-$ARCH/bionic/crtbegin_static.S + * which is directly invoked by the kernel when the program is launched. * - * The arguments are: - * uintptr_t *elfdata -- The ELF loader data block; usually from the stack. - * Basically a pointer to argc. - * void (*onexit)(void) -- Function to install into onexit + * The 'structors' parameter contains pointers to various initializer + * arrays that must be run before the program's 'main' routine is launched. */ -/* - * Several Linux ABIs don't pass the onexit pointer, and the ones that - * do never use it. Therefore, unless USE_ONEXIT is defined, we just - * ignore the onexit pointer. - */ -/* #define USE_ONEXIT */ - #include #include #include @@ -58,19 +49,41 @@ #include #include +static void call_array(void(**list)()) +{ + // First element is -1, list is null-terminated + while (*++list) { + (*list)(); + } +} + __noreturn void __libc_init(uintptr_t *elfdata, void (*onexit)(void), int (*slingshot)(int, char**, char**), structors_array_t const * const structors) { -/* - * To enable malloc checks for statically linked programs, add - * "WITH_MALLOC_CHECK_LIBC_A := true" in device/buildspec.mk - */ -#ifdef MALLOC_LEAK_CHECK - extern void malloc_debug_init(); - __libc_init_common(elfdata, onexit, slingshot, structors, malloc_debug_init); -#else - __libc_init_common(elfdata, onexit, slingshot, structors, NULL); -#endif + int argc; + char **argv, **envp; + + /* Initialize the C runtime environment */ + __libc_init_common(elfdata); + + /* Several Linux ABIs don't pass the onexit pointer, and the ones that + * do never use it. Therefore, we ignore it. + */ + + /* pre-init array. */ + call_array(structors->preinit_array); + + /* .ctors section initializers, for non-arm-eabi ABIs */ + call_array(structors->ctors_array); + + // call static constructors + call_array(structors->init_array); + + argc = (int) *elfdata; + argv = (char**)(elfdata + 1); + envp = argv + argc + 1; + + exit(slingshot(argc, argv, envp)); }