x86_64 linker.
Based on I8dc3e2cb596f75dc58ae82e4dc58f8c177dd3323 by
Pavel Chupin <pavel.v.chupin@intel.com>.
Change-Id: Icd582d277cbe273477b450f2848343d72c86ec9f
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 983d0a0..d218590 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -404,7 +404,7 @@
soinfo *si;
unsigned addr = (unsigned)pc;
- for (si = solist; si != 0; si = si->next){
+ for (si = solist; si != 0; si = si->next) {
if ((addr >= si->base) && (addr < (si->base + si->size))) {
*pcount = si->ARM_exidx_count;
return (_Unwind_Ptr)si->ARM_exidx;
@@ -441,23 +441,24 @@
Elf_Sym* symtab = si->symtab;
const char* strtab = si->strtab;
- TRACE_TYPE(LOOKUP, "SEARCH %s in %s@0x%08x %08x %zd",
- name, si->name, si->base, hash, hash % si->nbucket);
+ TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p %x %zd",
+ name, si->name, reinterpret_cast<void*>(si->base), hash, hash % si->nbucket);
for (unsigned n = si->bucket[hash % si->nbucket]; n != 0; n = si->chain[n]) {
Elf_Sym* s = symtab + n;
if (strcmp(strtab + s->st_name, name)) continue;
/* only concern ourselves with global and weak symbol definitions */
- switch(ELF32_ST_BIND(s->st_info)){
+ switch (ELF_ST_BIND(s->st_info)) {
case STB_GLOBAL:
case STB_WEAK:
if (s->st_shndx == SHN_UNDEF) {
continue;
}
- TRACE_TYPE(LOOKUP, "FOUND %s in %s (%08x) %d",
- name, si->name, s->st_value, s->st_size);
+ TRACE_TYPE(LOOKUP, "FOUND %s in %s (%p) %zd",
+ name, si->name, reinterpret_cast<void*>(s->st_value),
+ static_cast<size_t>(s->st_size));
return s;
}
}
@@ -568,10 +569,11 @@
done:
if (s != NULL) {
- TRACE_TYPE(LOOKUP, "si %s sym %s s->st_value = 0x%08x, "
- "found in %s, base = 0x%08x, load bias = 0x%08x",
- si->name, name, s->st_value,
- (*lsi)->name, (*lsi)->base, (*lsi)->load_bias);
+ TRACE_TYPE(LOOKUP, "si %s sym %s s->st_value = %p, "
+ "found in %s, base = %p, load bias = %p",
+ si->name, name, reinterpret_cast<void*>(s->st_value),
+ (*lsi)->name, reinterpret_cast<void*>((*lsi)->base),
+ reinterpret_cast<void*>((*lsi)->load_bias));
return s;
}
@@ -613,8 +615,8 @@
}
if (s != NULL) {
- TRACE_TYPE(LOOKUP, "%s s->st_value = 0x%08x, found->base = 0x%08x",
- name, s->st_value, (*found)->base);
+ TRACE_TYPE(LOOKUP, "%s s->st_value = %p, found->base = %p",
+ name, reinterpret_cast<void*>(s->st_value), reinterpret_cast<void*>((*found)->base));
}
return s;
@@ -767,8 +769,8 @@
// At this point we know that whatever is loaded @ base is a valid ELF
// shared library whose segments are properly mapped in.
- TRACE("[ init_library base=0x%08x sz=0x%08x name='%s' ]",
- si->base, si->size, si->name);
+ TRACE("[ init_library base=%p sz=0x%08x name='%s' ]",
+ reinterpret_cast<void*>(si->base), si->size, si->name);
if (!soinfo_link_image(si)) {
munmap(reinterpret_cast<void*>(si->base), si->size);
@@ -838,10 +840,124 @@
return result;
}
-/* TODO: don't use unsigned for addrs below. It works, but is not
- * ideal. They should probably be either uint32_t, Elf_Addr, or unsigned
- * long.
- */
+#if defined(ANDROID_X86_64_LINKER)
+static int soinfo_relocate_a(soinfo* si, Elf_Rela* rela, unsigned count, soinfo* needed[]) {
+ Elf_Sym* symtab = si->symtab;
+ const char* strtab = si->strtab;
+ Elf_Sym* s;
+ Elf_Rela* start = rela;
+ soinfo* lsi;
+
+ for (size_t idx = 0; idx < count; ++idx, ++rela) {
+ unsigned type = ELF_R_TYPE(rela->r_info);
+ unsigned sym = ELF_R_SYM(rela->r_info);
+ Elf_Addr reloc = static_cast<Elf_Addr>(rela->r_offset + si->load_bias);
+ Elf_Addr sym_addr = 0;
+ char* sym_name = NULL;
+
+ DEBUG("Processing '%s' relocation at index %zd", si->name, idx);
+ if (type == 0) { // R_*_NONE
+ continue;
+ }
+ if (sym != 0) {
+ sym_name = (char *)(strtab + symtab[sym].st_name);
+ s = soinfo_do_lookup(si, sym_name, &lsi, needed);
+ if (s == NULL) {
+ // We only allow an undefined symbol if this is a weak reference...
+ s = &symtab[sym];
+ if (ELF_ST_BIND(s->st_info) != STB_WEAK) {
+ DL_ERR("cannot locate symbol \"%s\" referenced by \"%s\"...", sym_name, si->name);
+ return -1;
+ }
+
+ /* IHI0044C AAELF 4.5.1.1:
+
+ Libraries are not searched to resolve weak references.
+ It is not an error for a weak reference to remain unsatisfied.
+
+ During linking, the value of an undefined weak reference is:
+ - Zero if the relocation type is absolute
+ - The address of the place if the relocation is pc-relative
+ - The address of nominal base address if the relocation
+ type is base-relative.
+ */
+
+ switch (type) {
+ case R_X86_64_JUMP_SLOT:
+ case R_X86_64_GLOB_DAT:
+ case R_X86_64_32:
+ case R_X86_64_RELATIVE:
+ // No need to do anything.
+ break;
+
+ case R_X86_64_PC32:
+ sym_addr = reloc;
+ break;
+
+ default:
+ DL_ERR("unknown weak reloc type %d @ %p (%d)", type, rela, (int) (rela - start));
+ return -1;
+ }
+ } else {
+ // We got a definition.
+ sym_addr = static_cast<Elf_Addr>(s->st_value + lsi->load_bias);
+ }
+ count_relocation(kRelocSymbol);
+ } else {
+ s = NULL;
+ }
+
+ switch (type) {
+ case R_X86_64_JUMP_SLOT:
+ count_relocation(kRelocAbsolute);
+ MARK(rela->r_offset);
+ TRACE_TYPE(RELO, "RELO JMP_SLOT %08zx <- %08zx %s", static_cast<size_t>(reloc),
+ static_cast<size_t>(sym_addr + rela->r_addend), sym_name);
+ *reinterpret_cast<Elf_Addr*>(reloc) = sym_addr + rela->r_addend;
+ break;
+ case R_X86_64_GLOB_DAT:
+ count_relocation(kRelocAbsolute);
+ MARK(rela->r_offset);
+ TRACE_TYPE(RELO, "RELO GLOB_DAT %08zx <- %08zx %s", static_cast<size_t>(reloc),
+ static_cast<size_t>(sym_addr + rela->r_addend), sym_name);
+ *reinterpret_cast<Elf_Addr*>(reloc) = sym_addr + rela->r_addend;
+ break;
+ case R_X86_64_RELATIVE:
+ count_relocation(kRelocRelative);
+ MARK(rela->r_offset);
+ if (sym) {
+ DL_ERR("odd RELATIVE form...");
+ return -1;
+ }
+ TRACE_TYPE(RELO, "RELO RELATIVE %08zx <- +%08zx", static_cast<size_t>(reloc),
+ static_cast<size_t>(si->base));
+ *reinterpret_cast<Elf_Addr*>(reloc) = si->base + rela->r_addend;
+ break;
+
+ case R_X86_64_32:
+ count_relocation(kRelocRelative);
+ MARK(rela->r_offset);
+ TRACE_TYPE(RELO, "RELO R_X86_64_32 %08zx <- +%08zx %s", static_cast<size_t>(reloc),
+ static_cast<size_t>(sym_addr), sym_name);
+ *reinterpret_cast<Elf_Addr*>(reloc) = sym_addr + rela->r_addend;
+ break;
+
+ case R_X86_64_PC32:
+ count_relocation(kRelocRelative);
+ MARK(rela->r_offset);
+ TRACE_TYPE(RELO, "RELO R_X86_64_PC32 %08zx <- +%08zx (%08zx - %08zx) %s",
+ static_cast<size_t>(reloc), static_cast<size_t>(sym_addr - reloc),
+ static_cast<size_t>(sym_addr), static_cast<size_t>(reloc), sym_name);
+ *reinterpret_cast<Elf_Addr*>(reloc) = sym_addr + rela->r_addend - reloc;
+ break;
+ default:
+ DL_ERR("unknown reloc type %d @ %p (%d)", type, rela, (int) (rela - start));
+ return -1;
+ }
+ }
+ return 0;
+}
+#else
static int soinfo_relocate(soinfo* si, Elf_Rel* rel, unsigned count,
soinfo* needed[])
{
@@ -852,8 +968,9 @@
soinfo* lsi;
for (size_t idx = 0; idx < count; ++idx, ++rel) {
- unsigned type = ELF32_R_TYPE(rel->r_info);
- unsigned sym = ELF32_R_SYM(rel->r_info);
+ unsigned type = ELF_R_TYPE(rel->r_info);
+ // TODO: don't use unsigned for 'sym'. Use uint32_t or Elf_Addr instead.
+ unsigned sym = ELF_R_SYM(rel->r_info);
Elf_Addr reloc = static_cast<Elf_Addr>(rel->r_offset + si->load_bias);
Elf_Addr sym_addr = 0;
char* sym_name = NULL;
@@ -869,7 +986,7 @@
/* We only allow an undefined symbol if this is a weak
reference.. */
s = &symtab[sym];
- if (ELF32_ST_BIND(s->st_info) != STB_WEAK) {
+ if (ELF_ST_BIND(s->st_info) != STB_WEAK) {
DL_ERR("cannot locate symbol \"%s\" referenced by \"%s\"...", sym_name, si->name);
return -1;
}
@@ -897,7 +1014,7 @@
case R_386_JMP_SLOT:
case R_386_GLOB_DAT:
case R_386_32:
- case R_386_RELATIVE: /* Dont' care. */
+ case R_386_RELATIVE: /* Don't care. */
#endif /* ANDROID_*_LINKER */
/* sym_addr was initialized to be zero above or relocation
code below does not care about value of sym_addr.
@@ -940,7 +1057,7 @@
/* TODO: This is ugly. Split up the relocations by arch into
* different files.
*/
- switch(type){
+ switch (type) {
#if defined(ANDROID_ARM_LINKER)
case R_ARM_JUMP_SLOT:
count_relocation(kRelocAbsolute);
@@ -1005,7 +1122,8 @@
DL_ERR("odd RELATIVE form...");
return -1;
}
- TRACE_TYPE(RELO, "RELO RELATIVE %08x <- +%08x", reloc, si->base);
+ TRACE_TYPE(RELO, "RELO RELATIVE %p <- +%p",
+ reinterpret_cast<void*>(reloc), reinterpret_cast<void*>(si->base));
*reinterpret_cast<Elf_Addr*>(reloc) += si->base;
break;
@@ -1080,6 +1198,7 @@
}
return 0;
}
+#endif
#ifdef ANDROID_MIPS_LINKER
static bool mips_relocate_got(soinfo* si, soinfo* needed[]) {
@@ -1129,7 +1248,7 @@
/* We only allow an undefined symbol if this is a weak
reference.. */
s = &symtab[g];
- if (ELF32_ST_BIND(s->st_info) != STB_WEAK) {
+ if (ELF_ST_BIND(s->st_info) != STB_WEAK) {
DL_ERR("cannot locate \"%s\"...", sym_name);
return false;
}
@@ -1307,7 +1426,7 @@
/* We can't debug anything until the linker is relocated */
if (!relocating_linker) {
INFO("[ linking %s ]", si->name);
- DEBUG("si->base = 0x%08x si->flags = 0x%08x", si->base, si->flags);
+ DEBUG("si->base = %p si->flags = 0x%08x", reinterpret_cast<void*>(si->base), si->flags);
}
/* Extract dynamic section */
@@ -1334,8 +1453,9 @@
// Extract useful information from dynamic section.
uint32_t needed_count = 0;
for (Elf_Dyn* d = si->dynamic; d->d_tag != DT_NULL; ++d) {
- DEBUG("d = %p, d[0](tag) = 0x%08x d[1](val) = 0x%08x", d, d->d_tag, d->d_un.d_val);
- switch(d->d_tag){
+ 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:
si->nbucket = ((unsigned *) (base + d->d_un.d_ptr))[0];
si->nchain = ((unsigned *) (base + d->d_un.d_ptr))[1];
@@ -1348,28 +1468,34 @@
case DT_SYMTAB:
si->symtab = (Elf_Sym *) (base + d->d_un.d_ptr);
break;
+#if !defined(ANDROID_X86_64_LINKER)
case DT_PLTREL:
if (d->d_un.d_val != DT_REL) {
DL_ERR("unsupported DT_RELA in \"%s\"", si->name);
return false;
}
break;
+#endif
case DT_JMPREL:
+#if defined(ANDROID_X86_64_LINKER)
+ si->plt_rela = (Elf_Rela*) (base + d->d_un.d_ptr);
+#else
si->plt_rel = (Elf_Rel*) (base + d->d_un.d_ptr);
+#endif
break;
case DT_PLTRELSZ:
+#if defined(ANDROID_X86_64_LINKER)
+ si->plt_rela_count = d->d_un.d_val / sizeof(Elf_Rela);
+#else
si->plt_rel_count = d->d_un.d_val / sizeof(Elf_Rel);
- break;
- case DT_REL:
- si->rel = (Elf_Rel*) (base + d->d_un.d_ptr);
- break;
- case DT_RELSZ:
- si->rel_count = d->d_un.d_val / sizeof(Elf_Rel);
+#endif
break;
case DT_PLTGOT:
+#if !defined(ANDROID_X86_64_LINKER)
/* Save this in case we decide to do lazy binding. We don't yet. */
si->plt_got = (unsigned *)(base + d->d_un.d_ptr);
break;
+#endif
case DT_DEBUG:
// Set the DT_DEBUG entry to the address of _r_debug for GDB
// if the dynamic table is writable
@@ -1377,9 +1503,30 @@
d->d_un.d_val = reinterpret_cast<uintptr_t>(&_r_debug);
}
break;
+#if defined(ANDROID_X86_64_LINKER)
+ case DT_RELA:
+ si->rela = (Elf_Rela*) (base + d->d_un.d_ptr);
+ break;
+ case DT_RELASZ:
+ si->rela_count = d->d_un.d_val / sizeof(Elf_Rela);
+ break;
+ case DT_REL:
+ DL_ERR("unsupported DT_REL in \"%s\"", si->name);
+ return false;
+ case DT_RELSZ:
+ DL_ERR("unsupported DT_RELSZ in \"%s\"", si->name);
+ return false;
+#else
+ case DT_REL:
+ si->rel = (Elf_Rel*) (base + d->d_un.d_ptr);
+ break;
+ case DT_RELSZ:
+ si->rel_count = d->d_un.d_val / sizeof(Elf_Rel);
+ break;
case DT_RELA:
DL_ERR("unsupported DT_RELA in \"%s\"", si->name);
return false;
+#endif
case DT_INIT:
si->init_func = reinterpret_cast<linker_function_t>(base + d->d_un.d_ptr);
DEBUG("%s constructors (DT_INIT) found at %p", si->name, si->init_func);
@@ -1466,8 +1613,8 @@
}
}
- DEBUG("si->base = 0x%08x, si->strtab = %p, si->symtab = %p",
- si->base, si->strtab, si->symtab);
+ DEBUG("si->base = %p, si->strtab = %p, si->symtab = %p",
+ reinterpret_cast<void*>(si->base), si->strtab, si->symtab);
// Sanity checks.
if (relocating_linker && needed_count != 0) {
@@ -1537,6 +1684,20 @@
}
}
+#if defined(ANDROID_X86_64_LINKER)
+ if (si->plt_rela != NULL) {
+ DEBUG("[ relocating %s plt ]\n", si->name );
+ if (soinfo_relocate_a(si, si->plt_rela, si->plt_rela_count, needed)) {
+ return false;
+ }
+ }
+ if (si->rela != NULL) {
+ DEBUG("[ relocating %s ]\n", si->name );
+ if (soinfo_relocate_a(si, si->rela, si->rela_count, needed)) {
+ return false;
+ }
+ }
+#else
if (si->plt_rel != NULL) {
DEBUG("[ relocating %s plt ]", si->name );
if (soinfo_relocate(si, si->plt_rel, si->plt_rel_count, needed)) {
@@ -1549,6 +1710,7 @@
return false;
}
}
+#endif
#ifdef ANDROID_MIPS_LINKER
if (!mips_relocate_got(si, needed)) {
@@ -1781,7 +1943,7 @@
fflush(stdout);
#endif
- TRACE("[ Ready to execute '%s' @ 0x%08x ]", si->name, si->entry);
+ TRACE("[ Ready to execute '%s' @ %p ]", si->name, reinterpret_cast<void*>(si->entry));
return si->entry;
}