static int shdr_read(const struct buffer *in, struct parsed_elf *pelf, struct xdr *xdr, int bit64) { struct buffer b; Elf64_Shdr *shdr; Elf64_Ehdr *ehdr; int i; ehdr = &pelf->ehdr; /* cons up an input buffer for the section headers. * Note that the section headers can be anywhere, * per the ELF spec, You'd be surprised how many ELF * readers miss this little detail. */ buffer_splice(&b, in, ehdr->e_shoff, ehdr->e_shentsize * ehdr->e_shnum); if (check_size(in, ehdr->e_shoff, buffer_size(&b), "section headers")) return -1; /* gather up all the shdrs. */ shdr = calloc(ehdr->e_shnum, sizeof(*shdr)); for (i = 0; i < ehdr->e_shnum; i++) { DEBUG("Parsing section %d\n", i); elf_shdr(&b, &shdr[i], ehdr->e_shentsize, xdr, bit64); } pelf->shdr = shdr; return 0; }
void *elf_get_section_ptr(size_t shndx, Elf32_Word address, size_t size) { Elf32_Shdr *shdr = elf_shdr(shndx); if(shdr == nullptr) return nullptr; if(address + size > shdr->sh_size) return nullptr; if(shdr->sh_offset + shdr->sh_size > g_elf_size) return nullptr; return g_elf_buf + shdr->sh_offset + address; }
size_t elf_find_reloc_section(size_t shndx) { /* find the relocation section */ for(size_t i = 0; i < elf_ehdr()->e_shnum; i++) { Elf32_Shdr *shdr = elf_shdr(i); if(shdr->sh_type != SHT_REL && shdr->sh_type != SHT_RELA) continue; if(shdr->sh_info != shndx) continue; return i; } return 0; }
size_t elf_map_virt_addr(uint32_t address, Elf32_Word& out_off) { /* for relocatable file, this is trivial */ for(size_t i = 0; i < elf_ehdr()->e_shnum; i++) { Elf32_Shdr *shdr = elf_shdr(i); if(shdr->sh_offset <= address && address < shdr->sh_offset + shdr->sh_size) { out_off = address - shdr->sh_offset; if(g_verbose) { printf("[map %#x to section %zi (%s) at %#x]\n", address, i, elf_get_section_name(i), out_off); } return i; } } return 0; /* section 0 is always invalid */ }
/* make sure the string has a final zero in the section, optionally check characters * are printable */ const char *elf_get_string_ptr_safe(size_t shndx, Elf32_Word offset, bool want_print = true) { Elf32_Shdr *shdr = elf_shdr(shndx); if(shdr == nullptr) return nullptr; /* address must be in the section */ if(offset >= shdr->sh_size) return nullptr; /* determine maximum size */ size_t max_sz = shdr->sh_size - offset; const char *ptr = (const char *)(g_elf_buf + shdr->sh_offset + offset); for(size_t i = 0; i < max_sz; i++) { if(ptr[i] == 0) /* found final 0, everything is fine */ return ptr; if(want_print && !isprint(ptr[i])) return nullptr; } return nullptr; }
bool elf_init() { if(g_elf_size < sizeof(Elf32_Ehdr)) { printf("Invalid ELF file: too small\n"); return false; } Elf32_Ehdr *ehdr = elf_ehdr(); if(ehdr->e_ident[EI_MAG0] != ELFMAG0 || ehdr->e_ident[EI_MAG1] != ELFMAG1 || ehdr->e_ident[EI_MAG2] != ELFMAG2 || ehdr->e_ident[EI_MAG3] != ELFMAG3) { printf("Invalid ELF file: invalid ident\n"); return false; } /* we only support relocatable files */ if(ehdr->e_type != ET_REL) { printf("Unsupported ELF file: this is not a relocatable file\n"); return false; } if(ehdr->e_ident[EI_CLASS] != ELFCLASS32 || ehdr->e_machine != EM_ARM) { printf("Unsupported ELF file: this is not a 32-bit ARM ELF file\n"); return false; } /* go through sections */ if(ehdr->e_shoff == 0) { printf("Invalid ELF file: no sections\n"); return false; } if(ehdr->e_shentsize < sizeof(Elf32_Shdr)) { printf("Invalid ELF file: section entry size too small\n"); return false; } if(NTH_SHDR_OFF(ehdr->e_shnum) > g_elf_size) { printf("Invalid ELF file: sections header does not fit in the file\n"); return false; } for(size_t i = 0; i < ehdr->e_shnum; i++) { Elf32_Shdr *shdr = (Elf32_Shdr *)(g_elf_buf + NTH_SHDR_OFF(i)); if(shdr->sh_type == SHT_SYMTAB) g_elf_symtab = shdr; } /* handle symbol table */ if(g_elf_symtab) { if(g_elf_symtab->sh_offset + g_elf_symtab->sh_size > g_elf_size) { printf("Invalid ELF file: symtab does not file in the file\n"); return false; } g_elf_symtab_strtab = elf_shdr(g_elf_symtab->sh_link); if(g_elf_symtab_strtab == nullptr) { printf("Invalid ELF file: symtab's strtab is not valid\n"); } if(g_elf_symtab_strtab->sh_type != SHT_STRTAB) { printf("Invalid ELF file: symtab's strtab is not a string table\n"); return false; } } /* handle section string table */ if(ehdr->e_shstrndx != SHN_UNDEF) { g_elf_shstrtab = elf_shdr(ehdr->e_shstrndx); if(g_elf_shstrtab == nullptr) { printf("Invalid ELF file: section string table is invalid\n"); return false; } } return true; }
/* take the position of a 32-bit address in the section and apply relocation if * any */ void *elf_reloc_addr32(size_t shndx, Elf32_Word offset) { /* read value */ uint32_t *val = (uint32_t *)elf_get_section_ptr(shndx, offset, 4); if(val == nullptr) return 0; /* invalid */ /* find reloc section if any */ size_t relshndx = elf_find_reloc_section(shndx); if(relshndx == 0) return g_elf_buf + *val; /* no relocation applies */ Elf32_Shdr *shdr = elf_shdr(relshndx); /* find relocation that applies */ if(shdr->sh_type == SHT_RELA) { printf("Warning: unsupported RELA relocation type\n"); return 0; } Elf32_Rel *rel = (Elf32_Rel *)elf_get_section_ptr(relshndx, 0, shdr->sh_size); if(rel == nullptr) { printf("Warning: invalid relocation section\n"); return 0; } size_t sym_count = shdr->sh_size / sizeof(Elf32_Rel); for(size_t i = 0; i < sym_count; i++) { /* for relocatable files, r_offset is the offset in the section */ if(rel[i].r_offset != offset) continue; /* find symbol, ignore shdr->sh_link and assume it is g_elf_symtab * since the file should have only one symbol table anyway */ Elf32_Sym *sym = elf_get_symbol_by_index(ELF32_R_SYM(rel[i].r_info)); /* found it! */ if(g_verbose) { printf("[section %zu (%s) offset %#x reloc val %#x type %d sym %d (%s)]\n", shndx, elf_get_section_name(shndx), offset, *val, ELF32_R_TYPE(rel[i].r_info), ELF32_R_SYM(rel[i].r_info), sym ? elf_get_symbol_name(sym) : "<undef>"); } /* apply reloc */ if(ELF32_R_TYPE(rel[i].r_info) == R_ARM_ABS32) { if(sym == nullptr) { printf("Warning: R_ARM_ABS32 reloc with invalid symbol reference\n"); return 0; } return *val + (uint8_t *)elf_get_symbol_ptr(sym, 0); } else { printf("Warning: unsupported relocation type %d\n", ELF32_R_TYPE(rel[i].r_info)); return 0; } } /* no reloc applies */ if(g_verbose) { printf("[section %zu (%s) offset %#x no reloc found]\n", shndx, elf_get_section_name(shndx), offset); } return g_elf_buf + *val; /* no relocation applies */ }
const char *elf_get_section_name(size_t index) { Elf32_Shdr *shdr = elf_shdr(index); return shdr ? elf_get_str(g_elf_shstrtab, shdr->sh_name) : nullptr; }