Put __*_ARRAY__ symbols before prioritized init/fini funcs
A constructor or destructor function with an integral priority is
placed in an .init_array or .fini_array section with the priority
suffixed to the section name:
- __attribute__((constructor)) ==> .init_array
- __attribute__((constructor(42))) ==> .init_array.42
The suffixed init/fini sections appear before the unsuffixed sections,
so the prioritized functions appeared before the __{INIT,FINI}_ARRAY__
symbols and were dropped when the symbols were used.
The (static) linker doesn't recognize priority suffixes on
.preinit_array.
This bug affected .init_array and .fini_array for static executables.
For dynamic executables, only .fini_array was affected, because
.init_array is handled by the dynamic loader instead, which uses
DT_INIT_ARRAY[SZ]. For DSOs, neither is affected, because the two
sections are only handled by the dynamic loader.
This patch also fixes a minor inconsistency where dynamic init/preinit
were passed argc/argv/envp, but static were not.
Bug: http://b/170983066
Test: bionic-unit-tests
Change-Id: I0fffa776e5d9bdb6f8af06b4c1af148236742fef
diff --git a/libc/arch-common/bionic/crtbegin.c b/libc/arch-common/bionic/crtbegin.c
index 1f8dfd2..29b02dc 100644
--- a/libc/arch-common/bionic/crtbegin.c
+++ b/libc/arch-common/bionic/crtbegin.c
@@ -31,9 +31,9 @@
#include <stdint.h>
#define SECTION(name) __attribute__((__section__(name)))
-SECTION(".preinit_array") void (*__PREINIT_ARRAY__)(void) = (void (*)(void)) -1;
-SECTION(".init_array") void (*__INIT_ARRAY__)(void) = (void (*)(void)) -1;
-SECTION(".fini_array") void (*__FINI_ARRAY__)(void) = (void (*)(void)) -1;
+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
__used static void _start_main(void* raw_args) {
diff --git a/libc/bionic/libc_init_common.h b/libc/bionic/libc_init_common.h
index 0c2e78a..be7526f 100644
--- a/libc/bionic/libc_init_common.h
+++ b/libc/bionic/libc_init_common.h
@@ -30,10 +30,13 @@
#include <sys/cdefs.h>
+typedef void init_func_t(int, char*[], char*[]);
+typedef void fini_func_t(void);
+
typedef struct {
- void (**preinit_array)(void);
- void (**init_array)(void);
- void (**fini_array)(void);
+ init_func_t** preinit_array;
+ init_func_t** init_array;
+ fini_func_t** fini_array;
} structors_array_t;
__BEGIN_DECLS
diff --git a/libc/bionic/libc_init_static.cpp b/libc/bionic/libc_init_static.cpp
index e3a447d..4a73918 100644
--- a/libc/bionic/libc_init_static.cpp
+++ b/libc/bionic/libc_init_static.cpp
@@ -60,10 +60,10 @@
extern "C" int __cxa_atexit(void (*)(void *), void *, void *);
-static void call_array(void(**list)()) {
+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)();
+ (*list)(argc, argv, envp);
}
}
@@ -183,8 +183,8 @@
// 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);
- call_array(structors->init_array);
+ call_array(structors->preinit_array, args.argc, args.argv, args.envp);
+ call_array(structors->init_array, 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
diff --git a/tests/Android.bp b/tests/Android.bp
index 9b95c61..e9e1b21 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -365,6 +365,7 @@
"netinet_udp_test.cpp",
"nl_types_test.cpp",
"poll_test.cpp",
+ "prio_ctor_test.cpp",
"pthread_test.cpp",
"pty_test.cpp",
"regex_test.cpp",
diff --git a/tests/prio_ctor_test.cpp b/tests/prio_ctor_test.cpp
new file mode 100644
index 0000000..0dfa66d
--- /dev/null
+++ b/tests/prio_ctor_test.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <stdint.h>
+
+static const char* record[4] = {};
+static int idx = 0;
+
+__attribute__((constructor(1000))) static void prio1000() {
+ record[idx++] = "prio1000";
+}
+
+__attribute__((constructor(1))) static void prio1() {
+ record[idx++] = "prio1";
+}
+
+__attribute__((constructor)) static void noprio() {
+ record[idx++] = "noprio";
+}
+
+TEST(prio_ctor, order) {
+ EXPECT_EQ(idx, 3);
+ EXPECT_STREQ(record[0], "prio1");
+ EXPECT_STREQ(record[1], "prio1000");
+ EXPECT_STREQ(record[2], "noprio");
+}