diff options
author | David 'Digit' Turner <digit@google.com> | 2010-10-21 04:16:50 +0200 |
---|---|---|
committer | David 'Digit' Turner <digit@google.com> | 2010-10-21 04:16:50 +0200 |
commit | 1df986c21ee52c6756846b4a5e45cb316f772112 (patch) | |
tree | da7effa96dd8e57e1cd718e69b79827d9011038e | |
parent | d3f0638aeec0b1daf4d3347386a5e441d5a4bcc4 (diff) | |
download | bionic-1df986c21ee52c6756846b4a5e45cb316f772112.zip bionic-1df986c21ee52c6756846b4a5e45cb316f772112.tar.gz bionic-1df986c21ee52c6756846b4a5e45cb316f772112.tar.bz2 |
libc: fix executable destruction support.
This change allows an executable to call its destructor functions
(declared with __attribute__((destructor))) to be properly called
when it normally exits.
Note that this is different from calling the destructors of a shared
library when it is unloaded with dlclose() or through program exit,
which are already supported.
Bug: 3106500
Change-Id: I1412ef5407f13b613fc6cb6103e0a691dbee4b1a
-rw-r--r-- | libc/bionic/libc_init_common.c | 36 | ||||
-rw-r--r-- | libc/bionic/libc_init_common.h | 1 | ||||
-rw-r--r-- | libc/bionic/libc_init_dynamic.c | 22 | ||||
-rw-r--r-- | libc/bionic/libc_init_static.c | 7 |
4 files changed, 61 insertions, 5 deletions
diff --git a/libc/bionic/libc_init_common.c b/libc/bionic/libc_init_common.c index d78d673..f7579bd 100644 --- a/libc/bionic/libc_init_common.c +++ b/libc/bionic/libc_init_common.c @@ -84,3 +84,39 @@ void __libc_init_common(uintptr_t *elfdata) /* setup system properties - requires environment */ __system_properties_init(); } + +/* This function will be called during normal program termination + * to run the destructors that are listed in the .fini_array section + * of the executable, if any. + * + * 'fini_array' points to a list of function addresses. The first + * entry in the list has value -1, the last one has value 0. + */ +void __libc_fini(void* array) +{ + int count; + void** fini_array = array; + const size_t minus1 = ~(size_t)0; /* ensure proper sign extension */ + + /* Sanity check - first entry must be -1 */ + if (array == NULL || (size_t)fini_array[0] != minus1) { + return; + } + + /* skip over it */ + fini_array += 1; + + /* Count the number of destructors. */ + for (count = 0; fini_array[count] != NULL; count++); + + /* Now call each destructor in reverse order. */ + while (count > 0) { + void (*func)() = (void (*)) fini_array[--count]; + + /* Sanity check, any -1 in the list is ignored */ + if ((size_t)func == minus1) + continue; + + func(); + } +} diff --git a/libc/bionic/libc_init_common.h b/libc/bionic/libc_init_common.h index 8663c61..6016d4d 100644 --- a/libc/bionic/libc_init_common.h +++ b/libc/bionic/libc_init_common.h @@ -39,5 +39,6 @@ typedef struct } structors_array_t; extern void __libc_init_common(uintptr_t *elfdata); +extern void __libc_fini(void* finit_array); #endif diff --git a/libc/bionic/libc_init_dynamic.c b/libc/bionic/libc_init_dynamic.c index 682ebcf..f64b6f2 100644 --- a/libc/bionic/libc_init_dynamic.c +++ b/libc/bionic/libc_init_dynamic.c @@ -57,9 +57,9 @@ * 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 __attribute__((constructor)) __libc_preinit(void); -void __libc_prenit(void) +void __libc_preinit(void) { /* Read the ELF data pointer form a special slot of the * TLS area, then call __libc_init_common with it. @@ -83,14 +83,19 @@ void __libc_prenit(void) malloc_debug_init(); } +/* This function is called from the executable's _start entry point + * (see arch-$ARCH/bionic/crtbegin_dynamic.S), which is itself + * called by the dynamic linker after it has loaded all shared + * libraries the executable depends on. + * + * Note that the dynamic linker has also run all constructors in the + * executable at this point. + */ __noreturn void __libc_init(uintptr_t *elfdata, void (*onexit)(void), int (*slingshot)(int, char**, char**), structors_array_t const * const structors) { - /* 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; @@ -99,5 +104,12 @@ __noreturn void __libc_init(uintptr_t *elfdata, * do never use it. Therefore, we ignore it. */ + /* The executable may have its own destructors listed in its .fini_array + * so we need to ensure that these are called when the program exits + * normally. + */ + if (structors->fini_array) + __cxa_atexit(__libc_fini,structors->fini_array,NULL); + exit(slingshot(argc, argv, envp)); } diff --git a/libc/bionic/libc_init_static.c b/libc/bionic/libc_init_static.c index d097b6b..3634c7b 100644 --- a/libc/bionic/libc_init_static.c +++ b/libc/bionic/libc_init_static.c @@ -85,5 +85,12 @@ __noreturn void __libc_init(uintptr_t *elfdata, argv = (char**)(elfdata + 1); envp = argv + argc + 1; + /* The executable may have its own destructors listed in its .fini_array + * so we need to ensure that these are called when the program exits + * normally. + */ + if (structors->fini_array) + __cxa_atexit(__libc_fini,structors->fini_array,NULL); + exit(slingshot(argc, argv, envp)); } |