am 2bd88d45
: Merge "Run constructors before resolving ifunc functions"
* commit '2bd88d450ead2ad8b44a76126c720cf4a9772002': Run constructors before resolving ifunc functions
This commit is contained in:
commit
87c5f24e4e
@ -80,6 +80,4 @@ typedef struct {
|
||||
#define STT_LOPROC 13
|
||||
#define STT_HIPROC 15
|
||||
|
||||
#define R_386_IRELATIVE 42
|
||||
|
||||
#endif /* _ELF_H */
|
||||
|
@ -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<ElfW(Addr)(*)()>(s->st_value + si->base);
|
||||
s->st_value = (ifunc_ptr() - si->base);
|
||||
ifunc_ptr = reinterpret_cast<ElfW(Addr)(*)()>(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) {
|
||||
@ -809,10 +812,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;
|
||||
}
|
||||
|
||||
@ -1567,6 +1566,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() {
|
||||
|
@ -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
|
||||
|
@ -355,6 +355,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))
|
||||
|
@ -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<fn_ptr>(dlsym(handle, "foo"));
|
||||
fn_ptr foo_library_ptr = reinterpret_cast<fn_ptr>(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<fn_ptr>(dlsym(handle, "foo"));
|
||||
foo_library_ptr = reinterpret_cast<fn_ptr>(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<fn_ptr>(dlsym(handle, "is_ctor_called"));
|
||||
ASSERT_TRUE(is_ctor_called != NULL) << dlerror();
|
||||
ASSERT_STREQ("true", is_ctor_called());
|
||||
dlclose(handle);
|
||||
}
|
||||
#endif
|
||||
|
@ -17,7 +17,22 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user