return fd;
}
-static soinfo* load_library(const char* name, int dlflags, const android_dlextinfo* extinfo) {
- int fd = -1;
- ScopedFd file_guard(-1);
+template<typename F>
+static void for_each_dt_needed(const soinfo* si, F action) {
+ for (ElfW(Dyn)* d = si->dynamic; d->d_tag != DT_NULL; ++d) {
+ if (d->d_tag == DT_NEEDED) {
+ action(si->get_string(d->d_un.d_val));
+ }
+ }
+}
- if (extinfo != nullptr && (extinfo->flags & ANDROID_DLEXT_USE_LIBRARY_FD) != 0) {
- fd = extinfo->library_fd;
- } else {
- // Open the file.
- fd = open_library(name);
- if (fd == -1) {
- DL_ERR("library \"%s\" not found", name);
- return nullptr;
- }
+static soinfo* load_library(LoadTaskList& load_tasks, const char* name, int rtld_flags, const android_dlextinfo* extinfo) {
+ int fd = -1;
+ ScopedFd file_guard(-1);
- file_guard.reset(fd);
+ if (extinfo != nullptr && (extinfo->flags & ANDROID_DLEXT_USE_LIBRARY_FD) != 0) {
+ fd = extinfo->library_fd;
+ } else {
+ // Open the file.
+ fd = open_library(name);
+ if (fd == -1) {
+ DL_ERR("library \"%s\" not found", name);
+ return nullptr;
}
- struct stat file_stat;
- if (TEMP_FAILURE_RETRY(fstat(fd, &file_stat)) != 0) {
- DL_ERR("unable to stat file for the library %s: %s", name, strerror(errno));
- return nullptr;
- }
+ file_guard.reset(fd);
+ }
+
+ ElfReader elf_reader(name, fd);
- // Check for symlink and other situations where
- // file can have different names.
- for (soinfo* si = solist; si != nullptr; si = si->next) {
- if (si->get_st_dev() != 0 &&
- si->get_st_ino() != 0 &&
- si->get_st_dev() == file_stat.st_dev &&
- si->get_st_ino() == file_stat.st_ino) {
- TRACE("library \"%s\" is already loaded under different name/path \"%s\" - will return existing soinfo", name, si->name);
- return si;
+ struct stat file_stat;
+ if (TEMP_FAILURE_RETRY(fstat(fd, &file_stat)) != 0) {
+ DL_ERR("unable to stat file for the library %s: %s", name, strerror(errno));
+ return nullptr;
+ }
+
+ // Check for symlink and other situations where
+ // file can have different names.
+ for (soinfo* si = solist; si != nullptr; si = si->next) {
+ if (si->get_st_dev() != 0 &&
+ si->get_st_ino() != 0 &&
+ si->get_st_dev() == file_stat.st_dev &&
+ si->get_st_ino() == file_stat.st_ino) {
+ TRACE("library \"%s\" is already loaded under different name/path \"%s\" - will return existing soinfo", name, si->name);
+ return si;
+ }
}
- }
- if ((dlflags & RTLD_NOLOAD) != 0) {
- return nullptr;
- }
+ if ((rtld_flags & RTLD_NOLOAD) != 0) {
+ DL_ERR("library \"%s\" wasn't loaded and RTLD_NOLOAD prevented it", name);
+ return nullptr;
+ }
- // Read the ELF header and load the segments.
- ElfReader elf_reader(name, fd);
- if (!elf_reader.Load(extinfo)) {
- return nullptr;
- }
+ // Read the ELF header and load the segments.
+ if (!elf_reader.Load(extinfo)) {
+ return nullptr;
+ }
- soinfo* si = soinfo_alloc(SEARCH_NAME(name), &file_stat);
- if (si == nullptr) {
- return nullptr;
- }
- si->base = elf_reader.load_start();
- si->size = elf_reader.load_size();
- si->load_bias = elf_reader.load_bias();
- si->phnum = elf_reader.phdr_count();
- si->phdr = elf_reader.loaded_phdr();
+ soinfo* si = soinfo_alloc(SEARCH_NAME(name), &file_stat, rtld_flags);
+ if (si == nullptr) {
+ return nullptr;
+ }
+ si->base = elf_reader.load_start();
+ si->size = elf_reader.load_size();
+ si->load_bias = elf_reader.load_bias();
+ si->phnum = elf_reader.phdr_count();
+ si->phdr = elf_reader.loaded_phdr();
- if (!si->PrelinkImage()) {
- soinfo_free(si);
- return nullptr;
- }
+ // At this point we know that whatever is loaded @ base is a valid ELF
+ // shared library whose segments are properly mapped in.
+ TRACE("[ load_library base=%p size=%zu name='%s' ]",
+ reinterpret_cast<void*>(si->base), si->size, si->name);
- for_each_dt_needed(si, [&] (const char* name) {
- load_tasks.push_back(LoadTask::create(name, si));
- });
+ if (!si->LinkImage(extinfo)) {
+ soinfo_free(si);
+ return nullptr;
+ }
- return si;
+ return si;
}
static soinfo *find_loaded_library_by_name(const char* name) {
soinfo* si = find_loaded_library_by_name(name);
// Library might still be loaded, the accurate detection
- // of this fact is done by load_library.
+ // of this fact is done by load_library
if (si == nullptr) {
TRACE("[ '%s' has not been found by name. Trying harder...]", name);
- si = load_library(name, dlflags, extinfo);
+ si = load_library(load_tasks, name, rtld_flags, extinfo);
}
- return si;
- }
-
- static void soinfo_unload(soinfo* si);
-
- static bool is_recursive(soinfo* si, soinfo* parent) {
- if (parent == nullptr) {
- return false;
- }
-
- if (si == parent) {
+ if (si != nullptr && (si->flags & FLAG_LINKED) == 0) {
DL_ERR("recursive link to \"%s\"", si->name);
- return nullptr;
+ return true;
}
- return si;
+ return !parent->get_parents().visit([&](soinfo* grandparent) {
+ return !is_recursive(si, grandparent);
+ });
}
-static soinfo* find_library(const char* name, int dlflags, const android_dlextinfo* extinfo) {
- soinfo* si = find_library_internal(name, dlflags, extinfo);
- if (si != nullptr) {
+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) {
+ // Step 0: prepare.
+ LoadTaskList load_tasks;
+ for (size_t i = 0; i < library_names_size; ++i) {
+ const char* name = library_names[i];
+ load_tasks.push_back(LoadTask::create(name, nullptr));
+ }
+
+ // 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;
+
+ auto failure_guard = make_scope_guard([&]() {
+ // Housekeeping
+ load_tasks.for_each([] (LoadTask* t) {
+ LoadTask::deleter(t);
+ });
+
+ for (size_t i = 0; i<soinfos_size; ++i) {
+ soinfo_unload(soinfos[i]);
+ }
+ });
+
+ // Step 1: load and pre-link all DT_NEEDED libraries in breadth first order.
+ for (LoadTask::unique_ptr task(load_tasks.pop_front()); task.get() != nullptr; task.reset(load_tasks.pop_front())) {
+ soinfo* si = find_library_internal(load_tasks, task->get_name(), rtld_flags, extinfo);
+ if (si == nullptr) {
+ return false;
+ }
+
+ soinfo* needed_by = task->get_needed_by();
+
+ if (is_recursive(si, needed_by)) {
+ return false;
+ }
+
si->ref_count++;
- // All is well - found_libs and load_tasks are empty at this point
- // and all libs are successfully linked.
- failure_guard.disable();
- return true;
+ if (needed_by != nullptr) {
+ needed_by->add_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;
+ }
+
+ if (soinfos_size<library_names_size) {
+ soinfos[soinfos_size++] = si;
+ }
+ }
+
+ // Step 2: link libraries.
+ soinfo* si;
+ while ((si = found_libs.pop_front()) != nullptr) {
+ if ((si->flags & FLAG_LINKED) == 0) {
+ if (!si->LinkImage(extinfo)) {
+ return false;
+ }
+ si->flags |= FLAG_LINKED;
+ }
+ }
+
++ return si;
+}
+
+static soinfo* find_library(const char* name, int rtld_flags, const android_dlextinfo* extinfo) {
+ if (name == nullptr) {
+ somain->ref_count++;
+ return somain;
+ }
+
+ soinfo* si;
+
+ if (!find_libraries(&name, 1, &si, nullptr, 0, rtld_flags, extinfo)) {
+ return nullptr;
}
-
return si;
}
si->CallDestructors();
if (si->has_min_version(0)) {
- // It is not safe to do si->get_children().for_each, because
- // during soinfo_free the child will concurrently modify the si->children
- // list, therefore we create a copy and use it to unload children.
- size_t children_count = si->get_children().size();
- soinfo* children[children_count];
- si->get_children().copy_to_array(children, children_count);
-
- for (size_t i = 0; i < children_count; ++i) {
- TRACE("%s needs to unload %s", si->name, children[i]->name);
- soinfo_unload(children[i]);
+ soinfo* child = nullptr;
+ while ((child = si->get_children().pop_front()) != nullptr) {
+ TRACE("%s needs to unload %s", si->name, child->name);
+ soinfo_unload(child);
}
} else {
- for_each_dt_needed(si, [&] (const char* library_name) {
- TRACE("deprecated (old format of soinfo): %s needs to unload %s", si->name, library_name);
- soinfo* needed = find_library(library_name, RTLD_NOLOAD, nullptr);
- if (needed != nullptr) {
- soinfo_unload(needed);
- } else {
- // Not found: for example if symlink was deleted between dlopen and dlclose
- // Since we cannot really handle errors at this point - print and continue.
- PRINT("warning: couldn't find %s needed by %s on unload.", library_name, si->name);
+ for (ElfW(Dyn)* d = si->dynamic; d->d_tag != DT_NULL; ++d) {
+ if (d->d_tag == DT_NEEDED) {
+ const char* library_name = si->strtab + d->d_un.d_val;
+ TRACE("%s needs to unload %s", si->name, library_name);
+ soinfo* needed = find_library(library_name, RTLD_NOLOAD, nullptr);
+ if (needed != nullptr) {
+ soinfo_unload(needed);
+ } else {
+ // Not found: for example if symlink was deleted between dlopen and dlclose
+ // Since we cannot really handle errors at this point - print and continue.
+ PRINT("warning: couldn't find %s needed by %s on unload.", library_name, si->name);
+ }
}
- });
+ }
}
notify_gdb_of_unload(si);
if (type == 0) { // R_*_NONE
continue;
}
-
- ElfW(Sym)* s = nullptr;
- soinfo* lsi = nullptr;
-
if (sym != 0) {
- sym_name = reinterpret_cast<const char*>(strtab + symtab[sym].st_name);
+ sym_name = get_string(symtab[sym].st_name);
s = soinfo_do_lookup(this, sym_name, &lsi);
if (s == nullptr) {
// We only allow an undefined symbol if this is a weak reference...
}
} else {
// We got a definition.
- sym_addr = static_cast<ElfW(Addr)>(s->st_value + lsi->load_bias);
+ sym_addr = lsi->resolve_symbol_address(s);
}
count_relocation(kRelocSymbol);
+ } else {
+ s = nullptr;
}
switch (type) {
return 0;
}
-bool soinfo::get_has_ifuncs() {
+int soinfo::get_rtld_flags() {
if (has_min_version(1)) {
- return has_ifuncs;
+ return rtld_flags;
}
- return false;
+ return 0;
}
- // This is a return on get_children()/get_parents() if
+ // This is a return on get_children() in case
// 'this->flags' does not have FLAG_NEW_SOINFO set.
static soinfo::soinfo_list_t g_empty_list;
/* Force any of the closed stdin, stdout and stderr to be associated with
/dev/null. */
static int nullify_closed_stdio() {
- int dev_null, i, status;
- int return_value = 0;
+ int dev_null, i, status;
+ int return_value = 0;
- dev_null = TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR));
- if (dev_null < 0) {
- DL_ERR("cannot open /dev/null: %s", strerror(errno));
- return -1;
- }
- TRACE("[ Opened /dev/null file-descriptor=%d]", dev_null);
+ dev_null = TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR));
+ if (dev_null < 0) {
+ DL_ERR("cannot open /dev/null: %s", strerror(errno));
+ return -1;
+ }
+ TRACE("[ Opened /dev/null file-descriptor=%d]", dev_null);
- /* If any of the stdio file descriptors is valid and not associated
- with /dev/null, dup /dev/null to it. */
- for (i = 0; i < 3; i++) {
- /* If it is /dev/null already, we are done. */
- if (i == dev_null) {
- continue;
- }
+ /* If any of the stdio file descriptors is valid and not associated
+ with /dev/null, dup /dev/null to it. */
+ for (i = 0; i < 3; i++) {
+ /* If it is /dev/null already, we are done. */
+ if (i == dev_null) {
+ continue;
+ }
- TRACE("[ Nullifying stdio file descriptor %d]", i);
- status = TEMP_FAILURE_RETRY(fcntl(i, F_GETFL));
+ TRACE("[ Nullifying stdio file descriptor %d]", i);
+ status = TEMP_FAILURE_RETRY(fcntl(i, F_GETFL));
- /* If file is opened, we are good. */
- if (status != -1) {
- continue;
- }
+ /* If file is opened, we are good. */
+ if (status != -1) {
+ continue;
+ }
- /* The only error we allow is that the file descriptor does not
- exist, in which case we dup /dev/null to it. */
- if (errno != EBADF) {
- DL_ERR("fcntl failed: %s", strerror(errno));
- return_value = -1;
- continue;
- }
+ /* The only error we allow is that the file descriptor does not
+ exist, in which case we dup /dev/null to it. */
+ if (errno != EBADF) {
+ DL_ERR("fcntl failed: %s", strerror(errno));
+ return_value = -1;
+ continue;
+ }
- /* Try dupping /dev/null to this stdio file descriptor and
- repeat if there is a signal. Note that any errors in closing
- the stdio descriptor are lost. */
- status = TEMP_FAILURE_RETRY(dup2(dev_null, i));
- if (status < 0) {
- DL_ERR("dup2 failed: %s", strerror(errno));
- return_value = -1;
- continue;
- }
+ /* Try dupping /dev/null to this stdio file descriptor and
+ repeat if there is a signal. Note that any errors in closing
+ the stdio descriptor are lost. */
+ status = TEMP_FAILURE_RETRY(dup2(dev_null, i));
+ if (status < 0) {
+ DL_ERR("dup2 failed: %s", strerror(errno));
+ return_value = -1;
+ continue;
}
+ }
- /* If /dev/null is not one of the stdio file descriptors, close it. */
- if (dev_null > 2) {
- TRACE("[ Closing /dev/null file-descriptor=%d]", dev_null);
- status = TEMP_FAILURE_RETRY(close(dev_null));
- if (status == -1) {
- DL_ERR("close failed: %s", strerror(errno));
- return_value = -1;
- }
+ /* If /dev/null is not one of the stdio file descriptors, close it. */
+ if (dev_null > 2) {
+ TRACE("[ Closing /dev/null file-descriptor=%d]", dev_null);
+ status = TEMP_FAILURE_RETRY(close(dev_null));
+ if (status == -1) {
+ DL_ERR("close failed: %s", strerror(errno));
+ return_value = -1;
}
+ }
- return return_value;
+ return return_value;
}
-bool soinfo::LinkImage(const android_dlextinfo* extinfo) {
- bool relocating_linker = (flags & FLAG_LINKER) != 0;
+bool soinfo::PrelinkImage() {
+ /* Extract dynamic section */
+ ElfW(Word) dynamic_flags = 0;
+ phdr_table_get_dynamic_section(phdr, phnum, load_bias, &dynamic, &dynamic_flags);
+
+ /* We can't log anything until the linker is relocated */
+ bool relocating_linker = (flags & FLAG_LINKER) != 0;
+ if (!relocating_linker) {
+ INFO("[ linking %s ]", name);
+ DEBUG("si->base = %p si->flags = 0x%08x", reinterpret_cast<void*>(base), flags);
+ }
- /* We can't debug anything until the linker is relocated */
+ if (dynamic == nullptr) {
if (!relocating_linker) {
- INFO("[ linking %s ]", name);
- DEBUG("si->base = %p si->flags = 0x%08x", reinterpret_cast<void*>(base), flags);
+ DL_ERR("missing PT_DYNAMIC in \"%s\"", name);
}
-
- /* Extract dynamic section */
- size_t dynamic_count;
- ElfW(Word) dynamic_flags;
- phdr_table_get_dynamic_section(phdr, phnum, load_bias, &dynamic,
- &dynamic_count, &dynamic_flags);
- if (dynamic == nullptr) {
- if (!relocating_linker) {
- DL_ERR("missing PT_DYNAMIC in \"%s\"", name);
- }
- return false;
- } else {
- if (!relocating_linker) {
- DEBUG("dynamic = %p", dynamic);
- }
+ return false;
+ } else {
+ if (!relocating_linker) {
+ DEBUG("dynamic = %p", dynamic);
}
+ }
#if defined(__arm__)
- (void) phdr_table_get_arm_exidx(phdr, phnum, load_bias,
- &ARM_exidx, &ARM_exidx_count);
+ (void) phdr_table_get_arm_exidx(phdr, phnum, load_bias,
+ &ARM_exidx, &ARM_exidx_count);
#endif
- // Extract useful information from dynamic section.
- uint32_t needed_count = 0;
- for (ElfW(Dyn)* d = dynamic; d->d_tag != DT_NULL; ++d) {
- DEBUG("d = %p, d[0](tag) = %p d[1](val) = %p",
- d, reinterpret_cast<void*>(d->d_tag), reinterpret_cast<void*>(d->d_un.d_val));
- switch (d->d_tag) {
- case DT_HASH:
- nbucket = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[0];
- nchain = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[1];
- bucket = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr + 8);
- chain = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr + 8 + nbucket * 4);
- break;
- case DT_STRTAB:
- strtab = reinterpret_cast<const char*>(load_bias + d->d_un.d_ptr);
- break;
- case DT_SYMTAB:
- symtab = reinterpret_cast<ElfW(Sym)*>(load_bias + d->d_un.d_ptr);
- break;
+ // Extract useful information from dynamic section.
+ uint32_t needed_count = 0;
+ for (ElfW(Dyn)* d = dynamic; d->d_tag != DT_NULL; ++d) {
+ DEBUG("d = %p, d[0](tag) = %p d[1](val) = %p",
+ d, reinterpret_cast<void*>(d->d_tag), reinterpret_cast<void*>(d->d_un.d_val));
+ switch (d->d_tag) {
+ case DT_SONAME:
+ // TODO: glibc dynamic linker uses this name for
+ // initial library lookup; consider doing the same here.
+ break;
+ case DT_HASH:
+ nbucket = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[0];
+ nchain = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[1];
+ bucket = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr + 8);
+ chain = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr + 8 + nbucket * 4);
+ break;
+ case DT_STRTAB:
+ strtab = reinterpret_cast<const char*>(load_bias + d->d_un.d_ptr);
+ break;
+ case DT_STRSZ:
+ strtab_size = d->d_un.d_val;
+ break;
+ case DT_SYMTAB:
+ symtab = reinterpret_cast<ElfW(Sym)*>(load_bias + d->d_un.d_ptr);
+ break;
+ case DT_SYMENT:
+ if (d->d_un.d_val != sizeof(ElfW(Sym))) {
+ DL_ERR("invalid DT_SYMENT: %zd", static_cast<size_t>(d->d_un.d_val));
+ return false;
+ }
+ break;
#if !defined(__LP64__)
- case DT_PLTREL:
- if (d->d_un.d_val != DT_REL) {
- DL_ERR("unsupported DT_RELA in \"%s\"", name);
- return false;
- }
- break;
+ case DT_PLTREL:
+ if (d->d_un.d_val != DT_REL) {
+ DL_ERR("unsupported DT_RELA in \"%s\"", name);
+ return false;
+ }
+ break;
#endif
- case DT_JMPREL:
+ case DT_JMPREL:
#if defined(USE_RELA)
- plt_rela = reinterpret_cast<ElfW(Rela)*>(load_bias + d->d_un.d_ptr);
+ plt_rela = reinterpret_cast<ElfW(Rela)*>(load_bias + d->d_un.d_ptr);
#else
- plt_rel = reinterpret_cast<ElfW(Rel)*>(load_bias + d->d_un.d_ptr);
+ plt_rel = reinterpret_cast<ElfW(Rel)*>(load_bias + d->d_un.d_ptr);
#endif
- break;
- case DT_PLTRELSZ:
+ break;
+ case DT_PLTRELSZ:
#if defined(USE_RELA)
- plt_rela_count = d->d_un.d_val / sizeof(ElfW(Rela));
+ plt_rela_count = d->d_un.d_val / sizeof(ElfW(Rela));
#else
- plt_rel_count = d->d_un.d_val / sizeof(ElfW(Rel));
+ plt_rel_count = d->d_un.d_val / sizeof(ElfW(Rel));
#endif
- break;
+ break;
+ case DT_PLTGOT:
#if defined(__mips__)
- case DT_PLTGOT:
- // Used by mips and mips64.
- plt_got = reinterpret_cast<ElfW(Addr)**>(load_bias + d->d_un.d_ptr);
- break;
+ // Used by mips and mips64.
+ plt_got = reinterpret_cast<ElfW(Addr)**>(load_bias + d->d_un.d_ptr);
#endif
- case DT_DEBUG:
- // Set the DT_DEBUG entry to the address of _r_debug for GDB
- // if the dynamic table is writable
+ // Ignore for other platforms... (because RTLD_LAZY is not supported)
+ break;
+ case DT_DEBUG:
+ // Set the DT_DEBUG entry to the address of _r_debug for GDB
+ // if the dynamic table is writable
// FIXME: not working currently for N64
// The flags for the LOAD and DYNAMIC program headers do not agree.
- // The LOAD section containing the dynamic table has been mapped as
+ // The LOAD section containng the dynamic table has been mapped as
// read-only, but the DYNAMIC header claims it is writable.
#if !(defined(__mips__) && defined(__LP64__))
- if ((dynamic_flags & PF_W) != 0) {
- d->d_un.d_val = reinterpret_cast<uintptr_t>(&_r_debug);
- }
- break;
+ if ((dynamic_flags & PF_W) != 0) {
+ d->d_un.d_val = reinterpret_cast<uintptr_t>(&_r_debug);
+ }
+ break;
#endif
#if defined(USE_RELA)
- case DT_RELA:
- rela = reinterpret_cast<ElfW(Rela)*>(load_bias + d->d_un.d_ptr);
- break;
- case DT_RELASZ:
- rela_count = d->d_un.d_val / sizeof(ElfW(Rela));
- break;
- case DT_REL:
- DL_ERR("unsupported DT_REL in \"%s\"", name);
- return false;
- case DT_RELSZ:
- DL_ERR("unsupported DT_RELSZ in \"%s\"", name);
- return false;
+ case DT_RELA:
+ rela = reinterpret_cast<ElfW(Rela)*>(load_bias + d->d_un.d_ptr);
+ break;
+ case DT_RELASZ:
+ rela_count = d->d_un.d_val / sizeof(ElfW(Rela));
+ break;
+ case DT_RELAENT:
+ if (d->d_un.d_val != sizeof(ElfW(Rela))) {
+ DL_ERR("invalid DT_RELAENT: %zd", static_cast<size_t>(d->d_un.d_val));
+ return false;
+ }
+ break;
+ case DT_RELACOUNT:
+ // ignored (see DT_RELCOUNT comments for details)
+ break;
+ case DT_REL:
+ DL_ERR("unsupported DT_REL in \"%s\"", name);
+ return false;
+ case DT_RELSZ:
+ DL_ERR("unsupported DT_RELSZ in \"%s\"", name);
+ return false;
#else
- case DT_REL:
- rel = reinterpret_cast<ElfW(Rel)*>(load_bias + d->d_un.d_ptr);
- break;
- case DT_RELSZ:
- rel_count = d->d_un.d_val / sizeof(ElfW(Rel));
- break;
- case DT_RELA:
- DL_ERR("unsupported DT_RELA in \"%s\"", name);
- return false;
+ case DT_REL:
+ rel = reinterpret_cast<ElfW(Rel)*>(load_bias + d->d_un.d_ptr);
+ break;
+ case DT_RELSZ:
+ rel_count = d->d_un.d_val / sizeof(ElfW(Rel));
+ break;
+ case DT_RELENT:
+ if (d->d_un.d_val != sizeof(ElfW(Rel))) {
+ DL_ERR("invalid DT_RELENT: %zd", static_cast<size_t>(d->d_un.d_val));
+ return false;
+ }
+ break;
+ case DT_RELCOUNT:
+ // "Indicates that all RELATIVE relocations have been concatenated together,
+ // and specifies the RELATIVE relocation count."
+ //
+ // TODO: Spec also mentions that this can be used to optimize relocation process;
+ // Not currently used by bionic linker - ignored.
+ break;
+ case DT_RELA:
+ DL_ERR("unsupported DT_RELA in \"%s\"", name);
+ return false;
#endif
- case DT_INIT:
- init_func = reinterpret_cast<linker_function_t>(load_bias + d->d_un.d_ptr);
- DEBUG("%s constructors (DT_INIT) found at %p", name, init_func);
- break;
- case DT_FINI:
- fini_func = reinterpret_cast<linker_function_t>(load_bias + d->d_un.d_ptr);
- DEBUG("%s destructors (DT_FINI) found at %p", name, fini_func);
- break;
- case DT_INIT_ARRAY:
- init_array = reinterpret_cast<linker_function_t*>(load_bias + d->d_un.d_ptr);
- DEBUG("%s constructors (DT_INIT_ARRAY) found at %p", name, init_array);
- break;
- case DT_INIT_ARRAYSZ:
- init_array_count = ((unsigned)d->d_un.d_val) / sizeof(ElfW(Addr));
- break;
- case DT_FINI_ARRAY:
- fini_array = reinterpret_cast<linker_function_t*>(load_bias + d->d_un.d_ptr);
- DEBUG("%s destructors (DT_FINI_ARRAY) found at %p", name, fini_array);
- break;
- case DT_FINI_ARRAYSZ:
- fini_array_count = ((unsigned)d->d_un.d_val) / sizeof(ElfW(Addr));
- break;
- case DT_PREINIT_ARRAY:
- preinit_array = reinterpret_cast<linker_function_t*>(load_bias + d->d_un.d_ptr);
- DEBUG("%s constructors (DT_PREINIT_ARRAY) found at %p", name, preinit_array);
- break;
- case DT_PREINIT_ARRAYSZ:
- preinit_array_count = ((unsigned)d->d_un.d_val) / sizeof(ElfW(Addr));
- break;
- case DT_TEXTREL:
+ case DT_INIT:
+ init_func = reinterpret_cast<linker_function_t>(load_bias + d->d_un.d_ptr);
+ DEBUG("%s constructors (DT_INIT) found at %p", name, init_func);
+ break;
+ case DT_FINI:
+ fini_func = reinterpret_cast<linker_function_t>(load_bias + d->d_un.d_ptr);
+ DEBUG("%s destructors (DT_FINI) found at %p", name, fini_func);
+ break;
+ case DT_INIT_ARRAY:
+ init_array = reinterpret_cast<linker_function_t*>(load_bias + d->d_un.d_ptr);
+ DEBUG("%s constructors (DT_INIT_ARRAY) found at %p", name, init_array);
+ break;
+ case DT_INIT_ARRAYSZ:
+ init_array_count = ((unsigned)d->d_un.d_val) / sizeof(ElfW(Addr));
+ break;
+ case DT_FINI_ARRAY:
+ fini_array = reinterpret_cast<linker_function_t*>(load_bias + d->d_un.d_ptr);
+ DEBUG("%s destructors (DT_FINI_ARRAY) found at %p", name, fini_array);
+ break;
+ case DT_FINI_ARRAYSZ:
+ fini_array_count = ((unsigned)d->d_un.d_val) / sizeof(ElfW(Addr));
+ break;
+ case DT_PREINIT_ARRAY:
+ preinit_array = reinterpret_cast<linker_function_t*>(load_bias + d->d_un.d_ptr);
+ DEBUG("%s constructors (DT_PREINIT_ARRAY) found at %p", name, preinit_array);
+ break;
+ case DT_PREINIT_ARRAYSZ:
+ preinit_array_count = ((unsigned)d->d_un.d_val) / sizeof(ElfW(Addr));
+ break;
+ case DT_TEXTREL:
#if defined(__LP64__)
- DL_ERR("text relocations (DT_TEXTREL) found in 64-bit ELF file \"%s\"", name);
- return false;
+ DL_ERR("text relocations (DT_TEXTREL) found in 64-bit ELF file \"%s\"", name);
+ return false;
#else
- has_text_relocations = true;
- break;
+ has_text_relocations = true;
+ break;
#endif
- case DT_SYMBOLIC:
- has_DT_SYMBOLIC = true;
- break;
- case DT_NEEDED:
- ++needed_count;
- break;
- case DT_FLAGS:
- if (d->d_un.d_val & DF_TEXTREL) {
+ case DT_SYMBOLIC:
+ has_DT_SYMBOLIC = true;
+ break;
+ case DT_NEEDED:
+ ++needed_count;
+ break;
+ case DT_FLAGS:
+ if (d->d_un.d_val & DF_TEXTREL) {
#if defined(__LP64__)
- DL_ERR("text relocations (DF_TEXTREL) found in 64-bit ELF file \"%s\"", name);
- return false;
+ DL_ERR("text relocations (DF_TEXTREL) found in 64-bit ELF file \"%s\"", name);
+ return false;
#else
- has_text_relocations = true;
+ has_text_relocations = true;
#endif
- }
- if (d->d_un.d_val & DF_SYMBOLIC) {
- has_DT_SYMBOLIC = true;
- }
- break;
+ }
+ if (d->d_un.d_val & DF_SYMBOLIC) {
+ has_DT_SYMBOLIC = true;
+ }
+ break;
+ case DT_FLAGS_1:
+ if ((d->d_un.d_val & DF_1_GLOBAL) != 0) {
+ rtld_flags |= RTLD_GLOBAL;
+ }
+ // TODO: Implement other flags
+
+ if ((d->d_un.d_val & ~(DF_1_NOW | DF_1_GLOBAL)) != 0) {
+ DL_WARN("Unsupported flags DT_FLAGS_1=%p", reinterpret_cast<void*>(d->d_un.d_val));
+ }
+ break;
#if defined(__mips__)
- case DT_STRSZ:
- case DT_SYMENT:
- case DT_RELENT:
- break;
- case DT_MIPS_RLD_MAP:
- // Set the DT_MIPS_RLD_MAP entry to the address of _r_debug for GDB.
- {
- r_debug** dp = reinterpret_cast<r_debug**>(load_bias + d->d_un.d_ptr);
- *dp = &_r_debug;
- }
- break;
- case DT_MIPS_RLD_VERSION:
- case DT_MIPS_FLAGS:
- case DT_MIPS_BASE_ADDRESS:
- case DT_MIPS_UNREFEXTNO:
- break;
+ case DT_MIPS_RLD_MAP:
+ // Set the DT_MIPS_RLD_MAP entry to the address of _r_debug for GDB.
+ {
+ r_debug** dp = reinterpret_cast<r_debug**>(load_bias + d->d_un.d_ptr);
+ *dp = &_r_debug;
+ }
+ break;
+ case DT_MIPS_RLD_VERSION:
+ case DT_MIPS_FLAGS:
+ case DT_MIPS_BASE_ADDRESS:
+ case DT_MIPS_UNREFEXTNO:
+ break;
- case DT_MIPS_SYMTABNO:
- mips_symtabno = d->d_un.d_val;
- break;
+ case DT_MIPS_SYMTABNO:
+ mips_symtabno = d->d_un.d_val;
+ break;
- case DT_MIPS_LOCAL_GOTNO:
- mips_local_gotno = d->d_un.d_val;
- break;
+ case DT_MIPS_LOCAL_GOTNO:
+ mips_local_gotno = d->d_un.d_val;
+ break;
- case DT_MIPS_GOTSYM:
- mips_gotsym = d->d_un.d_val;
- break;
+ case DT_MIPS_GOTSYM:
+ mips_gotsym = d->d_un.d_val;
+ break;
#endif
- default:
- DEBUG("Unused DT entry: type %p arg %p",
- reinterpret_cast<void*>(d->d_tag), reinterpret_cast<void*>(d->d_un.d_val));
- break;
+ default:
+ if (!relocating_linker) {
+ DL_WARN("%s: unused DT entry: type %p arg %p", name,
+ reinterpret_cast<void*>(d->d_tag), reinterpret_cast<void*>(d->d_un.d_val));
}
+ break;
}
+ }
- DEBUG("si->base = %p, si->strtab = %p, si->symtab = %p",
- reinterpret_cast<void*>(base), strtab, symtab);
+ DEBUG("si->base = %p, si->strtab = %p, si->symtab = %p",
+ reinterpret_cast<void*>(base), strtab, symtab);
- // Sanity checks.
- if (relocating_linker && needed_count != 0) {
- DL_ERR("linker cannot have DT_NEEDED dependencies on other libraries");
- return false;
- }
- if (nbucket == 0) {
- DL_ERR("empty/missing DT_HASH in \"%s\" (built with --hash-style=gnu?)", name);
- return false;
- }
- if (strtab == 0) {
- DL_ERR("empty/missing DT_STRTAB in \"%s\"", name);
- return false;
- }
- if (symtab == 0) {
- DL_ERR("empty/missing DT_SYMTAB in \"%s\"", name);
- return false;
- }
+ // Sanity checks.
+ if (relocating_linker && needed_count != 0) {
+ DL_ERR("linker cannot have DT_NEEDED dependencies on other libraries");
+ return false;
+ }
+ if (nbucket == 0) {
+ DL_ERR("empty/missing DT_HASH in \"%s\" (built with --hash-style=gnu?)", name);
+ return false;
+ }
+ if (strtab == 0) {
+ DL_ERR("empty/missing DT_STRTAB in \"%s\"", name);
+ return false;
+ }
+ if (symtab == 0) {
+ DL_ERR("empty/missing DT_SYMTAB in \"%s\"", name);
+ return false;
+ }
+ return true;
+}
- bool soinfo::LinkImage(const android_dlextinfo* extinfo) {
+ // If this is the main executable, then load all of the libraries from LD_PRELOAD now.
+ if (flags & FLAG_EXE) {
+ memset(g_ld_preloads, 0, sizeof(g_ld_preloads));
+ size_t preload_count = 0;
+ for (size_t i = 0; g_ld_preload_names[i] != nullptr; i++) {
+ soinfo* lsi = find_library(g_ld_preload_names[i], 0, nullptr);
+ if (lsi != nullptr) {
+ g_ld_preloads[preload_count++] = lsi;
+ } else {
+ // As with glibc, failure to load an LD_PRELOAD library is just a warning.
+ DL_WARN("could not load library \"%s\" from LD_PRELOAD for \"%s\"; caused by %s",
+ g_ld_preload_names[i], name, linker_get_error_buffer());
+ }
+ }
+ }
+
+ for (ElfW(Dyn)* d = dynamic; d->d_tag != DT_NULL; ++d) {
+ if (d->d_tag == DT_NEEDED) {
+ const char* library_name = strtab + d->d_un.d_val;
+ DEBUG("%s needs %s", name, library_name);
+ soinfo* lsi = find_library(library_name, 0, nullptr);
+ if (lsi == nullptr) {
+ strlcpy(tmp_err_buf, linker_get_error_buffer(), sizeof(tmp_err_buf));
+ DL_ERR("could not load library \"%s\" needed by \"%s\"; caused by %s",
+ library_name, name, tmp_err_buf);
+ return false;
+ }
+
+ add_child(lsi);
+ }
+ }
#if !defined(__LP64__)
- if (has_text_relocations) {
- // Make segments writable to allow text relocations to work properly. We will later call
- // phdr_table_protect_segments() after all of them are applied and all constructors are run.
- DL_WARN("%s has text relocations. This is wasting memory and prevents "
- "security hardening. Please fix.", name);
- if (phdr_table_unprotect_segments(phdr, phnum, load_bias) < 0) {
- DL_ERR("can't unprotect loadable segments for \"%s\": %s",
- name, strerror(errno));
- return false;
- }
+ if (has_text_relocations) {
+ // Make segments writable to allow text relocations to work properly. We will later call
+ // phdr_table_protect_segments() after all of them are applied and all constructors are run.
+ DL_WARN("%s has text relocations. This is wasting memory and prevents "
+ "security hardening. Please fix.", name);
+ if (phdr_table_unprotect_segments(phdr, phnum, load_bias) < 0) {
+ DL_ERR("can't unprotect loadable segments for \"%s\": %s",
+ name, strerror(errno));
+ return false;
}
+ }
#endif
#if defined(USE_RELA)
dlopen_testlib_simple.cpp
module := libtest_simple
-build_type := target
-build_target := SHARED_LIBRARY
-include $(TEST_PATH)/Android.build.mk
+include $(LOCAL_PATH)/Android.build.testlib.mk
+
+# -----------------------------------------------------------------------------
+# Libraries used by dlfcn tests to verify correct load order:
+# libtest_check_order_2_right.so
+# -----------------------------------------------------------------------------
+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
+
+# -----------------------------------------------------------------------------
+# libtest_check_order_left.so
+# -----------------------------------------------------------------------------
+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
+
+# -----------------------------------------------------------------------------
+# libtest_check_order.so
+# -----------------------------------------------------------------------------
+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
+
+# -----------------------------------------------------------------------------
+# Library with dependency loop used by dlfcn tests
+#
+# libtest_with_dependency_loop -> a -> b -> c -> a
+# -----------------------------------------------------------------------------
+libtest_with_dependency_loop_src_files := dlopen_testlib_invalid.cpp
+
+libtest_with_dependency_loop_shared_libraries := \
+ libtest_with_dependency_loop_a
+
+module := libtest_with_dependency_loop
+include $(LOCAL_PATH)/Android.build.testlib.mk
+
+# -----------------------------------------------------------------------------
+# libtest_with_dependency_loop_a.so
+# -----------------------------------------------------------------------------
+libtest_with_dependency_loop_a_src_files := dlopen_testlib_invalid.cpp
+
+libtest_with_dependency_loop_a_shared_libraries := \
+ libtest_with_dependency_loop_b_tmp
+
+module := libtest_with_dependency_loop_a
+include $(LOCAL_PATH)/Android.build.testlib.mk
+
+# -----------------------------------------------------------------------------
+# libtest_with_dependency_loop_b.so
+#
+# this is temporary placeholder - will be removed
+# -----------------------------------------------------------------------------
+libtest_with_dependency_loop_b_tmp_src_files := dlopen_testlib_invalid.cpp
+libtest_with_dependency_loop_b_tmp_ldflags := -Wl,-soname=libtest_with_dependency_loop_b.so
+
+module := libtest_with_dependency_loop_b_tmp
+include $(LOCAL_PATH)/Android.build.testlib.mk
+
+# -----------------------------------------------------------------------------
+# libtest_with_dependency_loop_b.so
+# -----------------------------------------------------------------------------
+libtest_with_dependency_loop_b_src_files := dlopen_testlib_invalid.cpp
+libtest_with_dependency_loop_b_shared_libraries := libtest_with_dependency_loop_c
+
+module := libtest_with_dependency_loop_b
+include $(LOCAL_PATH)/Android.build.testlib.mk
+
+# -----------------------------------------------------------------------------
+# libtest_with_dependency_loop_c.so
+# -----------------------------------------------------------------------------
+libtest_with_dependency_loop_c_src_files := dlopen_testlib_invalid.cpp
+
+libtest_with_dependency_loop_c_shared_libraries := \
+ libtest_with_dependency_loop_a
+
+module := libtest_with_dependency_loop_c
+include $(LOCAL_PATH)/Android.build.testlib.mk
+
+# -----------------------------------------------------------------------------
+# libtest_relo_check_dt_needed_order.so
+# |
+# +-> libtest_relo_check_dt_needed_order_1.so
+# |
+# +-> libtest_relo_check_dt_needed_order_2.so
+# -----------------------------------------------------------------------------
+libtest_relo_check_dt_needed_order_shared_libraries := \
+ libtest_relo_check_dt_needed_order_1 libtest_relo_check_dt_needed_order_2
+
+libtest_relo_check_dt_needed_order_src_files := dlopen_testlib_relo_check_dt_needed_order.cpp
+libtest_relo_check_dt_needed_order_1_src_files := dlopen_testlib_relo_check_dt_needed_order_1.cpp
+libtest_relo_check_dt_needed_order_2_src_files := dlopen_testlib_relo_check_dt_needed_order_2.cpp
+
+module := libtest_relo_check_dt_needed_order
+include $(LOCAL_PATH)/Android.build.testlib.mk
+module := libtest_relo_check_dt_needed_order_1
+include $(LOCAL_PATH)/Android.build.testlib.mk
+module := libtest_relo_check_dt_needed_order_2
+include $(LOCAL_PATH)/Android.build.testlib.mk
# -----------------------------------------------------------------------------
- # Libraries used by dlfcn tests to verify correct load order:
- # libtest_check_order_2_right.so
- # -----------------------------------------------------------------------------
- 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
- build_type := target
- build_target := SHARED_LIBRARY
- include $(TEST_PATH)/Android.build.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
- build_type := target
- build_target := SHARED_LIBRARY
- include $(TEST_PATH)/Android.build.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
- build_type := target
- build_target := SHARED_LIBRARY
- include $(TEST_PATH)/Android.build.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
- build_type := target
- build_target := SHARED_LIBRARY
- include $(TEST_PATH)/Android.build.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
- build_type := target
- build_target := SHARED_LIBRARY
- include $(TEST_PATH)/Android.build.mk
-
- # -----------------------------------------------------------------------------
- # libtest_check_order_left.so
- # -----------------------------------------------------------------------------
- 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
- build_type := target
- build_target := SHARED_LIBRARY
- include $(TEST_PATH)/Android.build.mk
-
- # -----------------------------------------------------------------------------
- # libtest_check_order.so
- # -----------------------------------------------------------------------------
- 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
- build_type := target
- build_target := SHARED_LIBRARY
- include $(TEST_PATH)/Android.build.mk
-
- # -----------------------------------------------------------------------------
- # Library with dependency loop used by dlfcn tests
- #
- # libtest_with_dependency_loop -> a -> b -> c -> a
- # -----------------------------------------------------------------------------
- libtest_with_dependency_loop_src_files := empty.cpp
-
- libtest_with_dependency_loop_shared_libraries := \
- libtest_with_dependency_loop_a
-
- module := libtest_with_dependency_loop
- build_type := target
- build_target := SHARED_LIBRARY
- include $(TEST_PATH)/Android.build.mk
-
- # -----------------------------------------------------------------------------
- # libtest_with_dependency_loop_a.so
- # -----------------------------------------------------------------------------
- libtest_with_dependency_loop_a_src_files := empty.cpp
-
- libtest_with_dependency_loop_a_shared_libraries := \
- libtest_with_dependency_loop_b_tmp
-
- module := libtest_with_dependency_loop_a
- build_type := target
- build_target := SHARED_LIBRARY
- include $(TEST_PATH)/Android.build.mk
-
- # -----------------------------------------------------------------------------
- # libtest_with_dependency_loop_b.so
- #
- # this is temporary placeholder - will be removed
- # -----------------------------------------------------------------------------
- libtest_with_dependency_loop_b_tmp_src_files := empty.cpp
- libtest_with_dependency_loop_b_tmp_ldflags := -Wl,-soname=libtest_with_dependency_loop_b.so
-
- module := libtest_with_dependency_loop_b_tmp
- build_type := target
- build_target := SHARED_LIBRARY
- include $(TEST_PATH)/Android.build.mk
-
- # -----------------------------------------------------------------------------
- # libtest_with_dependency_loop_b.so
- # -----------------------------------------------------------------------------
- libtest_with_dependency_loop_b_src_files := empty.cpp
- libtest_with_dependency_loop_b_shared_libraries := libtest_with_dependency_loop_c
-
- module := libtest_with_dependency_loop_b
- build_type := target
- build_target := SHARED_LIBRARY
- include $(TEST_PATH)/Android.build.mk
-
- # -----------------------------------------------------------------------------
- # libtest_with_dependency_loop_c.so
- # -----------------------------------------------------------------------------
- libtest_with_dependency_loop_c_src_files := empty.cpp
-
- libtest_with_dependency_loop_c_shared_libraries := \
- libtest_with_dependency_loop_a
-
- module := libtest_with_dependency_loop_c
- build_type := target
- build_target := SHARED_LIBRARY
- include $(TEST_PATH)/Android.build.mk
-
- # -----------------------------------------------------------------------------
# libtest_relo_check_dt_needed_order.so
# |
# +-> libtest_relo_check_dt_needed_order_1.so