rizin/libr/bin/format/coff/coff.c
2017-01-22 10:27:48 +01:00

218 lines
5.8 KiB
C

/* radare - LGPL - Copyright 2008-2017 pancake, inisider */
#include <r_util.h>
#include "coff.h"
#define bprintf if(obj->verbose)eprintf
bool r_coff_supported_arch(const ut8 *buf) {
ut16 arch = *(ut16*)buf;
switch (arch) {
case COFF_FILE_MACHINE_AMD64:
case COFF_FILE_MACHINE_I386:
case COFF_FILE_MACHINE_H8300:
case COFF_FILE_TI_COFF:
case COFF_FILE_MACHINE_R4000:
return true;
default:
return false;
}
}
int r_coff_is_stripped(struct r_bin_coff_obj *obj) {
return !!(obj->hdr.f_flags & (COFF_FLAGS_TI_F_RELFLG | \
COFF_FLAGS_TI_F_LNNO | COFF_FLAGS_TI_F_LSYMS));
}
char *r_coff_symbol_name(struct r_bin_coff_obj *obj, void *ptr) {
char n[256] = {0};
int len = 0, offset = 0;
union {
char name[8];
struct {
ut32 zero;
ut32 offset;
};
} *p = ptr;
if (!ptr) {
return NULL;
}
if (p->zero) {
return strdup (p->name);
}
offset = obj->hdr.f_symptr + obj->hdr.f_nsyms * sizeof (struct coff_symbol) + p->offset;
if (offset > obj->size) {
return NULL;
}
len = r_buf_read_at (obj->b, offset, (ut8*)n, sizeof (n));
if (len < 1) {
return NULL;
}
/* ensure null terminated string */
n[sizeof (n) - 1] = 0;
return strdup (n);
}
static int r_coff_rebase_sym(struct r_bin_coff_obj *obj, RBinAddr *addr, struct coff_symbol *sym) {
if (sym->n_scnum < 1 || sym->n_scnum > obj->hdr.f_nscns) {
return 0;
}
addr->paddr = obj->scn_hdrs[sym->n_scnum - 1].s_scnptr + sym->n_value;
return 1;
}
/* Try to get a valid entrypoint using the methods outlined in
* http://ftp.gnu.org/old-gnu/Manuals/ld-2.9.1/html_mono/ld.html#SEC24 */
RBinAddr *r_coff_get_entry(struct r_bin_coff_obj *obj) {
RBinAddr *addr = R_NEW0 (RBinAddr);
int i;
if (!addr) {
return NULL;
}
/* Simplest case, the header provides the entrypoint address */
if (obj->hdr.f_opthdr) {
addr->paddr = obj->opt_hdr.entry;
return addr;
}
/* No help from the header eh? Use the address of the symbols '_start'
* or 'main' if present */
if (obj->symbols) {
for (i = 0; i < obj->hdr.f_nsyms; i++) {
if ((!strcmp (obj->symbols[i].n_name, "_start") ||
!strcmp (obj->symbols[i].n_name, "start")) &&
r_coff_rebase_sym (obj, addr, &obj->symbols[i]))
return addr;
}
for (i = 0; i < obj->hdr.f_nsyms; i++) {
if ((!strcmp (obj->symbols[i].n_name, "_main") ||
!strcmp (obj->symbols[i].n_name, "main")) &&
r_coff_rebase_sym (obj, addr, &obj->symbols[i]))
return addr;
}
}
/* Still clueless ? Let's just use the address of .text */
if (obj->scn_hdrs) {
for (i = 0; i < obj->hdr.f_nscns; i++) {
//avoid doing string matching and use x bit from the section
if (obj->scn_hdrs[i].s_flags & COFF_SCN_MEM_EXECUTE) {
addr->paddr = obj->scn_hdrs[i].s_scnptr;
return addr;
}
}
}
return addr;
}
static bool r_bin_coff_init_hdr(struct r_bin_coff_obj *obj) {
ut16 magic = *(ut16 *)obj->b->buf;
obj->endian = (magic == COFF_FILE_MACHINE_H8300)? 1 : 0;
int ret = 0;
ret = r_buf_fread_at (obj->b, 0, (ut8 *)&obj->hdr, obj->endian? "2S3I2S": "2s3i2s", 1);
if (ret != sizeof (struct coff_hdr)) {
return false;
}
if (obj->hdr.f_magic == COFF_FILE_TI_COFF) {
ret = r_buf_fread_at (obj->b, R_BUF_CUR, (ut8 *)&obj->target_id, obj->endian? "S": "s", 1);
if (ret != sizeof (ut16)) {
return false;
}
}
return true;
}
static bool r_bin_coff_init_opt_hdr(struct r_bin_coff_obj *obj) {
int ret;
if (!obj->hdr.f_opthdr) {
return false;
}
ret = r_buf_fread_at (obj->b, sizeof (struct coff_hdr),
(ut8 *)&obj->opt_hdr, obj->endian? "2S6I": "2s6i", 1);
if (ret != sizeof (struct coff_opt_hdr)) {
return false;
}
return true;
}
static bool r_bin_coff_init_scn_hdr(struct r_bin_coff_obj *obj) {
int ret, size;
ut64 offset = sizeof (struct coff_hdr) + (obj->hdr.f_opthdr ? sizeof (struct coff_opt_hdr) : 0);
if (obj->hdr.f_magic == COFF_FILE_TI_COFF) {
offset += 2;
}
size = obj->hdr.f_nscns * sizeof (struct coff_scn_hdr);
if (offset > obj->size || offset + size > obj->size || size < 0) {
return false;
}
obj->scn_hdrs = calloc (1, size + sizeof (struct coff_scn_hdr));
if (!obj->scn_hdrs) {
return false;
}
ret = r_buf_fread_at (obj->b, offset, (ut8 *)obj->scn_hdrs, obj->endian? "8c6I2S1I": "8c6i2s1i", obj->hdr.f_nscns);
if (ret != size) {
R_FREE (obj->scn_hdrs);
return false;
}
return true;
}
static bool r_bin_coff_init_symtable(struct r_bin_coff_obj *obj) {
int ret, size;
ut64 offset = obj->hdr.f_symptr;
if (obj->hdr.f_nsyms >= 0xffff || !obj->hdr.f_nsyms) { // too much symbols, probably not allocatable
return false;
}
size = obj->hdr.f_nsyms * sizeof (struct coff_symbol);
if (size < 0 ||
size > obj->size ||
offset > obj->size ||
offset + size > obj->size) {
return false;
}
obj->symbols = calloc (1, size + sizeof (struct coff_symbol));
if (!obj->symbols) {
return false;
}
ret = r_buf_fread_at (obj->b, offset, (ut8 *)obj->symbols, obj->endian? "8c1I2S2c": "8c1i2s2c", obj->hdr.f_nsyms);
if (ret != size) {
R_FREE (obj->symbols);
return false;
}
return true;
}
static int r_bin_coff_init(struct r_bin_coff_obj *obj, RBuffer *buf, bool verbose) {
obj->b = r_buf_new ();
obj->size = buf->length;
obj->verbose = verbose;
if (!r_buf_set_bytes (obj->b, buf->buf, obj->size)){
r_buf_free (obj->b);
return false;
}
if (!r_bin_coff_init_hdr (obj)) {
bprintf ("Warning: failed to init hdr\n");
return false;
}
r_bin_coff_init_opt_hdr (obj);
if (!r_bin_coff_init_scn_hdr (obj)) {
bprintf ("Warning: failed to init section header\n");
return false;
}
if (!r_bin_coff_init_symtable (obj)) {
bprintf ("Warning: failed to init symtable\n");
return false;
}
return true;
}
void r_bin_coff_free(struct r_bin_coff_obj *obj) {
free (obj->scn_hdrs);
free (obj->symbols);
R_FREE (obj);
}
struct r_bin_coff_obj* r_bin_coff_new_buf(RBuffer *buf, bool verbose) {
struct r_bin_coff_obj* bin = R_NEW0 (struct r_bin_coff_obj);
r_bin_coff_init (bin, buf, verbose);
return bin;
}