Add SO to solist before processing DT_NEEDED
NEXE needs: A_SO, C_SO
A_SO needs: B_SO, C_SO
C_SO needs: B_SO
Here, NEXE causes A to load, which then make B load. followed by C.
However C can't find B, because it hasn't been added to the solist yet.
This causes B to get loaded twice, however only one isntance of B
gets relocated correctly.
To fix this, break load into two parts, the first part maps the SO
into memory then adds it to the solist. The second part then processes
the dynamic section which causes other SO loads to happen.
diff --git a/newlinker/linker.c b/newlinker/linker.c
index 4774fb6..4dfb4b2 100644
--- a/newlinker/linker.c
+++ b/newlinker/linker.c
@@ -86,7 +86,6 @@
static soinfo *freelist = NULL;
static soinfo *solist = &libdl_info;
-static soinfo *sonext = &libdl_info;
static soinfo *somain;
@@ -164,10 +163,8 @@
/* Make sure we get a clean block of soinfo */
memset(si, 0, sizeof(soinfo));
strlcpy((char*) si->name, name, sizeof(si->name));
- sonext->next = si;
si->next = NULL;
si->refcount = 0;
- sonext = si;
DEBUG(3, "%s allocated soinfo @ %p\n", name, si);
return si;
@@ -198,11 +195,29 @@
always the static libdl_info.
*/
prev->next = si->next;
- if (si == sonext) sonext = prev;
si->next = freelist;
freelist = si;
}
+static int soinfo_chain(soinfo* si) {
+ // Find parent of lsi and unlink lsi
+ soinfo* me_prev = solist;
+ if (NULL == solist) {
+ DEBUG(1, " SOLIST broken, sould have a parent.\n");
+ return 1;
+ }
+
+ while (me_prev->next && me_prev->next->depth <= si->depth)
+ me_prev = me_prev->next;
+
+ DEBUG(2, " Inserting %s (%d) after %s (%d).\n",
+ si->name, si->depth, me_prev->name, me_prev->depth);
+
+ si->next = me_prev->next;
+ me_prev->next = si;
+ return 0;
+}
+
#ifdef ANDROID_ARM_LINKER
/* For a given PC, find the .so that it belongs to.
@@ -683,13 +698,17 @@
return NULL;
}
+ return si;
+}
+
+soinfo* load_library_process_dynamic(soinfo* si) {
#if defined(ANDROID_X86_64_LINKER)
static const size_t rel_size = sizeof(Elf64_Rela);
#else
static const size_t rel_size = sizeof(Elf32_Rel);
#endif
- Elf32_Addr base = load_bias;
+ Elf32_Addr base = si->load_bias;
for(Elf32_Addr* d = si->dynamic; *d; d++){
switch(*d++){
case DT_HASH:
@@ -879,12 +898,12 @@
const char* need_name = si->strtab + d[1];
soinfo *lsi = find_loaded_library(need_name);
if (lsi) {
- if (lsi->depth <= depth + 1) {
+ if (lsi->depth <= si->depth + 1) {
DEBUG(3, " DT_NEEDED %s already loaded at higher priority %d.\n",
need_name, lsi->depth);
} else {
DEBUG(3, " DT_NEEDED %s updating priority from %d to %d.\n",
- need_name, lsi->depth, depth + 1);
+ need_name, lsi->depth, si->depth + 1);
// Find parent of lsi and unlink lsi
soinfo* me_prev = solist;
@@ -898,7 +917,7 @@
// Find lsi's new parent
soinfo* prev = solist;
- while (prev->next && prev->next->depth <= depth + 1) prev = prev->next;
+ while (prev->next && prev->next->depth <= si->depth + 1) prev = prev->next;
lsi->next = prev->next;
prev->next = lsi;
DEBUG(4, " NEW PARENT: %s.\n", prev->name);
@@ -906,7 +925,7 @@
} else {
DEBUG(0, "%s DT_NEEDED %s not found, trying to load\n",
si->name, need_name);
- if (NULL == load_library(need_name, depth + 1)) {
+ if (NULL == load_library(need_name, si->depth + 1)) {
DL_ERR("Could not load library %s needed by %s.\n",
si->name, need_name);
soinfo_free(si);
@@ -933,10 +952,15 @@
}
soinfo* info = load_library_fd_phdr(fd, &phdr_mmap, &phdr_size, name, depth);
+ close(fd);
+
if (phdr_mmap != NULL)
phdr_table_unload(phdr_mmap, phdr_size);
- close(fd);
+ if (info != NULL) {
+ soinfo_chain(info);
+ load_library_process_dynamic(info);
+ }
return info;
}