/* radare - LGPL - Copyright 2008-2013 - nibble, pancake */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <r_types.h>
#include <r_util.h>
#include "elf.h"

static ut64 Elf_(r_bin_elf_get_section_offset)(struct Elf_(r_bin_elf_obj_t) *bin, const char *section_name);

static inline int __strnlen(const char *str, int len) {
	int l = 0;
	while (*str && --len) {
		str++;
		l++;
	}
	return l+1;
}

static int Elf_(r_bin_elf_init_ehdr)(struct Elf_(r_bin_elf_obj_t) *bin) {
	ut8 e_ident[16];
	int len;
	if (r_buf_read_at (bin->b, 0, e_ident, 16) == -1) {
		eprintf ("Warning: read (magic)\n");
		return R_FALSE;
	}
	bin->endian = (e_ident[EI_DATA] == ELFDATA2MSB)?
		LIL_ENDIAN: !LIL_ENDIAN;
	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) {
	int phdr_size, len;
	if (bin->ehdr.e_phnum == 0)
		return R_FALSE;
	if (bin->phdr) return R_TRUE;
	phdr_size = bin->ehdr.e_phnum * sizeof (Elf_(Phdr));
	if ((bin->phdr = (Elf_(Phdr) *)malloc (phdr_size)) == 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;
	}
	return R_TRUE;
}

static int Elf_(r_bin_elf_init_shdr)(struct Elf_(r_bin_elf_obj_t) *bin) {
	int len, shdr_size;
	if (bin->shdr) return R_TRUE;
	shdr_size = bin->ehdr.e_shnum * sizeof (Elf_(Shdr));
	if ((bin->shdr = (Elf_(Shdr) *)malloc (shdr_size)) == 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) {
	int sz;
	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;
	bin->shstrtab_section =
		bin->strtab_section = &bin->shdr[bin->ehdr.e_shstrndx];
	if (bin->strtab_section == NULL)
		return R_FALSE;
        if (bin->strtab_section->sh_size > (0xffff - sizeof (struct r_bin_elf_section_t)))
            bin->strtab_section->sh_size = 0xffff - sizeof (struct r_bin_elf_section_t);
	bin->shstrtab_size =
		bin->strtab_size = bin->strtab_section->sh_size;
	sz = sizeof (struct r_bin_elf_section_t) + bin->strtab_section->sh_size;
	if ((bin->strtab = (char *)malloc (sz)) == NULL) {
		perror ("malloc");
		bin->shstrtab = NULL;
		return R_FALSE;
	}
	memset (bin->strtab, 0, sz);
	bin->shstrtab = bin->strtab;
	//bin->strtab_section->sh_offset = 0;
	if (!bin->strtab_section->sh_offset) {
		bin->strtab = NULL;
		return R_TRUE;
	}
	if (r_buf_read_at (bin->b, bin->strtab_section->sh_offset, (ut8*)bin->strtab,
				bin->strtab_section->sh_size) == -1) {
		eprintf ("Warning: read (strtab) at 0x%"PFMT64x"\n",
				(ut64) bin->strtab_section->sh_offset);
		R_FREE (bin->strtab);
		bin->shstrtab = NULL;
		return R_FALSE;
	}
	return R_TRUE;
}

#if 0
static ut64 Elf_(r_bin_elf_get_section_size)(struct Elf_(r_bin_elf_obj_t) *bin, const char *section_name) {
	int i;
	if (!bin->shdr || !bin->strtab)
		return -1;
	for (i = 0; i < bin->ehdr.e_shnum; i++) {
		if (bin->shdr[i].sh_name > bin->shstrtab_section->sh_size)
			continue;
		if (!strcmp (&bin->shstrtab[bin->shdr[i].sh_name], section_name))
			return (ut64)bin->shdr[i].sh_size;
	}
	return -1;
}

static int Elf_(r_bin_elf_init_strtab)(struct Elf_(r_bin_elf_obj_t) *bin) {
	ut64 size;
	if (!bin->shstrtab)
		return  R_FALSE;
	bin->shstrtab_section = &bin->shdr[bin->ehdr.e_shstrndx];
	bin->shstrtab_size = size = 4096; //bin->shstrtab_section->sh_size;

	if ((bin->shstrtab = (char *)malloc (size)) == NULL) {
		perror ("malloc");
		return R_FALSE;
	}
//eprintf ("BUFBUFBUF %p\n",
	eprintf ("%p off=%x buf=%p sz=%llx\n",
bin->b, bin->shstrtab_section->sh_offset, (ut8*)bin->shstrtab, size);
	if (r_buf_read_at (bin->b, (ut64)bin->shstrtab_section->sh_offset, (ut8*)bin->shstrtab, size) == -1) {
		eprintf ("Warning: read (strtab)\n");
		R_FREE (bin->strtab);
		return R_FALSE;
	}
write (1, "\n\n", 2);
write (1, bin->shstrtab, size);
write (1, "\n\n", 2);

	size = Elf_(r_bin_elf_get_section_size)(bin, ".strtab");
	if (size != UT64_MAX) {
		ut64 off = Elf_(r_bin_elf_get_section_offset)(bin, ".strtab");
		bin->strtab_size = size;
		free (bin->strtab);
		if ((bin->strtab = (char *)malloc (size+1)) == NULL) {
			perror ("malloc");
			return R_FALSE;
		}
		if (r_buf_read_at (bin->b, off, (ut8*)bin->strtab, size) == -1) {
			eprintf ("Warning: read (strtab)\n");
			R_FREE (bin->strtab);
			return R_FALSE;
		}
	}
	return R_TRUE;
}
#endif

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->strtab_size = 0;
	bin->strtab_section = NULL;
	if (!Elf_(r_bin_elf_init_ehdr) (bin)) {
		//eprintf ("Warning: File is not ELF\n");
		return R_FALSE;
	}
	Elf_(r_bin_elf_init_phdr) (bin);
        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->imports_by_ord_size = 0;
	bin->imports_by_ord = NULL;
	bin->symbols_by_ord_size = 0;
	bin->symbols_by_ord = NULL;

	bin->baddr = Elf_(r_bin_elf_get_baddr) (bin);

	return R_TRUE;
}

static ut64 Elf_(r_bin_elf_get_section_offset)(struct Elf_(r_bin_elf_obj_t) *bin, const char *section_name) {
	int i;
	if (!bin->shdr || !bin->strtab)
		return -1;
	for (i = 0; i < bin->ehdr.e_shnum; i++) {
		if (bin->shdr[i].sh_name > bin->shstrtab_section->sh_size)
			continue;
		if (!strcmp (&bin->shstrtab[bin->shdr[i].sh_name], section_name))
			return (ut64)bin->shdr[i].sh_offset;
	}
	return -1;
}

ut64 Elf_(r_bin_elf_get_section_addr)(struct Elf_(r_bin_elf_obj_t) *bin, const char *section_name) {
	int i;
	if (!bin->shdr || !bin->strtab)
		return -1;
	for (i = 0; i < bin->ehdr.e_shnum; i++) {
		if (bin->shdr[i].sh_name > bin->shstrtab_section->sh_size)
			continue;
		if (!strcmp (&bin->strtab[bin->shdr[i].sh_name], section_name))
			return (ut64)bin->shdr[i].sh_addr;
	}
	return -1;
}

static ut64 Elf_(get_import_addr)(struct Elf_(r_bin_elf_obj_t) *bin, int sym) {
	Elf_(Rel) *rel = NULL;
	Elf_(Addr) plt_sym_addr;
	ut64 got_addr, got_offset;
	int i, j, k, tsize, len;

	if (!bin->shdr || !bin->strtab)
		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;
	for (i = 0; i < bin->ehdr.e_shnum; i++) {
		if (bin->shdr[i].sh_name > bin->shstrtab_section->sh_size)
			continue;
		if (!strcmp (&bin->strtab[bin->shdr[i].sh_name], ".rel.plt"))
			tsize = sizeof (Elf_(Rel));
		else if (!strcmp (&bin->strtab[bin->shdr[i].sh_name], ".rela.plt"))
			tsize = sizeof (Elf_(Rela));
		else continue;

		free (rel);
		if ((rel = (Elf_(Rel) *)malloc ((int)(bin->shdr[i].sh_size / tsize) * sizeof (Elf_(Rel)))) == NULL) {
			perror ("malloc (rel)");
			return -1;
		}
		for (j = k = 0; j < bin->shdr[i].sh_size; j += tsize, k++) {
			len = r_buf_fread_at (bin->b, bin->shdr[i].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");
				free (rel);
				return -1;
			}
		}
		for (j = k = 0; j < bin->shdr[i].sh_size; j += tsize, k++) {
			if (ELF_R_SYM (rel[k].r_info) == sym) {
				if (r_buf_read_at (bin->b, rel[k].r_offset-got_addr+got_offset,
							(ut8*)&plt_sym_addr, sizeof (Elf_(Addr))) == -1) {
					eprintf ("Warning: read (got)\n");
					return UT64_MAX;
				}
				free (rel);
				return (ut64)(plt_sym_addr - 6);
			}
		}
		break;
	}
	free (rel);
	return UT64_MAX;
}

ut64 Elf_(r_bin_elf_get_baddr)(struct Elf_(r_bin_elf_obj_t) *bin) {
	int i;
	if (!bin->phdr) {
		//eprintf ("r_bin_elf: canot get_baddr() because no phdr found\n");
		return 0;
	}
	/* hopefully.. the first PT_LOAD is base */
	for (i = 0; i < bin->ehdr.e_phnum; i++)
		if (bin->phdr[i].p_type == PT_LOAD)
			return (ut64)(bin->phdr[i].p_vaddr -
				bin->phdr[i].p_offset);
		//eprintf ("oh fuck .. cant find any valid ptload?\n");
	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 (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 (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 = (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 (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

	// MIPS
	/* get .got, calculate offset of main symbol */
	if (!memcmp (buf, "\x21\x00\xe0\x03\x01\x00\x11\x04\x00\x00\x00\x00", 12)) {
		ut64 got_addr = 0LL; // TODO: get .got offset
		short delta = (buf[28]+(buf[29]<<8));
		// NOTE: This is the way to resolve 'gp' register
		r_buf_read_at (bin->b, got_addr+(32734+delta), buf, 4);
		return (ut64)((int)(buf[0]+(buf[1]<<8)+(buf[2]<<16)+(buf[3]<<24)))-bin->baddr;
	}
	// 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
#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
	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_dup_printf ("<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_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_SPARC:
	case EM_SPARC32PLUS:
	case EM_SPARCV9:
		return strdup ("sparc");
	case EM_PPC:
	case EM_PPC64:
		return strdup ("ppc");
	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 big-endian");
	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_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");
	default:             return r_str_dup_printf ("<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 = (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_dup_printf ("Processor Specific: %x", e_type);
	else if ((e_type >= ET_LOOS) && (e_type <= ET_HIOS))
		return r_str_dup_printf ("OS Specific: %x", e_type);
	else return r_str_dup_printf ("<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_dup_printf ("<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) {
		int 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_dup_printf ("<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) {
	Elf_(Dyn) *dyn = NULL;
	ut64 stroff = 0;
	char *ret = NULL;
	int ndyn, i, j, len;

	if (!bin->phdr)
		return NULL;
	for (i = 0; i < bin->ehdr.e_phnum; i++)
		if (bin->phdr[i].p_type == PT_DYNAMIC) {
			free (dyn); // TODO: reuse dyn allocation
			if (!(dyn = malloc (1+bin->phdr[i].p_filesz))) {
				perror ("malloc (dyn)");
				free (ret);
				free (dyn);
				return NULL;
			}
			ndyn = (int)(bin->phdr[i].p_filesz / sizeof (Elf_(Dyn)));
			len = 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
					ndyn);
			if (len  == -1) {
				eprintf ("Warning: read (dyn)\n");
				free (ret);
				free (dyn);
				return NULL;
			}
			for (j = 0; j < ndyn; j++)
				if (dyn[j].d_tag == DT_STRTAB) {
					stroff = (ut64)(dyn[j].d_un.d_ptr - bin->baddr);
					break;
				}
			for (j = 0; j < ndyn; j++)
				if (dyn[j].d_tag == DT_RPATH || dyn[j].d_tag == DT_RUNPATH) {
					free (ret);
					if ((ret = malloc (ELF_STRING_LENGTH)) == NULL) {
						perror ("malloc (rpath)");
						free (dyn);
						return NULL;
					}
					if (r_buf_read_at (bin->b, stroff + dyn[j].d_un.d_val,
								(ut8*)ret, ELF_STRING_LENGTH) == -1) {
						eprintf ("Warning: read (rpath)\n");
						free (ret);
						free (dyn);
						return NULL;
					}
					break;
				}
			break;
		}
	free (dyn);
	return ret;
}

struct r_bin_elf_reloc_t* Elf_(r_bin_elf_get_relocs)(struct Elf_(r_bin_elf_obj_t) *bin) {
	struct r_bin_elf_reloc_t *ret = NULL;
	Elf_(Sym) *sym = NULL;
	Elf_(Rela) *rel = NULL;
	ut64 got_addr, got_offset;
	char *strtab = NULL, rel_fmt[] = "2i";
	int i, j, nrel, tsize, nsym;

	if (!bin->shdr || !bin->strtab)
		return NULL;
	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 NULL;
	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 NULL;
	for (i = 0, nsym = 0; i < bin->ehdr.e_shnum; i++)
		if (bin->shdr[i].sh_type == (bin->ehdr.e_type == ET_REL ? SHT_SYMTAB : SHT_DYNSYM)) {
			bin->strtab_section = &bin->shdr[bin->shdr[i].sh_link];
			if ((strtab = (char *)malloc (8+bin->strtab_section->sh_size)) == NULL) {
				perror ("malloc (syms strtab)");
				return NULL;
			}
			if (r_buf_read_at (bin->b, bin->strtab_section->sh_offset, (ut8*)strtab, bin->strtab_section->sh_size) == -1) {
				eprintf ("Warning: read (syms strtab)\n");
				return NULL;
			}
			if ((sym = (Elf_(Sym) *)malloc (1+bin->shdr[i].sh_size)) == NULL) { // LEAKS
				perror ("malloc (syms)");
				return NULL;
			}
			nsym = (int)(bin->shdr[i].sh_size/sizeof (Elf_(Sym)));
			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");
				return NULL;
			}
		}

	for (i = 0; i < bin->ehdr.e_shnum; i++) {
		if (bin->shdr[i].sh_name > bin->strtab_size) {
			eprintf ("Invalid shdr index in strtab %d/%"PFMT64d"\n",
				bin->shdr[i].sh_name, (ut64) bin->strtab_size);
			continue;
		}
		if (!strcmp (&bin->strtab[bin->shdr[i].sh_name], ".rel.plt")) {
			tsize = sizeof (Elf_(Rel));
			rel_fmt[0] = '2';
		} else if (!strcmp (&bin->strtab[bin->shdr[i].sh_name], ".rela.plt")) {
			tsize = sizeof (Elf_(Rela));
			rel_fmt[0] = '3';
		} else continue;

		rel_fmt[1] =
#if R_BIN_ELF64
			bin->endian?'L':'l';
#else
			bin->endian?'I':'i';
#endif

		if (tsize <1) // NOTE(eddyb) UNREACHABLE.
			return ret; // -1 ?

		if ((rel = (Elf_(Rela)*)malloc ((int)(bin->shdr[i].sh_size / tsize) * sizeof (Elf_(Rela)))) == NULL) {
			perror ("malloc (rel)");
			return NULL;
		}
		for (j = nrel = 0; j < bin->shdr[i].sh_size; j += tsize, nrel++) {
			if (r_buf_fread_at (bin->b, bin->shdr[i].sh_offset + j, (ut8*)&rel[nrel], rel_fmt, 1) == -1) {
				eprintf ("Warning: read (rel)\n");
				free(rel);
				return NULL;
			}
			if (tsize < sizeof (Elf_(Rela)))
				rel[nrel].r_addend = 0;
		}
		if ((ret = (struct r_bin_elf_reloc_t *)malloc ((nrel+1) * sizeof (struct r_bin_elf_reloc_t))) == NULL) {
			perror ("malloc (reloc)");
			free(rel);
			return NULL;
		}
		j = 0;
		if (sym) for (; j < nrel; j++) {
			ret[j].sym = ELF_R_SYM (rel[j].r_info);
			ret[j].type = ELF_R_TYPE (rel[j].r_info);
			ret[j].offset = rel[j].r_offset-got_addr+got_offset; // HACK FIXME(eddyb) there has to be a better way of getting the offset (for relocs outside GOT).
			ret[j].rva = rel[j].r_offset - bin->baddr;
			ret[j].addend = rel[j].r_addend;
			ret[j].is_rela = tsize == sizeof (Elf_(Rela));
			ret[j].last = 0;
		}
		ret[j].last = 1;
		break;
	}
	free(rel);
	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;
	Elf_(Dyn) *dyn = NULL;
	ut64 stroff = 0;
	int ndyn, i, j, k, len;

	if (!bin->phdr)
		return NULL;
	for (i = 0; i < bin->ehdr.e_phnum; i++)
		if (bin->phdr[i].p_type == PT_DYNAMIC) {
			if (!(dyn = malloc (bin->phdr[i].p_filesz))) {
				perror ("malloc (dyn)");
				return NULL;
			}
			ndyn = (int)(bin->phdr[i].p_filesz / sizeof (Elf_(Dyn)));
			len = 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
					ndyn);
			if (len  == -1) {
				eprintf ("Warning: read (dyn)\n");
				free (dyn);
				return NULL;
			}
			for (j = 0; j < ndyn; j++)
				if (dyn[j].d_tag == DT_STRTAB) {
					stroff = (ut64)(dyn[j].d_un.d_ptr - bin->baddr);
					break;
				}
			for (j = 0, k = 0; j < ndyn; j++)
				if (dyn[j].d_tag == DT_NEEDED) {
					ret = realloc (ret, (k+1) * sizeof (struct r_bin_elf_lib_t));
					if (ret == NULL) {
						perror ("realloc (libs)");
						free (dyn);
						return NULL;
					}
					if (r_buf_read_at (bin->b, stroff + dyn[j].d_un.d_val,
							(ut8*)ret[k].name, ELF_STRING_LENGTH) == -1) {
						eprintf ("Warning: read (libs)\n");
						free (ret);
						free (dyn);
						return NULL;
					}
					ret[k].last = 0;
					k++;
				}
			ret = realloc (ret, (k+1) * sizeof (struct r_bin_elf_lib_t));
			if (ret == NULL) {
				perror ("realloc (libs)");
				free (dyn);
				return NULL;
			}
			ret[k].last = 1;
			free (dyn);
			break;
		}
	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 ((ret = malloc ((bin->ehdr.e_shnum + 1) * sizeof (struct r_bin_elf_section_t))) == NULL)
		return NULL;
	for (i = 0; i < bin->ehdr.e_shnum; i++) {
		if (bin->shdr == NULL) {
			free (ret);
			return NULL;
		}
		ret[i].offset = bin->shdr[i].sh_offset;
		ret[i].rva = 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;
		if (nidx<0 || !bin->shstrtab_section ||
                    !bin->shstrtab_section->sh_size || nidx > bin->shstrtab_section->sh_size) {
                        snprintf(invalid_s, sizeof(invalid_s)-4, "invalid%d", invalid_c);
                        strncpy (ret[i].name, invalid_s, sizeof (ret[i].name)-4);
                        invalid_c++;
                }
		else {
                    if (bin->shstrtab && bin->shstrtab_size > bin->shdr[i].sh_name && bin->shdr[i].sh_name > 0)
                        strncpy (ret[i].name, &bin->shstrtab[bin->shdr[i].sh_name], sizeof (ret[i].name)-4);
                    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[sizeof (ret[i].name)-2] = 0;
		ret[i].last = 0;
	}
	ret[i].last = 1;
	return ret;
}

struct r_bin_elf_symbol_t* Elf_(r_bin_elf_get_symbols)(struct Elf_(r_bin_elf_obj_t) *bin, int type) {
	int shdr_size, tsize, nsym, ret_ctr, i, j, k, len;
	ut64 sym_offset = 0, data_offset = 0, toffset;
	struct r_bin_elf_symbol_t *ret = NULL;
	Elf_(Shdr) *strtab_section;
	Elf_(Sym) *sym;
	char *strtab;

	if (!bin->shdr || bin->ehdr.e_shnum == 0 || bin->ehdr.e_shnum == 0xffff)
		return NULL;
	if (bin->ehdr.e_type == ET_REL) {
		// 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;
	}
	shdr_size = bin->ehdr.e_shnum * sizeof (Elf_(Shdr));
	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 > shdr_size) {
				/* oops. fix out of range pointers */
				continue;
			}
			strtab_section = &bin->shdr[bin->shdr[i].sh_link];
			if ((strtab = (char *)malloc (8+strtab_section->sh_size)) == NULL) {
				eprintf ("malloc (syms 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");
				return NULL;
			}

			if ((sym = (Elf_(Sym) *)malloc (1+bin->shdr[i].sh_size)) == NULL) {
				eprintf ("malloc (syms)");
				return NULL;
			}
			nsym = (int)(bin->shdr[i].sh_size/sizeof (Elf_(Sym)));
			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");
				return NULL;
			}
			for (j = k = ret_ctr = 0; j < bin->shdr[i].sh_size; j += sizeof (Elf_(Sym)), 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 ((ret = realloc (ret, (ret_ctr + 1) * sizeof (struct r_bin_elf_symbol_t))) == NULL) {
					perror ("realloc (symbols|imports)");
					return NULL;
				}
				if (bin->baddr) {
					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;
					}
				}
				ret[ret_ctr].offset = toffset; //(toffset >= bin->baddr ? toffset -= bin->baddr : toffset);
				ret[ret_ctr].size = tsize;
				if (sym[k].st_name > strtab_section->sh_size) {
					perror ("index out of strtab range\n");
					return NULL;
				}
				len = __strnlen (&strtab[sym[k].st_name], ELF_STRING_LENGTH-1);
				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';
				#define s_bind(x) snprintf (ret[ret_ctr].bind, ELF_STRING_LENGTH, x);
				switch (ELF_ST_BIND(sym[k].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[ret_ctr].type, ELF_STRING_LENGTH, x);
				switch (ELF_ST_TYPE (sym[k].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");
				}
				ret[ret_ctr].last = 0;
				ret_ctr++;
			}
			{
			ut8 *p = (ut8*)realloc (ret, (ret_ctr+1)* sizeof (struct r_bin_elf_symbol_t));
			if (!p) {
				free (ret);
				return NULL;
			}
			ret = (struct r_bin_elf_symbol_t *) p;
			}
			ret[ret_ctr].last = 1; // ugly dirty hack :D

			if (type == R_BIN_ELF_IMPORTS && !bin->imports_by_ord_size) {
				bin->imports_by_ord_size = nsym;
				bin->imports_by_ord = (RBinImport**)malloc (nsym * sizeof (RBinImport*));
				memset (bin->imports_by_ord, 0, nsym * sizeof (RBinImport*));
			} else if (type == R_BIN_ELF_SYMBOLS && !bin->imports_by_ord_size) {
				bin->symbols_by_ord_size = nsym;
				bin->symbols_by_ord = (RBinSymbol**)malloc (nsym * sizeof (RBinSymbol*));
				memset (bin->symbols_by_ord, 0, nsym * sizeof (RBinSymbol*));
			} else

			break;
		}
	}
	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 ((ret = malloc ((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) {
	if (!bin) return NULL;
	free (bin->phdr);
	free (bin->shdr);
	free (bin->strtab);
	//free (bin->strtab_section);
	free (bin->imports_by_ord);
	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) {
	struct Elf_(r_bin_elf_obj_t) *bin;
	ut8 *buf;
// TODO: use R_NEW0 here
	if (!(bin = malloc (sizeof (struct Elf_(r_bin_elf_obj_t)))))
		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))
		return Elf_(r_bin_elf_free) (bin);
	free (buf);
	if (!Elf_(r_bin_elf_init) (bin))
		return Elf_(r_bin_elf_free) (bin);
	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->b = buf;
	bin->size = buf->length;
	if (!Elf_(r_bin_elf_init) (bin))
		return Elf_(r_bin_elf_free) (bin);
	return bin;
}
