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/Android.bp b/libc/Android.bp
index fe263fd..99455c1 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -2282,6 +2282,8 @@
"bionic", // crtbegin.c includes bionic/libc_init_common.h
],
+ cflags: [ "-DCRTBEGIN_STATIC", ],
+
srcs: ["arch-common/bionic/crtbegin.c"],
objs: [
"crtbrand",
diff --git a/libc/arch-common/bionic/crtbegin.c b/libc/arch-common/bionic/crtbegin.c
index b87db64..127896a 100644
--- a/libc/arch-common/bionic/crtbegin.c
+++ b/libc/arch-common/bionic/crtbegin.c
@@ -30,17 +30,53 @@
#include <stddef.h>
#include <stdint.h>
-#define SECTION(name) __attribute__((__section__(name)))
-SECTION(".preinit_array") init_func_t* __PREINIT_ARRAY__ = (init_func_t*)-1;
-SECTION(".init_array.0") init_func_t* __INIT_ARRAY__ = (init_func_t*)-1;
-SECTION(".fini_array.0") fini_func_t* __FINI_ARRAY__ = (fini_func_t*)-1;
-#undef SECTION
+extern init_func_t* __preinit_array_start[];
+extern init_func_t* __preinit_array_end[];
+extern init_func_t* __init_array_start[];
+extern init_func_t* __init_array_end[];
+extern fini_func_t* __fini_array_start[];
+extern fini_func_t* __fini_array_end[];
+
+#if !defined(CRTBEGIN_STATIC)
+/* 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.
+ */
+static void call_fini_array() {
+ fini_func_t** array = __fini_array_start;
+ size_t count = __fini_array_end - __fini_array_start;
+ // Call fini functions in reverse order.
+ while (count-- > 0) {
+ fini_func_t* function = array[count];
+ (*function)();
+ }
+}
+
+// libc.so needs fini_array with sentinels. So create a fake fini_array with sentinels.
+// It contains a function to call functions in real fini_array.
+static fini_func_t* fini_array_with_sentinels[] = {
+ (fini_func_t*)-1,
+ &call_fini_array,
+ (fini_func_t*)0,
+};
+#endif // !defined(CRTBEGIN_STATIC)
__used static void _start_main(void* raw_args) {
- structors_array_t array;
- array.preinit_array = &__PREINIT_ARRAY__;
- array.init_array = &__INIT_ARRAY__;
- array.fini_array = &__FINI_ARRAY__;
+ structors_array_t array = {};
+#if defined(CRTBEGIN_STATIC)
+ array.preinit_array = __preinit_array_start;
+ array.preinit_array_count = __preinit_array_end - __preinit_array_start;
+ array.init_array = __init_array_start;
+ array.init_array_count = __init_array_end - __init_array_start;
+ array.fini_array = __fini_array_start;
+ array.fini_array_count = __fini_array_end - __fini_array_start;
+#else
+ if (__fini_array_end - __fini_array_start > 0) {
+ array.fini_array = fini_array_with_sentinels;
+ }
+#endif // !defined(CRTBEGIN_STATIC)
__libc_init(raw_args, NULL, &main, &array);
}
diff --git a/libc/arch-common/bionic/crtend.S b/libc/arch-common/bionic/crtend.S
index 49c729f..74b3aa9 100644
--- a/libc/arch-common/bionic/crtend.S
+++ b/libc/arch-common/bionic/crtend.S
@@ -34,18 +34,6 @@
__bionic_asm_custom_note_gnu_section()
#endif
- .section .preinit_array, "aw"
- ASM_ALIGN_TO_PTR_SIZE
- ASM_PTR_SIZE(0)
-
- .section .init_array, "aw"
- ASM_ALIGN_TO_PTR_SIZE
- ASM_PTR_SIZE(0)
-
- .section .fini_array, "aw"
- ASM_ALIGN_TO_PTR_SIZE
- ASM_PTR_SIZE(0)
-
.section .note.GNU-stack, "", %progbits
#if !defined(__arm__)
diff --git a/libc/bionic/libc_init_common.h b/libc/bionic/libc_init_common.h
index 6b39d6d..126f002 100644
--- a/libc/bionic/libc_init_common.h
+++ b/libc/bionic/libc_init_common.h
@@ -38,6 +38,10 @@
init_func_t** preinit_array;
init_func_t** init_array;
fini_func_t** fini_array;
+ // Below fields are only available in static executables.
+ size_t preinit_array_count;
+ size_t init_array_count;
+ size_t fini_array_count;
} structors_array_t;
__BEGIN_DECLS
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();