diff --git a/libr/bin/format/coff/coff.c b/libr/bin/format/coff/coff.c index 26e0aedc93..84d3cce0c9 100644 --- a/libr/bin/format/coff/coff.c +++ b/libr/bin/format/coff/coff.c @@ -89,16 +89,73 @@ static int r_coff_rebase_sym(RBinCoffObj *obj, RBinAddr *addr, struct coff_symbo 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 */ -R_IPI RBinAddr *r_coff_get_entry(RBinCoffObj *obj) { - if (obj->xcoff) { +/* In XCOFF32, the entrypoint seems to be indirect. + At the entrypoint address, we find a pointer in .data, + that resolves to the actual entrypoint. */ + +static RBinAddr *r_xcoff_get_entry(RBinCoffObj *obj) { + /* Scan XCOFF loader symbol table */ + int ptr_scnum = 0; + ut64 ptr_vaddr; + if (obj->x_ldsyms) { + int i; + for (i = 0; i < obj->x_ldhdr.l_nsyms; i++) { + if (!strcmp (obj->x_ldsyms[i].l_name, "__start")) { + ptr_scnum = obj->x_ldsyms[i].l_scnum; + ptr_vaddr = obj->x_ldsyms[i].l_value; + break; + } + } + } + if (!ptr_scnum) { return NULL; } + /* Translate the pointer to a file offset */ + if (ptr_scnum < 1 || ptr_scnum > obj->hdr.f_nscns) { + R_LOG_WARN ("__start l_scnum invalid (%d)", ptr_scnum); + return NULL; + } + ut64 ptr_offset = obj->scn_hdrs[ptr_scnum - 1].s_scnptr + ptr_vaddr - obj->scn_hdrs[ptr_scnum - 1].s_vaddr; + + /* Read the actual entrypoint */ + ut32 entry_vaddr = r_buf_read_be32_at (obj->b, ptr_offset); + if (entry_vaddr == UT32_MAX) { + R_LOG_WARN ("__start vaddr invalid (vaddr=%#x off=%#x)", ptr_vaddr, ptr_offset); + return NULL; + } + + /* Double check that the entrypoint is in .text */ + int sntext = obj->x_opt_hdr.o_sntext; + if (sntext < 1 || sntext > obj->hdr.f_nscns) { + R_LOG_WARN ("o_sntext invalid (%d)", sntext); + return NULL; + } + ut32 text_vaddr = obj->scn_hdrs[sntext - 1].s_vaddr; + ut32 text_size = obj->scn_hdrs[sntext - 1].s_size; + if (entry_vaddr < text_vaddr || entry_vaddr >= text_vaddr + text_size) { + R_LOG_WARN ("*__start OOB (vaddr=%#lx text=%#lx..%#lx)", entry_vaddr, text_vaddr, text_vaddr + text_size); + return NULL; + } + RBinAddr *addr = R_NEW0 (RBinAddr); if (!addr) { return NULL; } + addr->vaddr = entry_vaddr; + return addr; +} + +/* 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 */ +R_IPI RBinAddr *r_coff_get_entry(RBinCoffObj *obj) { + RBinAddr *addr = R_NEW0 (RBinAddr); + if (!addr) { + return NULL; + } + /* Special case for XCOFF */ + if (obj->xcoff) { + return r_xcoff_get_entry (obj); + } /* Simplest case, the header provides the entrypoint address */ if (obj->hdr.f_opthdr) { addr->paddr = obj->opt_hdr.entry; @@ -188,7 +245,7 @@ static bool r_bin_coff_init_opt_hdr(RBinCoffObj *obj) { static bool r_bin_xcoff_init_opt_hdr(RBinCoffObj *obj) { int ret; ret = r_buf_fread_at (obj->b, sizeof (struct coff_hdr) + sizeof (struct coff_opt_hdr), - (ut8 *)&obj->xcoff_opt_hdr, "1I8S4c3I4c2S", 1); + (ut8 *)&obj->x_opt_hdr, "1I8S4c3I4c2S", 1); if (ret != sizeof (struct xcoff32_opt_hdr)) { return false; } @@ -217,6 +274,57 @@ static bool r_bin_coff_init_scn_hdr(RBinCoffObj *obj) { return true; } +/* init_ldhdr reads the XCOFF32 loader header, which is at the beginning of the .loader section */ + +static bool r_bin_xcoff_init_ldhdr(RBinCoffObj *obj) { + int ret; + ut16 loader_idx = obj->x_opt_hdr.o_snloader; + if (!loader_idx) { + return true; + } + if (loader_idx > obj->hdr.f_nscns) { + R_LOG_WARN ("invalid loader section number (%d > %d)", loader_idx, obj->hdr.f_nscns); + return false; + } + ut64 offset = obj->scn_hdrs[loader_idx-1].s_scnptr; // section numbers start at 1 + ret = r_buf_fread_at (obj->b, offset, (ut8 *)&obj->x_ldhdr, "8I", 1); + if (ret != sizeof (struct xcoff32_ldhdr)) { + R_LOG_WARN ("failed to read loader header"); + return false; + } + if (obj->x_ldhdr.l_version != 1) { + R_LOG_WARN ("unsupported loader version (%u)", obj->x_ldhdr.l_version); + return false; + } + return true; +} + +static bool r_bin_xcoff_init_ldsyms(RBinCoffObj *obj) { + int ret; + size_t size; + ut64 offset = obj->scn_hdrs[obj->x_opt_hdr.o_snloader-1].s_scnptr + sizeof (struct xcoff32_ldhdr); + if (!obj->x_ldhdr.l_nsyms) { + return true; + } + if (obj->x_ldhdr.l_nsyms >= 0xffff) { // too much symbols, probably not allocatable + R_LOG_DEBUG ("too many loader symbols (%u)", obj->x_ldhdr.l_nsyms); + return false; + } + // USHORT_MAX * 24UL cannot overflow size_t + size = obj->x_ldhdr.l_nsyms * sizeof (struct xcoff32_ldsym); + obj->x_ldsyms = calloc (1, size); + if (!obj->x_ldsyms) { + return false; + } + ret = r_buf_fread_at (obj->b, offset, (ut8 *)obj->x_ldsyms, "8cIS2c2I", obj->x_ldhdr.l_nsyms); + if (ret != size) { + R_LOG_DEBUG ("failed to read loader symbol table (%lu, %lu)", ret, size); + R_FREE (obj->x_ldsyms); + return false; + } + return true; +} + static bool r_bin_coff_init_symtable(RBinCoffObj *obj) { int ret, size; ut64 offset = obj->hdr.f_symptr; @@ -290,6 +398,15 @@ static bool r_bin_coff_init(RBinCoffObj *obj, RBuffer *buf, bool verbose) { R_LOG_WARN ("failed to init section VA table"); return false; } + } else { + if (!r_bin_xcoff_init_ldhdr (obj)) { + R_LOG_WARN ("failed to init xcoff loader header"); + return false; + } + if (!r_bin_xcoff_init_ldsyms (obj)) { + R_LOG_WARN ("failed to init xcoff loader symbol table"); + return false; + } } if (!r_bin_coff_init_symtable (obj)) { R_LOG_WARN ("failed to init symtable"); @@ -304,6 +421,7 @@ R_IPI void r_bin_coff_free(RBinCoffObj *obj) { ht_up_free (obj->imp_ht); free (obj->scn_va); free (obj->scn_hdrs); + free (obj->x_ldsyms); free (obj->symbols); r_buf_free (obj->b); free (obj); diff --git a/libr/bin/format/coff/coff.h b/libr/bin/format/coff/coff.h index a047bbd645..62a8a4b330 100644 --- a/libr/bin/format/coff/coff.h +++ b/libr/bin/format/coff/coff.h @@ -13,10 +13,14 @@ typedef struct r_bin_coff_obj { struct coff_hdr hdr; struct coff_opt_hdr opt_hdr; - struct xcoff32_opt_hdr xcoff_opt_hdr; struct coff_scn_hdr *scn_hdrs; struct coff_symbol *symbols; + /* XCOFF specific */ + struct xcoff32_opt_hdr x_opt_hdr; + struct xcoff32_ldhdr x_ldhdr; + struct xcoff32_ldsym *x_ldsyms; + ut16 target_id; /* TI COFF specific */ RBuffer *b; diff --git a/libr/bin/format/coff/coff_specs.h b/libr/bin/format/coff/coff_specs.h index 5522ac8030..7c2185b5c1 100644 --- a/libr/bin/format/coff/coff_specs.h +++ b/libr/bin/format/coff/coff_specs.h @@ -181,6 +181,16 @@ #define COFF_SYM_CLASS_WEAK_EXTERNAL 105 #define COFF_SYM_CLASS_CLR_TOKEN 107 +/* XCOFF32 loader */ +#define XCOFF_LDSYM_FLAGS(x) ((x)&0xF8) +#define XCOFF_LDSYM_FLAG_EXPORT 0x10 +#define XCOFF_LDSYM_FLAG_ENTRYPOINT 0x20 +#define XCOFF_LDSYM_FLAG_IMPORT 0x40 + +#define XCOFF_LDSYM_TYPE(x) ((x)&0x07) + +#define XCOFF_LDSYM_CLASS_FUNCTION 0x0a + /* XCOFF64 auxiliary entry type */ #define XCOFF_AUX_EXCEPT 255 #define XCOFF_AUX_FCN 254 @@ -460,7 +470,7 @@ struct xcoff64_ldhdr { /* XCOFF32 loader symbol */ R_PACKED ( struct xcoff32_ldsym { - ut8 l_name[8]; + char l_name[8]; ut32 l_value; ut16 l_scnum; ut8 l_smtype; diff --git a/libr/bin/p/bin_coff.c b/libr/bin/p/bin_coff.c index 7776531ce5..d0e7ce571a 100644 --- a/libr/bin/p/bin_coff.c +++ b/libr/bin/p/bin_coff.c @@ -138,6 +138,39 @@ static RBinImport *_fill_bin_import(struct r_bin_coff_obj *bin, int idx) { return ptr; } +static bool xcoff_is_imported_symbol(struct xcoff32_ldsym *s) { + return XCOFF_LDSYM_FLAGS (s->l_smtype) == XCOFF_LDSYM_FLAG_IMPORT; +} + +static RBinImport *_xcoff_fill_bin_import(struct r_bin_coff_obj *bin, int idx) { + RBinImport *ptr = R_NEW0 (RBinImport); + if (!ptr || idx < 0 || idx > bin->x_ldhdr.l_nsyms) { + free (ptr); + return NULL; + } + struct xcoff32_ldsym *s = &bin->x_ldsyms[idx]; + if (!xcoff_is_imported_symbol (s)) { + free (ptr); + return NULL; + } + if (strnlen (s->l_name, 8)) { + ptr->name = r_str_ndup (s->l_name, 8); + } + if (!ptr->name) { + free (ptr); + return NULL; + } + switch (s->l_smclas) { + case XCOFF_LDSYM_CLASS_FUNCTION: + ptr->type = R_BIN_TYPE_FUNC_STR; + break; + default: + ptr->type = R_BIN_TYPE_UNKNOWN_STR; + break; + } + return ptr; +} + static RList *entries(RBinFile *bf) { struct r_bin_coff_obj *obj = (struct r_bin_coff_obj*)bf->bo->bin_obj; RList *ret; @@ -199,6 +232,8 @@ static const char *xcoff_section_type_tostring(int i) { return "BSS"; case XCOFF_SCN_TYPE_EXCEPT: return "EXCEPT"; + case XCOFF_SCN_TYPE_LOADER: + return "LOADER"; } return NULL; } @@ -315,8 +350,17 @@ static RList *imports(RBinFile *bf) { if (!ret) { return NULL; } - if (obj->symbols) { - int ord = 0; + int ord = 0; + if (obj->x_ldsyms) { + for (i = 0; i < obj->x_ldhdr.l_nsyms; i++) { + RBinImport *ptr = _xcoff_fill_bin_import (obj, i); + if (ptr) { + ptr->ordinal = ord++; + r_list_append (ret, ptr); + ht_up_insert (obj->imp_ht, (ut64)i, ptr); + } + } + } else if (obj->symbols) { for (i = 0; i < obj->hdr.f_nsyms; i++) { RBinImport *ptr = _fill_bin_import (obj, i); if (ptr) { @@ -586,9 +630,34 @@ static RBinInfo *info(RBinFile *bf) { struct r_bin_coff_obj *obj = (struct r_bin_coff_obj*)bf->bo->bin_obj; ret->file = bf->file? strdup (bf->file): NULL; + /* XXX also set bclass or class to xcoff? */ ret->rclass = strdup ("coff"); ret->bclass = strdup ("coff"); - ret->type = strdup ("COFF (Executable file)"); + switch (obj->hdr.f_magic) { + case COFF_FILE_MACHINE_ALPHA: + case COFF_FILE_MACHINE_POWERPC: + case COFF_FILE_MACHINE_R4000: + ret->type = strdup ("COFF (Object file)"); + break; + case XCOFF32_FILE_MACHINE_U800WR: + case XCOFF32_FILE_MACHINE_U800RO: + case XCOFF32_FILE_MACHINE_U800TOC: + case XCOFF32_FILE_MACHINE_U802WR: + case XCOFF32_FILE_MACHINE_U802RO: + ret->type = strdup ("XCOFF32"); + break; + case XCOFF32_FILE_MACHINE_U802TOC: + ret->type = strdup ("XCOFF32 (Executable file, RO text, TOC)"); + break; + case XCOFF64_FILE_MACHINE_U803TOC: + case XCOFF64_FILE_MACHINE_U803XTOC: + case XCOFF64_FILE_MACHINE_U64: + ret->type = strdup ("XCOFF64"); + break; + default: + ret->type = strdup ("COFF (Executable file)"); + break; + } ret->os = strdup ("any"); ret->subsystem = strdup ("any"); ret->big_endian = obj->endian; diff --git a/test/db/formats/xcoff b/test/db/formats/xcoff index 80074c8d7d..e7ed8bb694 100644 --- a/test/db/formats/xcoff +++ b/test/db/formats/xcoff @@ -20,12 +20,12 @@ CMDS=iS EXPECT=<