From 1df986c21ee52c6756846b4a5e45cb316f772112 Mon Sep 17 00:00:00 2001 From: David 'Digit' Turner Date: Thu, 21 Oct 2010 04:16:50 +0200 Subject: [PATCH] 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 --- libc/bionic/libc_init_common.c | 36 +++++++++++++++++++++++++++++++++ libc/bionic/libc_init_common.h | 1 + libc/bionic/libc_init_dynamic.c | 22 +++++++++++++++----- 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 d78d673f0..f7579bda7 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 8663c611b..6016d4d2d 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 682ebcfd3..f64b6f228 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 d097b6baf..3634c7ba8 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)); }