Let executables not rely on sentinels in preinit_array/init_array/fini_array
Currently, we use sentinels (starting with -1 and ending with 0) in
preinit_array/init_array/fini_array in executables. But after using LTO,
the sentinels can be reordered by LLD and no longer work. So make below
changes to not rely on them:
1. In crtbegin.c, use symbols (like __init_array_start) inserted by the
linker.
2. Add array_count fields in structors_array_t.
3. In static libc, use array_count fields to decide array lengths.
4. To make new dynamic executables work with old libc.so, create a fake
fini_array with sentinels, and pass it to __libc_init. The fake
fini_array contains a function to call functions in real fini_array.
5. To make old dynamic executables work with new libc.so, libc.so
still uses sentinels to decide the length of fini_array.
Bug: 295944813
Bug: https://github.com/android/ndk/issues/1461
Test: run bionic-unit-tests-static
Test: test static executables manually
Test: boot cf_gwear_x86-trunk_staging-userdebug
Change-Id: I1ce31f07bcfe0e99b4237984898a8fc9e98ff426
diff --git a/libc/bionic/libc_init_static.cpp b/libc/bionic/libc_init_static.cpp
index a3c66d4..1591785 100644
--- a/libc/bionic/libc_init_static.cpp
+++ b/libc/bionic/libc_init_static.cpp
@@ -69,10 +69,21 @@
extern "C" int __cxa_atexit(void (*)(void *), void *, void *);
extern "C" const char* __gnu_basename(const char* path);
-static void call_array(init_func_t** list, int argc, char* argv[], char* envp[]) {
- // First element is -1, list is null-terminated
- while (*++list) {
- (*list)(argc, argv, envp);
+static void call_array(init_func_t** list, size_t count, int argc, char* argv[], char* envp[]) {
+ while (count-- > 0) {
+ init_func_t* function = *list++;
+ (*function)(argc, argv, envp);
+ }
+}
+
+static void call_fini_array(void* arg) {
+ structors_array_t* structors = reinterpret_cast<structors_array_t*>(arg);
+ fini_func_t** array = structors->fini_array;
+ size_t count = structors->fini_array_count;
+ // Now call each destructor in reverse order.
+ while (count-- > 0) {
+ fini_func_t* function = array[count];
+ (*function)();
}
}
@@ -413,14 +424,15 @@
// Several Linux ABIs don't pass the onexit pointer, and the ones that
// do never use it. Therefore, we ignore it.
- call_array(structors->preinit_array, args.argc, args.argv, args.envp);
- call_array(structors->init_array, args.argc, args.argv, args.envp);
+ call_array(structors->preinit_array, structors->preinit_array_count, args.argc, args.argv,
+ args.envp);
+ call_array(structors->init_array, structors->init_array_count, args.argc, args.argv, args.envp);
// 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 != nullptr) {
- __cxa_atexit(__libc_fini,structors->fini_array,nullptr);
+ if (structors->fini_array_count > 0) {
+ __cxa_atexit(call_fini_array, const_cast<structors_array_t*>(structors), nullptr);
}
__libc_init_mte_late();