rizin/libr/bin/format/elf/elf.c
Álvaro Felipe Melchor 9dd078dfa5 Fix #2883
2015-07-05 20:44:03 +02:00

1715 lines
53 KiB
C

/* radare - LGPL - Copyright 2008-2015 - nibble, pancake */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <r_types.h>
#include <r_util.h>
#include "elf.h"
static inline int __strnlen(const char *str, int len) {
int l = 0;
while (IS_PRINTABLE(*str) && --len) {
if (((ut8)*str)==0xff)
break;
str++;
l++;
}
return l+1;
}
static int Elf_(r_bin_elf_init_ehdr)(struct Elf_(r_bin_elf_obj_t) *bin) {
ut8 e_ident[EI_NIDENT];
int len;
if (r_buf_read_at (bin->b, 0, e_ident, EI_NIDENT) == -1) {
eprintf ("Warning: read (magic)\n");
return R_FALSE;
}
sdb_set (bin->kv, "elf_type.cparse", "enum elf_type { ET_NONE=0, ET_REL=1,"
" ET_EXEC=2, ET_DYN=3, ET_CORE=4, ET_LOOS=0xfe00, ET_HIOS=0xfeff,"
" ET_LOPROC=0xff00, ET_HIPROC=0xffff };", 0);
sdb_set (bin->kv, "elf_machine.cparse", "enum elf_machine{EM_NONE=0, EM_M32=1,"
" EM_SPARC=2, EM_386=3, EM_68K=4, EM_88K=5, EM_486=6, "
" EM_860=7, EM_MIPS=8, EM_S370=9, EM_MIPS_RS3_LE=10, EM_RS6000=11,"
" EM_UNKNOWN12=12, EM_UNKNOWN13=13, EM_UNKNOWN14=14, "
" EM_PA_RISC=15, EM_PARISC=EM_PA_RISC, EM_nCUBE=16, EM_VPP500=17,"
" EM_SPARC32PLUS=18, EM_960=19, EM_PPC=20, EM_PPC64=21, "
" EM_S390=22, EM_UNKNOWN22=EM_S390, EM_UNKNOWN23=23, EM_UNKNOWN24=24,"
" EM_UNKNOWN25=25, EM_UNKNOWN26=26, EM_UNKNOWN27=27, EM_UNKNOWN28=28,"
" EM_UNKNOWN29=29, EM_UNKNOWN30=30, EM_UNKNOWN31=31, EM_UNKNOWN32=32,"
" EM_UNKNOWN33=33, EM_UNKNOWN34=34, EM_UNKNOWN35=35, EM_V800=36,"
" EM_FR20=37, EM_RH32=38, EM_RCE=39, EM_ARM=40, EM_ALPHA=41, EM_SH=42,"
" EM_SPARCV9=43, EM_TRICORE=44, EM_ARC=45, EM_H8_300=46, EM_H8_300H=47,"
" EM_H8S=48, EM_H8_500=49, EM_IA_64=50, EM_MIPS_X=51, EM_COLDFIRE=52,"
" EM_68HC12=53, EM_MMA=54, EM_PCP=55, EM_NCPU=56, EM_NDR1=57,"
" EM_STARCORE=58, EM_ME16=59, EM_ST100=60, EM_TINYJ=61, EM_AMD64=62,"
" EM_X86_64=EM_AMD64, EM_PDSP=63, EM_UNKNOWN64=64, EM_UNKNOWN65=65,"
" EM_FX66=66, EM_ST9PLUS=67, EM_ST7=68, EM_68HC16=69, EM_68HC11=70,"
" EM_68HC08=71, EM_68HC05=72, EM_SVX=73, EM_ST19=74, EM_VAX=75, "
" EM_CRIS=76, EM_JAVELIN=77, EM_FIREPATH=78, EM_ZSP=79, EM_MMIX=80,"
" EM_HUANY=81, EM_PRISM=82, EM_AVR=83, EM_FR30=84, EM_D10V=85, EM_D30V=86,"
" EM_V850=87, EM_M32R=88, EM_MN10300=89, EM_MN10200=90, EM_PJ=91,"
" EM_OPENRISC=92, EM_ARC_A5=93, EM_XTENSA=94, EM_NUM=95};", 0);
sdb_num_set (bin->kv, "elf_header.offset", 0, 0);
#if R_BIN_ELF64
sdb_set (bin->kv, "elf_header.format", "[16]z[2]E[2]Exqqqxwwwwww"
" ident (elf_type)type (elf_machine)machine version entry phoff shoff flags ehsize"
" phentsize phnum shentsize shnum shstrndx", 0);
#else
sdb_set (bin->kv, "elf_header.format", "[16]z[2]E[2]Exxxxxwwwwww"
" ident (elf_type)type (elf_machine)machine version entry phoff shoff flags ehsize"
" phentsize phnum shentsize shnum shstrndx", 0);
#endif
bin->endian = (e_ident[EI_DATA] == ELFDATA2MSB)?
LIL_ENDIAN: !LIL_ENDIAN;
memset (&bin->ehdr, 0, sizeof (Elf_(Ehdr)));
len = r_buf_fread_at (bin->b, 0, (ut8*)&bin->ehdr,
#if R_BIN_ELF64
bin->endian?"16c2SI3LI6S":"16c2si3li6s",
#else
bin->endian?"16c2S5I6S":"16c2s5i6s",
#endif
1);
if (len == -1) {
eprintf ("Warning: read (ehdr)\n");
return R_FALSE;
}
if (strncmp ((char *)bin->ehdr.e_ident, ELFMAG, SELFMAG))
return R_FALSE;
return R_TRUE;
}
static int Elf_(r_bin_elf_init_phdr)(struct Elf_(r_bin_elf_obj_t) *bin) {
ut32 phdr_size;
int len;
if (bin->ehdr.e_phnum == 0)
return R_FALSE;
if (bin->phdr) return R_TRUE;
if (!UT32_MUL (&phdr_size, bin->ehdr.e_phnum, sizeof (Elf_(Phdr))))
return R_FALSE;
if (!phdr_size)
return R_FALSE;
if (phdr_size > bin->size)
return R_FALSE;
if (bin->ehdr.e_phoff > bin->size)
return R_FALSE;
if (bin->ehdr.e_phoff + phdr_size > bin->size)
return R_FALSE;
if ((bin->phdr = calloc (phdr_size, 1)) == NULL) {
perror ("malloc (phdr)");
return R_FALSE;
}
len = r_buf_fread_at (bin->b, bin->ehdr.e_phoff, (ut8*)bin->phdr,
#if R_BIN_ELF64
bin->endian? "2I6L": "2i6l",
#else
bin->endian? "8I": "8i",
#endif
bin->ehdr.e_phnum);
if (len == -1) {
eprintf ("Warning: read (phdr)\n");
R_FREE (bin->phdr);
return R_FALSE;
}
sdb_bool_set (bin->kv, "elf.relro", Elf_(r_bin_elf_has_relro)(bin), 0);
sdb_num_set (bin->kv, "elf_header_size.offset", sizeof (Elf_(Ehdr)), 0);
sdb_num_set (bin->kv, "elf_phdr_size.offset", sizeof (Elf_(Phdr)), 0);
sdb_num_set (bin->kv, "elf_shdr_size.offset", sizeof (Elf_(Shdr)), 0);
#if R_BIN_ELF64
sdb_num_set (bin->kv, "elf_phdr.offset", bin->ehdr.e_phoff, 0);
sdb_set (bin->kv, "elf_phdr.format", "xxqqqqqq type flags offset vaddr paddr filesz memsz align", 0);
sdb_num_set (bin->kv, "elf_shdr.offset", bin->ehdr.e_shoff, 0);
sdb_set (bin->kv, "elf_shdr.format", "xxqqqqxxqq name type flags addr offset size link info addralign entsize", 0);
#else
sdb_num_set (bin->kv, "elf_phdr.offset", bin->ehdr.e_phoff, 0);
sdb_set (bin->kv, "elf_phdr.format", "xxxxxxxx type offset vaddr paddr filesz memsz flags align", 0);
sdb_num_set (bin->kv, "elf_shdr.offset", bin->ehdr.e_shoff, 0);
sdb_set (bin->kv, "elf_shdr.format", "xxxxxxxxxx name type flags addr offset size link info addralign entsize", 0);
#endif
// Usage example:
// > pf `k bin/cur/info/elf.phdr.format` @ `k bin/cur/info/elf.phdr.offset`
return R_TRUE;
}
static int Elf_(r_bin_elf_init_shdr)(struct Elf_(r_bin_elf_obj_t) *bin) {
ut32 shdr_size;
int len;
if (!bin || bin->shdr) return R_TRUE;
if (!UT32_MUL(&shdr_size, bin->ehdr.e_shnum, sizeof (Elf_(Shdr))))
return R_FALSE;
if (shdr_size < 1)
return R_FALSE;
if (shdr_size > bin->size)
return R_FALSE;
if (bin->ehdr.e_shoff > bin->size)
return R_FALSE;
if (bin->ehdr.e_shoff + shdr_size > bin->size)
return R_FALSE;
if ((bin->shdr = calloc (1, shdr_size+1)) == NULL) {
perror ("malloc (shdr)");
return R_FALSE;
}
len = r_buf_fread_at (bin->b, bin->ehdr.e_shoff, (ut8*)bin->shdr,
#if R_BIN_ELF64
bin->endian?"2I4L2I2L":"2i4l2i2l",
#else
bin->endian?"10I":"10i",
#endif
bin->ehdr.e_shnum);
if (len == -1) {
eprintf ("Warning: read (shdr) at 0x%"PFMT64x"\n", (ut64) bin->ehdr.e_shoff);
R_FREE (bin->shdr);
return R_FALSE;
}
return R_TRUE;
}
static int Elf_(r_bin_elf_init_strtab)(struct Elf_(r_bin_elf_obj_t) *bin) {
if (bin->strtab || !bin->shdr) return R_FALSE;
if (bin->ehdr.e_shstrndx != SHN_UNDEF &&
(bin->ehdr.e_shstrndx >= bin->ehdr.e_shnum ||
(bin->ehdr.e_shstrndx >= SHN_LORESERVE && bin->ehdr.e_shstrndx <= SHN_HIRESERVE)))
return R_FALSE;
/* sh_size must be lower than UT32_MAX and not equal to zero, to avoid bugs
on malloc() */
if (bin->shdr[bin->ehdr.e_shstrndx].sh_size > UT32_MAX)
return R_FALSE;
if (!bin->shdr[bin->ehdr.e_shstrndx].sh_size)
return R_FALSE;
//TODO ehdr.e_shstrndx check
bin->shstrtab_section =
bin->strtab_section = &bin->shdr[bin->ehdr.e_shstrndx];
bin->shstrtab_size = bin->strtab_section->sh_size;
if (bin->shstrtab_size > bin->size) return R_FALSE;
if ((bin->shstrtab = calloc (1, bin->shstrtab_size+1)) == NULL) {
perror ("malloc");
bin->shstrtab = NULL;
return R_FALSE;
}
if (bin->shstrtab_section->sh_offset > bin->size){
R_FREE (bin->shstrtab);
return R_FALSE;
}
if (bin->shstrtab_section->sh_offset +
bin->shstrtab_section->sh_size > bin->size){
R_FREE (bin->shstrtab);
return R_FALSE;
}
if (r_buf_read_at (bin->b, bin->shstrtab_section->sh_offset, (ut8*)bin->shstrtab,
bin->shstrtab_section->sh_size) == -1) {
eprintf ("Warning: read (shstrtab) at 0x%"PFMT64x"\n",
(ut64) bin->shstrtab_section->sh_offset);
R_FREE (bin->shstrtab);
return R_FALSE;
}
sdb_num_set (bin->kv, "elf_shstrtab.offset", bin->shstrtab_section->sh_offset, 0);
sdb_num_set (bin->kv, "elf_shstrtab.size", bin->shstrtab_section->sh_size, 0);
return R_TRUE;
}
static int Elf_(r_bin_elf_init_dynamic_section) (struct Elf_(r_bin_elf_obj_t) *bin){
Elf_(Dyn) *dyn = NULL;
Elf_(Addr) strtabaddr = 0;
char *strtab = NULL;
size_t strsize = 0;
int entries;
int i, r;
ut32 dyn_size;
if (!bin || !bin->phdr || bin->ehdr.e_phnum == 0)
return R_FALSE;
for (i = 0; i < bin->ehdr.e_phnum ; i++){
if (bin->phdr[i].p_type == PT_DYNAMIC) break;
}
if (i == bin->ehdr.e_phnum){
// we didn't find the PT_DYNAMIC section
return R_FALSE;
}
if (bin->phdr[i].p_filesz > bin->size){
return R_FALSE;
}
if (bin->phdr[i].p_offset > bin->size)
return R_FALSE;
entries = (int)(bin->phdr[i].p_filesz / sizeof (Elf_(Dyn)));
if (entries < 1)
return R_FALSE;
dyn = (Elf_(Dyn)*)calloc (entries, sizeof (Elf_(Dyn)));
if (!dyn) return R_FALSE;
if (!UT32_MUL (&dyn_size, entries, sizeof (Elf_(Dyn)))) {
free (dyn);
return R_FALSE;
}
if (!dyn_size) {
free (dyn);
return R_FALSE;
}
if (bin->phdr[i].p_offset + dyn_size > bin->size){
free (dyn);
return R_FALSE;
}
r = r_buf_fread_at (bin->b, bin->phdr[i].p_offset, (ut8 *)dyn,
#if R_BIN_ELF64
bin->endian ? "2L":"2l",
#else
bin->endian ? "2I":"2i",
#endif
entries);
if (r == -1 || r == 0){
free (dyn);
return R_FALSE;
}
for (i = 0; i < entries; i++) {
switch (dyn[i].d_tag){
case DT_STRTAB: strtabaddr = dyn[i].d_un.d_ptr - bin->baddr; break;
case DT_STRSZ: strsize = dyn[i].d_un.d_val; break;
default: break;
}
}
if (!strtabaddr || strtabaddr > bin->size ||
strsize > ST32_MAX || strsize == 0 || strsize > bin->size){
free (dyn);
return R_FALSE;
}
strtab = (char *)calloc (1, strsize+1);
if (!strtab){
free (dyn);
return R_FALSE;
}
if (strtabaddr + strsize > bin->size){
free (dyn);
free (strtab);
return R_FALSE;
}
r = r_buf_read_at (bin->b, strtabaddr, (ut8 *)strtab, strsize);
if (r == 0 || r == -1){
free (dyn);
free (strtab);
return R_FALSE;
}
bin->dyn_buf = dyn;
bin->dyn_entries = entries;
bin->strtab = strtab;
bin->strtab_size = strsize;
sdb_num_set (bin->kv, "elf_strtab.offset", strtabaddr, 0);
sdb_num_set (bin->kv, "elf_strtab.size", strsize, 0);
return R_TRUE;
}
static int Elf_(r_bin_elf_init)(struct Elf_(r_bin_elf_obj_t) *bin) {
bin->phdr = NULL;
bin->shdr = NULL;
bin->strtab = NULL;
bin->shstrtab = NULL;
bin->strtab_size = 0;
bin->strtab_section = NULL;
bin->dyn_buf = NULL;
/* bin is not an ELF */
if (!Elf_(r_bin_elf_init_ehdr) (bin))
return R_FALSE;
if (!Elf_(r_bin_elf_init_phdr) (bin))
eprintf ("Warning: Cannot initialize program headers\n");
if (!Elf_(r_bin_elf_init_shdr) (bin))
eprintf ("Warning: Cannot initialize section headers\n");
if (!Elf_(r_bin_elf_init_strtab) (bin))
eprintf ("Warning: Cannot initialize strings table\n");
bin->baddr = Elf_(r_bin_elf_get_baddr) (bin);
if (!Elf_(r_bin_elf_init_dynamic_section) (bin))
eprintf ("Warning: Cannot initialize dynamic section\n");
bin->imports_by_ord_size = 0;
bin->imports_by_ord = NULL;
bin->symbols_by_ord_size = 0;
bin->symbols_by_ord = NULL;
bin->boffset = Elf_(r_bin_elf_get_boffset) (bin);
return R_TRUE;
}
static Elf_(Shdr)* Elf_(r_bin_elf_get_section_by_name)(struct Elf_(r_bin_elf_obj_t) *bin, const char *section_name) {
int i;
ut32 cur_strtab_len;
if (!bin || !bin->shdr || !bin->shstrtab)
return NULL;
for (i = 0; i < bin->ehdr.e_shnum; i++) {
if(!UT32_SUB(&cur_strtab_len, bin->shstrtab_size, bin->shdr[i].sh_name))
continue;
if (bin->shdr[i].sh_name > bin->shstrtab_size)
continue;
if (!strncmp (&bin->shstrtab[bin->shdr[i].sh_name], section_name, cur_strtab_len))
return &bin->shdr[i];
}
return NULL;
}
ut64 Elf_(r_bin_elf_get_section_offset)(struct Elf_(r_bin_elf_obj_t) *bin, const char *section_name) {
Elf_(Shdr)* shdr = Elf_(r_bin_elf_get_section_by_name) (bin, section_name);
if (!shdr) return UT64_MAX;
return (ut64)shdr->sh_offset;
}
ut64 Elf_(r_bin_elf_get_section_addr)(struct Elf_(r_bin_elf_obj_t) *bin, const char *section_name) {
Elf_(Shdr)* shdr = Elf_(r_bin_elf_get_section_by_name) (bin, section_name);
if (!shdr) return UT64_MAX;
return (ut64)shdr->sh_addr;
}
static ut64 Elf_(get_import_addr)(struct Elf_(r_bin_elf_obj_t) *bin, int sym) {
Elf_(Rel) *rel = NULL;
Elf_(Shdr) *rel_shdr;
Elf_(Addr) plt_sym_addr;
ut64 got_addr, got_offset;
ut64 plt_addr, plt_offset;
int j, k, tsize, len, nrel;
if (!bin->shdr || !bin->strtab)
return -1;
if ((plt_offset = Elf_(r_bin_elf_get_section_offset) (bin, ".plt")) == -1)
return -1;
if ((plt_addr = Elf_(r_bin_elf_get_section_addr) (bin, ".plt")) == -1)
return -1;
if ((got_offset = Elf_(r_bin_elf_get_section_offset) (bin, ".got")) == -1 &&
(got_offset = Elf_(r_bin_elf_get_section_offset) (bin, ".got.plt")) == -1)
return -1;
if ((got_addr = Elf_(r_bin_elf_get_section_addr) (bin, ".got")) == -1 &&
(got_addr = Elf_(r_bin_elf_get_section_addr) (bin, ".got.plt")) == -1)
return -1;
if((rel_shdr = Elf_(r_bin_elf_get_section_by_name)(bin, ".rel.plt")) != NULL) {
tsize = sizeof (Elf_(Rel));
} else if((rel_shdr = Elf_(r_bin_elf_get_section_by_name)(bin, ".rela.plt")) != NULL) {
tsize = sizeof (Elf_(Rela));
} else {
return -1;
}
nrel = (ut32)((int)rel_shdr->sh_size / (int)tsize);
if (nrel < 1)
return -1;
int relsz = (int)nrel * sizeof (Elf_(Rel));
if (relsz<1 || (rel = calloc (1, relsz)) == NULL) {
perror ("malloc (rel)");
return -1;
}
plt_sym_addr = -1;
for (j = k = 0; j < rel_shdr->sh_size && k <nrel; j += tsize, k++) {
if (rel_shdr->sh_offset+j > bin->size || rel_shdr->sh_offset+j+sizeof (Elf_(Rel)) > bin->size){
free (rel);
return -1;
}
len = r_buf_fread_at (bin->b, rel_shdr->sh_offset + j,
(ut8*)(&rel[k]),
#if R_BIN_ELF64
bin->endian?"2L":"2l",
#else
bin->endian?"2I":"2i",
#endif
1);
if (len == -1) {
eprintf ("Warning: read (rel)\n");
break;
}
int reloc_type = ELF_R_TYPE (rel[k].r_info);
int reloc_sym = ELF_R_SYM(rel[k].r_info);
if (reloc_sym == sym) {
int of = rel[k].r_offset;
of = of - got_addr + got_offset;
switch (bin->ehdr.e_machine) {
case EM_ARM:
switch (reloc_type) {
case 22:
{
plt_addr += (k*12) + 20;
if (plt_addr&1) {
// thumb symbol
plt_addr--;
}
free (rel);
return plt_addr;
}
break;
default:
eprintf ("Unsupported relocation type for imports %d\n", reloc_type);
break;
}
break;
case EM_386:
case EM_X86_64:
switch (reloc_type) {
case 7:
if (of+sizeof(Elf_(Addr)) >= bin->b->length) {
// do nothing
} else {
// ONLY FOR X86
if (of > bin->size || of + sizeof (Elf_(Addr)) > bin->size){
free (rel);
return -1;
}
if (r_buf_read_at (bin->b, of,
(ut8*)&plt_sym_addr, sizeof (Elf_(Addr))) == -1) {
eprintf ("Warning: read (got)\n");
break;
}
}
plt_sym_addr -= 6;
goto done;
break;
default:
eprintf ("Unsupported relocation type for imports %d\n", reloc_type);
eprintf ("0x%"PFMT64x" - 0x%"PFMT64x" i \n", (ut64)rel[k].r_offset, (ut64)rel[k].r_info);
free (rel);
return of;
break;
}
break;
default:
eprintf ("Unsupported relocs for this arch\n");
break;
}
}
}
done:
free (rel);
return plt_sym_addr;
}
int Elf_(r_bin_elf_has_nx)(struct Elf_(r_bin_elf_obj_t) *bin) {
int i;
if (bin && bin->phdr)
for (i = 0; i < bin->ehdr.e_phnum; i++)
if (bin->phdr[i].p_type == PT_GNU_STACK)
return (!(bin->phdr[i].p_flags & 1))? 1: 0;
return 0;
}
int Elf_(r_bin_elf_has_relro)(struct Elf_(r_bin_elf_obj_t) *bin) {
int i;
if (bin && bin->phdr)
for (i = 0; i < bin->ehdr.e_phnum; i++)
if (bin->phdr[i].p_type == PT_GNU_RELRO)
return 1;
return 0;
}
ut64 Elf_(r_bin_elf_get_baddr)(struct Elf_(r_bin_elf_obj_t) *bin) {
int i;
/* hopefully.. the first PT_LOAD is base */
if (bin && bin->phdr) {
for (i = 0; i < bin->ehdr.e_phnum; i++) {
if (bin->phdr[i].p_type == PT_LOAD) {
return (ut64)bin->phdr[i].p_vaddr;
}
}
}
return 0;
}
ut64 Elf_(r_bin_elf_get_boffset)(struct Elf_(r_bin_elf_obj_t) *bin) {
int i;
/* hopefully.. the first PT_LOAD is base */
if (bin && bin->phdr)
for (i = 0; i < bin->ehdr.e_phnum; i++)
if (bin->phdr[i].p_type == PT_LOAD)
return (ut64) bin->phdr[i].p_offset;
return 0;
}
ut64 Elf_(r_bin_elf_get_init_offset)(struct Elf_(r_bin_elf_obj_t) *bin) {
ut64 entry = Elf_(r_bin_elf_get_entry_offset) (bin);
ut8 buf[512];
if (!bin)
return 0LL;
if (r_buf_read_at (bin->b, entry+16, buf, sizeof (buf)) == -1) {
eprintf ("Warning: read (init_offset)\n");
return 0;
}
if (buf[0] == 0x68) { // push // x86 only
memmove (buf, buf+1, 4);
return (ut64)((int)(buf[0]+(buf[1]<<8)+(buf[2]<<16)+(buf[3]<<24)))-bin->baddr;
}
return 0;
}
ut64 Elf_(r_bin_elf_get_fini_offset)(struct Elf_(r_bin_elf_obj_t) *bin) {
ut64 entry = Elf_(r_bin_elf_get_entry_offset) (bin);
ut8 buf[512];
if (!bin) return 0LL;
if (r_buf_read_at (bin->b, entry+11, buf, sizeof (buf)) == -1) {
eprintf ("Warning: read (get_fini)\n");
return 0;
}
if (*buf == 0x68) { // push // x86/32 only
memmove (buf, buf+1, 4);
return (ut64)((int)(buf[0]+(buf[1]<<8)+
(buf[2]<<16)+(buf[3]<<24)))-bin->baddr;
}
return 0;
}
ut64 Elf_(r_bin_elf_get_entry_offset)(struct Elf_(r_bin_elf_obj_t) *bin) {
ut64 entry;
if (!bin)
return 0LL;
entry = (ut64) bin->ehdr.e_entry;
if (entry == 0LL) {
entry = Elf_(r_bin_elf_get_section_offset)(bin, ".init.text");
if (entry != UT64_MAX) return entry;
entry = Elf_(r_bin_elf_get_section_offset)(bin, ".text");
if (entry != UT64_MAX) return entry;
entry = Elf_(r_bin_elf_get_section_offset)(bin, ".init");
if (entry != UT64_MAX) return entry;
}
if (bin->ehdr.e_entry < bin->baddr)
return bin->ehdr.e_entry;
return bin->ehdr.e_entry - bin->baddr;
}
ut64 Elf_(r_bin_elf_get_main_offset)(struct Elf_(r_bin_elf_obj_t) *bin) {
ut64 entry = Elf_(r_bin_elf_get_entry_offset) (bin);
ut8 buf[512];
if (!bin)
return 0LL;
if (entry > bin->size || (entry + sizeof (buf)) > bin->size)
return 0;
if (r_buf_read_at (bin->b, entry, buf, sizeof (buf)) == -1) {
eprintf ("Warning: read (main)\n");
return 0;
}
// TODO: Use arch to identify arch before memcmp's
// ARM
ut64 text = Elf_(r_bin_elf_get_section_offset)(bin, ".text");
ut64 text_end = text + bin->size;
if (!memcmp (buf, "\x00\xb0\xa0\xe3\x00\xe0\xa0\xe3", 8)) {
// endian stuff here
ut32 *addr = (ut32*)(buf+0x34);
/*
0x00012000 00b0a0e3 mov fp, 0
0x00012004 00e0a0e3 mov lr, 0
*/
if (*addr > text && *addr < (text_end))
return *addr - bin->baddr;
}
// MIPS
/* get .got, calculate offset of main symbol */
if (!memcmp (buf, "\x21\x00\xe0\x03\x01\x00\x11\x04", 8)) {
/*
assuming the startup code looks like
got = gp-0x7ff0
got[index__libc_start_main] ( got[index_main] );
looking for the instruction generating the first argument to find main
lw a0, offset(gp)
*/
ut64 got_offset;
if ((got_offset = Elf_(r_bin_elf_get_section_offset) (bin, ".got")) != -1 ||
(got_offset = Elf_(r_bin_elf_get_section_offset) (bin, ".got.plt")) != -1)
{
const ut64 gp = got_offset + 0x7ff0;
unsigned i;
#define BUF_U32(i) ((ut32)(buf[i+0]+(buf[i+1]<<8)+(buf[i+2]<<16)+(buf[i+3]<<24)))
for (i=0; i < sizeof(buf)/sizeof(buf[0]); i+=4) {
const ut32 instr = BUF_U32(i);
if ((instr & 0xffff0000) == 0x8f840000) { // lw a0, offset(gp)
const short delta = instr & 0x0000ffff;
r_buf_read_at (bin->b, /* got_entry_offset = */ gp + delta, buf, 4);
return (/* main_vaddr = */ BUF_U32(0)) - bin->baddr;
}
}
#undef BUF_U32
}
return 0;
}
// ARM
if (!memcmp (buf, "\x24\xc0\x9f\xe5\x00\xb0\xa0\xe3", 8)) {
return (ut64)((int)(buf[48+0]+(buf[48+1]<<8)+
(buf[48+2]<<16)+(buf[48+3]<<24)))-bin->baddr;
}
// X86-PIE
if (buf[0x1d] == 0x48 && buf[0x1e] == 0x8b) {
if (!memcmp (buf, "\x31\xed\x49\x89", 4)) {// linux
ut64 maddr, baddr;
ut32 n32, *num = (ut32 *)(buf+0x20);
maddr = entry + 0x24 + *num;
if (r_buf_read_at (bin->b, maddr, (ut8*)&n32, sizeof (n32)) == -1) {
eprintf ("Warning: read (maddr) 2\n");
return 0;
}
maddr = (ut64)n32;
baddr = (bin->ehdr.e_entry >> 16) << 16;
if (bin->phdr) {
baddr = Elf_(r_bin_elf_get_baddr) (bin);
}
maddr += baddr;
return maddr;
}
}
// X86-NONPIE
#if R_BIN_ELF64
if (!memcmp (buf, "\x49\x89\xd9", 3) && buf[156] == 0xe8) {// openbsd
return (ut64)((int)(buf[157+0]+(buf[157+1]<<8)+
(buf[157+2]<<16)+(buf[157+3]<<24)))+ entry + 156 + 5;
}
if (!memcmp (buf+29, "\x48\xc7\xc7", 3)) // linux
return (ut64)((int)(buf[29+3]+(buf[29+4]<<8)+
(buf[29+5]<<16)+(buf[29+6]<<24)))-bin->baddr;
#else
if (buf[23] == '\x68')
return (ut64)((int)(buf[23+1]+(buf[23+2]<<8)+
(buf[23+3]<<16)+(buf[23+4]<<24)))-bin->baddr;
#endif
/* linux64 pie main */
if (buf[29] == 0x48 && buf[30] == 0x8d) { // lea rdi, qword [rip-0x21c4]
ut8 *p = buf+29+3;
st32 maindelta = p[0] | p[1]<<8 | p[2]<<16 | p[3]<<24;
return (ut64)(entry + 29 + maindelta) + 7;
}
return 0;
}
int Elf_(r_bin_elf_get_stripped)(struct Elf_(r_bin_elf_obj_t) *bin) {
int i;
if (!bin->shdr)
return R_FALSE;
for (i = 0; i < bin->ehdr.e_shnum; i++)
if (bin->shdr[i].sh_type == SHT_SYMTAB)
return R_FALSE;
return R_TRUE;
}
int Elf_(r_bin_elf_get_static)(struct Elf_(r_bin_elf_obj_t) *bin) {
int i;
if (!bin->phdr)
return R_FALSE;
for (i = 0; i < bin->ehdr.e_phnum; i++)
if (bin->phdr[i].p_type == PT_INTERP)
return R_FALSE;
return R_TRUE;
}
char* Elf_(r_bin_elf_get_data_encoding)(struct Elf_(r_bin_elf_obj_t) *bin) {
switch (bin->ehdr.e_ident[EI_DATA]) {
case ELFDATANONE: return strdup ("none");
case ELFDATA2LSB: return strdup ("2's complement, little endian");
case ELFDATA2MSB: return strdup ("2's complement, big endian");
default: return r_str_newf ("<unknown: %x>", bin->ehdr.e_ident[EI_DATA]);
}
}
int Elf_(r_bin_elf_has_va)(struct Elf_(r_bin_elf_obj_t) *bin) {
ut32 e_type = (ut32)bin->ehdr.e_type; // cast to avoid warn in iphone-gcc, must be ut16
//if (bin->ehdr.e_phnum == 0)
return (e_type == ET_REL)? 0: 1;
}
// TODO: do not strdup here
char* Elf_(r_bin_elf_get_arch)(struct Elf_(r_bin_elf_obj_t) *bin) {
switch (bin->ehdr.e_machine) {
case EM_ARC:
case EM_ARC_A5:
return strdup ("arc");
case EM_AVR: return strdup ("avr");
case EM_CRIS: return strdup ("cris");
case EM_68K: return strdup ("m68k");
case EM_MIPS:
case EM_MIPS_RS3_LE:
case EM_MIPS_X:
return strdup ("mips");
case EM_ARM:
case EM_AARCH64:
return strdup ("arm");
case EM_BLACKFIN:
return strdup ("blackfin");
case EM_SPARC:
case EM_SPARC32PLUS:
case EM_SPARCV9:
return strdup ("sparc");
case EM_PPC:
case EM_PPC64:
return strdup ("ppc");
case EM_PARISC:
return strdup ("hppa");
case EM_PROPELLER:
return strdup ("propeller");
case EM_SH: return strdup ("sh");
default: return strdup ("x86");
}
}
// TODO: do not strdup here
char* Elf_(r_bin_elf_get_machine_name)(struct Elf_(r_bin_elf_obj_t) *bin) {
switch (bin->ehdr.e_machine) {
case EM_NONE: return strdup ("No machine");
case EM_M32: return strdup ("AT&T WE 32100");
case EM_SPARC: return strdup ("SUN SPARC");
case EM_386: return strdup ("Intel 80386");
case EM_68K: return strdup ("Motorola m68k family");
case EM_88K: return strdup ("Motorola m88k family");
case EM_860: return strdup ("Intel 80860");
case EM_MIPS: return strdup ("MIPS R3000");
case EM_S370: return strdup ("IBM System/370");
case EM_MIPS_RS3_LE: return strdup ("MIPS R3000 little-endian");
case EM_PARISC: return strdup ("HPPA");
case EM_VPP500: return strdup ("Fujitsu VPP500");
case EM_SPARC32PLUS: return strdup ("Sun's \"v8plus\"");
case EM_960: return strdup ("Intel 80960");
case EM_PPC: return strdup ("PowerPC");
case EM_PPC64: return strdup ("PowerPC 64-bit");
case EM_S390: return strdup ("IBM S390");
case EM_V800: return strdup ("NEC V800 series");
case EM_FR20: return strdup ("Fujitsu FR20");
case EM_RH32: return strdup ("TRW RH-32");
case EM_RCE: return strdup ("Motorola RCE");
case EM_ARM: return strdup ("ARM");
case EM_BLACKFIN: return strdup ("Analog Devices Blackfin");
case EM_FAKE_ALPHA: return strdup ("Digital Alpha");
case EM_SH: return strdup ("Hitachi SH");
case EM_SPARCV9: return strdup ("SPARC v9 64-bit");
case EM_TRICORE: return strdup ("Siemens Tricore");
case EM_ARC: return strdup ("Argonaut RISC Core");
case EM_H8_300: return strdup ("Hitachi H8/300");
case EM_H8_300H: return strdup ("Hitachi H8/300H");
case EM_H8S: return strdup ("Hitachi H8S");
case EM_H8_500: return strdup ("Hitachi H8/500");
case EM_IA_64: return strdup ("Intel Merced");
case EM_MIPS_X: return strdup ("Stanford MIPS-X");
case EM_COLDFIRE: return strdup ("Motorola Coldfire");
case EM_68HC12: return strdup ("Motorola M68HC12");
case EM_MMA: return strdup ("Fujitsu MMA Multimedia Accelerator");
case EM_PCP: return strdup ("Siemens PCP");
case EM_NCPU: return strdup ("Sony nCPU embeeded RISC");
case EM_NDR1: return strdup ("Denso NDR1 microprocessor");
case EM_STARCORE: return strdup ("Motorola Start*Core processor");
case EM_ME16: return strdup ("Toyota ME16 processor");
case EM_ST100: return strdup ("STMicroelectronic ST100 processor");
case EM_TINYJ: return strdup ("Advanced Logic Corp. Tinyj emb.fam");
case EM_X86_64: return strdup ("AMD x86-64 architecture");
case EM_PDSP: return strdup ("Sony DSP Processor");
case EM_FX66: return strdup ("Siemens FX66 microcontroller");
case EM_ST9PLUS: return strdup ("STMicroelectronics ST9+ 8/16 mc");
case EM_ST7: return strdup ("STmicroelectronics ST7 8 bit mc");
case EM_68HC16: return strdup ("Motorola MC68HC16 microcontroller");
case EM_68HC11: return strdup ("Motorola MC68HC11 microcontroller");
case EM_68HC08: return strdup ("Motorola MC68HC08 microcontroller");
case EM_68HC05: return strdup ("Motorola MC68HC05 microcontroller");
case EM_SVX: return strdup ("Silicon Graphics SVx");
case EM_ST19: return strdup ("STMicroelectronics ST19 8 bit mc");
case EM_VAX: return strdup ("Digital VAX");
case EM_CRIS: return strdup ("Axis Communications 32-bit embedded processor");
case EM_JAVELIN: return strdup ("Infineon Technologies 32-bit embedded processor");
case EM_FIREPATH: return strdup ("Element 14 64-bit DSP Processor");
case EM_ZSP: return strdup ("LSI Logic 16-bit DSP Processor");
case EM_MMIX: return strdup ("Donald Knuth's educational 64-bit processor");
case EM_HUANY: return strdup ("Harvard University machine-independent object files");
case EM_PRISM: return strdup ("SiTera Prism");
case EM_AVR: return strdup ("Atmel AVR 8-bit microcontroller");
case EM_FR30: return strdup ("Fujitsu FR30");
case EM_D10V: return strdup ("Mitsubishi D10V");
case EM_D30V: return strdup ("Mitsubishi D30V");
case EM_V850: return strdup ("NEC v850");
case EM_M32R: return strdup ("Mitsubishi M32R");
case EM_MN10300: return strdup ("Matsushita MN10300");
case EM_MN10200: return strdup ("Matsushita MN10200");
case EM_PJ: return strdup ("picoJava");
case EM_OPENRISC: return strdup ("OpenRISC 32-bit embedded processor");
case EM_ARC_A5: return strdup ("ARC Cores Tangent-A5");
case EM_XTENSA: return strdup ("Tensilica Xtensa Architecture");
case EM_AARCH64: return strdup ("ARM aarch64");
case EM_PROPELLER: return strdup ("Parallax Propeller");
default: return r_str_newf ("<unknown>: 0x%x", bin->ehdr.e_machine);
}
}
char* Elf_(r_bin_elf_get_file_type)(struct Elf_(r_bin_elf_obj_t) *bin) {
ut32 e_type;
if (!bin)
return NULL;
e_type = (ut32)bin->ehdr.e_type; // cast to avoid warn in iphone-gcc, must be ut16
switch (e_type) {
case ET_NONE: return strdup ("NONE (None)");
case ET_REL: return strdup ("REL (Relocatable file)");
case ET_EXEC: return strdup ("EXEC (Executable file)");
case ET_DYN: return strdup ("DYN (Shared object file)");
case ET_CORE: return strdup ("CORE (Core file)");
}
if ((e_type >= ET_LOPROC) && (e_type <= ET_HIPROC))
return r_str_newf ("Processor Specific: %x", e_type);
else if ((e_type >= ET_LOOS) && (e_type <= ET_HIOS))
return r_str_newf ("OS Specific: %x", e_type);
else return r_str_newf ("<unknown>: %x", e_type);
}
char* Elf_(r_bin_elf_get_elf_class)(struct Elf_(r_bin_elf_obj_t) *bin) {
switch (bin->ehdr.e_ident[EI_CLASS]) {
case ELFCLASSNONE: return strdup ("none");
case ELFCLASS32: return strdup ("ELF32");
case ELFCLASS64: return strdup ("ELF64");
default: return r_str_newf ("<unknown: %x>", bin->ehdr.e_ident[EI_CLASS]);
}
}
int Elf_(r_bin_elf_get_bits)(struct Elf_(r_bin_elf_obj_t) *bin) {
/* Hack for ARCompact */
if (bin->ehdr.e_machine == EM_ARC_A5)
return 16;
switch (bin->ehdr.e_ident[EI_CLASS]) {
case ELFCLASS32: return 32;
case ELFCLASS64: return 64;
case ELFCLASSNONE:
default: return 32; // defaults
}
}
static inline int noodle(struct Elf_(r_bin_elf_obj_t) *bin, const char *s) {
const ut8 *p = bin->b->buf;
if (bin->b->length>64) {
p += bin->b->length-64;
} else return 0;
return r_mem_mem (p, 64, (const ut8 *)s, strlen (s)) != NULL;
}
static inline int needle(struct Elf_(r_bin_elf_obj_t) *bin, const char *s) {
if (bin->shstrtab) {
ut32 len = bin->shstrtab_size;
if (len > 4096) len = 4096; // avoid slow loading .. can be buggy?
return r_mem_mem ((const ut8*)bin->shstrtab, len,
(const ut8*)s, strlen (s)) != NULL;
}
return 0;
}
// TODO: must return const char * all those strings must be const char os[LINUX] or so
char* Elf_(r_bin_elf_get_osabi_name)(struct Elf_(r_bin_elf_obj_t) *bin) {
/* Hack to identify OS */
if (needle (bin, "openbsd")) return strdup ("openbsd");
if (needle (bin, "netbsd")) return strdup ("netbsd");
if (needle (bin, "freebsd")) return strdup ("freebsd");
if (noodle (bin, "BEOS:APP_VERSION")) return strdup ("beos");
if (needle (bin, "GNU")) return strdup ("linux");
return strdup ("linux");
#if 0
// XXX: this is wrong. openbsd bins are identified as linux ones.
switch (bin->ehdr.e_ident[EI_OSABI]) {
case ELFOSABI_ARM_AEABI:
case ELFOSABI_ARM: return strdup ("arm");
case ELFOSABI_NONE: return strdup ("linux"); // sysv
case ELFOSABI_HPUX: return strdup ("hpux");
case ELFOSABI_NETBSD: return strdup ("netbsd");
case ELFOSABI_LINUX: return strdup ("linux");
case ELFOSABI_SOLARIS: return strdup ("solaris");
case ELFOSABI_AIX: return strdup ("aix");
case ELFOSABI_IRIX: return strdup ("irix");
case ELFOSABI_FREEBSD: return strdup ("freebsd");
case ELFOSABI_TRU64: return strdup ("tru64");
case ELFOSABI_MODESTO: return strdup ("modesto");
case ELFOSABI_OPENBSD: return strdup ("openbsd");
case ELFOSABI_STANDALONE: return strdup ("standalone");
default: return r_str_newf ("<unknown: %x>", bin->ehdr.e_ident[EI_OSABI]);
}
#endif
}
int Elf_(r_bin_elf_is_big_endian)(struct Elf_(r_bin_elf_obj_t) *bin) {
return (bin->ehdr.e_ident[EI_DATA] == ELFDATA2MSB);
}
/* XXX Init dt_strtab? */
char *Elf_(r_bin_elf_get_rpath)(struct Elf_(r_bin_elf_obj_t) *bin) {
char *ret = NULL;
int j;
if (!bin || !bin->phdr || !bin->dyn_buf || !bin->strtab)
return NULL;
for (j = 0; j< bin->dyn_entries; j++){
if (bin->dyn_buf[j].d_tag == DT_RPATH || bin->dyn_buf[j].d_tag == DT_RUNPATH){
if ((ret = calloc (1,ELF_STRING_LENGTH)) == NULL) {
perror ("malloc (rpath)");
return NULL;
}
if (bin->dyn_buf[j].d_un.d_val > bin->strtab_size){
free (ret);
return NULL;
}
strncpy (ret, bin->strtab + bin->dyn_buf[j].d_un.d_val, ELF_STRING_LENGTH);
ret[ELF_STRING_LENGTH - 1] = '\0';
break;
}
}
return ret;
}
static size_t Elf_(r_bin_elf_get_relocs_num)(struct Elf_(r_bin_elf_obj_t) *bin) {
int nidx;
size_t i, ret = 0;
const char *sh_name;
if (bin->shdr == NULL) {
return 0;
}
for (i = 0; i < bin->ehdr.e_shnum; i++) {
nidx = bin->shdr[i].sh_name;
if (bin->shdr[i].sh_size > bin->size) return 0;
if (nidx < 0 || !bin->shstrtab_section ||
!bin->shstrtab_size || nidx > bin->shstrtab_size) {
continue;
} else if (!bin->shstrtab || !(nidx > 0) || !(nidx + 8 < bin->shstrtab_size)) {
continue;
}
if (bin->shdr[i].sh_link >= bin->ehdr.e_shnum) {
continue;
}
if (nidx > bin->shstrtab_size) {
eprintf ("Invalid shdr index in strtab %d/%"PFMT64d"\n",
bin->shdr[i].sh_name, (ut64) bin->shstrtab_size);
continue;
}
sh_name = &bin->shstrtab[nidx];
if (!sh_name)
continue;
if (!strncmp (sh_name, ".rela.", strlen (".rela."))) {
ret += bin->ehdr.e_ident[EI_CLASS] == 1 ? (bin->shdr[i].sh_size) / (sizeof (ut32) * 3) :
(bin->shdr[i].sh_size) / (sizeof (ut64) * 3);
} else if (!strncmp (sh_name, ".rel.", strlen (".rel."))) {
ret += bin->ehdr.e_ident[EI_CLASS] == 1 ? (bin->shdr[i].sh_size) / (sizeof (ut32) * 2) :
(bin->shdr[i].sh_size) / (sizeof (ut64) * 2);
}
}
return ret;
}
static int Elf_(r_bin_elf_read_reloc)(struct Elf_(r_bin_elf_obj_t) *bin,
struct r_bin_elf_reloc_t *r, int is_rela, ut64 offset)
{
char *fmt;
st64 l1, l2, l3;
st32 i1, i2, i3;
if (offset > bin->size)
return -1;
if (bin->ehdr.e_ident[EI_CLASS] == 1) {
fmt = bin->endian ? "I" : "i";
if (r_buf_fread_at (bin->b, offset, (ut8*)&i1, fmt, 1) == -1) {
eprintf ("Error reading r_offset\n");
return -1;
}
if (r_buf_fread_at (bin->b, offset + sizeof (ut32), (ut8*)&i2, fmt, 1) == -1) {
eprintf ("Error reading r_info\n");
return -1;
}
if (is_rela && (r_buf_fread_at (bin->b, offset + sizeof (ut32) * 2, (ut8*)&i3, fmt, 1) == -1)) {
eprintf ("Error reading r_addend\n");
return -1;
}
r->is_rela = is_rela;
r->offset = i1;
r->type = ELF32_R_TYPE(i2);
r->sym = ELF32_R_SYM(i2);
r->last = 0;
if (is_rela)
r->addend = i3;
return is_rela ? sizeof (ut32) * 3 : sizeof (ut32) * 2;
} else {
fmt = bin->endian ? "L" : "l";
if (r_buf_fread_at (bin->b, offset, (ut8*)&l1, fmt, 1) == -1) {
eprintf ("Error reading r_offset\n");
return -1;
}
if (r_buf_fread_at (bin->b, offset + sizeof (ut64), (ut8*)&l2, fmt, 1) == -1) {
eprintf ("Error reading r_info\n");
return -1;
}
if (is_rela && (r_buf_fread_at (bin->b, offset + 2 * sizeof (ut64), (ut8*)&l3, fmt, 1) == -1)) {
eprintf ("Error reading r_addend\n");
return -1;
}
r->is_rela = is_rela;
r->offset = l1;
r->type = ELF64_R_TYPE(l2);
r->sym = ELF64_R_SYM(l2);
r->last = 0;
if (is_rela)
r->addend = l3;
return is_rela ? sizeof (ut64) * 3 : sizeof (ut64) * 2;
}
}
struct r_bin_elf_reloc_t* Elf_(r_bin_elf_get_relocs)(struct Elf_(r_bin_elf_obj_t) *bin) {
int nidx, res;
const char *sh_name;
size_t reloc_num = 0;
size_t i, j, rel;
struct r_bin_elf_reloc_t *ret = NULL;
Elf_(Shdr)* section_text = NULL;
ut64 section_text_offset = 0LL;
if (!bin || !bin->shdr || !bin->shstrtab)
return NULL;
reloc_num = Elf_(r_bin_elf_get_relocs_num) (bin);
if (!reloc_num)
return NULL;
ret = (struct r_bin_elf_reloc_t*)calloc ((size_t)reloc_num+2, sizeof (struct r_bin_elf_reloc_t));
if (!ret)
return NULL;
section_text = Elf_(r_bin_elf_get_section_by_name) (bin, ".text");
if (section_text) {
section_text_offset = section_text->sh_offset;
}
// TODO: check boundaries for e_shnum and filesize
for (i = 0, rel = 0; i < bin->ehdr.e_shnum && rel < reloc_num ; i++) {
nidx = bin->shdr[i].sh_name;
if (nidx < 0 || !bin->shstrtab_section ||
!bin->shstrtab_size || nidx > bin->shstrtab_size) {
continue;
} else if (!bin->shstrtab || !(bin->shdr[i].sh_name > 0) || !(bin->shdr[i].sh_name + 8 < bin->shstrtab_size)) {
continue;
}
if (bin->shdr[i].sh_link >= bin->ehdr.e_shnum) {
continue;
}
if (bin->shdr[i].sh_name > bin->shstrtab_size) {
eprintf ("Invalid shdr index in shstrtab %d/%"PFMT64d"\n",
bin->shdr[i].sh_name, (ut64) bin->shstrtab_size);
continue;
}
sh_name = &bin->shstrtab[nidx];
// TODO: check boundaries!!!
if (!sh_name || !*sh_name)
continue;
if (bin->shdr[i].sh_size > bin->size) {
eprintf ("Ignore section with invalid shsize\n");
continue;
}
if (!strncmp (sh_name, ".rela.", strlen (".rela."))) {
for (j = 0; j < bin->shdr[i].sh_size; j += res) {
if (bin->shdr[i].sh_size > bin->size || bin->shdr[i].sh_offset > bin->size)
break;
if (&ret[rel]+1 > ret+reloc_num)
break;
res = Elf_(r_bin_elf_read_reloc) (bin, &ret[rel],
1, bin->shdr[i].sh_offset + j);
ret[rel].rva = ret[rel].offset + section_text_offset;
ret[rel].sto = section_text_offset;
ret[rel].offset = ret[rel].offset - bin->baddr;
ret[rel].last = 0;
if (res < 0)
break;
rel++;
}
} else if (!strncmp (sh_name, ".rel.", strlen (".rel."))) {
for (j = 0; j < bin->shdr[i].sh_size; j += res) {
if (bin->shdr[i].sh_size > bin->size || bin->shdr[i].sh_offset > bin->size)
break;
res = Elf_(r_bin_elf_read_reloc) (bin, &ret[rel],
0, bin->shdr[i].sh_offset + j);
ret[rel].rva = ret[rel].offset;
ret[rel].offset = ret[rel].offset - bin->baddr;
ret[rel].last = 0;
if (res < 0)
break;
rel++;
}
}
}
ret[reloc_num].last = 1;
return ret;
}
struct r_bin_elf_lib_t* Elf_(r_bin_elf_get_libs)(struct Elf_(r_bin_elf_obj_t) *bin) {
struct r_bin_elf_lib_t *ret = NULL;
int j, k;
if (!bin || !bin->phdr || !bin->dyn_buf || !bin->strtab || *(bin->strtab+1) == '0')
return NULL;
for (j = 0, k = 0; j < bin->dyn_entries; j++)
if (bin->dyn_buf[j].d_tag == DT_NEEDED) {
ret = realloc (ret, (k+1) * sizeof (struct r_bin_elf_lib_t));
if (ret == NULL) {
perror ("realloc (libs)");
return NULL;
}
if (bin->dyn_buf[j].d_un.d_val > bin->strtab_size){
free (ret);
return NULL;
}
strncpy (ret[k].name, bin->strtab + bin->dyn_buf[j].d_un.d_val, ELF_STRING_LENGTH);
ret[k].name[ELF_STRING_LENGTH - 1] = '\0';
ret[k].last = 0;
if (ret[k].name[0]) {
k++;
}
}
ret = realloc (ret, (k+1) * sizeof (struct r_bin_elf_lib_t));
if (ret == NULL) {
perror ("realloc (libs)");
return NULL;
}
ret[k].last = 1;
return ret;
}
struct r_bin_elf_section_t* Elf_(r_bin_elf_get_sections)(struct Elf_(r_bin_elf_obj_t) *bin) {
struct r_bin_elf_section_t *ret = NULL;
char unknown_s[20], invalid_s[20];
int i, nidx, unknown_c=0, invalid_c=0;
if (!bin || !bin->shdr)
return NULL;
if ((ret = calloc ((bin->ehdr.e_shnum + 1), sizeof (struct r_bin_elf_section_t))) == NULL)
return NULL;
for (i = 0; i < bin->ehdr.e_shnum; i++) {
ret[i].offset = bin->shdr[i].sh_offset;
ret[i].rva = bin->shdr[i].sh_addr;//bin->shdr[i].sh_addr > bin->baddr?
//bin->shdr[i].sh_addr-bin->baddr: bin->shdr[i].sh_addr;
ret[i].size = bin->shdr[i].sh_size;
ret[i].align = bin->shdr[i].sh_addralign;
ret[i].flags = bin->shdr[i].sh_flags;
//memset (ret[i].name, 0, sizeof (ret[i].name));
nidx = bin->shdr[i].sh_name;
#define SHNAME (int)bin->shdr[i].sh_name
#define SHNLEN ELF_STRING_LENGTH-4
#define SHSIZE (int)bin->shstrtab_size
if (nidx<0 || !bin->shstrtab_section ||
!bin->shstrtab_size|| nidx > bin->shstrtab_size) {
snprintf(invalid_s, sizeof(invalid_s)-4, "invalid%d", invalid_c);
strncpy (ret[i].name, invalid_s, SHNLEN);
invalid_c++;
}
else {
if (bin->shstrtab && (SHNAME > 0) && (SHNAME+8 < SHSIZE)) {
strncpy (ret[i].name, &bin->shstrtab[SHNAME], SHNLEN);
} else {
snprintf(unknown_s, sizeof(unknown_s)-4, "unknown%d", unknown_c);
strncpy (ret[i].name, unknown_s, sizeof (ret[i].name)-4);
unknown_c++;
}
}
ret[i].name[ELF_STRING_LENGTH-2] = '\0';
ret[i].last = 0;
//eprintf ("%d) %s Sh_addr: 0x%04x, bin_base: 0x%04x, base_addr - bin_shdr: 0x%04x\n", i, ret[i].name, bin->shdr[i].sh_addr, bin->baddr, bin->shdr[i].sh_addr-bin->baddr);
}
ret[i].last = 1;
return ret;
}
static void fill_symbol_bind_and_type (struct r_bin_elf_symbol_t *ret, Elf_(Sym) *sym){
#define s_bind(x) snprintf (ret->bind, ELF_STRING_LENGTH, x);
switch (ELF_ST_BIND(sym->st_info)) {
case STB_LOCAL: s_bind ("LOCAL"); break;
case STB_GLOBAL: s_bind ("GLOBAL"); break;
case STB_NUM: s_bind ("NUM"); break;
case STB_LOOS: s_bind ("LOOS"); break;
case STB_HIOS: s_bind ("HIOS"); break;
case STB_LOPROC: s_bind ("LOPROC"); break;
case STB_HIPROC: s_bind ("HIPROC"); break;
default: s_bind ("UNKNOWN");
}
#define s_type(x) snprintf (ret->type, ELF_STRING_LENGTH, x);
switch (ELF_ST_TYPE (sym->st_info)) {
case STT_NOTYPE: s_type ("NOTYPE"); break;
case STT_OBJECT: s_type ("OBJECT"); break;
case STT_FUNC: s_type ("FUNC"); break;
case STT_SECTION: s_type ("SECTION"); break;
case STT_FILE: s_type ("FILE"); break;
case STT_COMMON: s_type ("COMMON"); break;
case STT_TLS: s_type ("TLS"); break;
case STT_NUM: s_type ("NUM"); break;
case STT_LOOS: s_type ("LOOS"); break;
case STT_HIOS: s_type ("HIOS"); break;
case STT_LOPROC: s_type ("LOPROC"); break;
case STT_HIPROC: s_type ("HIPROC"); break;
default: s_type ("UNKNOWN");
}
}
static struct r_bin_elf_symbol_t* get_symbols_from_phdr (struct Elf_(r_bin_elf_obj_t) *bin, int type) {
Elf_(Sym) *sym = NULL;
Elf_(Addr) addr_sym_table = 0;
struct r_bin_elf_symbol_t *ret = NULL;
int j, k, r, tsize, len, nsym, ret_ctr;
ut64 toffset;
ut32 size;
if (!bin || !bin->phdr || bin->ehdr.e_phnum == 0)
return NULL;
for (j = 0; j < bin->dyn_entries; j++) {
if (bin->dyn_buf[j].d_tag == DT_SYMTAB){
addr_sym_table = bin->dyn_buf[j].d_un.d_ptr - bin->baddr;
break;
}
}
if (addr_sym_table){
//since ELF doesn't specify the symbol table size we are going to read until the end of the buffer
// this might be overkill.
nsym = (int)(bin->b->length - addr_sym_table) / sizeof (Elf_(Sym));
if (nsym < 1)
return NULL;
sym = (Elf_(Sym)*) calloc (nsym, sizeof (Elf_(Sym)));
if (!sym){
return NULL;
}
if (!UT32_MUL (&size, nsym, sizeof (Elf_(Sym)))){
free (sym);
return NULL;
}
if (size < 1){
free (sym);
return NULL;
}
if (addr_sym_table > bin->size || addr_sym_table+size > bin->size){
free (sym);
return NULL;
}
r = r_buf_fread_at (bin->b, addr_sym_table , (ut8*)sym,
#if R_BIN_ELF64
bin->endian? "I2cS2L": "i2cs2l",
#else
bin->endian? "3I2cS": "3i2cs",
#endif
nsym);
if (r == 0 || r == -1){
free (sym);
return NULL;
}
for (k = ret_ctr = 0 ; k < nsym ; k++){
if (k == 0)
continue;
if (type == R_BIN_ELF_IMPORTS && sym[k].st_shndx == STN_UNDEF) {
if (sym[k].st_value)
toffset = sym[k].st_value;
else if ((toffset = Elf_(get_import_addr) (bin, k)) == -1)
toffset = 0;
tsize = 16;
} else if (type == R_BIN_ELF_SYMBOLS && sym[k].st_shndx != STN_UNDEF &&
ELF_ST_TYPE(sym[k].st_info) != STT_SECTION && ELF_ST_TYPE(sym[k].st_info) != STT_FILE){
tsize = sym[k].st_size;
toffset = (ut64)sym[k].st_value;
} else continue;
if ((ret = realloc (ret, (ret_ctr + 1) * sizeof (struct r_bin_elf_symbol_t))) == NULL){
free (sym);
return NULL;
}
if (sym[k].st_name+2 > bin->strtab_size)
// Since we are reading beyond the symbol table what's happening
// is that some entry is trying to dereference the strtab beyond its capacity
// is not a symbol so is the end
goto done;
ret[ret_ctr].offset = (toffset >= bin->baddr ? toffset -= bin->baddr : toffset);
ret[ret_ctr].size = tsize;
{
int rest = R_MIN (ELF_STRING_LENGTH,128)-1;
int st_name = sym[k].st_name;
int maxsize = R_MIN (bin->size, bin->strtab_size);
if (st_name < 0 || st_name >= maxsize) {
len = 0;
ret[ret_ctr].name[0] = 0;
} else {
len = __strnlen (bin->strtab+st_name, rest);
memcpy (ret[ret_ctr].name, &bin->strtab[st_name], len);
}
}
ret[ret_ctr].ordinal = k;
ret[ret_ctr].name[ELF_STRING_LENGTH-2] = '\0';
fill_symbol_bind_and_type (&ret[ret_ctr], &sym[k]);
ret[ret_ctr].last = 0;
ret_ctr++;
}
done:
{
ut8 *p = (ut8*)realloc (ret, (ret_ctr+1) * sizeof (struct r_bin_elf_symbol_t));
if (!p) {
free (ret);
free (sym);
return NULL;
}
ret = (struct r_bin_elf_symbol_t *) p;
}
ret[ret_ctr].last = 1;
if (type == R_BIN_ELF_IMPORTS && !bin->imports_by_ord_size) {
bin->imports_by_ord_size = ret_ctr;
bin->imports_by_ord = (RBinImport**)calloc (ret_ctr, sizeof (RBinImport*));
} else if (type == R_BIN_ELF_SYMBOLS && !bin->symbols_by_ord_size) {
bin->symbols_by_ord_size = ret_ctr;
bin->symbols_by_ord = (RBinSymbol**)calloc (ret_ctr, sizeof (RBinSymbol*));
}
}
free (sym);
return ret;
}
struct r_bin_elf_symbol_t* Elf_(r_bin_elf_get_symbols)(struct Elf_(r_bin_elf_obj_t) *bin, int type) {
ut32 shdr_size;
int tsize, nsym, ret_ctr, i, k, len, newsize;
ut64 sym_offset = 0, data_offset = 0, toffset;
ut32 size = 0;
struct r_bin_elf_symbol_t *ret = NULL;
Elf_(Shdr) *strtab_section = NULL;
Elf_(Sym) *sym = NULL;
char *strtab = NULL;
Elf_(Shdr)* section_text = NULL;
ut64 section_text_offset = 0LL;
if (!bin || !bin->shdr || bin->ehdr.e_shnum == 0 || bin->ehdr.e_shnum == 0xffff)
//ok we don't give up
return get_symbols_from_phdr (bin, type);
if (bin->ehdr.e_type == ET_REL) {
section_text = Elf_(r_bin_elf_get_section_by_name)(bin, ".text");
if (section_text) {
section_text_offset = section_text->sh_offset;
}
// XXX: we must obey shndx here
if ((sym_offset = Elf_(r_bin_elf_get_section_offset)(bin, ".text")) == -1)
sym_offset = 0;
if ((data_offset = Elf_(r_bin_elf_get_section_offset)(bin, ".rodata")) == -1)
data_offset = 0;
}
if (!UT32_MUL (&shdr_size, bin->ehdr.e_shnum, sizeof (Elf_(Shdr))))
return R_FALSE;
if (shdr_size+8>bin->size)
return R_FALSE;
for (i = 0; i < bin->ehdr.e_shnum; i++) {
#define BUGGY 0
#if BUGGY
/* XXX: this regression was introduced because some binary was wrongly parsed.. must be reviewed */
if (
(
(type == R_BIN_ELF_IMPORTS) || (type == R_BIN_ELF_SYMBOLS)
) && (
(bin->shdr[i].sh_type == SHT_DYNSYM) || (bin->shdr[i].sh_type == SHT_SYMTAB)
)
) {
#else
if ((type == R_BIN_ELF_IMPORTS && bin->shdr[i].sh_type == (bin->ehdr.e_type == ET_REL ? SHT_SYMTAB : SHT_DYNSYM)) ||
(type == R_BIN_ELF_SYMBOLS && bin->shdr[i].sh_type == (Elf_(r_bin_elf_get_stripped) (bin) ? SHT_DYNSYM : SHT_SYMTAB))) {
#endif
if (bin->shdr[i].sh_link < 1) {
/* oops. fix out of range pointers */
continue;
}
// hack to avoid asan cry
if ((bin->shdr[i].sh_link*sizeof(Elf_(Shdr)))>= shdr_size) {
/* oops. fix out of range pointers */
continue;
}
strtab_section = &bin->shdr[bin->shdr[i].sh_link];
if (strtab_section->sh_size > ST32_MAX || strtab_section->sh_size+8 > bin->size) {
eprintf ("size (syms strtab)");
free (ret);
free (strtab);
return NULL;
}
if (!strtab) {
if ((strtab = (char *)calloc (1, 8+strtab_section->sh_size)) == NULL) {
eprintf ("malloc (syms strtab)");
free (ret);
free (strtab);
return NULL;
}
if (strtab_section->sh_offset > bin->size ||
strtab_section->sh_offset + strtab_section->sh_size > bin->size){
free (ret);
free (strtab);
return NULL;
}
if (r_buf_read_at (bin->b, strtab_section->sh_offset,
(ut8*)strtab, strtab_section->sh_size) == -1) {
eprintf ("Warning: read (syms strtab)\n");
free (ret);
free (strtab);
return NULL;
}
}
newsize = 1+bin->shdr[i].sh_size;
if (newsize<0 || newsize > bin->size) {
eprintf ("invalid shdr %d size\n", i);
free (ret);
free (strtab);
return NULL;
}
nsym = (int)(bin->shdr[i].sh_size/sizeof (Elf_(Sym)));
if (nsym < 1){
free (ret);
free (strtab);
return NULL;
}
if ((sym = (Elf_(Sym) *)calloc (nsym, sizeof(Elf_(Sym)))) == NULL) {
eprintf ("calloc (syms)");
free (ret);
free (strtab);
return NULL;
}
if (!UT32_MUL (&size, nsym, sizeof (Elf_(Sym)))){
free (ret);
free (strtab);
free (sym);
return NULL;
}
if (size < 1 || size > bin->size ||
bin->shdr[i].sh_offset > bin->size || bin->shdr[i].sh_offset+size > bin->size){
free (ret);
free (strtab);
free (sym);
return NULL;
}
if (r_buf_fread_at (bin->b, bin->shdr[i].sh_offset, (ut8*)sym,
#if R_BIN_ELF64
bin->endian? "I2cS2L": "i2cs2l",
#else
bin->endian? "3I2cS": "3i2cs",
#endif
nsym) == -1) {
eprintf ("Warning: read (sym)\n");
free (ret);
free (sym);
free (strtab);
return NULL;
}
ret = calloc (nsym, sizeof (struct r_bin_elf_symbol_t));
if (!ret) {
eprintf ("Cannot allocate %d symbols\n", nsym);
free (ret);
free (sym);
free (strtab);
return NULL;
}
for (k = ret_ctr = 0; k < nsym; k++) {
if (k == 0)
continue;
if (type == R_BIN_ELF_IMPORTS && sym[k].st_shndx == STN_UNDEF) {
if (sym[k].st_value)
toffset = sym[k].st_value;
else if ((toffset = Elf_(get_import_addr) (bin, k)) == -1)
toffset = 0;
tsize = 16;
} else if (type == R_BIN_ELF_SYMBOLS && sym[k].st_shndx != STN_UNDEF &&
ELF_ST_TYPE(sym[k].st_info) != STT_SECTION && ELF_ST_TYPE(sym[k].st_info) != STT_FILE) {
//int idx = sym[k].st_shndx;
tsize = sym[k].st_size;
toffset = (ut64)sym[k].st_value; //-sym_offset; // + (ELF_ST_TYPE(sym[k].st_info) == STT_FUNC?sym_offset:data_offset);
} else continue;
#if SKIP_SYMBOLS_WITH_VALUE
if (sym[k].st_value) {
/* skip symbols with value */
continue;
}
#endif
#if 0
if (bin->laddr) {
int idx = sym[k].st_shndx;
if (idx>=0 && idx < bin->ehdr.e_shnum) {
if (bin->baddr && toffset>bin->baddr)
toffset -= bin->baddr;
else
toffset += bin->shdr[idx].sh_offset;
} else {
//eprintf ("orphan symbol %d %d %s\n", idx, STN_UNDEF, &strtab[sym[k].st_name] );
continue;
}
}
#endif
ret[ret_ctr].offset = (toffset >= bin->baddr ? toffset -= bin->baddr : toffset);
if (section_text)
ret[ret_ctr].offset += section_text_offset;
ret[ret_ctr].size = tsize;
if (sym[k].st_name+2 > strtab_section->sh_size) {
eprintf ("Warning: index out of strtab range\n");
free (ret);
free (sym);
free (strtab);
return NULL;
}
{
int rest = R_MIN (ELF_STRING_LENGTH,128)-1; //strtab_section->sh_size - sym[k].st_name;
//len = r_str_nlen (strtab+sym[k].st_name, ELF_STRING_LENGTH-1);
int st_name = sym[k].st_name;
int maxsize = R_MIN (bin->b->length, strtab_section->sh_size);
if (st_name<0 || st_name>=maxsize) {
len = 0;
ret[ret_ctr].name[0] = 0;
} else {
len = __strnlen (strtab+sym[k].st_name, rest);
memcpy (ret[ret_ctr].name, &strtab[sym[k].st_name], len);
}
}
ret[ret_ctr].ordinal = k;
ret[ret_ctr].name[ELF_STRING_LENGTH-2] = '\0';
fill_symbol_bind_and_type (&ret[ret_ctr], &sym[k]);
ret[ret_ctr].last = 0;
ret_ctr++;
}
free (sym);
sym = NULL;
ret[ret_ctr].last = 1; // ugly dirty hack :D
R_FREE (strtab);
if (type == R_BIN_ELF_IMPORTS && !bin->imports_by_ord_size) {
bin->imports_by_ord_size = nsym;
bin->imports_by_ord = (RBinImport**)calloc (nsym, sizeof (RBinImport*));
} else if (type == R_BIN_ELF_SYMBOLS && !bin->symbols_by_ord_size) {
bin->symbols_by_ord_size = nsym;
bin->symbols_by_ord = (RBinSymbol**)calloc (nsym, sizeof (RBinSymbol*));
} else break;
}
}
// maybe it had some section header but not the symtab
if (!ret) return get_symbols_from_phdr (bin, type);
return ret;
}
struct r_bin_elf_field_t* Elf_(r_bin_elf_get_fields)(struct Elf_(r_bin_elf_obj_t) *bin) {
struct r_bin_elf_field_t *ret = NULL;
int i = 0, j;
if (!bin)
return NULL;
if ((ret = calloc ((bin->ehdr.e_phnum+3 + 1),
sizeof (struct r_bin_elf_field_t))) == NULL)
return NULL;
strncpy (ret[i].name, "ehdr", ELF_STRING_LENGTH);
ret[i].offset = 0;
ret[i++].last = 0;
strncpy (ret[i].name, "shoff", ELF_STRING_LENGTH);
ret[i].offset = bin->ehdr.e_shoff;
ret[i++].last = 0;
strncpy (ret[i].name, "phoff", ELF_STRING_LENGTH);
ret[i].offset = bin->ehdr.e_phoff;
ret[i++].last = 0;
for (j = 0; bin->phdr && j < bin->ehdr.e_phnum; i++, j++) {
snprintf (ret[i].name, ELF_STRING_LENGTH, "phdr_%i", j);
ret[i].offset = bin->phdr[j].p_offset;
ret[i].last = 0;
}
ret[i].last = 1;
return ret;
}
void* Elf_(r_bin_elf_free)(struct Elf_(r_bin_elf_obj_t)* bin) {
int i;
if (!bin) return NULL;
if (bin->phdr) free (bin->phdr);
if (bin->shdr) free (bin->shdr);
if (bin->strtab) free (bin->strtab);
if (bin->dyn_buf) free (bin->dyn_buf);
if (bin->shstrtab) free (bin->shstrtab);
//free (bin->strtab_section);
if (bin->imports_by_ord) {
for (i=0; i<bin->imports_by_ord_size; i++)
free (bin->imports_by_ord[i]);
free (bin->imports_by_ord);
}
if (bin->symbols_by_ord) {
for (i=0; i<bin->symbols_by_ord_size; i++)
free (bin->symbols_by_ord[i]);
free (bin->symbols_by_ord);
}
r_buf_free (bin->b);
free (bin);
return NULL;
}
struct Elf_(r_bin_elf_obj_t)* Elf_(r_bin_elf_new)(const char* file) {
ut8 *buf;
struct Elf_(r_bin_elf_obj_t) *bin = R_NEW0 (struct Elf_(r_bin_elf_obj_t));
if (!bin) return NULL;
memset (bin, 0, sizeof (struct Elf_(r_bin_elf_obj_t)));
bin->file = file;
if (!(buf = (ut8*)r_file_slurp (file, &bin->size)))
return Elf_(r_bin_elf_free) (bin);
bin->b = r_buf_new ();
if (!r_buf_set_bytes (bin->b, buf, bin->size)){
free (buf);
return Elf_(r_bin_elf_free) (bin);
}
if (!Elf_(r_bin_elf_init) (bin)) {
return Elf_(r_bin_elf_free) (bin);
}
free (buf);
return bin;
}
struct Elf_(r_bin_elf_obj_t)* Elf_(r_bin_elf_new_buf)(struct r_buf_t *buf) {
struct Elf_(r_bin_elf_obj_t) *bin = R_NEW0 (struct Elf_(r_bin_elf_obj_t));
bin->kv = sdb_new0 ();
bin->b = r_buf_new ();
bin->size = buf->length;
if (!r_buf_set_bytes (bin->b, buf->buf, buf->length))
return Elf_(r_bin_elf_free) (bin);
if (!Elf_(r_bin_elf_init) (bin))
return Elf_(r_bin_elf_free) (bin);
return bin;
}