elf: Use PHDRs instead of sections for resolving relocations
Co-authored-by: Connor Horman <chorman64@gmail.com>
diff --git a/common/lib/elf.c b/common/lib/elf.c
index d8601ade..4eb6e2f0 100644
--- a/common/lib/elf.c
+++ b/common/lib/elf.c
@@ -9,9 +9,16 @@
#include <fs/file.h>
#define PT_LOAD 0x00000001
+#define PT_DYNAMIC 0x00000002
#define PT_INTERP 0x00000003
#define PT_PHDR 0x00000006
+#define DT_NULL 0x00000000
+#define DT_NEEDED 0x00000001
+#define DT_RELA 0x00000007
+#define DT_RELASZ 0x00000008
+#define DT_RELAENT 0x00000009
+
#define ABI_SYSV 0x00
#define ARCH_X86_64 0x3e
#define ARCH_X86_32 0x03
@@ -26,7 +33,6 @@
#define EI_VERSION 6
#define EI_OSABI 7
-
struct elf32_hdr {
uint8_t ident[16];
uint16_t type;
@@ -86,6 +92,11 @@ struct elf64_rela {
uint64_t r_addend;
};
+struct elf64_dyn {
+ uint64_t d_tag;
+ uint64_t d_un;
+};
+
int elf_bits(uint8_t *elf) {
struct elf64_hdr hdr;
memcpy(&hdr, elf + (0), 20);
@@ -106,46 +117,75 @@ int elf_bits(uint8_t *elf) {
}
static bool elf64_is_relocatable(uint8_t *elf, struct elf64_hdr *hdr) {
- // Find RELA sections
- for (uint16_t i = 0; i < hdr->sh_num; i++) {
- struct elf64_shdr section;
- memcpy(§ion, elf + (hdr->shoff + i * sizeof(struct elf64_shdr)),
- sizeof(struct elf64_shdr));
-
- if (section.sh_type != SHT_RELA)
- continue;
-
- if (section.sh_entsize != sizeof(struct elf64_rela)) {
- print("elf: Unknown sh_entsize for RELA section!\n");
- continue;
- }
-
- return true;
+ // Find DYN segment
+ for (uint16_t i = 0; i < hdr->ph_num; i++) {
+ struct elf64_phdr phdr;
+ memcpy(&phdr, elf + (hdr->phoff + i * sizeof(struct elf64_phdr)),
+ sizeof(struct elf64_phdr));
+ if (phdr.p_type == PT_DYNAMIC)
+ return true;
}
return false;
}
static int elf64_apply_relocations(uint8_t *elf, struct elf64_hdr *hdr, void *buffer, uint64_t vaddr, size_t size, uint64_t slide) {
- // Find RELA sections
- for (uint16_t i = 0; i < hdr->sh_num; i++) {
- struct elf64_shdr section;
- memcpy(§ion, elf + (hdr->shoff + i * sizeof(struct elf64_shdr)),
- sizeof(struct elf64_shdr));
+ // Find DYN segment
+ for (uint16_t i = 0; i < hdr->ph_num; i++) {
+ struct elf64_phdr phdr;
+ memcpy(&phdr, elf + (hdr->phoff + i * sizeof(struct elf64_phdr)),
+ sizeof(struct elf64_phdr));
- if (section.sh_type != SHT_RELA)
+ if (phdr.p_type != PT_DYNAMIC)
continue;
- if (section.sh_entsize != sizeof(struct elf64_rela)) {
+ uint64_t rela_offset = 0;
+ uint64_t rela_size = 0;
+ uint64_t rela_ent = 0;
+ for (uint16_t j = 0; j < phdr.p_filesz / sizeof(struct elf64_dyn); j++) {
+ struct elf64_dyn dyn;
+ memcpy(&dyn, elf + (phdr.p_offset + j * sizeof(struct elf64_dyn)),
+ sizeof(struct elf64_dyn));
+
+ switch (dyn.d_tag) {
+ case DT_RELA:
+ rela_offset = dyn.d_un;
+ break;
+ case DT_RELAENT:
+ rela_ent = dyn.d_un;
+ break;
+ case DT_RELASZ:
+ rela_size = dyn.d_un;
+ break;
+ }
+ }
+
+ if (rela_offset == 0) {
+ break;
+ }
+
+ if (rela_ent != sizeof(struct elf64_rela)) {
print("elf: Unknown sh_entsize for RELA section!\n");
return 1;
}
+ for (uint16_t j = 0; j < hdr->ph_num; j++) {
+ struct elf64_phdr _phdr;
+ memcpy(&_phdr, elf + (hdr->phoff + j * sizeof(struct elf64_phdr)),
+ sizeof(struct elf64_phdr));
+
+ if (_phdr.p_vaddr <= rela_offset && _phdr.p_vaddr + _phdr.p_filesz > rela_offset) {
+ rela_offset -= _phdr.p_vaddr;
+ rela_offset += _phdr.p_offset;
+ break;
+ }
+ }
+
// This is a RELA header, get and apply all relocations
- for (uint64_t offset = 0; offset < section.sh_size; offset += section.sh_entsize) {
+ for (uint64_t offset = 0; offset < rela_size; offset += rela_ent) {
struct elf64_rela relocation;
- memcpy(&relocation, elf + (section.sh_offset + offset), sizeof(relocation));
+ memcpy(&relocation, elf + (rela_offset + offset), sizeof(struct elf64_rela));
switch (relocation.r_info) {
case R_X86_64_RELATIVE: {
@@ -169,6 +209,8 @@ static int elf64_apply_relocations(uint8_t *elf, struct elf64_hdr *hdr, void *bu
return 1;
}
}
+
+ break;
}
return 0;
