From cfa97f172dc1b10d650fefbb6ccffd88ce72a5fb Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Tue, 21 Oct 2014 09:23:18 -0700 Subject: [PATCH] Fix relocation to look for symbols in local group The local group is a sequence of libraries in default (breadth-first) order. It allows RTLD_LOCALLY loaded library to correctly relocate symbols within its group (see test-cases). Local group lookup is performed after main executable and ld_preloads. Bug: 2643900 Bug: 15432753 Change-Id: I9bb013b46d17dbb5cbdfb8fef26f552748385541 --- linker/linked_list.h | 4 +- linker/linker.cpp | 193 +++++++++++++-------- linker/linker.h | 6 +- tests/dlfcn_test.cpp | 186 ++++++++++++++++++-- .../libs/Android.build.dlopen_check_order_dlsym.mk | 90 ++++++++++ ...ild.dlopen_check_order_reloc_main_executable.mk | 56 ++++++ ...roid.build.dlopen_check_order_reloc_siblings.mk | 133 ++++++++++++++ tests/libs/Android.mk | 75 +------- ...wer.cpp => dlopen_check_order_dlsym_answer.cpp} | 4 +- tests/libs/dlopen_check_order_reloc_answer.cpp | 23 +++ .../libs/dlopen_check_order_reloc_answer_impl.cpp | 19 ++ .../dlopen_check_order_reloc_nephew_answer.cpp | 21 +++ .../libs/dlopen_check_order_reloc_root_answer.cpp | 21 +++ .../dlopen_check_order_reloc_root_answer_impl.cpp | 19 ++ 14 files changed, 688 insertions(+), 162 deletions(-) create mode 100644 tests/libs/Android.build.dlopen_check_order_dlsym.mk create mode 100644 tests/libs/Android.build.dlopen_check_order_reloc_main_executable.mk create mode 100644 tests/libs/Android.build.dlopen_check_order_reloc_siblings.mk rename tests/libs/{dlopen_testlib_answer.cpp => dlopen_check_order_dlsym_answer.cpp} (87%) create mode 100644 tests/libs/dlopen_check_order_reloc_answer.cpp create mode 100644 tests/libs/dlopen_check_order_reloc_answer_impl.cpp create mode 100644 tests/libs/dlopen_check_order_reloc_nephew_answer.cpp create mode 100644 tests/libs/dlopen_check_order_reloc_root_answer.cpp create mode 100644 tests/libs/dlopen_check_order_reloc_root_answer_impl.cpp diff --git a/linker/linked_list.h b/linker/linked_list.h index 4e62e208f..72a32b4ba 100644 --- a/linker/linked_list.h +++ b/linker/linked_list.h @@ -86,7 +86,7 @@ class LinkedList { } template - void for_each(F action) { + void for_each(F action) const { visit([&] (T* si) { action(si); return true; @@ -94,7 +94,7 @@ class LinkedList { } template - bool visit(F action) { + bool visit(F action) const { for (LinkedListEntry* e = head_; e != nullptr; e = e->next) { if (!action(e->element)) { return false; diff --git a/linker/linker.cpp b/linker/linker.cpp index 41557e231..eb1a483ae 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -415,7 +415,7 @@ int dl_iterate_phdr(int (*cb)(dl_phdr_info* info, size_t size, void* data), void return rv; } -static ElfW(Sym)* soinfo_elf_lookup(soinfo* si, unsigned hash, const char* name) { +static ElfW(Sym)* soinfo_elf_lookup(const soinfo* si, unsigned hash, const char* name) { ElfW(Sym)* symtab = si->symtab; TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p %x %zd", @@ -481,7 +481,7 @@ static unsigned elfhash(const char* _name) { return h; } -static ElfW(Sym)* soinfo_do_lookup(soinfo* si, const char* name, soinfo** lsi) { +static ElfW(Sym)* soinfo_do_lookup(soinfo* si, const char* name, soinfo** lsi, const soinfo::soinfo_list_t& local_group) { unsigned elf_hash = elfhash(name); ElfW(Sym)* s = nullptr; @@ -527,16 +527,21 @@ static ElfW(Sym)* soinfo_do_lookup(soinfo* si, const char* name, soinfo** lsi) { } } - /* Look for symbols in the local scope (the object who is - * searching). This happens with C++ templates on x86 for some - * reason. - * - * Notes on weak symbols: - * The ELF specs are ambiguous about treatment of weak definitions in - * dynamic linking. Some systems return the first definition found - * and some the first non-weak definition. This is system dependent. - * Here we return the first definition found for simplicity. */ + // 3. Look for it in the local group + if (s == nullptr) { + local_group.visit([&](soinfo* local_si) { + DEBUG("%s: looking up %s in %s (from local group)", si->name, name, local_si->name); + s = soinfo_elf_lookup(local_si, elf_hash, name); + if (s != nullptr) { + *lsi = local_si; + return false; + } + return true; + }); + } + + // 4. Look for it in this library (unless we already did it because of DT_SYMBOLIC) if (s == nullptr && !si->has_DT_SYMBOLIC) { DEBUG("%s: looking up %s in local scope", si->name, name); s = soinfo_elf_lookup(si, elf_hash, name); @@ -545,6 +550,7 @@ static ElfW(Sym)* soinfo_do_lookup(soinfo* si, const char* name, soinfo** lsi) { } } + // 5. Dependencies if (s == nullptr) { si->get_children().visit([&](soinfo* child) { DEBUG("%s: looking up %s in %s", si->name, name, child->name); @@ -643,33 +649,61 @@ typedef linked_list_t StringLinkedList; typedef linked_list_t LoadTaskList; -// This is used by dlsym(3). It performs symbol lookup only within the -// specified soinfo object and its dependencies in breadth first order. -ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name) { +// This function walks down the tree of soinfo dependencies +// in breadth-first order and +// * calls action(soinfo* si) for each node, and +// * terminates walk if action returns false. +// +// walk_dependencies_tree returns false if walk was terminated +// by the action and true otherwise. +template +static bool walk_dependencies_tree(soinfo* root_soinfos[], size_t root_soinfos_size, F action) { SoinfoLinkedList visit_list; SoinfoLinkedList visited; - visit_list.push_back(si); - soinfo* current_soinfo; - while ((current_soinfo = visit_list.pop_front()) != nullptr) { - if (visited.contains(current_soinfo)) { + for (size_t i = 0; i < root_soinfos_size; ++i) { + visit_list.push_back(root_soinfos[i]); + } + + soinfo* si; + while ((si = visit_list.pop_front()) != nullptr) { + if (visited.contains(si)) { continue; } - ElfW(Sym)* result = soinfo_elf_lookup(current_soinfo, elfhash(name), name); - - if (result != nullptr) { - *found = current_soinfo; - return result; + if (!action(si)) { + return false; } - visited.push_back(current_soinfo); - current_soinfo->get_children().for_each([&](soinfo* child) { + visited.push_back(si); + + si->get_children().for_each([&](soinfo* child) { visit_list.push_back(child); }); } - return nullptr; + return true; +} + + +// This is used by dlsym(3). It performs symbol lookup only within the +// specified soinfo object and its dependencies in breadth first order. +ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name) { + ElfW(Sym)* result = nullptr; + uint32_t elf_hash = elfhash(name); + + + walk_dependencies_tree(&si, 1, [&](soinfo* current_soinfo) { + result = soinfo_elf_lookup(current_soinfo, elf_hash, name); + if (result != nullptr) { + *found = current_soinfo; + return false; + } + + return true; + }); + + return result; } /* This is used by dlsym(3) to performs a global symbol lookup. If the @@ -899,19 +933,30 @@ static bool is_recursive(soinfo* si, soinfo* parent) { }); } -static bool find_libraries(const char* const library_names[], size_t library_names_size, soinfo* soinfos[], - soinfo* ld_preloads[], size_t ld_preloads_size, int rtld_flags, const android_dlextinfo* extinfo) { +static bool find_libraries(soinfo* start_with, const char* const library_names[], size_t library_names_count, soinfo* soinfos[], + soinfo* ld_preloads[], size_t ld_preloads_count, int rtld_flags, const android_dlextinfo* extinfo) { // Step 0: prepare. LoadTaskList load_tasks; - for (size_t i = 0; i < library_names_size; ++i) { + for (size_t i = 0; i < library_names_count; ++i) { const char* name = library_names[i]; - load_tasks.push_back(LoadTask::create(name, nullptr)); + load_tasks.push_back(LoadTask::create(name, start_with)); + } + + // If soinfos array is null allocate one on stack. + // The array is needed in case of failure; for example + // when library_names[] = {libone.so, libtwo.so} and libone.so + // is loaded correctly but libtwo.so failed for some reason. + // In this case libone.so should be unloaded on return. + // See also implementation of failure_guard below. + + if (soinfos == nullptr) { + size_t soinfos_size = sizeof(soinfo*)*library_names_count; + soinfos = reinterpret_cast(alloca(soinfos_size)); + memset(soinfos, 0, soinfos_size); } - // Libraries added to this list in reverse order so that we can - // start linking from bottom-up - see step 2. - SoinfoLinkedList found_libs; - size_t soinfos_size = 0; + // list of libraries to link - see step 2. + size_t soinfos_count = 0; auto failure_guard = make_scope_guard([&]() { // Housekeeping @@ -919,7 +964,7 @@ static bool find_libraries(const char* const library_names[], size_t library_nam LoadTask::deleter(t); }); - for (size_t i = 0; iadd_child(si); } - found_libs.push_front(si); - // When ld_preloads is not null first - // ld_preloads_size libs are in fact ld_preloads. - if (ld_preloads != nullptr && soinfos_size < ld_preloads_size) { - ld_preloads[soinfos_size] = si; + // When ld_preloads is not null, the first + // ld_preloads_count libs are in fact ld_preloads. + if (ld_preloads != nullptr && soinfos_count < ld_preloads_count) { + ld_preloads[soinfos_count] = si; } - if (soinfos_sizeflags & FLAG_LINKED) == 0) { - if (!si->LinkImage(extinfo)) { + if (!si->LinkImage(local_group, extinfo)) { return false; } si->flags |= FLAG_LINKED; } + + return true; + }); + + if (linked) { + failure_guard.disable(); } - // All is well - found_libs and load_tasks are empty at this point - // and all libs are successfully linked. - failure_guard.disable(); - return true; + return linked; } static soinfo* find_library(const char* name, int rtld_flags, const android_dlextinfo* extinfo) { @@ -979,7 +1034,7 @@ static soinfo* find_library(const char* name, int rtld_flags, const android_dlex soinfo* si; - if (!find_libraries(&name, 1, &si, nullptr, 0, rtld_flags, extinfo)) { + if (!find_libraries(nullptr, &name, 1, &si, nullptr, 0, rtld_flags, extinfo)) { return nullptr; } @@ -1090,7 +1145,7 @@ static ElfW(Addr) call_ifunc_resolver(ElfW(Addr) resolver_addr) { } #if defined(USE_RELA) -int soinfo::Relocate(ElfW(Rela)* rela, unsigned count) { +int soinfo::Relocate(ElfW(Rela)* rela, unsigned count, const soinfo_list_t& local_group) { for (size_t idx = 0; idx < count; ++idx, ++rela) { unsigned type = ELFW(R_TYPE)(rela->r_info); unsigned sym = ELFW(R_SYM)(rela->r_info); @@ -1108,7 +1163,7 @@ int soinfo::Relocate(ElfW(Rela)* rela, unsigned count) { if (sym != 0) { sym_name = get_string(symtab[sym].st_name); - s = soinfo_do_lookup(this, sym_name, &lsi); + s = soinfo_do_lookup(this, sym_name, &lsi, local_group); if (s == nullptr) { // We only allow an undefined symbol if this is a weak reference... s = &symtab[sym]; @@ -1367,7 +1422,7 @@ int soinfo::Relocate(ElfW(Rela)* rela, unsigned count) { } #else // REL, not RELA. -int soinfo::Relocate(ElfW(Rel)* rel, unsigned count) { +int soinfo::Relocate(ElfW(Rel)* rel, unsigned count, const soinfo_list_t& local_group) { for (size_t idx = 0; idx < count; ++idx, ++rel) { unsigned type = ELFW(R_TYPE)(rel->r_info); // TODO: don't use unsigned for 'sym'. Use uint32_t or ElfW(Addr) instead. @@ -1386,7 +1441,7 @@ int soinfo::Relocate(ElfW(Rel)* rel, unsigned count) { if (sym != 0) { sym_name = get_string(symtab[sym].st_name); - s = soinfo_do_lookup(this, sym_name, &lsi); + s = soinfo_do_lookup(this, sym_name, &lsi, local_group); if (s == nullptr) { // We only allow an undefined symbol if this is a weak reference... s = &symtab[sym]; @@ -1572,7 +1627,7 @@ int soinfo::Relocate(ElfW(Rel)* rel, unsigned count) { #endif #if defined(__mips__) -static bool mips_relocate_got(soinfo* si) { +static bool mips_relocate_got(soinfo* si, const soinfo::soinfo_list_t& local_group) { ElfW(Addr)** got = si->plt_got; if (got == nullptr) { return true; @@ -1605,7 +1660,7 @@ static bool mips_relocate_got(soinfo* si) { // This is an undefined reference... try to locate it. const char* sym_name = si->get_string(sym->st_name); soinfo* lsi = nullptr; - ElfW(Sym)* s = soinfo_do_lookup(si, sym_name, &lsi); + ElfW(Sym)* s = soinfo_do_lookup(si, sym_name, &lsi, local_group); if (s == nullptr) { // We only allow an undefined symbol if this is a weak reference. s = &symtab[g]; @@ -2198,7 +2253,7 @@ bool soinfo::PrelinkImage() { return true; } -bool soinfo::LinkImage(const android_dlextinfo* extinfo) { +bool soinfo::LinkImage(const soinfo_list_t& local_group, const android_dlextinfo* extinfo) { #if !defined(__LP64__) if (has_text_relocations) { @@ -2217,26 +2272,26 @@ bool soinfo::LinkImage(const android_dlextinfo* extinfo) { #if defined(USE_RELA) if (rela != nullptr) { DEBUG("[ relocating %s ]", name); - if (Relocate(rela, rela_count)) { + if (Relocate(rela, rela_count, local_group)) { return false; } } if (plt_rela != nullptr) { DEBUG("[ relocating %s plt ]", name); - if (Relocate(plt_rela, plt_rela_count)) { + if (Relocate(plt_rela, plt_rela_count, local_group)) { return false; } } #else if (rel != nullptr) { DEBUG("[ relocating %s ]", name); - if (Relocate(rel, rel_count)) { + if (Relocate(rel, rel_count, local_group)) { return false; } } if (plt_rel != nullptr) { DEBUG("[ relocating %s plt ]", name); - if (Relocate(plt_rel, plt_rel_count)) { + if (Relocate(plt_rel, plt_rel_count, local_group)) { return false; } } @@ -2310,7 +2365,7 @@ static void add_vdso(KernelArgumentBlock& args __unused) { si->load_bias = get_elf_exec_load_bias(ehdr_vdso); si->PrelinkImage(); - si->LinkImage(nullptr); + si->LinkImage(g_empty_list, nullptr); #endif } @@ -2456,21 +2511,11 @@ static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args, ElfW( }); const char* needed_library_names[needed_libraries_count]; - soinfo* needed_library_si[needed_libraries_count]; memset(needed_library_names, 0, sizeof(needed_library_names)); needed_library_name_list.copy_to_array(needed_library_names, needed_libraries_count); - if (needed_libraries_count > 0 && !find_libraries(needed_library_names, needed_libraries_count, needed_library_si, g_ld_preloads, ld_preloads_count, RTLD_GLOBAL, nullptr)) { - __libc_format_fd(2, "CANNOT LINK EXECUTABLE DEPENDENCIES: %s\n", linker_get_error_buffer()); - exit(EXIT_FAILURE); - } - - for (size_t i = 0; iadd_child(needed_library_si[i]); - } - - if (!si->LinkImage(nullptr)) { + if (needed_libraries_count > 0 && !find_libraries(si, needed_library_names, needed_libraries_count, nullptr, g_ld_preloads, ld_preloads_count, RTLD_GLOBAL, nullptr)) { __libc_format_fd(2, "CANNOT LINK EXECUTABLE: %s\n", linker_get_error_buffer()); exit(EXIT_FAILURE); } @@ -2594,7 +2639,7 @@ extern "C" ElfW(Addr) __linker_init(void* raw_args) { linker_so.phnum = elf_hdr->e_phnum; linker_so.flags |= FLAG_LINKER; - if (!(linker_so.PrelinkImage() && linker_so.LinkImage(nullptr))) { + if (!(linker_so.PrelinkImage() && linker_so.LinkImage(g_empty_list, nullptr))) { // It would be nice to print an error message, but if the linker // can't link itself, there's no guarantee that we'll be able to // call write() (because it involves a GOT reference). We may as diff --git a/linker/linker.h b/linker/linker.h index 08dd625bb..02cfa10bd 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -207,7 +207,7 @@ struct soinfo { void CallDestructors(); void CallPreInitConstructors(); bool PrelinkImage(); - bool LinkImage(const android_dlextinfo* extinfo); + bool LinkImage(const soinfo_list_t& local_group, const android_dlextinfo* extinfo); void add_child(soinfo* child); void remove_all_links(); @@ -234,9 +234,9 @@ struct soinfo { 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); #if defined(USE_RELA) - int Relocate(ElfW(Rela)* rela, unsigned count); + int Relocate(ElfW(Rela)* rela, unsigned count, const soinfo_list_t& local_group); #else - int Relocate(ElfW(Rel)* rel, unsigned count); + int Relocate(ElfW(Rel)* rel, unsigned count, const soinfo_list_t& local_group); #endif private: diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp index 1d7c29a54..2ab3dc1ed 100644 --- a/tests/dlfcn_test.cpp +++ b/tests/dlfcn_test.cpp @@ -162,39 +162,39 @@ TEST(dlfcn, dlopen_check_relocation_dt_needed_order) { ASSERT_EQ(1, fn()); } -TEST(dlfcn, dlopen_check_order) { +TEST(dlfcn, dlopen_check_order_dlsym) { // Here is how the test library and its dt_needed // libraries are arranged // - // libtest_check_order.so + // libtest_check_order_children.so // | - // +-> libtest_check_order_1_left.so + // +-> ..._1_left.so // | | - // | +-> libtest_check_order_a.so + // | +-> ..._a.so // | | - // | +-> libtest_check_order_b.so + // | +-> ...r_b.so // | - // +-> libtest_check_order_2_right.so + // +-> ..._2_right.so // | | - // | +-> libtest_check_order_d.so + // | +-> ..._d.so // | | - // | +-> libtest_check_order_b.so + // | +-> ..._b.so // | - // +-> libtest_check_order_3_c.so + // +-> ..._3_c.so // // load order should be (1, 2, 3, a, b, d) // // get_answer() is defined in (2, 3, a, b, c) // get_answer2() is defined in (b, d) - void* sym = dlsym(RTLD_DEFAULT, "dlopen_test_get_answer"); + void* sym = dlsym(RTLD_DEFAULT, "check_order_dlsym_get_answer"); ASSERT_TRUE(sym == nullptr); - void* handle = dlopen("libtest_check_order.so", RTLD_NOW | RTLD_GLOBAL); - ASSERT_TRUE(handle != nullptr); + void* handle = dlopen("libtest_check_order_dlsym.so", RTLD_NOW | RTLD_GLOBAL); + ASSERT_TRUE(handle != nullptr) << dlerror(); typedef int (*fn_t) (void); fn_t fn, fn2; - fn = reinterpret_cast(dlsym(RTLD_DEFAULT, "dlopen_test_get_answer")); + fn = reinterpret_cast(dlsym(RTLD_DEFAULT, "check_order_dlsym_get_answer")); ASSERT_TRUE(fn != NULL) << dlerror(); - fn2 = reinterpret_cast(dlsym(RTLD_DEFAULT, "dlopen_test_get_answer2")); + fn2 = reinterpret_cast(dlsym(RTLD_DEFAULT, "check_order_dlsym_get_answer2")); ASSERT_TRUE(fn2 != NULL) << dlerror(); ASSERT_EQ(42, fn()); @@ -202,6 +202,163 @@ TEST(dlfcn, dlopen_check_order) { dlclose(handle); } +TEST(dlfcn, dlopen_check_order_reloc_siblings) { + // This is how this one works: + // we lookup and call get_answer which is defined in '_2.so' + // and in turn calls external get_answer_impl() defined in _1.so and in '_[a-f].so' + // the correct _impl() is implemented by '_a.so'; + // + // Note that this is test for RTLD_LOCAL (TODO: test for GLOBAL?) + // + // Here is the picture: + // + // libtest_check_order_reloc_siblings.so + // | + // +-> ..._1.so <- empty + // | | + // | +-> ..._a.so <- exports correct answer_impl() + // | | + // | +-> ..._b.so <- every other letter exporting incorrect one. + // | + // +-> ..._2.so <- empty + // | | + // | +-> ..._c.so + // | | + // | +-> ..._d.so + // | + // +-> ..._3.so <- empty + // | + // +-> ..._e.so + // | + // +-> ..._f.so <- exports get_answer() that calls get_anser_impl(); + // implements incorrect get_answer_impl() + + void* handle = dlopen("libtest_check_order_reloc_siblings.so", RTLD_NOW | RTLD_NOLOAD); + ASSERT_TRUE(handle == nullptr); +#ifdef __BIONIC__ + // TODO: glibc returns nullptr on dlerror() here. Is it bug? + ASSERT_STREQ("dlopen failed: library \"libtest_check_order_reloc_siblings.so\" wasn't loaded and RTLD_NOLOAD prevented it", dlerror()); +#endif + + handle = dlopen("libtest_check_order_reloc_siblings.so", RTLD_NOW | RTLD_LOCAL); + ASSERT_TRUE(handle != nullptr) << dlerror(); + + typedef int (*fn_t) (void); + fn_t fn = reinterpret_cast(dlsym(handle, "check_order_reloc_get_answer")); + ASSERT_TRUE(fn != nullptr) << dlerror(); + ASSERT_EQ(42, fn()); + + ASSERT_EQ(0, dlclose(handle)); +} + +TEST(dlfcn, dlopen_check_order_reloc_siblings_with_preload) { + // This test uses the same library as dlopen_check_order_reloc_siblings. + // Unlike dlopen_check_order_reloc_siblings it preloads + // libtest_check_order_reloc_siblings_1.so (first dependency) prior to + // dlopen(libtest_check_order_reloc_siblings.so) + + void* handle = dlopen("libtest_check_order_reloc_siblings.so", RTLD_NOW | RTLD_NOLOAD); + ASSERT_TRUE(handle == nullptr); + handle = dlopen("libtest_check_order_reloc_siblings_1.so", RTLD_NOW | RTLD_NOLOAD); + ASSERT_TRUE(handle == nullptr); + + void* handle_for_1 = dlopen("libtest_check_order_reloc_siblings_1.so", RTLD_NOW | RTLD_LOCAL); + ASSERT_TRUE(handle_for_1 != nullptr) << dlerror(); + + handle = dlopen("libtest_check_order_reloc_siblings.so", RTLD_NOW | RTLD_LOCAL); + ASSERT_TRUE(handle != nullptr) << dlerror(); + + ASSERT_EQ(0, dlclose(handle_for_1)); + + typedef int (*fn_t) (void); + fn_t fn = reinterpret_cast(dlsym(handle, "check_order_reloc_get_answer")); + ASSERT_TRUE(fn != nullptr) << dlerror(); + ASSERT_EQ(42, fn()); + + ASSERT_EQ(0, dlclose(handle)); +} + +TEST(dlfcn, dlopen_check_order_reloc_nephew) { + // This is how this one works: + // we lookup and call nephew_get_answer which is defined in '_2.so' + // and in turn calls external get_answer_impl() defined in '_[a-f].so' + // the correct _impl() is implemented by '_a.so'; + // + // Here is the picture: + // + // libtest_check_order_reloc_siblings.so + // | + // +-> ..._1.so <- empty + // | | + // | +-> ..._a.so <- exports correct answer_impl() + // | | + // | +-> ..._b.so <- every other letter exporting incorrect one. + // | + // +-> ..._2.so <- empty + // | | + // | +-> ..._c.so + // | | + // | +-> ..._d.so + // | + // +-> ..._3.so <- nephew_get_answer() that calls get_answer_impl(); + // | + // +-> ..._e.so + // | + // +-> ..._f.so + + void* handle = dlopen("libtest_check_order_reloc_siblings.so", RTLD_NOW | RTLD_NOLOAD); + ASSERT_TRUE(handle == nullptr); +#ifdef __BIONIC__ + // TODO: glibc returns nullptr on dlerror() here. Is it bug? + ASSERT_STREQ("dlopen failed: library \"libtest_check_order_reloc_siblings.so\" wasn't loaded and RTLD_NOLOAD prevented it", dlerror()); +#endif + + handle = dlopen("libtest_check_order_reloc_siblings.so", RTLD_NOW | RTLD_LOCAL); + ASSERT_TRUE(handle != nullptr) << dlerror(); + + typedef int (*fn_t) (void); + fn_t fn = reinterpret_cast(dlsym(handle, "check_order_reloc_nephew_get_answer")); + ASSERT_TRUE(fn != nullptr) << dlerror(); + ASSERT_EQ(42, fn()); + + ASSERT_EQ(0, dlclose(handle)); +} + +extern "C" int check_order_reloc_root_get_answer_impl() { + return 42; +} + +TEST(dlfcn, dlopen_check_order_reloc_main_executable) { + // This is how this one works: + // we lookup and call get_answer3 which is defined in 'root.so' + // and in turn calls external root_get_answer_impl() defined in _2.so and + // above the correct _impl() is one in the executable. + // + // libtest_check_order_reloc_root.so + // | + // +-> ..._1.so <- empty + // | + // +-> ..._2.so <- gives incorrect answer for answer_main_impl() + // + + void* handle = dlopen("libtest_check_order_reloc_root.so", RTLD_NOW | RTLD_NOLOAD); + ASSERT_TRUE(handle == nullptr); +#ifdef __BIONIC__ + // TODO: glibc returns nullptr on dlerror() here. Is it bug? + ASSERT_STREQ("dlopen failed: library \"libtest_check_order_reloc_root.so\" wasn't loaded and RTLD_NOLOAD prevented it", dlerror()); +#endif + + handle = dlopen("libtest_check_order_reloc_root.so", RTLD_NOW | RTLD_LOCAL); + ASSERT_TRUE(handle != nullptr) << dlerror(); + + typedef int (*fn_t) (void); + fn_t fn = reinterpret_cast(dlsym(handle, "check_order_reloc_root_get_answer")); + ASSERT_TRUE(fn != nullptr) << dlerror(); + ASSERT_EQ(42, fn()); + + ASSERT_EQ(0, dlclose(handle)); +} + TEST(dlfcn, dlopen_check_rtld_local) { void* sym = dlsym(RTLD_DEFAULT, "dlopen_testlib_simple_func"); ASSERT_TRUE(sym == nullptr); @@ -342,7 +499,6 @@ TEST(dlfcn, dlopen_nodelete_dt_flags_1) { ASSERT_TRUE(!is_unloaded); } - TEST(dlfcn, dlopen_failure) { void* self = dlopen("/does/not/exist", RTLD_NOW); ASSERT_TRUE(self == NULL); diff --git a/tests/libs/Android.build.dlopen_check_order_dlsym.mk b/tests/libs/Android.build.dlopen_check_order_dlsym.mk new file mode 100644 index 000000000..73d8c1a83 --- /dev/null +++ b/tests/libs/Android.build.dlopen_check_order_dlsym.mk @@ -0,0 +1,90 @@ +# +# Copyright (C) 2012 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. +# + +# ----------------------------------------------------------------------------- +# Libraries used by dlfcn tests to verify correct load order: +# libtest_check_order_2_right.so +# ----------------------------------------------------------------------------- +libtest_check_order_dlsym_2_right_src_files := \ + dlopen_check_order_dlsym_answer.cpp + +libtest_check_order_dlsym_2_right_cflags := -D__ANSWER=42 +module := libtest_check_order_dlsym_2_right +include $(LOCAL_PATH)/Android.build.testlib.mk + +# ----------------------------------------------------------------------------- +# libtest_check_order_a.so +# ----------------------------------------------------------------------------- +libtest_check_order_dlsym_a_src_files := \ + dlopen_check_order_dlsym_answer.cpp + +libtest_check_order_dlsym_a_cflags := -D__ANSWER=1 +module := libtest_check_order_dlsym_a +include $(LOCAL_PATH)/Android.build.testlib.mk + +# ----------------------------------------------------------------------------- +# libtest_check_order_b.so +# ----------------------------------------------------------------------------- +libtest_check_order_dlsym_b_src_files := \ + dlopen_check_order_dlsym_answer.cpp + +libtest_check_order_dlsym_b_cflags := -D__ANSWER=2 -D__ANSWER2=43 +module := libtest_check_order_dlsym_b +include $(LOCAL_PATH)/Android.build.testlib.mk + +# ----------------------------------------------------------------------------- +# libtest_check_order_c.so +# ----------------------------------------------------------------------------- +libtest_check_order_dlsym_3_c_src_files := \ + dlopen_check_order_dlsym_answer.cpp + +libtest_check_order_dlsym_3_c_cflags := -D__ANSWER=3 +module := libtest_check_order_dlsym_3_c +include $(LOCAL_PATH)/Android.build.testlib.mk + +# ----------------------------------------------------------------------------- +# libtest_check_order_d.so +# ----------------------------------------------------------------------------- +libtest_check_order_dlsym_d_src_files := \ + dlopen_check_order_dlsym_answer.cpp + +libtest_check_order_dlsym_d_shared_libraries := libtest_check_order_dlsym_b +libtest_check_order_dlsym_d_cflags := -D__ANSWER=4 -D__ANSWER2=4 +module := libtest_check_order_dlsym_d +include $(LOCAL_PATH)/Android.build.testlib.mk + +# ----------------------------------------------------------------------------- +# libtest_check_order_left.so +# ----------------------------------------------------------------------------- +libtest_check_order_dlsym_1_left_src_files := \ + empty.cpp + +libtest_check_order_dlsym_1_left_shared_libraries := libtest_check_order_dlsym_a libtest_check_order_dlsym_b + +module := libtest_check_order_dlsym_1_left +include $(LOCAL_PATH)/Android.build.testlib.mk + +# ----------------------------------------------------------------------------- +# libtest_check_order.so +# ----------------------------------------------------------------------------- +libtest_check_order_dlsym_src_files := \ + empty.cpp + +libtest_check_order_dlsym_shared_libraries := libtest_check_order_dlsym_1_left \ + libtest_check_order_dlsym_2_right libtest_check_order_dlsym_3_c + +module := libtest_check_order_dlsym +include $(LOCAL_PATH)/Android.build.testlib.mk diff --git a/tests/libs/Android.build.dlopen_check_order_reloc_main_executable.mk b/tests/libs/Android.build.dlopen_check_order_reloc_main_executable.mk new file mode 100644 index 000000000..639696b25 --- /dev/null +++ b/tests/libs/Android.build.dlopen_check_order_reloc_main_executable.mk @@ -0,0 +1,56 @@ +# +# Copyright (C) 2012 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. +# + +# ----------------------------------------------------------------------------- +# Libraries used by dlfcn tests to verify correct relocation order: +# libtest_check_order_reloc_root*.so +# ----------------------------------------------------------------------------- + + +# ----------------------------------------------------------------------------- +# ..._1.so - empty +# ----------------------------------------------------------------------------- +libtest_check_order_reloc_root_1_src_files := \ + empty.cpp + + +module := libtest_check_order_reloc_root_1 +include $(LOCAL_PATH)/Android.build.testlib.mk + + +# ----------------------------------------------------------------------------- +# ..._2.so - this one has the incorrect answer +# ----------------------------------------------------------------------------- +libtest_check_order_reloc_root_2_src_files := \ + dlopen_check_order_reloc_root_answer_impl.cpp + +libtest_check_order_reloc_root_2_cflags := -D__ANSWER=2 + +module := libtest_check_order_reloc_root_2 +include $(LOCAL_PATH)/Android.build.testlib.mk + +# ----------------------------------------------------------------------------- +# libtest_check_order_reloc_root.so <- implements get_answer3() +# ----------------------------------------------------------------------------- +libtest_check_order_reloc_root_src_files := \ + dlopen_check_order_reloc_root_answer.cpp + +libtest_check_order_reloc_root_shared_libraries := \ + libtest_check_order_reloc_root_1 \ + libtest_check_order_reloc_root_2 + +module := libtest_check_order_reloc_root +include $(LOCAL_PATH)/Android.build.testlib.mk diff --git a/tests/libs/Android.build.dlopen_check_order_reloc_siblings.mk b/tests/libs/Android.build.dlopen_check_order_reloc_siblings.mk new file mode 100644 index 000000000..0f1a2b4da --- /dev/null +++ b/tests/libs/Android.build.dlopen_check_order_reloc_siblings.mk @@ -0,0 +1,133 @@ +# +# Copyright (C) 2014 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. +# + +# ----------------------------------------------------------------------------- +# Libraries used by dlfcn tests to verify correct relocation order: +# libtest_check_order_reloc_siblings*.so +# ----------------------------------------------------------------------------- + +# ----------------------------------------------------------------------------- +# ..._1.so - empty +# ----------------------------------------------------------------------------- +libtest_check_order_reloc_siblings_1_src_files := \ + empty.cpp + +libtest_check_order_reloc_siblings_1_shared_libraries := \ + libtest_check_order_reloc_siblings_a \ + libtest_check_order_reloc_siblings_b + +module := libtest_check_order_reloc_siblings_1 +include $(LOCAL_PATH)/Android.build.testlib.mk + + +# ----------------------------------------------------------------------------- +# ..._2.so - empty +# ----------------------------------------------------------------------------- +libtest_check_order_reloc_siblings_2_src_files := \ + empty.cpp + +libtest_check_order_reloc_siblings_2_shared_libraries := \ + libtest_check_order_reloc_siblings_c \ + libtest_check_order_reloc_siblings_d + +module := libtest_check_order_reloc_siblings_2 +include $(LOCAL_PATH)/Android.build.testlib.mk + +# ----------------------------------------------------------------------------- +# ..._3.so - get_answer2(); +# ----------------------------------------------------------------------------- +libtest_check_order_reloc_siblings_3_src_files := \ + dlopen_check_order_reloc_nephew_answer.cpp + +libtest_check_order_reloc_siblings_3_shared_libraries := \ + libtest_check_order_reloc_siblings_e \ + libtest_check_order_reloc_siblings_f + +module := libtest_check_order_reloc_siblings_3 +include $(LOCAL_PATH)/Android.build.testlib.mk + +# ----------------------------------------------------------------------------- +# ..._a.so <- correct impl +# ----------------------------------------------------------------------------- +libtest_check_order_reloc_siblings_a_src_files := \ + dlopen_check_order_reloc_answer_impl.cpp + +libtest_check_order_reloc_siblings_a_cflags := -D__ANSWER=42 +module := libtest_check_order_reloc_siblings_a +include $(LOCAL_PATH)/Android.build.testlib.mk + +# ----------------------------------------------------------------------------- +# ..._b.so +# ----------------------------------------------------------------------------- +libtest_check_order_reloc_siblings_b_src_files := \ + dlopen_check_order_reloc_answer_impl.cpp + +libtest_check_order_reloc_siblings_b_cflags := -D__ANSWER=1 +module := libtest_check_order_reloc_siblings_b +include $(LOCAL_PATH)/Android.build.testlib.mk + +# ----------------------------------------------------------------------------- +# ..._c.so +# ----------------------------------------------------------------------------- +libtest_check_order_reloc_siblings_c_src_files := \ + dlopen_check_order_reloc_answer_impl.cpp + +libtest_check_order_reloc_siblings_c_cflags := -D__ANSWER=2 +module := libtest_check_order_reloc_siblings_c +include $(LOCAL_PATH)/Android.build.testlib.mk + +# ----------------------------------------------------------------------------- +# ..._d.so +# ----------------------------------------------------------------------------- +libtest_check_order_reloc_siblings_d_src_files := \ + dlopen_check_order_reloc_answer_impl.cpp + +libtest_check_order_reloc_siblings_d_cflags := -D__ANSWER=3 +module := libtest_check_order_reloc_siblings_d +include $(LOCAL_PATH)/Android.build.testlib.mk + +# ----------------------------------------------------------------------------- +# ..._e.so +# ----------------------------------------------------------------------------- +libtest_check_order_reloc_siblings_e_src_files := \ + dlopen_check_order_reloc_answer_impl.cpp + +libtest_check_order_reloc_siblings_e_cflags := -D__ANSWER=4 +module := libtest_check_order_reloc_siblings_e +include $(LOCAL_PATH)/Android.build.testlib.mk + +# ----------------------------------------------------------------------------- +# ..._f.so <- get_answer() +# ----------------------------------------------------------------------------- +libtest_check_order_reloc_siblings_f_src_files := \ + dlopen_check_order_reloc_answer.cpp + +module := libtest_check_order_reloc_siblings_f +include $(LOCAL_PATH)/Android.build.testlib.mk + +# ----------------------------------------------------------------------------- +# libtest_check_order_reloc_siblings.so +# ----------------------------------------------------------------------------- +libtest_check_order_reloc_siblings_src_files := \ + empty.cpp + +libtest_check_order_reloc_siblings_shared_libraries := \ + libtest_check_order_reloc_siblings_1 \ + libtest_check_order_reloc_siblings_2 \ + libtest_check_order_reloc_siblings_3 + +module := libtest_check_order_reloc_siblings +include $(LOCAL_PATH)/Android.build.testlib.mk diff --git a/tests/libs/Android.mk b/tests/libs/Android.mk index cfd7f2c53..eabc887a7 100644 --- a/tests/libs/Android.mk +++ b/tests/libs/Android.mk @@ -21,6 +21,9 @@ common_cppflags += -std=gnu++11 common_additional_dependencies := \ $(LOCAL_PATH)/Android.mk \ $(LOCAL_PATH)/Android.build.dlext_testzip.mk \ + $(LOCAL_PATH)/Android.build.dlopen_check_order_dlsym.mk \ + $(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_siblings.mk \ + $(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_main_executable.mk \ $(LOCAL_PATH)/Android.build.testlib.mk \ $(TEST_PATH)/Android.build.mk @@ -150,79 +153,19 @@ module := libtest_nodelete_dt_flags_1 include $(LOCAL_PATH)/Android.build.testlib.mk # ----------------------------------------------------------------------------- -# Libraries used by dlfcn tests to verify correct load order: -# libtest_check_order_2_right.so +# Build libtest_check_order_dlsym.so with its dependencies. # ----------------------------------------------------------------------------- -libtest_check_order_2_right_src_files := \ - dlopen_testlib_answer.cpp - -libtest_check_order_2_right_cflags := -D__ANSWER=42 -module := libtest_check_order_2_right -include $(LOCAL_PATH)/Android.build.testlib.mk - -# ----------------------------------------------------------------------------- -# libtest_check_order_a.so -# ----------------------------------------------------------------------------- -libtest_check_order_a_src_files := \ - dlopen_testlib_answer.cpp - -libtest_check_order_a_cflags := -D__ANSWER=1 -module := libtest_check_order_a -include $(LOCAL_PATH)/Android.build.testlib.mk - -# ----------------------------------------------------------------------------- -# libtest_check_order_b.so -# ----------------------------------------------------------------------------- -libtest_check_order_b_src_files := \ - dlopen_testlib_answer.cpp - -libtest_check_order_b_cflags := -D__ANSWER=2 -D__ANSWER2=43 -module := libtest_check_order_b -include $(LOCAL_PATH)/Android.build.testlib.mk - -# ----------------------------------------------------------------------------- -# libtest_check_order_c.so -# ----------------------------------------------------------------------------- -libtest_check_order_3_c_src_files := \ - dlopen_testlib_answer.cpp - -libtest_check_order_3_c_cflags := -D__ANSWER=3 -module := libtest_check_order_3_c -include $(LOCAL_PATH)/Android.build.testlib.mk - -# ----------------------------------------------------------------------------- -# libtest_check_order_d.so -# ----------------------------------------------------------------------------- -libtest_check_order_d_src_files := \ - dlopen_testlib_answer.cpp - -libtest_check_order_d_shared_libraries := libtest_check_order_b -libtest_check_order_d_cflags := -D__ANSWER=4 -D__ANSWER2=4 -module := libtest_check_order_d -include $(LOCAL_PATH)/Android.build.testlib.mk +include $(LOCAL_PATH)/Android.build.dlopen_check_order_dlsym.mk # ----------------------------------------------------------------------------- -# libtest_check_order_left.so +# Build libtest_check_order_siblings.so with its dependencies. # ----------------------------------------------------------------------------- -libtest_check_order_1_left_src_files := \ - empty.cpp - -libtest_check_order_1_left_shared_libraries := libtest_check_order_a libtest_check_order_b - -module := libtest_check_order_1_left -include $(LOCAL_PATH)/Android.build.testlib.mk +include $(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_siblings.mk # ----------------------------------------------------------------------------- -# libtest_check_order.so +# Build libtest_check_order_root.so with its dependencies. # ----------------------------------------------------------------------------- -libtest_check_order_src_files := \ - empty.cpp - -libtest_check_order_shared_libraries := libtest_check_order_1_left \ - libtest_check_order_2_right libtest_check_order_3_c - -module := libtest_check_order -include $(LOCAL_PATH)/Android.build.testlib.mk +include $(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_main_executable.mk # ----------------------------------------------------------------------------- # Library with dependency loop used by dlfcn tests diff --git a/tests/libs/dlopen_testlib_answer.cpp b/tests/libs/dlopen_check_order_dlsym_answer.cpp similarity index 87% rename from tests/libs/dlopen_testlib_answer.cpp rename to tests/libs/dlopen_check_order_dlsym_answer.cpp index a4d75046e..2ae6cf79e 100644 --- a/tests/libs/dlopen_testlib_answer.cpp +++ b/tests/libs/dlopen_check_order_dlsym_answer.cpp @@ -14,12 +14,12 @@ * limitations under the License. */ -extern "C" int dlopen_test_get_answer() { +extern "C" int check_order_dlsym_get_answer() { return __ANSWER; } #ifdef __ANSWER2 -extern "C" int dlopen_test_get_answer2() { +extern "C" int check_order_dlsym_get_answer2() { return __ANSWER2; } #endif diff --git a/tests/libs/dlopen_check_order_reloc_answer.cpp b/tests/libs/dlopen_check_order_reloc_answer.cpp new file mode 100644 index 000000000..036670bee --- /dev/null +++ b/tests/libs/dlopen_check_order_reloc_answer.cpp @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2014 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. + */ + +extern "C" int __attribute__((weak)) check_order_reloc_get_answer_impl() { + return 0; +} + +extern "C" int check_order_reloc_get_answer() { + return check_order_reloc_get_answer_impl(); +} diff --git a/tests/libs/dlopen_check_order_reloc_answer_impl.cpp b/tests/libs/dlopen_check_order_reloc_answer_impl.cpp new file mode 100644 index 000000000..324b905da --- /dev/null +++ b/tests/libs/dlopen_check_order_reloc_answer_impl.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2014 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. + */ + +extern "C" int check_order_reloc_get_answer_impl() { + return __ANSWER; +} diff --git a/tests/libs/dlopen_check_order_reloc_nephew_answer.cpp b/tests/libs/dlopen_check_order_reloc_nephew_answer.cpp new file mode 100644 index 000000000..065d1bef7 --- /dev/null +++ b/tests/libs/dlopen_check_order_reloc_nephew_answer.cpp @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2014 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. + */ + +extern "C" int check_order_reloc_get_answer_impl(); + +extern "C" int check_order_reloc_nephew_get_answer() { + return check_order_reloc_get_answer_impl(); +} diff --git a/tests/libs/dlopen_check_order_reloc_root_answer.cpp b/tests/libs/dlopen_check_order_reloc_root_answer.cpp new file mode 100644 index 000000000..b21abd77a --- /dev/null +++ b/tests/libs/dlopen_check_order_reloc_root_answer.cpp @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2014 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. + */ + +extern "C" int check_order_reloc_root_get_answer_impl(); + +extern "C" int check_order_reloc_root_get_answer() { + return check_order_reloc_root_get_answer_impl(); +} diff --git a/tests/libs/dlopen_check_order_reloc_root_answer_impl.cpp b/tests/libs/dlopen_check_order_reloc_root_answer_impl.cpp new file mode 100644 index 000000000..25fb9aca9 --- /dev/null +++ b/tests/libs/dlopen_check_order_reloc_root_answer_impl.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2014 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. + */ + +extern "C" int check_order_reloc_root_get_answer_impl() { + return __ANSWER; +} -- 2.11.0