Move ELF PLT import resolution into plt.c ##bin

This commit is contained in:
pancake 2026-08-14 20:03:52 +02:00
parent 126ccbd504
commit eaec5003d5
3 changed files with 442 additions and 427 deletions

View file

@ -11,19 +11,10 @@
/// XXX this should be a runtime option
#define PERMIT_UNNAMED_SYMBOLS 0
#define MIPS_PLT_OFFSET 0x20
#define RISCV_PLT_OFFSET 0x20
#define LOONGARCH_PLT_OFFSET 0x20
#define S390_PLT_OFFSET 0x20
#define DT_AARCH64_PAC_PLT (DT_LOPROC + 3)
#define RISCV_PLT_ENTRY_SIZE 0x10
#define LOONGARCH_PLT_ENTRY_SIZE 0x10
#define X86_PLT_ENTRY_SIZE 0x10
#define SPARC_OFFSET_PLT_ENTRY_FROM_GOT_ADDR -0x6
#define X86_OFFSET_PLT_ENTRY_FROM_GOT_ADDR -0x6
#define ELF_PAGE_MASK 0xFFFFFFFFFFFFF000LL
#define ELF_PAGE_SIZE 4096
@ -44,8 +35,6 @@
#define BREAD32(x, i) r_buf_read_ble32_at (x, i, eo->endian); (i) += 4
#define BREAD64(x, i) r_buf_read_ble64_at (x, i, eo->endian); (i) += 8
#define NUMENTRIES_ROUNDUP(sectionsize, entrysize) (((sectionsize) + (entrysize) - 1) / (entrysize))
#define COMPUTE_PLTGOT_POSITION(rel, pltgot_addr, n_initial_unused_entries) \
((rel->rva - pltgot_addr - n_initial_unused_entries * R_BIN_ELF_WORDSIZE) / R_BIN_ELF_WORDSIZE)
#define round_up(a) ((((a) + (4) - (1)) / (4)) * (4))
@ -1668,141 +1657,6 @@ ut64 Elf_(get_section_size)(ELFOBJ *eo, const char *section_name) {
return section? section->size: UT64_MAX;
}
static ut64 get_got_entry(ELFOBJ *eo, RBinElfReloc *rel) {
if (!rel || !rel->rva || rel->rva == UT64_MAX) {
return UT64_MAX;
}
ut64 p_sym_got_addr = Elf_(v2p) (eo, rel->rva);
ut64 addr = R_BIN_ELF_BREADWORD (eo->b, p_sym_got_addr);
return (!addr || addr == R_BIN_ELF_WORD_MAX) ? UT64_MAX : addr;
}
static ut64 get_import_addr_qdsp6(ELFOBJ *eo, RBinElfReloc *rel) {
ut64 got_addr = eo->dyn_info.dt_pltgot;
if (got_addr == R_BIN_ELF_ADDR_MAX) {
return UT64_MAX;
}
ut64 plt_addr = get_got_entry (eo, rel);
if (plt_addr == UT64_MAX) {
return UT64_MAX;
}
const ut64 pos = COMPUTE_PLTGOT_POSITION (rel, got_addr, 0x3);
switch (rel->type) {
case R_QDSP6_JUMP_SLOT:
return plt_addr + pos * 16 + 32;
}
return UT64_MAX;
}
static ut64 get_import_addr_arm(ELFOBJ *eo, RBinElfReloc *rel) {
ut64 got_addr = eo->dyn_info.dt_pltgot;
if (got_addr == R_BIN_ELF_ADDR_MAX) {
return UT64_MAX;
}
ut64 plt_addr = get_got_entry (eo, rel);
if (plt_addr == UT64_MAX) {
return UT64_MAX;
}
const ut64 pos = COMPUTE_PLTGOT_POSITION (rel, got_addr, 0x3);
switch (rel->type) {
case R_ARM_JUMP_SLOT:
plt_addr += pos * 12 + 20;
if (plt_addr & 1) {
plt_addr--;
}
return plt_addr;
case R_ARM_GLOB_DAT:
case R_ARM_ABS32:
return rel->rva;
default:
R_LOG_WARN ("Unsupported relocation type for imports %d", rel->type);
return UT64_MAX;
}
return UT64_MAX;
}
static ut64 get_import_addr_arm64(ELFOBJ *eo, RBinElfReloc *rel) {
ut64 got_addr = eo->dyn_info.dt_pltgot;
if (got_addr == R_BIN_ELF_ADDR_MAX) {
return UT64_MAX;
}
ut64 plt_addr = get_got_entry (eo, rel);
if (plt_addr == UT64_MAX) {
return UT64_MAX;
}
const ut64 pos = COMPUTE_PLTGOT_POSITION (rel, got_addr, 0x3);
switch (rel->type) {
case R_AARCH64_RELATIVE:
// Direct binding: adjust by program base for relative relocations.
return eo->baddr + rel->addend;
case R_AARCH64_IRELATIVE:
if (rel->addend > plt_addr) { // start
return Elf_(plt_arm64_entry) (eo, plt_addr, pos) + rel->addend;
}
// same as fallback to JUMP_SLOT
return Elf_(plt_arm64_entry) (eo, plt_addr, pos);
case R_AARCH64_JUMP_SLOT:
return Elf_(plt_arm64_entry) (eo, plt_addr, pos);
case R_AARCH64_GLOB_DAT:
return rel->rva;
default:
R_LOG_WARN ("Unsupported relocation type for imports %d", rel->type);
return UT64_MAX;
}
return UT64_MAX;
}
static ut64 get_import_addr_mips(ELFOBJ *bin, RBinElfReloc *rel) {
ut64 jmprel_addr = bin->dyn_info.dt_jmprel;
ut64 got_addr = bin->dyn_info.dt_mips_pltgot;
if (jmprel_addr != R_BIN_ELF_ADDR_MAX && got_addr != R_BIN_ELF_ADDR_MAX) {
ut64 pos = COMPUTE_PLTGOT_POSITION (rel, got_addr, 0x2);
ut8 buf[128]; /// XXX why arbitrary 128
ut64 plt_addr = jmprel_addr + bin->dyn_info.dt_pltrelsz;
ut64 p_plt_addr = Elf_(v2p) (bin, plt_addr);
int res = r_buf_read_at (bin->b, p_plt_addr, buf, sizeof (buf));
if (res == sizeof (buf)) {
const ut8 *base = r_mem_mem_aligned (buf, sizeof (buf), (const ut8 *)"\x3c\x0f\x00", 3, 4);
plt_addr += base? (int)(size_t) (base - buf): MIPS_PLT_OFFSET + 8; // HARDCODED HACK
plt_addr += pos * 16;
return plt_addr;
}
}
return UT64_MAX;
}
static ut64 get_import_addr_riscv(ELFOBJ *bin, RBinElfReloc *rel) {
ut64 got_addr = bin->dyn_info.dt_pltgot;
if (got_addr != R_BIN_ELF_ADDR_MAX) {
ut64 plt_addr = get_got_entry (bin, rel);
if (plt_addr != UT64_MAX) {
ut64 pos = COMPUTE_PLTGOT_POSITION (rel, got_addr, 2);
return plt_addr + RISCV_PLT_OFFSET + pos * RISCV_PLT_ENTRY_SIZE;
}
}
return UT64_MAX;
}
static ut64 get_import_addr_loongarch(ELFOBJ *bin, RBinElfReloc *rel) {
ut64 got_addr = bin->dyn_info.dt_pltgot;
if (got_addr != R_BIN_ELF_ADDR_MAX) {
ut64 plt_addr = get_got_entry (bin, rel);
if (plt_addr != UT64_MAX) {
ut64 pos = COMPUTE_PLTGOT_POSITION (rel, got_addr, 2);
return plt_addr + LOONGARCH_PLT_OFFSET + pos * LOONGARCH_PLT_ENTRY_SIZE;
}
}
return UT64_MAX;
}
static size_t get_size_rel_mode(Elf_(Xword) mode) {
if (mode == DT_RELA) {
@ -1838,42 +1692,11 @@ ut64 Elf_(plt_num_relocs)(ELFOBJ *eo) {
return get_num_relocs_dynamic_plt (eo);
}
static ut64 get_import_addr_sparc(ELFOBJ *eo, RBinElfReloc *rel) {
if (rel->type != R_SPARC_JMP_SLOT) {
R_LOG_DEBUG ("Unknown sparc reloc type %d", rel->type);
return UT64_MAX;
}
ut64 tmp = get_got_entry (eo, rel);
return (tmp == UT64_MAX) ? UT64_MAX : tmp + SPARC_OFFSET_PLT_ENTRY_FROM_GOT_ADDR;
}
static ut64 get_import_addr_s390x(ELFOBJ *eo, RBinElfReloc *rel) {
ut64 a = get_got_entry (eo, rel);
if (a == UT64_MAX) {
// GLOBALS, OBJECTS, NOTYPE, ..
return UT64_MAX;
}
return a - 14;
}
// EF_PPC64_ABI: 1 = ELFv1, 2 = ELFv2, 3 = undefined; unflagged objects predate the field, so guess from the endian
static int ppc64_abi(ELFOBJ *eo) {
if (eo->ehdr.e_machine != EM_PPC64) {
return 0;
}
switch (eo->ehdr.e_flags & EF_PPC64_ABI) {
case 1: return 1;
case 2: return 2;
case 3: return 0;
}
return eo->endian? 1: 2;
}
#if R_BIN_ELF64
// ELFv1 function st_value points at a .opd descriptor [code, toc, env]; deref the code address
static ut64 ppc64v1_opd_deref(ELFOBJ *eo, ut64 vaddr) {
if (ppc64_abi (eo) != 1) {
if (Elf_(plt_ppc64_abi) (eo) != 1) {
return UT64_MAX;
}
if (!eo->sections_loaded) {
@ -1887,254 +1710,8 @@ static ut64 ppc64v1_opd_deref(ELFOBJ *eo, ut64 vaddr) {
return r_buf_read_ble64_at (eo->b, foff, eo->endian);
}
// PLT slot vaddr -> lazy glink stub vaddr; the N-th DT_JMPREL entry owns the N-th
// stub after DT_PPC64_GLINK + 32 (binutils ppc64_elf_get_synthetic_symtab)
static HtUU *ppc64_build_glink_map(ELFOBJ *eo, int abi) {
const RBinElfDynamicInfo *di = &eo->dyn_info;
if (di->dt_ppc64_glink == R_BIN_ELF_ADDR_MAX) {
return NULL;
}
if (di->dt_jmprel == R_BIN_ELF_ADDR_MAX || !di->dt_pltrelsz) {
return NULL;
}
size_t relsize = get_size_rel_mode (di->dt_pltrel);
if (!relsize) {
return NULL;
}
HtUU *map = ht_uu_new0 ();
if (!map) {
return NULL;
}
ut64 stub_vma = di->dt_ppc64_glink + 32;
ut64 num_plts = di->dt_pltrelsz / relsize;
ut64 n;
for (n = 0; n < num_plts; n++) {
ut64 rela_off = Elf_(v2p) (eo, di->dt_jmprel + n * relsize);
if (rela_off == UT64_MAX) {
break;
}
ut64 slot_vaddr = r_buf_read_ble64_at (eo->b, rela_off, eo->endian);
if (slot_vaddr == UT64_MAX) {
break;
}
ht_uu_insert (map, slot_vaddr, stub_vma);
// an ELFv2 stub is a single branch; ELFv1 stubs grow to 12 bytes past slot 0x8000
stub_vma += (abi == 2)? 4: (n >= 0x8000)? 12: 8;
}
return map;
}
#endif
// PLT stub vaddr for the given GOT slot in a ppc64 binary, building the stub cache on demand
ut64 Elf_(ppc64_get_plt_stub_for_slot)(ELFOBJ *eo, ut64 slot_vaddr) {
#if R_BIN_ELF64
const int abi = ppc64_abi (eo);
if (!abi) {
return UT64_MAX;
}
if (!eo->ppc64_plt_stubs) {
eo->ppc64_plt_stubs = ppc64_build_glink_map (eo, abi);
}
if (eo->ppc64_plt_stubs) {
bool found = false;
ut64 stub = ht_uu_find (eo->ppc64_plt_stubs, slot_vaddr, &found);
if (found) {
return stub;
}
}
#endif
return UT64_MAX;
}
static ut64 get_import_addr_ppc(ELFOBJ *eo, RBinElfReloc *rel) {
#if R_BIN_ELF64
if (ppc64_abi (eo)) {
ut64 stub = Elf_(ppc64_get_plt_stub_for_slot) (eo, rel->rva);
if (stub != UT64_MAX) {
return stub;
}
return rel->rva; // no DT_PPC64_GLINK or no map entry
}
#endif
ut64 plt_addr = eo->dyn_info.dt_pltgot;
if (plt_addr == R_BIN_ELF_ADDR_MAX) {
return UT64_MAX;
}
// -fPIC secure-plt objects reach the slot through r30-relative call
// thunks; when the whole map decodes it overrides the legacy math
const ut64 thunk = Elf_(plt_ppc32_thunk) (eo, rel->rva);
if (thunk != UT64_MAX) {
return thunk;
}
if (rel->rva < plt_addr) {
ut64 delta = plt_addr - rel->rva;
ut64 orva = rel->rva + (2 * delta);
R_LOG_DEBUG ("Massaged pointer below plt from 0x%"PFMT64x" to 0x%"PFMT64x, rel->rva, orva);
return orva;
}
ut64 p_plt_addr = Elf_(v2p) (eo, plt_addr);
if (p_plt_addr == UT64_MAX) {
return UT64_MAX;
}
ut64 base = r_buf_read_ble32_at (eo->b, p_plt_addr, eo->endian);
if (base == UT32_MAX) {
return UT64_MAX;
}
ut64 nrel = get_num_relocs_dynamic_plt (eo);
ut64 pos = COMPUTE_PLTGOT_POSITION (rel, plt_addr, 0x0);
if (eo->endian) {
base -= nrel * 16;
base += pos * 16;
return base;
}
base -= (nrel * 12) + 20;
base += (pos * 8);
return base;
}
static ut64 get_import_addr_x86_manual(ELFOBJ *eo, RBinElfReloc *rel) {
ut64 got_addr = eo->dyn_info.dt_pltgot;
if (got_addr == R_BIN_ELF_ADDR_MAX) {
return UT64_MAX;
}
ut64 got_offset = Elf_(v2p) (eo, got_addr);
if (got_offset == UT64_MAX) {
return UT64_MAX;
}
//XXX HACK ALERT!!!! full relro?? try to fix it
//will there always be .plt.got, what would happen if is .got.plt?
RBinElfSection *s = get_section_by_name (eo, ".plt.got");
if (Elf_(has_relro) (eo) < R_ELF_PART_RELRO || !s) {
return UT64_MAX;
}
ut8 buf[sizeof (Elf_(Addr))] = {0};
// Elf_(Addr) buf;
ut64 plt_addr = s->offset;
ut64 plt_sym_addr;
while (plt_addr + 2 + 4 < s->offset + s->size && plt_addr + 2 + 4 < eo->size) {
/*we try to locate the plt entry that correspond with the relocation
since got does not point back to .plt. In this case it has the following
form
ff253a152000 JMP QWORD [RIP + 0x20153A]
6690 NOP
----
ff25ec9f0408 JMP DWORD [reloc.puts_236]
plt_addr + 2 to remove jmp opcode and get the imm reading 4
and if RIP (plt_addr + 6) + imm == rel->offset
return plt_addr, that will be our sym addr
perhaps this hack doesn't work on 32 bits
*/
int res = r_buf_read_at (eo->b, plt_addr + 2, buf, sizeof (ut32));
if (res < 0) {
return UT64_MAX;
}
size_t i = 0;
plt_sym_addr = R_BIN_ELF_READWORD (buf, i);
//relative address
if ((plt_addr + 6 + plt_sym_addr) == rel->rva) {
return plt_addr;
}
if (plt_sym_addr == rel->rva) {
return plt_addr;
}
plt_addr += 8;
}
return UT64_MAX;
}
static ut64 get_import_addr_x86(ELFOBJ *eo, RBinElfReloc *rel) {
ut64 tmp = get_got_entry (eo, rel);
if (tmp == UT64_MAX) {
return get_import_addr_x86_manual (eo, rel);
}
RBinElfSection *pltsec = get_section_by_name (eo, ".plt.sec");
if (pltsec) {
ut64 got_addr = eo->dyn_info.dt_pltgot;
ut64 pos = COMPUTE_PLTGOT_POSITION (rel, got_addr, 3);
return pltsec->rva + pos * X86_PLT_ENTRY_SIZE;
}
return tmp + X86_OFFSET_PLT_ENTRY_FROM_GOT_ADDR;
}
static ut64 get_import_addr(ELFOBJ *eo, int sym) {
if ((!eo->shdr || !eo->strtab) && !eo->phdr) {
return UT64_MAX;
}
if (!eo->rel_cache) {
return UT64_MAX;
}
int index = ht_uu_find (eo->rel_cache, sym + 1, NULL);
if (index < 1) {
return UT64_MAX;
}
// lookup the right rel/rela entry
RBinElfReloc *rel = RVecRBinElfReloc_at (&eo->g_relocs, index - 1);
if (!rel) {
return UT64_MAX;
}
switch (eo->ehdr.e_machine) {
case EM_S390:
return get_import_addr_s390x (eo, rel);
case EM_ARM:
return get_import_addr_arm (eo, rel);
case EM_AARCH64:
return get_import_addr_arm64 (eo, rel);
case EM_MIPS: // MIPS32 BIG ENDIAN relocs
return get_import_addr_mips (eo, rel);
case EM_QDSP6: // also known as HEXAGON
return get_import_addr_qdsp6 (eo, rel);
case EM_VAX:
// as beautiful as riscv <3
return get_import_addr_riscv (eo, rel);
case EM_RISCV:
return get_import_addr_riscv (eo, rel);
case EM_SPARC:
case EM_SPARCV9:
case EM_SPARC32PLUS:
return get_import_addr_sparc (eo, rel);
case EM_PPC:
case EM_PPC64:
return get_import_addr_ppc (eo, rel);
case EM_386:
case EM_X86_64:
case EM_IAMCU:
return get_import_addr_x86 (eo, rel);
case EM_LOONGARCH:
return get_import_addr_loongarch (eo, rel);
case EM_SBPF:
// sBPF relocations are handled in patch_reloc, return the offset for imports
return rel->offset;
case EM_BPF:
return rel->offset;
case EM_V800:
case EM_V850:
return rel->offset;
case EM_NDS32:
return rel->offset;
default:
R_LOG_WARN ("Unsupported relocs type %" PFMT64u " for arch %d",
(ut64) rel->type, eo->ehdr.e_machine);
return UT64_MAX;
}
}
bool Elf_(has_nobtcfi)(ELFOBJ *eo) {
return eo->has_nobtcfi;
}
@ -2897,7 +2474,7 @@ char* Elf_(get_abi)(ELFOBJ *eo) {
}
break;
case EM_PPC64:
switch (ppc64_abi (eo)) {
switch (Elf_(plt_ppc64_abi) (eo)) {
case 1: return strdup ("elfv1");
case 2: return strdup ("elfv2");
}
@ -5030,7 +4607,7 @@ static bool _read_symbols_from_phdr(ELFOBJ *eo, ReadPhdrSymbolState *state) {
if (type == R_BIN_ELF_IMPORT_SYMBOLS && (new_symbol.st_shndx == SHT_NULL || new_symbol.st_shndx == SHT_DYNSYM)) {
if (new_symbol.st_value) {
toffset = new_symbol.st_value;
} else if ((toffset = get_import_addr (eo, i)) == UT64_MAX) {
} else if ((toffset = Elf_(plt_get_import_addr) (eo, i)) == UT64_MAX) {
// toffset = 0;
}
tsize = 16;
@ -5655,7 +5232,7 @@ static bool _process_symbols_and_imports_in_section(ELFOBJ *eo, int type, Proces
if (type == R_BIN_ELF_IMPORT_SYMBOLS) {
if (sym.st_value) {
toffset = sym.st_value;
} else if ((toffset = get_import_addr (eo, k)) == UT64_MAX) {
} else if ((toffset = Elf_(plt_get_import_addr) (eo, k)) == UT64_MAX) {
// toffset = 0;
}
tsize = 16;

View file

@ -263,6 +263,8 @@ ut8 *Elf_(grab_regstate)(struct Elf_(obj_t) *bin, int *len);
RList *Elf_(get_maps)(ELFOBJ *bin);
ut64 Elf_(ppc64_get_plt_stub_for_slot)(ELFOBJ *eo, ut64 slot_vaddr);
/* plt.c */
ut64 Elf_(plt_get_import_addr)(ELFOBJ *eo, int sym);
int Elf_(plt_ppc64_abi)(ELFOBJ *eo);
ut64 Elf_(plt_arm64_entry)(ELFOBJ *eo, ut64 plt_addr, ut64 pos);
ut64 Elf_(plt_ppc32_thunk)(ELFOBJ *eo, ut64 slot_vaddr);
/* elf.c helpers exported for plt.c */

View file

@ -228,3 +228,439 @@ ut64 Elf_(plt_ppc32_thunk)(ELFOBJ *eo, ut64 slot_vaddr) {
}
return eo->ppc32_thunks[idx];
}
#define MIPS_PLT_OFFSET 0x20
#define RISCV_PLT_OFFSET 0x20
#define LOONGARCH_PLT_OFFSET 0x20
#define RISCV_PLT_ENTRY_SIZE 0x10
#define LOONGARCH_PLT_ENTRY_SIZE 0x10
#define X86_PLT_ENTRY_SIZE 0x10
#define SPARC_OFFSET_PLT_ENTRY_FROM_GOT_ADDR -0x6
#define X86_OFFSET_PLT_ENTRY_FROM_GOT_ADDR -0x6
#define R_ELF_PART_RELRO 1
#define COMPUTE_PLTGOT_POSITION(rel, pltgot_addr, n_initial_unused_entries) \
((rel->rva - pltgot_addr - n_initial_unused_entries * R_BIN_ELF_WORDSIZE) / R_BIN_ELF_WORDSIZE)
static ut64 get_got_entry(ELFOBJ *eo, RBinElfReloc *rel) {
if (!rel || !rel->rva || rel->rva == UT64_MAX) {
return UT64_MAX;
}
ut64 p_sym_got_addr = Elf_(v2p) (eo, rel->rva);
ut64 addr;
#if R_BIN_ELF64
addr = r_buf_read_ble64_at (eo->b, p_sym_got_addr, eo->endian);
#else
addr = r_buf_read_ble32_at (eo->b, p_sym_got_addr, eo->endian);
#endif
return (!addr || addr == R_BIN_ELF_WORD_MAX) ? UT64_MAX : addr;
}
static ut64 get_import_addr_qdsp6(ELFOBJ *eo, RBinElfReloc *rel) {
ut64 got_addr = eo->dyn_info.dt_pltgot;
if (got_addr == R_BIN_ELF_ADDR_MAX) {
return UT64_MAX;
}
ut64 plt_addr = get_got_entry (eo, rel);
if (plt_addr == UT64_MAX) {
return UT64_MAX;
}
const ut64 pos = COMPUTE_PLTGOT_POSITION (rel, got_addr, 0x3);
switch (rel->type) {
case R_QDSP6_JUMP_SLOT:
return plt_addr + pos * 16 + 32;
}
return UT64_MAX;
}
static ut64 get_import_addr_arm(ELFOBJ *eo, RBinElfReloc *rel) {
ut64 got_addr = eo->dyn_info.dt_pltgot;
if (got_addr == R_BIN_ELF_ADDR_MAX) {
return UT64_MAX;
}
ut64 plt_addr = get_got_entry (eo, rel);
if (plt_addr == UT64_MAX) {
return UT64_MAX;
}
const ut64 pos = COMPUTE_PLTGOT_POSITION (rel, got_addr, 0x3);
switch (rel->type) {
case R_ARM_JUMP_SLOT:
plt_addr += pos * 12 + 20;
if (plt_addr & 1) {
plt_addr--;
}
return plt_addr;
case R_ARM_GLOB_DAT:
case R_ARM_ABS32:
return rel->rva;
default:
R_LOG_WARN ("Unsupported relocation type for imports %d", rel->type);
return UT64_MAX;
}
return UT64_MAX;
}
static ut64 get_import_addr_arm64(ELFOBJ *eo, RBinElfReloc *rel) {
ut64 got_addr = eo->dyn_info.dt_pltgot;
if (got_addr == R_BIN_ELF_ADDR_MAX) {
return UT64_MAX;
}
ut64 plt_addr = get_got_entry (eo, rel);
if (plt_addr == UT64_MAX) {
return UT64_MAX;
}
const ut64 pos = COMPUTE_PLTGOT_POSITION (rel, got_addr, 0x3);
switch (rel->type) {
case R_AARCH64_RELATIVE:
// Direct binding: adjust by program base for relative relocations.
return eo->baddr + rel->addend;
case R_AARCH64_IRELATIVE:
if (rel->addend > plt_addr) { // start
return Elf_(plt_arm64_entry) (eo, plt_addr, pos) + rel->addend;
}
// same as fallback to JUMP_SLOT
return Elf_(plt_arm64_entry) (eo, plt_addr, pos);
case R_AARCH64_JUMP_SLOT:
return Elf_(plt_arm64_entry) (eo, plt_addr, pos);
case R_AARCH64_GLOB_DAT:
return rel->rva;
default:
R_LOG_WARN ("Unsupported relocation type for imports %d", rel->type);
return UT64_MAX;
}
return UT64_MAX;
}
static ut64 get_import_addr_mips(ELFOBJ *bin, RBinElfReloc *rel) {
ut64 jmprel_addr = bin->dyn_info.dt_jmprel;
ut64 got_addr = bin->dyn_info.dt_mips_pltgot;
if (jmprel_addr != R_BIN_ELF_ADDR_MAX && got_addr != R_BIN_ELF_ADDR_MAX) {
ut64 pos = COMPUTE_PLTGOT_POSITION (rel, got_addr, 0x2);
ut8 buf[128]; /// XXX why arbitrary 128
ut64 plt_addr = jmprel_addr + bin->dyn_info.dt_pltrelsz;
ut64 p_plt_addr = Elf_(v2p) (bin, plt_addr);
int res = r_buf_read_at (bin->b, p_plt_addr, buf, sizeof (buf));
if (res == sizeof (buf)) {
const ut8 *base = r_mem_mem_aligned (buf, sizeof (buf), (const ut8 *)"\x3c\x0f\x00", 3, 4);
plt_addr += base? (int)(size_t) (base - buf): MIPS_PLT_OFFSET + 8; // HARDCODED HACK
plt_addr += pos * 16;
return plt_addr;
}
}
return UT64_MAX;
}
static ut64 get_import_addr_riscv(ELFOBJ *bin, RBinElfReloc *rel) {
ut64 got_addr = bin->dyn_info.dt_pltgot;
if (got_addr != R_BIN_ELF_ADDR_MAX) {
ut64 plt_addr = get_got_entry (bin, rel);
if (plt_addr != UT64_MAX) {
ut64 pos = COMPUTE_PLTGOT_POSITION (rel, got_addr, 2);
return plt_addr + RISCV_PLT_OFFSET + pos * RISCV_PLT_ENTRY_SIZE;
}
}
return UT64_MAX;
}
static ut64 get_import_addr_loongarch(ELFOBJ *bin, RBinElfReloc *rel) {
ut64 got_addr = bin->dyn_info.dt_pltgot;
if (got_addr != R_BIN_ELF_ADDR_MAX) {
ut64 plt_addr = get_got_entry (bin, rel);
if (plt_addr != UT64_MAX) {
ut64 pos = COMPUTE_PLTGOT_POSITION (rel, got_addr, 2);
return plt_addr + LOONGARCH_PLT_OFFSET + pos * LOONGARCH_PLT_ENTRY_SIZE;
}
}
return UT64_MAX;
}
static ut64 get_import_addr_sparc(ELFOBJ *eo, RBinElfReloc *rel) {
if (rel->type != R_SPARC_JMP_SLOT) {
R_LOG_DEBUG ("Unknown sparc reloc type %d", rel->type);
return UT64_MAX;
}
ut64 tmp = get_got_entry (eo, rel);
return (tmp == UT64_MAX) ? UT64_MAX : tmp + SPARC_OFFSET_PLT_ENTRY_FROM_GOT_ADDR;
}
static ut64 get_import_addr_s390x(ELFOBJ *eo, RBinElfReloc *rel) {
ut64 a = get_got_entry (eo, rel);
if (a == UT64_MAX) {
// GLOBALS, OBJECTS, NOTYPE, ..
return UT64_MAX;
}
return a - 14;
}
// EF_PPC64_ABI: 1 = ELFv1, 2 = ELFv2, 3 = undefined; unflagged objects predate the field, so guess from the endian
int Elf_(plt_ppc64_abi)(ELFOBJ *eo) {
if (eo->ehdr.e_machine != EM_PPC64) {
return 0;
}
switch (eo->ehdr.e_flags & EF_PPC64_ABI) {
case 1: return 1;
case 2: return 2;
case 3: return 0;
}
return eo->endian? 1: 2;
}
#if R_BIN_ELF64
// PLT slot vaddr -> lazy glink stub vaddr; the N-th DT_JMPREL entry owns the N-th
// stub after DT_PPC64_GLINK + 32 (binutils ppc64_elf_get_synthetic_symtab)
static HtUU *ppc64_build_glink_map(ELFOBJ *eo, int abi) {
const RBinElfDynamicInfo *di = &eo->dyn_info;
if (di->dt_ppc64_glink == R_BIN_ELF_ADDR_MAX) {
return NULL;
}
if (di->dt_jmprel == R_BIN_ELF_ADDR_MAX || !di->dt_pltrelsz) {
return NULL;
}
const ut64 num_plts = Elf_(plt_num_relocs) (eo);
const size_t relsize = num_plts? di->dt_pltrelsz / num_plts: 0;
if (!relsize) {
return NULL;
}
HtUU *map = ht_uu_new0 ();
if (!map) {
return NULL;
}
ut64 stub_vma = di->dt_ppc64_glink + 32;
// num_plts was validated with relsize above
ut64 n;
for (n = 0; n < num_plts; n++) {
ut64 rela_off = Elf_(v2p) (eo, di->dt_jmprel + n * relsize);
if (rela_off == UT64_MAX) {
break;
}
ut64 slot_vaddr = r_buf_read_ble64_at (eo->b, rela_off, eo->endian);
if (slot_vaddr == UT64_MAX) {
break;
}
ht_uu_insert (map, slot_vaddr, stub_vma);
// an ELFv2 stub is a single branch; ELFv1 stubs grow to 12 bytes past slot 0x8000
stub_vma += (abi == 2)? 4: (n >= 0x8000)? 12: 8;
}
return map;
}
#endif
// PLT stub vaddr for the given GOT slot in a ppc64 binary, building the stub cache on demand
ut64 Elf_(ppc64_get_plt_stub_for_slot)(ELFOBJ *eo, ut64 slot_vaddr) {
#if R_BIN_ELF64
const int abi = Elf_(plt_ppc64_abi) (eo);
if (!abi) {
return UT64_MAX;
}
if (!eo->ppc64_plt_stubs) {
eo->ppc64_plt_stubs = ppc64_build_glink_map (eo, abi);
}
if (eo->ppc64_plt_stubs) {
bool found = false;
ut64 stub = ht_uu_find (eo->ppc64_plt_stubs, slot_vaddr, &found);
if (found) {
return stub;
}
}
#endif
return UT64_MAX;
}
static ut64 get_import_addr_ppc(ELFOBJ *eo, RBinElfReloc *rel) {
#if R_BIN_ELF64
if (Elf_(plt_ppc64_abi) (eo)) {
ut64 stub = Elf_(ppc64_get_plt_stub_for_slot) (eo, rel->rva);
if (stub != UT64_MAX) {
return stub;
}
return rel->rva; // no DT_PPC64_GLINK or no map entry
}
#endif
ut64 plt_addr = eo->dyn_info.dt_pltgot;
if (plt_addr == R_BIN_ELF_ADDR_MAX) {
return UT64_MAX;
}
// -fPIC secure-plt objects reach the slot through r30-relative call
// thunks; when the whole map decodes it overrides the legacy math
const ut64 thunk = Elf_(plt_ppc32_thunk) (eo, rel->rva);
if (thunk != UT64_MAX) {
return thunk;
}
if (rel->rva < plt_addr) {
ut64 delta = plt_addr - rel->rva;
ut64 orva = rel->rva + (2 * delta);
R_LOG_DEBUG ("Massaged pointer below plt from 0x%"PFMT64x" to 0x%"PFMT64x, rel->rva, orva);
return orva;
}
ut64 p_plt_addr = Elf_(v2p) (eo, plt_addr);
if (p_plt_addr == UT64_MAX) {
return UT64_MAX;
}
ut64 base = r_buf_read_ble32_at (eo->b, p_plt_addr, eo->endian);
if (base == UT32_MAX) {
return UT64_MAX;
}
ut64 nrel = Elf_(plt_num_relocs) (eo);
ut64 pos = COMPUTE_PLTGOT_POSITION (rel, plt_addr, 0x0);
if (eo->endian) {
base -= nrel * 16;
base += pos * 16;
return base;
}
base -= (nrel * 12) + 20;
base += (pos * 8);
return base;
}
static ut64 get_import_addr_x86_manual(ELFOBJ *eo, RBinElfReloc *rel) {
ut64 got_addr = eo->dyn_info.dt_pltgot;
if (got_addr == R_BIN_ELF_ADDR_MAX) {
return UT64_MAX;
}
ut64 got_offset = Elf_(v2p) (eo, got_addr);
if (got_offset == UT64_MAX) {
return UT64_MAX;
}
//XXX HACK ALERT!!!! full relro?? try to fix it
//will there always be .plt.got, what would happen if is .got.plt?
RBinElfSection *s = Elf_(plt_section_by_name) (eo, ".plt.got");
if (Elf_(has_relro) (eo) < R_ELF_PART_RELRO || !s) {
return UT64_MAX;
}
ut8 buf[sizeof (Elf_(Addr))] = {0};
// Elf_(Addr) buf;
ut64 plt_addr = s->offset;
ut64 plt_sym_addr;
while (plt_addr + 2 + 4 < s->offset + s->size && plt_addr + 2 + 4 < eo->size) {
/*we try to locate the plt entry that correspond with the relocation
since got does not point back to .plt. In this case it has the following
form
ff253a152000 JMP QWORD [RIP + 0x20153A]
6690 NOP
----
ff25ec9f0408 JMP DWORD [reloc.puts_236]
plt_addr + 2 to remove jmp opcode and get the imm reading 4
and if RIP (plt_addr + 6) + imm == rel->offset
return plt_addr, that will be our sym addr
perhaps this hack doesn't work on 32 bits
*/
int res = r_buf_read_at (eo->b, plt_addr + 2, buf, sizeof (ut32));
if (res < 0) {
return UT64_MAX;
}
#if R_BIN_ELF64
plt_sym_addr = r_read_ble64 (buf, eo->endian);
#else
plt_sym_addr = r_read_ble32 (buf, eo->endian);
#endif
//relative address
if ((plt_addr + 6 + plt_sym_addr) == rel->rva) {
return plt_addr;
}
if (plt_sym_addr == rel->rva) {
return plt_addr;
}
plt_addr += 8;
}
return UT64_MAX;
}
static ut64 get_import_addr_x86(ELFOBJ *eo, RBinElfReloc *rel) {
ut64 tmp = get_got_entry (eo, rel);
if (tmp == UT64_MAX) {
return get_import_addr_x86_manual (eo, rel);
}
RBinElfSection *pltsec = Elf_(plt_section_by_name) (eo, ".plt.sec");
if (pltsec) {
ut64 got_addr = eo->dyn_info.dt_pltgot;
ut64 pos = COMPUTE_PLTGOT_POSITION (rel, got_addr, 3);
return pltsec->rva + pos * X86_PLT_ENTRY_SIZE;
}
return tmp + X86_OFFSET_PLT_ENTRY_FROM_GOT_ADDR;
}
ut64 Elf_(plt_get_import_addr)(ELFOBJ *eo, int sym) {
if ((!eo->shdr || !eo->strtab) && !eo->phdr) {
return UT64_MAX;
}
if (!eo->rel_cache) {
return UT64_MAX;
}
int index = ht_uu_find (eo->rel_cache, sym + 1, NULL);
if (index < 1) {
return UT64_MAX;
}
// lookup the right rel/rela entry
RBinElfReloc *rel = RVecRBinElfReloc_at (&eo->g_relocs, index - 1);
if (!rel) {
return UT64_MAX;
}
switch (eo->ehdr.e_machine) {
case EM_S390:
return get_import_addr_s390x (eo, rel);
case EM_ARM:
return get_import_addr_arm (eo, rel);
case EM_AARCH64:
return get_import_addr_arm64 (eo, rel);
case EM_MIPS: // MIPS32 BIG ENDIAN relocs
return get_import_addr_mips (eo, rel);
case EM_QDSP6: // also known as HEXAGON
return get_import_addr_qdsp6 (eo, rel);
case EM_VAX:
// as beautiful as riscv <3
return get_import_addr_riscv (eo, rel);
case EM_RISCV:
return get_import_addr_riscv (eo, rel);
case EM_SPARC:
case EM_SPARCV9:
case EM_SPARC32PLUS:
return get_import_addr_sparc (eo, rel);
case EM_PPC:
case EM_PPC64:
return get_import_addr_ppc (eo, rel);
case EM_386:
case EM_X86_64:
case EM_IAMCU:
return get_import_addr_x86 (eo, rel);
case EM_LOONGARCH:
return get_import_addr_loongarch (eo, rel);
case EM_SBPF:
// sBPF relocations are handled in patch_reloc, return the offset for imports
return rel->offset;
case EM_BPF:
return rel->offset;
case EM_V800:
case EM_V850:
return rel->offset;
case EM_NDS32:
return rel->offset;
default:
R_LOG_WARN ("Unsupported relocs type %" PFMT64u " for arch %d",
(ut64) rel->type, eo->ehdr.e_machine);
return UT64_MAX;
}
}