Support XCOFF entrypoint and imports via loader ##bin

This commit is contained in:
Richard Patel 2023-11-30 10:48:52 +01:00 committed by pancake
parent 9996cfeb3d
commit 1a4d6e7fe3
5 changed files with 287 additions and 16 deletions

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -20,12 +20,12 @@ CMDS=iS
EXPECT=<<EOF
[Sections]
nth paddr size vaddr vsize perm type name
--------------------------------------------------------
0 0x00000128 0x3722 0x10000128 0x3722 -r-x TEXT .text-0
1 0x0000384a 0x4ba 0x2000084a 0x4ba -rw- DATA .data-1
2 0x00000000 0x1c 0x20000d04 0x1c -rw- BSS .bss-2
3 0x00003d04 0xc73 0x00000000 0xc73 ---- ---- .loader-3
nth paddr size vaddr vsize perm type name
----------------------------------------------------------
0 0x00000128 0x3722 0x10000128 0x3722 -r-x TEXT .text-0
1 0x0000384a 0x4ba 0x2000084a 0x4ba -rw- DATA .data-1
2 0x00000000 0x1c 0x20000d04 0x1c -rw- BSS .bss-2
3 0x00003d04 0xc73 0x00000000 0xc73 ---- LOADER .loader-3
EOF
RUN
@ -52,3 +52,73 @@ bl 0x100001f0
nop
EOF
RUN
NAME=xcoff entrypoint
FILE=bins/xcoff/file-xcoff-aix51
CMDS=ie
EXPECT=<<EOF
[Entrypoints]
vaddr=0x10000128 paddr=0x00000000 haddr=-1 type=program
1 entrypoints
EOF
RUN
NAME=xcoff imports
FILE=bins/xcoff/file-xcoff-aix51
CMDS=ii
EXPECT=<<EOF
[Imports]
nth vaddr bind type lib name
---------------------------------
0 ---------- NONE UNK errno
1 ---------- NONE UNK __mulh
2 ---------- NONE UNK _iob
3 ---------- NONE FUNC exit
4 ---------- NONE FUNC free
5 ---------- NONE FUNC strlen
6 ---------- NONE FUNC fopen64
7 ---------- NONE FUNC getenv
8 ---------- NONE FUNC atoi
9 ---------- NONE FUNC fprintf
10 ---------- NONE FUNC strchr
11 ---------- NONE FUNC printf
12 ---------- NONE FUNC fclose
13 ---------- NONE FUNC fflush
14 ---------- NONE FUNC __flsbuf
15 ---------- NONE FUNC vfprintf
16 ---------- NONE FUNC fwrite
17 ---------- NONE FUNC strncmp
18 ---------- NONE FUNC strrchr
19 ---------- NONE FUNC fputc
20 ---------- NONE FUNC rewind
21 ---------- NONE FUNC fputs
22 ---------- NONE FUNC strstr
23 ---------- NONE FUNC strerror
24 ---------- NONE FUNC wcwidth
25 ---------- NONE FUNC mbrtowc
26 ---------- NONE UNK __crt0v
27 ---------- NONE UNK optind
28 ---------- NONE FUNC getline
29 ---------- NONE UNK optarg
EOF
RUN
NAME=xcoff imports 2
FILE=bins/xcoff/gcc-ppc32-aix-dwarf2-exec
CMDS=ii
EXPECT=<<EOF
[Imports]
nth vaddr bind type lib name
---------------------------------
0 ---------- NONE UNK errno
1 ---------- NONE FUNC calloc
2 ---------- NONE FUNC exit
3 ---------- NONE FUNC __assert
4 ---------- NONE FUNC fflush
5 ---------- NONE FUNC puts
6 ---------- NONE UNK __crt0v
EOF
RUN