From bd321c1106ed30a71d55d5c365335dfe552b0883 Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Thu, 21 Aug 2014 13:54:03 -0700 Subject: [PATCH] Run constructors before resolving ifunc functions Bug: 17177284 (cherry picked from commit 9598b8c415e2fa9f240508185fe8c964b83f538d) Change-Id: I2c9631ee1cd77f8cf95ec0216a35b605c8786454 --- libc/include/elf.h | 2 -- linker/linker.cpp | 23 ++++++++++++----------- linker/linker.h | 1 + tests/Android.mk | 16 ++++++++++++++++ tests/dlfcn_test.cpp | 30 ++++++++++++++++++++---------- tests/libs/dlopen_testlib_ifunc.c | 21 ++++++++++++++++++++- 6 files changed, 69 insertions(+), 24 deletions(-) diff --git a/libc/include/elf.h b/libc/include/elf.h index faae73e92..7a9485aea 100644 --- a/libc/include/elf.h +++ b/libc/include/elf.h @@ -80,6 +80,4 @@ typedef struct { #define STT_LOPROC 13 #define STT_HIPROC 15 -#define R_386_IRELATIVE 42 - #endif /* _ELF_H */ diff --git a/linker/linker.cpp b/linker/linker.cpp index b28a01d49..ec0681fe6 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -466,14 +466,17 @@ static ElfW(Sym)* soinfo_elf_lookup(soinfo* si, unsigned hash, const char* name) return NULL; } -static void resolve_ifunc_symbols(soinfo* si) { +void soinfo::resolve_ifunc_symbols() { + if (!get_has_ifuncs()) { + return; + } - phdr_table_unprotect_segments(si->phdr, si->phnum, si->load_bias); + phdr_table_unprotect_segments(phdr, phnum, load_bias); TRACE_TYPE(IFUNC, "CHECKING FOR IFUNCS AND PERFORMING SYMBOL UPDATES"); - for (size_t i = 0; i < si->nchain; ++i) { - ElfW(Sym)* s = &si->symtab[i]; + for (size_t i = 0; i < nchain; ++i) { + ElfW(Sym)* s = &symtab[i]; if (ELF_ST_TYPE(s->st_info) == STT_GNU_IFUNC) { // The address of the ifunc in the symbol table is the address of the // function that chooses the function to which the ifunc will refer. @@ -481,12 +484,12 @@ static void resolve_ifunc_symbols(soinfo* si) { // in the linker and then return its result (minus the base offset). TRACE_TYPE(IFUNC, "FOUND IFUNC"); ElfW(Addr) (*ifunc_ptr)(); - ifunc_ptr = reinterpret_cast(s->st_value + si->base); - s->st_value = (ifunc_ptr() - si->base); + ifunc_ptr = reinterpret_cast(s->st_value + base); + s->st_value = (ifunc_ptr() - base); TRACE_TYPE(IFUNC, "NEW VALUE IS %p", (void*)s->st_value); } } - phdr_table_protect_segments(si->phdr, si->phnum, si->load_bias); + phdr_table_protect_segments(phdr, phnum, load_bias); } static unsigned elfhash(const char* _name) { @@ -827,10 +830,6 @@ static soinfo* load_library(const char* name, int dlflags, const android_dlextin return NULL; } - // if the library has any ifuncs, we will need to resolve them so that dlsym - // can handle them properly - resolve_ifunc_symbols(si); - return si; } @@ -1599,6 +1598,8 @@ void soinfo::CallConstructors() { // DT_INIT should be called before DT_INIT_ARRAY if both are present. CallFunction("DT_INIT", init_func); CallArray("DT_INIT_ARRAY", init_array, init_array_count, false); + + resolve_ifunc_symbols(); } void soinfo::CallDestructors() { diff --git a/linker/linker.h b/linker/linker.h index d7cf24bfa..684561a88 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -216,6 +216,7 @@ struct soinfo { private: void CallArray(const char* array_name, linker_function_t* functions, size_t count, bool reverse); void CallFunction(const char* function_name, linker_function_t function); + void resolve_ifunc_symbols(); private: // This part of the structure is only available diff --git a/tests/Android.mk b/tests/Android.mk index 8184bf74b..9ee33e004 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -356,6 +356,22 @@ bionic-unit-tests-run-on-host: bionic-unit-tests $(TARGET_OUT_EXECUTABLES)/$(LIN $(TARGET_OUT_DATA_NATIVE_TESTS)/bionic-unit-tests/bionic-unit-tests$(NATIVE_TEST_SUFFIX) $(BIONIC_TEST_FLAGS) endif +ifeq ($(TARGET_ARCH),$(filter $(TARGET_ARCH),x86_64)) +# add target to run lp32 tests +bionic-unit-tests-run-on-host32: bionic-unit-tests $(TARGET_OUT_EXECUTABLES)/$(LINKER) $(TARGET_OUT_EXECUTABLES)/sh + if [ ! -d /system -o ! -d /system/bin ]; then \ + echo "Attempting to create /system/bin"; \ + sudo mkdir -p -m 0777 /system/bin; \ + fi + mkdir -p $(TARGET_OUT_DATA)/local/tmp + cp $(TARGET_OUT_EXECUTABLES)/linker /system/bin + cp $(TARGET_OUT_EXECUTABLES)/sh /system/bin + ANDROID_DATA=$(TARGET_OUT_DATA) \ + ANDROID_ROOT=$(TARGET_OUT) \ + LD_LIBRARY_PATH=$(2ND_TARGET_OUT_SHARED_LIBRARIES) \ + $(2ND_TARGET_OUT_DATA_NATIVE_TESTS)/bionic-unit-tests/bionic-unit-tests32 $(BIONIC_TEST_FLAGS) +endif + endif # linux-x86 include $(call first-makefiles-under,$(LOCAL_PATH)) diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp index 260cbd601..6de38c89e 100644 --- a/tests/dlfcn_test.cpp +++ b/tests/dlfcn_test.cpp @@ -90,32 +90,42 @@ TEST(dlfcn, dlopen_noload) { // ifuncs are only supported on intel for now #if defined(__i386__) || defined(__x86_64__) TEST(dlfcn, ifunc) { - const char* (*foo_ptr)(); - const char* (*foo_library_ptr)(); + typedef const char* (*fn_ptr)(); // ifunc's choice depends on whether IFUNC_CHOICE has a value // first check the set case setenv("IFUNC_CHOICE", "set", 1); void* handle = dlopen("libtest_ifunc.so", RTLD_NOW); ASSERT_TRUE(handle != NULL); - *(void **)(&foo_ptr) = dlsym(handle, "foo"); - *(void **)(&foo_library_ptr) = dlsym(handle, "foo_library"); + fn_ptr foo_ptr = reinterpret_cast(dlsym(handle, "foo")); + fn_ptr foo_library_ptr = reinterpret_cast(dlsym(handle, "foo_library")); ASSERT_TRUE(foo_ptr != NULL); ASSERT_TRUE(foo_library_ptr != NULL); - ASSERT_EQ(strncmp("set", (*foo_ptr)(), 3), 0); - ASSERT_EQ(strncmp("set", (*foo_library_ptr)(), 3), 0); + ASSERT_EQ(strncmp("set", foo_ptr(), 3), 0); + ASSERT_EQ(strncmp("set", foo_library_ptr(), 3), 0); dlclose(handle); // then check the unset case unsetenv("IFUNC_CHOICE"); handle = dlopen("libtest_ifunc.so", RTLD_NOW); ASSERT_TRUE(handle != NULL); - *(void **)(&foo_ptr) = dlsym(handle, "foo"); - *(void **)(&foo_library_ptr) = dlsym(handle, "foo_library"); + foo_ptr = reinterpret_cast(dlsym(handle, "foo")); + foo_library_ptr = reinterpret_cast(dlsym(handle, "foo_library")); ASSERT_TRUE(foo_ptr != NULL); ASSERT_TRUE(foo_library_ptr != NULL); - ASSERT_EQ(strncmp("unset", (*foo_ptr)(), 5), 0); - ASSERT_EQ(strncmp("unset", (*foo_library_ptr)(), 3), 0); + ASSERT_EQ(strncmp("unset", foo_ptr(), 5), 0); + ASSERT_EQ(strncmp("unset", foo_library_ptr(), 3), 0); + dlclose(handle); +} + +TEST(dlfcn, ifunc_ctor_call) { + typedef const char* (*fn_ptr)(); + + void* handle = dlopen("libtest_ifunc.so", RTLD_NOW); + ASSERT_TRUE(handle != NULL) << dlerror(); + fn_ptr is_ctor_called = reinterpret_cast(dlsym(handle, "is_ctor_called")); + ASSERT_TRUE(is_ctor_called != NULL) << dlerror(); + ASSERT_STREQ("true", is_ctor_called()); dlclose(handle); } #endif diff --git a/tests/libs/dlopen_testlib_ifunc.c b/tests/libs/dlopen_testlib_ifunc.c index 1c4baface..48748417d 100644 --- a/tests/libs/dlopen_testlib_ifunc.c +++ b/tests/libs/dlopen_testlib_ifunc.c @@ -17,7 +17,22 @@ #include #include +static int g_flag = 0; + +static void __attribute__((constructor)) init_flag() { + g_flag = 1; +} + const char* foo() __attribute__ ((ifunc ("foo_ifunc"))); +const char* is_ctor_called() __attribute__ ((ifunc("is_ctor_called_ifun"))); + +const char* return_true() { + return "true"; +} + +const char* return_false() { + return "false"; +} const char* f1() { return "unset"; @@ -27,6 +42,10 @@ const char* f2() { return "set"; } +void* is_ctor_called_ifun() { + return g_flag == 0 ? return_false : return_true; +} + void* foo_ifunc() { char* choice = getenv("IFUNC_CHOICE"); return choice == NULL ? f1 : f2; @@ -34,4 +53,4 @@ void* foo_ifunc() { const char* foo_library() { return foo(); -} \ No newline at end of file +}