rizin/librz/bin/format/ecoff/ecoff.c
Giovanni d2494fac4a
librz/bin/{coff,ecoff}: fix various Coverity issues (#6023)
* Fix Coverity issues on ecoff
* Fix Alpha bitness
* Fix memleak in coff.c
* Fix tests
2026-03-13 00:44:59 +08:00

1962 lines
63 KiB
C

// SPDX-FileCopyrightText: 2025-2026 RizinOrg <info@rizin.re>
// SPDX-FileCopyrightText: 2025-2026 deroad <deroad@kumo.xn--q9jyb4c>
// SPDX-License-Identifier: LGPL-3.0-only
#include "ecoff.h"
#include "ecoff_gen.c"
#define ECOFF_SECTION_TYPE_DATA_MASK (ECOFF_SECTION_TYPE_DATA | \
ECOFF_SECTION_TYPE_BSS | \
ECOFF_SECTION_TYPE_RDATA | \
ECOFF_SECTION_TYPE_SDATA | \
ECOFF_SECTION_TYPE_SBSS)
static ut32 ecoff_section_flags_to_perms(ut64 s_flags);
static bool ecoff_is_data_section(const ut32 s_flags);
static bool ecoff_section_flags_to_structure(const ut32 flags, RzStructuredData *parent);
static const char *ecoff_file_descr_entry_get_lang(const ut16 lang);
static const char *ecoff_file_descr_entry_get_glevel(const ut16 glevel);
static const char *ecoff_local_symbol_get_type(const ut32 st);
static const char *ecoff_local_symbol_get_storage_class(const ut32 sc);
static bool ecoff_is_big_endian(const ut16 magic) {
switch (magic) {
case ECOFF_MACHINE_MIPS1_BE:
return true;
case ECOFF_MACHINE_MIPS2_BE:
return true;
case ECOFF_MACHINE_MIPS3_BE:
return true;
default:
return false;
}
}
static bool ecoff_is_little_endian(const ut16 magic) {
switch (magic) {
case ECOFF_MACHINE_MIPS1:
return true;
case ECOFF_MACHINE_MIPS1_EL:
return true;
case ECOFF_MACHINE_MIPS2_EL:
return true;
case ECOFF_MACHINE_MIPS3_EL:
return true;
case ECOFF_MACHINE_ALPHA:
return true;
case ECOFF_MACHINE_ALPHA_BSD:
return true;
default:
return false;
}
}
static inline bool ecoff_is_ecoff64(const ECoff *ecoff) {
return ecoff->type == ECOFF64;
}
static ut16 ecoff_machine(const ECoff *ecoff) {
if (ecoff_is_ecoff64(ecoff)) {
return ecoff->ecoff64.header.f_magic;
}
return ecoff->ecoff32.header.f_magic;
}
static inline bool ecoff_is_mips_magic(const ut16 magic) {
switch (magic) {
case ECOFF_MACHINE_MIPS1:
return true;
case ECOFF_MACHINE_MIPS1_EL:
return true;
case ECOFF_MACHINE_MIPS2_EL:
return true;
case ECOFF_MACHINE_MIPS3_EL:
return true;
case ECOFF_MACHINE_MIPS1_BE:
return true;
case ECOFF_MACHINE_MIPS2_BE:
return true;
case ECOFF_MACHINE_MIPS3_BE:
return true;
default:
return false;
}
}
static inline bool ecoff_is_alpha_magic(const ut16 magic) {
switch (magic) {
case ECOFF_MACHINE_ALPHA:
return true;
case ECOFF_MACHINE_ALPHA_BSD:
return true;
default:
return false;
}
}
ECOFF_GEN_FUNCTIONS(32);
ECOFF_GEN_FUNCTIONS(64);
void ecoff_free(ECoff *ecoff) {
if (!ecoff) {
return;
}
if (ecoff->type == ECOFF64) {
rz_vector_free(ecoff->ecoff64.sections);
rz_vector_free(ecoff->ecoff64.file_descs);
rz_vector_free(ecoff->ecoff64.proc_descs);
rz_vector_free(ecoff->ecoff64.local_symbols);
rz_vector_free(ecoff->ecoff64.extern_symbols);
} else {
rz_vector_free(ecoff->ecoff32.sections);
rz_vector_free(ecoff->ecoff32.file_descs);
rz_vector_free(ecoff->ecoff32.proc_descs);
rz_vector_free(ecoff->ecoff32.local_symbols);
rz_vector_free(ecoff->ecoff32.extern_symbols);
rz_vector_free(ecoff->ecoff32.symbols_old);
}
free(ecoff);
}
static bool ecoff_parse_magic(RzBuffer *buffer, ut16 *magic, bool *big_endian) {
if (!rz_buf_read_be16_at(buffer, 0, magic)) {
return false;
} else if (ecoff_is_big_endian(*magic)) {
*big_endian = true;
return true;
}
if (!rz_buf_read_le16_at(buffer, 0, magic)) {
return false;
} else if (ecoff_is_little_endian(*magic)) {
*big_endian = false;
return true;
}
return false;
}
bool ecoff_is_valid_buffer(RzBuffer *buffer) {
ut16 magic = 0;
bool big_endian = false;
return ecoff_parse_magic(buffer, &magic, &big_endian);
}
static bool ecoff_init_aouthdr_alpha(RzBuffer *b, ut64 *offset, ECoff_AOutHdr_Alpha *alpha, const bool big_endian) {
return rz_buf_read_ble16_offset(b, offset, &alpha->magic, big_endian) &&
rz_buf_read_ble16_offset(b, offset, &alpha->vstamp, big_endian) &&
rz_buf_read_ble16_offset(b, offset, &alpha->bldrev, big_endian) &&
rz_buf_read_ble16_offset(b, offset, &alpha->padding, big_endian) &&
rz_buf_read_ble64_offset(b, offset, &alpha->tsize, big_endian) &&
rz_buf_read_ble64_offset(b, offset, &alpha->dsize, big_endian) &&
rz_buf_read_ble64_offset(b, offset, &alpha->bsize, big_endian) &&
rz_buf_read_ble64_offset(b, offset, &alpha->entry, big_endian) &&
rz_buf_read_ble64_offset(b, offset, &alpha->text_start, big_endian) &&
rz_buf_read_ble64_offset(b, offset, &alpha->data_start, big_endian) &&
rz_buf_read_ble64_offset(b, offset, &alpha->bss_start, big_endian) &&
rz_buf_read_ble32_offset(b, offset, &alpha->gpr_mask, big_endian) &&
rz_buf_read_ble32_offset(b, offset, &alpha->fpr_mask, big_endian) &&
rz_buf_read_ble64_offset(b, offset, &alpha->gp_value, big_endian);
}
static bool ecoff_init_aouthdr_mips(RzBuffer *b, ut64 *offset, ECoff_AOutHdr_Mips *mips, const bool big_endian) {
return rz_buf_read_ble16_offset(b, offset, &mips->magic, big_endian) &&
rz_buf_read_ble16_offset(b, offset, &mips->vstamp, big_endian) &&
rz_buf_read_ble32_offset(b, offset, &mips->tsize, big_endian) &&
rz_buf_read_ble32_offset(b, offset, &mips->dsize, big_endian) &&
rz_buf_read_ble32_offset(b, offset, &mips->bsize, big_endian) &&
rz_buf_read_ble32_offset(b, offset, &mips->entry, big_endian) &&
rz_buf_read_ble32_offset(b, offset, &mips->text_start, big_endian) &&
rz_buf_read_ble32_offset(b, offset, &mips->data_start, big_endian) &&
rz_buf_read_ble32_offset(b, offset, &mips->bss_start, big_endian) &&
rz_buf_read_ble32_offset(b, offset, &mips->gpr_mask, big_endian) &&
rz_buf_read_ble32_offset(b, offset, &mips->cpr_mask[0], big_endian) &&
rz_buf_read_ble32_offset(b, offset, &mips->cpr_mask[1], big_endian) &&
rz_buf_read_ble32_offset(b, offset, &mips->cpr_mask[2], big_endian) &&
rz_buf_read_ble32_offset(b, offset, &mips->cpr_mask[3], big_endian) &&
rz_buf_read_ble32_offset(b, offset, &mips->gp_value, big_endian);
}
static bool ecoff_init_symbol_old(RzBuffer *b, ut64 *offset, ECoff_Symbol_Old *symbol, const bool big_endian) {
return rz_buf_read_offset(b, offset, (ut8 *)symbol->e_name, sizeof(symbol->e_name)) &&
rz_buf_read_ble32_offset(b, offset, &symbol->e_value, big_endian) &&
rz_buf_read_ble16_offset(b, offset, (ut16 *)&symbol->e_scnum, big_endian) &&
rz_buf_read_ble16_offset(b, offset, &symbol->e_type, big_endian) &&
rz_buf_read_ble8_offset(b, offset, (ut8 *)&symbol->e_sclass, big_endian) &&
rz_buf_read_ble8_offset(b, offset, &symbol->e_numaux, big_endian);
}
static void ecoff_symbol_old_fini(void *element, void *x) {
ECoff_Symbol_Old *symbol = element;
free(symbol->resolved_name);
}
static bool ecoff_parse_old_symbols(RzBuffer *b, ECoff_32 *ecoff) {
ut64 offset = ecoff->header.f_symptr;
const size_t count = ecoff->header.f_nsyms;
for (size_t i = 0; i < count; ++i) {
ut64 location = offset;
ECoff_Symbol_Old symbol = { 0 };
if (!ecoff_init_symbol_old(b, &offset, &symbol, ecoff->big_endian)) {
return false;
}
symbol.resolved_name = ecoff_resolve_name_32(b, ecoff, symbol.e_name);
if (!symbol.resolved_name) {
symbol.resolved_name = rz_str_newf("unknown_%" PFMT64x, location);
}
rz_vector_push(ecoff->symbols_old, &symbol);
}
return true;
}
static bool ecoff_parse_symbols_64(RzBuffer *buffer, ECoff_64 *ecoff) {
if (ecoff->header.f_symptr < 0) {
return false;
} else if (!ecoff->header.f_symptr) {
// there are no symbols so we do not fail.
return true;
}
ut64 offset = ecoff->header.f_symptr;
return ecoff_init_symbolic_header_64(buffer, &offset, &ecoff->symhdr, ecoff->big_endian) &&
ecoff_has_symbolic_header_64(ecoff) &&
ecoff_parse_local_symbols_64(buffer, ecoff) &&
ecoff_parse_external_symbols_64(buffer, ecoff) &&
ecoff_parse_file_descriptor_entries_64(buffer, ecoff) &&
ecoff_parse_proc_descriptor_entries_64(buffer, ecoff) &&
ecoff_resolve_symbols_64(buffer, ecoff);
}
static bool ecoff_init_aouthdr_64(RzBuffer *buffer, ut64 *offset, ECoff_64 *ecoff) {
if (!ecoff->header.f_opthdr) {
RZ_LOG_ERROR("ecoff: f_opthdr in ecoff64 header is 0\n");
return false;
} else if (ecoff_is_alpha_magic(ecoff->header.f_magic)) {
return ecoff_init_aouthdr_alpha(buffer, offset, &ecoff->aouthdr.alpha, ecoff->big_endian);
}
RZ_LOG_ERROR("ecoff: unsupported aouthdr for ecoff64\n");
return false;
}
static bool ecoff_parse_ecoff_64(RzBuffer *buffer, ECoff_64 *ecoff, const bool big_endian) {
ut64 offset = 0;
ecoff->big_endian = big_endian;
ecoff->sections = rz_vector_new(sizeof(ECoff_Section_64), ecoff_section_fini_64, NULL);
if (!ecoff->sections) {
RZ_LOG_ERROR("ecoff: failed to allocate sections vector\n");
return false;
}
ecoff->file_descs = rz_vector_new(sizeof(ECoff_FileDescEntry_64), NULL, NULL);
if (!ecoff->file_descs) {
RZ_LOG_ERROR("ecoff: failed to allocate fde vector\n");
return false;
}
ecoff->proc_descs = rz_vector_new(sizeof(ECoff_ProcDescEntry_64), NULL, NULL);
if (!ecoff->proc_descs) {
RZ_LOG_ERROR("ecoff: failed to allocate pde vector\n");
return false;
}
ecoff->local_symbols = rz_vector_new(sizeof(ECoff_LocalSymbol_64), ecoff_fini_local_symbol_64, NULL);
if (!ecoff->local_symbols) {
RZ_LOG_ERROR("ecoff: failed to allocate local symbols vector\n");
return false;
}
ecoff->extern_symbols = rz_vector_new(sizeof(ECoff_ExternSymbol_64), ecoff_fini_external_symbol_64, NULL);
if (!ecoff->extern_symbols) {
RZ_LOG_ERROR("ecoff: failed to allocate external symbols vector\n");
return false;
}
if (!ecoff_init_hdr_64(buffer, &offset, &ecoff->header, ecoff->big_endian)) {
RZ_LOG_ERROR("ecoff: failed to read ecoff64 header\n");
return false;
} else if (!ecoff_init_aouthdr_64(buffer, &offset, ecoff)) {
RZ_LOG_ERROR("ecoff: failed to read ecoff64 aouthdr\n");
return false;
// first we parse the symbols, then the sections because we depend on the magic
} else if (!ecoff_parse_symbols_64(buffer, ecoff)) {
RZ_LOG_ERROR("ecoff: failed to parse ecoff64 symbols\n");
return false;
} else if (!ecoff_parse_sections_64(buffer, &offset, ecoff)) {
RZ_LOG_ERROR("ecoff: failed to read ecoff32 section table\n");
return false;
}
return true;
}
static bool ecoff_parse_symbols_32(RzBuffer *buffer, ECoff_32 *ecoff) {
if (ecoff->header.f_symptr < 0) {
return false;
} else if (!ecoff->header.f_symptr) {
// there are no symbols so we do not fail.
return true;
}
ut64 offset = ecoff->header.f_symptr;
if (!ecoff_init_symbolic_header_32(buffer, &offset, &ecoff->symhdr, ecoff->big_endian)) {
return false;
}
if (!ecoff_has_symbolic_header_32(ecoff)) {
// there is no symbolic header, so it must be using the old format
return ecoff_parse_old_symbols(buffer, ecoff);
}
return ecoff_parse_local_symbols_32(buffer, ecoff) &&
ecoff_parse_external_symbols_32(buffer, ecoff) &&
ecoff_parse_file_descriptor_entries_32(buffer, ecoff) &&
ecoff_parse_proc_descriptor_entries_32(buffer, ecoff) &&
ecoff_resolve_symbols_32(buffer, ecoff);
}
static bool ecoff_init_aouthdr_32(RzBuffer *buffer, ut64 *offset, ECoff_32 *ecoff) {
if (!ecoff->header.f_opthdr) {
RZ_LOG_ERROR("ecoff: f_opthdr in ecoff32 header is 0\n");
return false;
} else if (ecoff_is_mips_magic(ecoff->header.f_magic)) {
return ecoff_init_aouthdr_mips(buffer, offset, &ecoff->aouthdr.mips, ecoff->big_endian);
}
RZ_LOG_ERROR("ecoff: unsupported aouthdr for ecoff32\n");
return false;
}
static bool ecoff_parse_ecoff_32(RzBuffer *buffer, ECoff_32 *ecoff, const bool big_endian) {
ut64 offset = 0;
ecoff->big_endian = big_endian;
ecoff->sections = rz_vector_new(sizeof(ECoff_Section_32), ecoff_section_fini_32, NULL);
if (!ecoff->sections) {
RZ_LOG_ERROR("ecoff: failed to allocate sections vector\n");
return false;
}
ecoff->file_descs = rz_vector_new(sizeof(ECoff_FileDescEntry_32), NULL, NULL);
if (!ecoff->file_descs) {
RZ_LOG_ERROR("ecoff: failed to allocate fde vector\n");
return false;
}
ecoff->proc_descs = rz_vector_new(sizeof(ECoff_ProcDescEntry_32), NULL, NULL);
if (!ecoff->proc_descs) {
RZ_LOG_ERROR("ecoff: failed to allocate pde vector\n");
return false;
}
ecoff->local_symbols = rz_vector_new(sizeof(ECoff_LocalSymbol_32), ecoff_fini_local_symbol_32, NULL);
if (!ecoff->local_symbols) {
RZ_LOG_ERROR("ecoff: failed to allocate local symbols vector\n");
return false;
}
ecoff->extern_symbols = rz_vector_new(sizeof(ECoff_ExternSymbol_32), ecoff_fini_external_symbol_32, NULL);
if (!ecoff->extern_symbols) {
RZ_LOG_ERROR("ecoff: failed to allocate external symbols vector\n");
return false;
}
ecoff->symbols_old = rz_vector_new(sizeof(ECoff_Symbol_Old), ecoff_symbol_old_fini, NULL);
if (!ecoff->symbols_old) {
RZ_LOG_ERROR("ecoff: failed to allocate symbols (old format)\n");
return false;
}
if (!ecoff_init_hdr_32(buffer, &offset, &ecoff->header, ecoff->big_endian)) {
RZ_LOG_ERROR("ecoff: failed to read ecoff32 header\n");
return false;
} else if (!ecoff_init_aouthdr_32(buffer, &offset, ecoff)) {
RZ_LOG_ERROR("ecoff: failed to read ecoff32 aouthdr\n");
return false;
// first we parse the symbols, then the sections because we depend on the magic
} else if (!ecoff_parse_symbols_32(buffer, ecoff)) {
RZ_LOG_ERROR("ecoff: failed to parse ecoff32 symbols\n");
return false;
} else if (!ecoff_parse_sections_32(buffer, &offset, ecoff)) {
RZ_LOG_ERROR("ecoff: failed to read ecoff32 section table\n");
return false;
}
return true;
}
ECoff *ecoff_parse_from_buffer(RzBuffer *buffer) {
ut16 f_magic = 0;
bool big_endian = false;
ECoff *ecoff = RZ_NEW0(ECoff);
if (!ecoff) {
return NULL;
} else if (!ecoff_parse_magic(buffer, &f_magic, &big_endian)) {
RZ_LOG_ERROR("ecoff: is not an ecoff file\n");
goto fail;
}
if (ecoff_is_alpha_magic(f_magic)) {
ecoff->type = ECOFF64;
if (!ecoff_parse_ecoff_64(buffer, &ecoff->ecoff64, big_endian)) {
goto fail;
}
} else if (ecoff_is_mips_magic(f_magic)) {
ecoff->type = ECOFF32;
if (!ecoff_parse_ecoff_32(buffer, &ecoff->ecoff32, big_endian)) {
goto fail;
}
} else {
goto fail;
}
return ecoff;
fail:
ecoff_free(ecoff);
return NULL;
}
static ut32 ecoff_section_flags_to_perms(ut64 s_flags) {
ut32 perms = 0;
if (s_flags == ECOFF_SECTION_TYPE_REG) {
// Regular section: allocated, relocated, loaded.
return RZ_PERM_RWX;
}
if (s_flags & ECOFF_SECTION_TYPE_TEXT) {
// Text section
perms |= RZ_PERM_RW;
}
if (s_flags & ECOFF_SECTION_TYPE_DATA) {
// Data section
perms |= RZ_PERM_RW;
}
if (s_flags & ECOFF_SECTION_TYPE_BSS) {
// Bss section
perms |= RZ_PERM_RW;
}
if (s_flags & ECOFF_SECTION_TYPE_RDATA) {
// Read-only data section
perms |= RZ_PERM_R;
}
if (s_flags & ECOFF_SECTION_TYPE_SDATA) {
// Small data
perms |= RZ_PERM_RW;
}
if (s_flags & ECOFF_SECTION_TYPE_SBSS) {
// Small bss
perms |= RZ_PERM_RW;
}
if (s_flags & ECOFF_SECTION_TYPE_UCODE) {
// U-Code
perms |= RZ_PERM_RX;
}
if (s_flags & ECOFF_SECTION_TYPE_GOT1) {
// Global offset table
perms |= RZ_PERM_R;
}
if (s_flags & ECOFF_SECTION_TYPE_DYNAMIC1) {
// Dynamic linking information
perms |= RZ_PERM_R;
}
if (s_flags & ECOFF_SECTION_TYPE_DYNSYM1) {
// Dynamic linking symbol table
perms |= RZ_PERM_R;
}
if (s_flags & ECOFF_SECTION_TYPE_REL_DYN1) {
// Dynamic relocation information
perms |= RZ_PERM_R;
}
if (s_flags & ECOFF_SECTION_TYPE_DYNSTR1) {
// Dynamic linking symbol table
perms |= RZ_PERM_R;
}
if (s_flags & ECOFF_SECTION_TYPE_HASH1) {
// Dynamic symbol hash table
perms |= RZ_PERM_R;
}
if (s_flags & ECOFF_SECTION_TYPE_DSOLIST1) {
// Shared library dependency list
perms |= RZ_PERM_R | RZ_PERM_SHAR;
}
if (s_flags & ECOFF_SECTION_TYPE_MSYM1) {
// Additional dynamic linking symbol table
perms |= RZ_PERM_R;
}
if (s_flags & ECOFF_SECTION_TYPE_LIT4) {
// 4-byte literals
perms |= RZ_PERM_R;
}
if (s_flags & ECOFF_SECTION_TYPE_NRELOC_OVFL2) {
// Indicates that section header field s_nreloc overflowed
perms |= RZ_PERM_R;
}
if (s_flags & ECOFF_SECTION_TYPE_LIB) {
// Shared Library
perms |= RZ_PERM_RX | RZ_PERM_SHAR;
}
if (s_flags & ECOFF_SECTION_TYPE_INIT) {
// Initialization text
perms |= RZ_PERM_RX;
}
ut32 extmask = s_flags & ECOFF_SECTION_EXT_TYPE_MASK;
if (extmask == ECOFF_SECTION_EXT_TYPE_CONFLICT1) {
// Additional dynamic linking information
perms |= RZ_PERM_R;
} else if (extmask == ECOFF_SECTION_EXT_TYPE_RESOURCE) {
// Resource
perms |= RZ_PERM_R;
} else if (extmask == ECOFF_SECTION_EXT_TYPE_FINI) {
// Termination text
perms |= RZ_PERM_RX;
} else if (extmask == ECOFF_SECTION_EXT_TYPE_RCONST) {
// Read-only constants
perms |= RZ_PERM_R;
} else if (extmask == ECOFF_SECTION_EXT_TYPE_XDATA) {
// Exception scope table
perms |= RZ_PERM_R;
} else if (extmask == ECOFF_SECTION_EXT_TYPE_TLSDATA) {
// Initialized TLS data
perms |= RZ_PERM_RW;
} else if (extmask == ECOFF_SECTION_EXT_TYPE_TLSBSS) {
// Uninitialized TLS data
perms |= RZ_PERM_RW;
} else if (extmask == ECOFF_SECTION_EXT_TYPE_TLSINIT) {
// Initialization for TLS data
perms |= RZ_PERM_R;
} else if (extmask == ECOFF_SECTION_EXT_TYPE_PDATA) {
// Exception procedure table
perms |= RZ_PERM_R;
} else if (extmask == ECOFF_SECTION_EXT_TYPE_LITA) {
// Address literals
perms |= RZ_PERM_R;
} else if (extmask == ECOFF_SECTION_EXT_TYPE_LIT8) {
// 8-byte literals
perms |= RZ_PERM_R;
}
return perms;
}
static bool ecoff_is_data_section(const ut32 s_flags) {
const ut32 extflag = s_flags & ECOFF_SECTION_EXT_TYPE_MASK;
return (s_flags & ECOFF_SECTION_TYPE_DATA_MASK) ||
extflag == ECOFF_SECTION_EXT_TYPE_RESOURCE ||
extflag == ECOFF_SECTION_EXT_TYPE_RCONST ||
extflag == ECOFF_SECTION_EXT_TYPE_XDATA ||
extflag == ECOFF_SECTION_EXT_TYPE_TLSDATA ||
extflag == ECOFF_SECTION_EXT_TYPE_TLSBSS ||
extflag == ECOFF_SECTION_EXT_TYPE_PDATA;
}
static RzBinAddr *ecoff_get_entrypoint(const ECoff *ecoff) {
ut64 vaddr = 0;
const ut16 magic = ecoff_machine(ecoff);
if (ecoff_is_alpha_magic(magic)) {
vaddr = ecoff->ecoff64.aouthdr.alpha.entry;
} else if (ecoff_is_mips_magic(magic)) {
vaddr = ecoff->ecoff32.aouthdr.mips.entry;
}
if (!vaddr) {
// entry is invalid.
return NULL;
}
RzBinAddr *baddr = RZ_NEW0(RzBinAddr);
if (!baddr) {
return NULL;
}
baddr->type = RZ_BIN_ENTRY_TYPE_INIT;
baddr->paddr = UT64_MAX;
baddr->vaddr = vaddr;
if (ecoff_is_ecoff64(ecoff)) {
ecoff_find_paddr_from_vaddr_64(&ecoff->ecoff64, baddr->vaddr, &baddr->paddr);
} else {
ecoff_find_paddr_from_vaddr_32(&ecoff->ecoff32, baddr->vaddr, &baddr->paddr);
}
return baddr;
}
static bool ecoff_local_symbol_is_function(const ut32 st) {
return st == ECOFF_LOCAL_SYM_ST_PROC || st == ECOFF_LOCAL_SYM_ST_STATICPROC;
}
static RzBinAddr *ecoff_get_main_symbols_64(const ECoff_64 *ecoff) {
ut64 vaddr = 0;
const ECoff_LocalSymbol_64 *lsym = NULL;
rz_vector_foreach (ecoff->local_symbols, lsym) {
if (!ecoff_local_symbol_is_function(lsym->sc)) {
continue;
} else if (lsym->resolved_name && RZ_STR_EQ(lsym->resolved_name, "main")) {
vaddr = lsym->value;
break;
}
}
if (!vaddr) {
// main pointer is invalid.
return NULL;
}
RzBinAddr *baddr = RZ_NEW0(RzBinAddr);
if (!baddr) {
return NULL;
}
baddr->type = RZ_BIN_ENTRY_TYPE_MAIN;
baddr->paddr = UT64_MAX;
baddr->vaddr = vaddr;
ecoff_find_paddr_from_vaddr_64(ecoff, baddr->vaddr, &baddr->paddr);
return baddr;
}
static RzBinAddr *ecoff_get_main_old_symbols(const ECoff *ecoff) {
ut64 vaddr = 0;
const ECoff_Symbol_Old *esym;
rz_vector_foreach (ecoff->ecoff32.symbols_old, esym) {
if (esym->resolved_name && RZ_STR_EQ(esym->resolved_name, "main")) {
vaddr = esym->e_value;
break;
}
}
if (!vaddr) {
// main pointer is invalid.
return NULL;
}
RzBinAddr *baddr = RZ_NEW0(RzBinAddr);
if (!baddr) {
return NULL;
}
baddr->type = RZ_BIN_ENTRY_TYPE_MAIN;
baddr->paddr = UT64_MAX;
baddr->vaddr = vaddr;
if (ecoff_is_ecoff64(ecoff)) {
ecoff_find_paddr_from_vaddr_64(&ecoff->ecoff64, baddr->vaddr, &baddr->paddr);
} else {
ecoff_find_paddr_from_vaddr_32(&ecoff->ecoff32, baddr->vaddr, &baddr->paddr);
}
return baddr;
}
static RzBinAddr *ecoff_get_main_symbols_32(const ECoff_32 *ecoff) {
ut64 vaddr = 0;
const ECoff_LocalSymbol_32 *lsym = NULL;
rz_vector_foreach (ecoff->local_symbols, lsym) {
if (!ecoff_local_symbol_is_function(lsym->sc)) {
continue;
} else if (lsym->resolved_name && RZ_STR_EQ(lsym->resolved_name, "main")) {
vaddr = lsym->value;
break;
}
}
if (!vaddr) {
// main pointer is invalid.
return NULL;
}
RzBinAddr *baddr = RZ_NEW0(RzBinAddr);
if (!baddr) {
return NULL;
}
baddr->type = RZ_BIN_ENTRY_TYPE_MAIN;
baddr->paddr = UT64_MAX;
baddr->vaddr = vaddr;
ecoff_find_paddr_from_vaddr_32(ecoff, baddr->vaddr, &baddr->paddr);
return baddr;
}
static RzBinAddr *ecoff_get_main(const ECoff *ecoff) {
if (ecoff_is_ecoff64(ecoff)) {
if (!ecoff_has_symbolic_header_64(&ecoff->ecoff64)) {
return NULL;
}
return ecoff_get_main_symbols_64(&ecoff->ecoff64);
} else if (ecoff_has_symbolic_header_32(&ecoff->ecoff32)) {
return ecoff_get_main_symbols_32(&ecoff->ecoff32);
}
return ecoff_get_main_old_symbols(ecoff);
}
RzPVector /*<RzBinAddr *>*/ *ecoff_get_entries(const ECoff *ecoff) {
RzPVector *ret = rz_pvector_new((RzPVectorFree)free);
if (!ret) {
return NULL;
}
RzBinAddr *baddr = ecoff_get_entrypoint(ecoff);
if (baddr) {
rz_pvector_push(ret, baddr);
}
baddr = ecoff_get_main(ecoff);
if (baddr) {
rz_pvector_push(ret, baddr);
}
return ret;
}
static bool ecoff_symbol_old_is_function(const ECoff_Symbol_Old *esym) {
ut16 derived_type = (esym->e_type & ECOFF_SYMBOL_OLD_DERIVED_TYPE_MASK) >> 4;
if (!derived_type) {
return true;
}
return derived_type == ECOFF_SYMBOL_OLD_DERIVED_TYPE_FCN;
}
static bool ecoff_symbol_old_is_imported(const ECoff_Symbol_Old *esym) {
return esym->e_scnum == ECOFF_SYMBOL_SECT_NUM_UNDEF &&
esym->e_sclass == ECOFF_SYMBOL_OLD_SC_EFCN;
}
static bool ecoff_symbol_old_has_vaddr(const ECoff_Symbol_Old *esym) {
ut16 derived_type = (esym->e_type & ECOFF_SYMBOL_OLD_DERIVED_TYPE_MASK) >> 4;
if (!derived_type) {
return true;
}
return derived_type == ECOFF_SYMBOL_OLD_DERIVED_TYPE_PTR ||
derived_type == ECOFF_SYMBOL_OLD_DERIVED_TYPE_FCN;
}
static ut32 ecoff_symbol_old_type_to_size(const ECoff_Symbol_Old *esym) {
switch (esym->e_type & ECOFF_SYMBOL_OLD_BASE_TYPE_MASK) {
default: return 0;
case ECOFF_SYMBOL_OLD_BASE_TYPE_CHAR: return 1;
case ECOFF_SYMBOL_OLD_BASE_TYPE_SHORT: return 2;
case ECOFF_SYMBOL_OLD_BASE_TYPE_INT: return 4;
case ECOFF_SYMBOL_OLD_BASE_TYPE_LONG: return 8;
case ECOFF_SYMBOL_OLD_BASE_TYPE_FLOAT: return 4;
case ECOFF_SYMBOL_OLD_BASE_TYPE_DOUBLE: return 8;
case ECOFF_SYMBOL_OLD_BASE_TYPE_ENUM: return 4;
case ECOFF_SYMBOL_OLD_BASE_TYPE_UCHAR: return 1;
case ECOFF_SYMBOL_OLD_BASE_TYPE_USHORT: return 2;
case ECOFF_SYMBOL_OLD_BASE_TYPE_UINT: return 4;
case ECOFF_SYMBOL_OLD_BASE_TYPE_ULONG: return 8;
}
}
static const char *ecoff_symbol_old_type_to_bin_symbol_type(const ECoff_Symbol_Old *esym) {
ut16 derived_type = (esym->e_type & ECOFF_SYMBOL_OLD_DERIVED_TYPE_MASK) >> 4;
switch (derived_type) {
default: return NULL;
case ECOFF_SYMBOL_OLD_DERIVED_TYPE_PTR: return RZ_BIN_TYPE_OBJECT_STR;
case ECOFF_SYMBOL_OLD_DERIVED_TYPE_FCN: return RZ_BIN_TYPE_FUNC_STR;
case ECOFF_SYMBOL_OLD_DERIVED_TYPE_ARY: return RZ_BIN_TYPE_STATIC_STR;
}
}
static RzBinSymbol *ecoff_symbol_old_to_bin_symbol(const ECoff_32 *ecoff, const ECoff_Symbol_Old *esym) {
RzBinSymbol *bsym = RZ_NEW0(RzBinSymbol);
if (!bsym) {
return NULL;
}
bsym->type = ecoff_symbol_old_type_to_bin_symbol_type(esym);
bsym->size = ecoff_symbol_old_type_to_size(esym);
bsym->name = rz_str_dup(esym->resolved_name);
bsym->forwarder = "NONE";
bsym->is_imported = ecoff_symbol_old_is_imported(esym);
if (bsym->is_imported) {
bsym->bind = RZ_BIN_BIND_IMPORT_STR;
} else if (ecoff_symbol_old_is_function(esym)) {
bsym->bind = RZ_BIN_BIND_GLOBAL_STR;
} else {
bsym->bind = RZ_BIN_BIND_LOCAL_STR;
}
bsym->paddr = UT64_MAX;
bsym->vaddr = UT64_MAX;
if (esym->e_value && ecoff_symbol_old_has_vaddr(esym)) {
bsym->vaddr = esym->e_value;
ecoff_find_paddr_from_vaddr_32(ecoff, bsym->vaddr, &bsym->paddr);
}
return bsym;
}
// this is a special symbol that is used for analysis.
// the analysis step will use `loc._gp` to know how to
// resolve values, pointers and functions.
static RzBinSymbol *ecoff_gp_to_bin_symbol(const ECoff *ecoff) {
ut64 vaddr = 0;
const ut16 magic = ecoff_machine(ecoff);
if (ecoff_is_alpha_magic(magic)) {
vaddr = ecoff->ecoff64.aouthdr.alpha.gp_value;
} else if (ecoff_is_mips_magic(magic)) {
vaddr = ecoff->ecoff32.aouthdr.mips.gp_value;
}
if (!vaddr) {
return NULL;
}
RzBinSymbol *bsym = RZ_NEW0(RzBinSymbol);
if (!bsym) {
return NULL;
}
bsym->name = rz_str_dup("_gp");
bsym->forwarder = "NONE";
bsym->bind = RZ_BIN_BIND_LOCAL_STR;
bsym->type = RZ_BIN_TYPE_NOTYPE_STR;
bsym->paddr = UT64_MAX;
bsym->vaddr = vaddr;
if (ecoff_is_ecoff64(ecoff)) {
ecoff_find_paddr_from_vaddr_64(&ecoff->ecoff64, bsym->vaddr, &bsym->paddr);
} else {
ecoff_find_paddr_from_vaddr_32(&ecoff->ecoff32, bsym->vaddr, &bsym->paddr);
}
return bsym;
}
static const char *ecoff_local_symbol_type_to_bin_symbol_type(const ut32 st) {
switch (st) {
default: return RZ_BIN_TYPE_NOTYPE_STR;
case ECOFF_LOCAL_SYM_ST_GLOBAL: return RZ_BIN_TYPE_STATIC_STR;
case ECOFF_LOCAL_SYM_ST_STATIC: return RZ_BIN_TYPE_STATIC_STR;
case ECOFF_LOCAL_SYM_ST_PARAM: return RZ_BIN_TYPE_FIELD_STR;
case ECOFF_LOCAL_SYM_ST_LOCAL: return RZ_BIN_TYPE_FIELD_STR;
case ECOFF_LOCAL_SYM_ST_PROC: return RZ_BIN_TYPE_FUNC_STR;
case ECOFF_LOCAL_SYM_ST_BLOCK: return RZ_BIN_TYPE_OBJECT_STR;
case ECOFF_LOCAL_SYM_ST_MEMBER: return RZ_BIN_TYPE_OBJECT_STR;
case ECOFF_LOCAL_SYM_ST_FILE: return RZ_BIN_TYPE_FILE_STR;
case ECOFF_LOCAL_SYM_ST_STATICPROC: return RZ_BIN_TYPE_FUNC_STR;
case ECOFF_LOCAL_SYM_ST_CONSTANT: return RZ_BIN_TYPE_STATIC_STR;
case ECOFF_LOCAL_SYM_ST_STAPARAM: return RZ_BIN_TYPE_FIELD_STR;
case ECOFF_LOCAL_SYM_ST_BASE: return RZ_BIN_TYPE_OBJECT_STR;
case ECOFF_LOCAL_SYM_ST_VIRTBASE: return RZ_BIN_TYPE_IFACE_STR;
case ECOFF_LOCAL_SYM_ST_TAG: return RZ_BIN_TYPE_OBJECT_STR;
case ECOFF_LOCAL_SYM_ST_NAMESPACE: return RZ_BIN_TYPE_OBJECT_STR;
case ECOFF_LOCAL_SYM_ST_STRUCT: return RZ_BIN_TYPE_OBJECT_STR;
case ECOFF_LOCAL_SYM_ST_UNION: return RZ_BIN_TYPE_OBJECT_STR;
case ECOFF_LOCAL_SYM_ST_ENUM: return RZ_BIN_TYPE_OBJECT_STR;
case ECOFF_LOCAL_SYM_ST_STR: return RZ_BIN_TYPE_STATIC_STR;
case ECOFF_LOCAL_SYM_ST_NUMBER: return RZ_BIN_TYPE_NUM_STR;
case ECOFF_LOCAL_SYM_ST_EXPR: return RZ_BIN_TYPE_OBJECT_STR;
}
}
static bool ecoff_local_symbol_value_is_size(const ut32 st, const ut32 sc) {
return (st == ECOFF_LOCAL_SYM_ST_GLOBAL && sc == ECOFF_LOCAL_SYM_SC_COMMON) ||
(st == ECOFF_LOCAL_SYM_ST_GLOBAL && sc == ECOFF_LOCAL_SYM_SC_SCOMMON) ||
(st == ECOFF_LOCAL_SYM_ST_GLOBAL && sc == ECOFF_LOCAL_SYM_SC_TLS_COMMON) ||
(st == ECOFF_LOCAL_SYM_ST_BLOCK && sc == ECOFF_LOCAL_SYM_SC_INFO) ||
(st == ECOFF_LOCAL_SYM_ST_BLOCK && sc == ECOFF_LOCAL_SYM_SC_COMMON) ||
(st == ECOFF_LOCAL_SYM_ST_END && sc == ECOFF_LOCAL_SYM_SC_TEXT);
}
static bool ecoff_local_symbol_value_is_address(const ut32 st, const ut32 sc) {
return (st == ECOFF_LOCAL_SYM_ST_GLOBAL && sc == ECOFF_LOCAL_SYM_SC_SDATA) ||
(st == ECOFF_LOCAL_SYM_ST_GLOBAL && sc == ECOFF_LOCAL_SYM_SC_DATA) ||
(st == ECOFF_LOCAL_SYM_ST_GLOBAL && sc == ECOFF_LOCAL_SYM_SC_SBSS) ||
(st == ECOFF_LOCAL_SYM_ST_GLOBAL && sc == ECOFF_LOCAL_SYM_SC_BSS) ||
(st == ECOFF_LOCAL_SYM_ST_GLOBAL && sc == ECOFF_LOCAL_SYM_SC_RDATA) ||
(st == ECOFF_LOCAL_SYM_ST_GLOBAL && sc == ECOFF_LOCAL_SYM_SC_RCONST) ||
(st == ECOFF_LOCAL_SYM_ST_STATIC && sc == ECOFF_LOCAL_SYM_SC_SDATA) ||
(st == ECOFF_LOCAL_SYM_ST_STATIC && sc == ECOFF_LOCAL_SYM_SC_DATA) ||
(st == ECOFF_LOCAL_SYM_ST_STATIC && sc == ECOFF_LOCAL_SYM_SC_SBSS) ||
(st == ECOFF_LOCAL_SYM_ST_STATIC && sc == ECOFF_LOCAL_SYM_SC_BSS) ||
(st == ECOFF_LOCAL_SYM_ST_STATIC && sc == ECOFF_LOCAL_SYM_SC_RDATA) ||
(st == ECOFF_LOCAL_SYM_ST_STATIC && sc == ECOFF_LOCAL_SYM_SC_RCONST) ||
(st == ECOFF_LOCAL_SYM_ST_PARAM && sc == ECOFF_LOCAL_SYM_SC_SDATA) ||
(st == ECOFF_LOCAL_SYM_ST_PARAM && sc == ECOFF_LOCAL_SYM_SC_DATA) ||
(st == ECOFF_LOCAL_SYM_ST_PARAM && sc == ECOFF_LOCAL_SYM_SC_SBSS) ||
(st == ECOFF_LOCAL_SYM_ST_PARAM && sc == ECOFF_LOCAL_SYM_SC_BSS) ||
(st == ECOFF_LOCAL_SYM_ST_PARAM && sc == ECOFF_LOCAL_SYM_SC_RDATA) ||
(st == ECOFF_LOCAL_SYM_ST_PARAM && sc == ECOFF_LOCAL_SYM_SC_RCONST) ||
(st == ECOFF_LOCAL_SYM_ST_LOCAL && sc == ECOFF_LOCAL_SYM_SC_TEXT) ||
(st == ECOFF_LOCAL_SYM_ST_LOCAL && sc == ECOFF_LOCAL_SYM_SC_INIT) ||
(st == ECOFF_LOCAL_SYM_ST_LOCAL && sc == ECOFF_LOCAL_SYM_SC_FINI) ||
(st == ECOFF_LOCAL_SYM_ST_LOCAL && sc == ECOFF_LOCAL_SYM_SC_SDATA) ||
(st == ECOFF_LOCAL_SYM_ST_LOCAL && sc == ECOFF_LOCAL_SYM_SC_DATA) ||
(st == ECOFF_LOCAL_SYM_ST_LOCAL && sc == ECOFF_LOCAL_SYM_SC_SBSS) ||
(st == ECOFF_LOCAL_SYM_ST_LOCAL && sc == ECOFF_LOCAL_SYM_SC_BSS) ||
(st == ECOFF_LOCAL_SYM_ST_LOCAL && sc == ECOFF_LOCAL_SYM_SC_RDATA) ||
(st == ECOFF_LOCAL_SYM_ST_LOCAL && sc == ECOFF_LOCAL_SYM_SC_RCONST) ||
(st == ECOFF_LOCAL_SYM_ST_LOCAL && sc == ECOFF_LOCAL_SYM_SC_TLS_DATA) ||
(st == ECOFF_LOCAL_SYM_ST_LOCAL && sc == ECOFF_LOCAL_SYM_SC_TLS_BSS) ||
(st == ECOFF_LOCAL_SYM_ST_LABEL && sc == ECOFF_LOCAL_SYM_SC_TEXT) ||
(st == ECOFF_LOCAL_SYM_ST_LABEL && sc == ECOFF_LOCAL_SYM_SC_INIT) ||
(st == ECOFF_LOCAL_SYM_ST_LABEL && sc == ECOFF_LOCAL_SYM_SC_FINI) ||
(st == ECOFF_LOCAL_SYM_ST_LABEL && sc == ECOFF_LOCAL_SYM_SC_SDATA) ||
(st == ECOFF_LOCAL_SYM_ST_LABEL && sc == ECOFF_LOCAL_SYM_SC_DATA) ||
(st == ECOFF_LOCAL_SYM_ST_LABEL && sc == ECOFF_LOCAL_SYM_SC_XDATA) ||
(st == ECOFF_LOCAL_SYM_ST_LABEL && sc == ECOFF_LOCAL_SYM_SC_PDATA) ||
(st == ECOFF_LOCAL_SYM_ST_LABEL && sc == ECOFF_LOCAL_SYM_SC_SBSS) ||
(st == ECOFF_LOCAL_SYM_ST_LABEL && sc == ECOFF_LOCAL_SYM_SC_BSS) ||
(st == ECOFF_LOCAL_SYM_ST_LABEL && sc == ECOFF_LOCAL_SYM_SC_RDATA) ||
(st == ECOFF_LOCAL_SYM_ST_LABEL && sc == ECOFF_LOCAL_SYM_SC_RCONST) ||
(st == ECOFF_LOCAL_SYM_ST_LABEL && sc == ECOFF_LOCAL_SYM_SC_TLS_DATA) ||
(st == ECOFF_LOCAL_SYM_ST_LABEL && sc == ECOFF_LOCAL_SYM_SC_TLS_BSS) ||
(st == ECOFF_LOCAL_SYM_ST_PROC && sc == ECOFF_LOCAL_SYM_SC_TEXT) ||
(st == ECOFF_LOCAL_SYM_ST_STATICPROC && sc == ECOFF_LOCAL_SYM_SC_TEXT) ||
(st == ECOFF_LOCAL_SYM_ST_STATICPROC && sc == ECOFF_LOCAL_SYM_SC_INIT) ||
(st == ECOFF_LOCAL_SYM_ST_STATICPROC && sc == ECOFF_LOCAL_SYM_SC_FINI) ||
(st == ECOFF_LOCAL_SYM_ST_CONSTANT && sc == ECOFF_LOCAL_SYM_SC_SDATA) ||
(st == ECOFF_LOCAL_SYM_ST_CONSTANT && sc == ECOFF_LOCAL_SYM_SC_DATA) ||
(st == ECOFF_LOCAL_SYM_ST_CONSTANT && sc == ECOFF_LOCAL_SYM_SC_SBSS) ||
(st == ECOFF_LOCAL_SYM_ST_CONSTANT && sc == ECOFF_LOCAL_SYM_SC_BSS) ||
(st == ECOFF_LOCAL_SYM_ST_CONSTANT && sc == ECOFF_LOCAL_SYM_SC_RDATA) ||
(st == ECOFF_LOCAL_SYM_ST_CONSTANT && sc == ECOFF_LOCAL_SYM_SC_RCONST);
}
static RzBinSymbol *ecoff_local_symbol_64_to_bin_symbol(const ECoff_64 *ecoff, const ECoff_LocalSymbol_64 *lsym, bool is_imported) {
RzBinSymbol *bsym = RZ_NEW0(RzBinSymbol);
if (!bsym) {
return NULL;
}
bsym->type = ecoff_local_symbol_type_to_bin_symbol_type(lsym->st);
if (ecoff_local_symbol_value_is_size(lsym->st, lsym->sc)) {
bsym->size = lsym->value;
}
bsym->name = rz_str_dup(lsym->resolved_name);
bsym->forwarder = "NONE";
bsym->is_imported = is_imported;
if (bsym->is_imported) {
bsym->bind = RZ_BIN_BIND_IMPORT_STR;
} else if (ecoff_local_symbol_is_function(lsym->st)) {
bsym->bind = RZ_BIN_BIND_GLOBAL_STR;
} else {
bsym->bind = RZ_BIN_BIND_LOCAL_STR;
}
bsym->paddr = UT64_MAX;
bsym->vaddr = UT64_MAX;
if (lsym->value && ecoff_local_symbol_value_is_address(lsym->st, lsym->sc)) {
bsym->vaddr = lsym->value;
ecoff_find_paddr_from_vaddr_64(ecoff, bsym->vaddr, &bsym->paddr);
}
return bsym;
}
static RzBinSymbol *ecoff_proc_desc_entry_64_to_bin_symbol(const ECoff_64 *ecoff, const ECoff_ProcDescEntry_64 *pde) {
RzBinSymbol *bsym = RZ_NEW0(RzBinSymbol);
if (!bsym) {
return NULL;
}
if (ecoff->symhdr.magic == ECOFF_SYMBOLIC_HEADER_MAGIC_1992) {
bsym->vaddr = pde->_1992.adr;
} else {
bsym->vaddr = pde->_7009.adr;
}
bsym->paddr = UT64_MAX;
ecoff_find_paddr_from_vaddr_64(ecoff, bsym->vaddr, &bsym->paddr);
bsym->type = RZ_BIN_TYPE_FUNC_STR;
bsym->name = rz_str_newf("fcn.%08" PFMT64x, bsym->vaddr);
bsym->forwarder = "NONE";
bsym->bind = RZ_BIN_BIND_GLOBAL_STR;
return bsym;
}
static bool ecoff_get_symbols_64(const ECoff_64 *ecoff, RzPVector /*<RzBinSymbol *>*/ *syms) {
const ECoff_ExternSymbol_64 *esym;
const ECoff_LocalSymbol_64 *lsym;
const ECoff_ProcDescEntry_64 *pde;
HtUP *local_syms = ht_up_new(NULL, NULL);
if (!local_syms) {
return false;
}
rz_vector_foreach (ecoff->extern_symbols, esym) {
RzBinSymbol *bsym = ecoff_local_symbol_64_to_bin_symbol(ecoff, &esym->asym, true);
if (!bsym) {
goto fail;
}
rz_pvector_push(syms, bsym);
}
size_t i = 0;
rz_vector_enumerate (ecoff->local_symbols, lsym, i) {
RzBinSymbol *bsym = ecoff_local_symbol_64_to_bin_symbol(ecoff, lsym, false);
if (!bsym) {
goto fail;
}
ht_up_insert(local_syms, i, bsym);
rz_pvector_push(syms, bsym);
}
rz_vector_foreach (ecoff->proc_descs, pde) {
ut64 adr = 0;
st32 isym = 0;
if (ecoff->symhdr.magic == ECOFF_SYMBOLIC_HEADER_MAGIC_1992) {
adr = pde->_1992.adr;
isym = pde->_1992.isym;
} else {
adr = pde->_7009.adr;
isym = pde->_7009.isym;
}
if (adr == UT32_MAX) {
continue;
}
bool found = false;
RzBinSymbol *bsym = ht_up_find(local_syms, (st64)isym, &found);
if (found) {
// resolve the symbol address using the procedure info
if (bsym->vaddr == UT64_MAX) {
bsym->vaddr = adr;
ecoff_find_paddr_from_vaddr_64(ecoff, bsym->vaddr, &bsym->paddr);
}
// procedures are always functions.
bsym->type = RZ_BIN_TYPE_FUNC_STR;
continue;
}
bsym = ecoff_proc_desc_entry_64_to_bin_symbol(ecoff, pde);
if (!bsym) {
goto fail;
}
rz_pvector_push(syms, bsym);
}
ht_up_free(local_syms);
return true;
fail:
ht_up_free(local_syms);
return false;
}
static RzBinSymbol *ecoff_local_symbol_32_to_bin_symbol(const ECoff_32 *ecoff, const ECoff_LocalSymbol_32 *lsym, bool is_imported) {
RzBinSymbol *bsym = RZ_NEW0(RzBinSymbol);
if (!bsym) {
return NULL;
}
bsym->type = ecoff_local_symbol_type_to_bin_symbol_type(lsym->st);
if (ecoff_local_symbol_value_is_size(lsym->st, lsym->sc)) {
bsym->size = lsym->value;
}
bsym->name = rz_str_dup(lsym->resolved_name);
bsym->forwarder = "NONE";
bsym->is_imported = is_imported;
if (bsym->is_imported) {
bsym->bind = RZ_BIN_BIND_IMPORT_STR;
} else if (ecoff_local_symbol_is_function(lsym->st)) {
bsym->bind = RZ_BIN_BIND_GLOBAL_STR;
} else {
bsym->bind = RZ_BIN_BIND_LOCAL_STR;
}
bsym->paddr = UT64_MAX;
bsym->vaddr = UT64_MAX;
if (lsym->value && ecoff_local_symbol_value_is_address(lsym->st, lsym->sc)) {
bsym->vaddr = lsym->value;
ecoff_find_paddr_from_vaddr_32(ecoff, bsym->vaddr, &bsym->paddr);
}
return bsym;
}
static RzBinSymbol *ecoff_proc_desc_entry_32_to_bin_symbol(const ECoff_32 *ecoff, const ECoff_ProcDescEntry_32 *pde) {
RzBinSymbol *bsym = RZ_NEW0(RzBinSymbol);
if (!bsym) {
return NULL;
}
if (ecoff->symhdr.magic == ECOFF_SYMBOLIC_HEADER_MAGIC_1992) {
bsym->vaddr = pde->_1992.adr;
} else {
bsym->vaddr = pde->_7009.adr;
}
bsym->paddr = UT64_MAX;
ecoff_find_paddr_from_vaddr_32(ecoff, bsym->vaddr, &bsym->paddr);
bsym->type = RZ_BIN_TYPE_FUNC_STR;
bsym->name = rz_str_newf("fcn.%08" PFMT64x, bsym->vaddr);
bsym->forwarder = "NONE";
bsym->bind = RZ_BIN_BIND_GLOBAL_STR;
return bsym;
}
static bool ecoff_get_symbols_32(const ECoff_32 *ecoff, RzPVector /*<RzBinSymbol *>*/ *syms) {
const ECoff_ExternSymbol_32 *esym;
const ECoff_LocalSymbol_32 *lsym;
const ECoff_ProcDescEntry_32 *pde;
HtUP *local_syms = ht_up_new(NULL, NULL);
if (!local_syms) {
return false;
}
rz_vector_foreach (ecoff->extern_symbols, esym) {
RzBinSymbol *bsym = ecoff_local_symbol_32_to_bin_symbol(ecoff, &esym->asym, true);
if (!bsym) {
goto fail;
}
rz_pvector_push(syms, bsym);
}
size_t i = 0;
rz_vector_enumerate (ecoff->local_symbols, lsym, i) {
RzBinSymbol *bsym = ecoff_local_symbol_32_to_bin_symbol(ecoff, lsym, false);
if (!bsym) {
goto fail;
}
ht_up_insert(local_syms, i, bsym);
rz_pvector_push(syms, bsym);
}
rz_vector_foreach (ecoff->proc_descs, pde) {
ut32 adr = 0;
st32 isym = 0;
if (ecoff->symhdr.magic == ECOFF_SYMBOLIC_HEADER_MAGIC_1992) {
adr = pde->_1992.adr;
isym = pde->_1992.isym;
} else {
adr = pde->_7009.adr;
isym = pde->_7009.isym;
}
if (adr == UT32_MAX) {
continue;
}
bool found = false;
RzBinSymbol *bsym = ht_up_find(local_syms, (st64)isym, &found);
if (found) {
// resolve the symbol address using the procedure info
if (bsym->vaddr == UT64_MAX) {
bsym->vaddr = adr;
ecoff_find_paddr_from_vaddr_32(ecoff, bsym->vaddr, &bsym->paddr);
}
// procedures are always functions.
bsym->type = RZ_BIN_TYPE_FUNC_STR;
continue;
}
bsym = ecoff_proc_desc_entry_32_to_bin_symbol(ecoff, pde);
if (!bsym) {
goto fail;
}
rz_pvector_push(syms, bsym);
}
ht_up_free(local_syms);
return true;
fail:
ht_up_free(local_syms);
return false;
}
static bool ecoff_get_symbols_old(const ECoff *ecoff, RzPVector /*<RzBinSymbol *>*/ *syms) {
const ECoff_Symbol_Old *esym;
rz_vector_foreach (ecoff->ecoff32.symbols_old, esym) {
RzBinSymbol *bsym = ecoff_symbol_old_to_bin_symbol(&ecoff->ecoff32, esym);
if (!bsym) {
return false;
}
rz_pvector_push(syms, bsym);
}
return true;
}
RzPVector /*<RzBinSymbol *>*/ *ecoff_get_symbols(const ECoff *ecoff) {
RzPVector *syms = rz_pvector_new((RzPVectorFree)rz_bin_symbol_free);
if (!syms) {
return NULL;
}
RzBinSymbol *bsym = ecoff_gp_to_bin_symbol(ecoff);
if (bsym) {
// only push the _gp symbol if valid
rz_pvector_push(syms, bsym);
}
if (ecoff_is_ecoff64(ecoff)) {
if (!ecoff_has_symbolic_header_64(&ecoff->ecoff64)) {
return syms;
}
ecoff_get_symbols_64(&ecoff->ecoff64, syms);
} else if (ecoff_has_symbolic_header_32(&ecoff->ecoff32)) {
ecoff_get_symbols_32(&ecoff->ecoff32, syms);
} else {
ecoff_get_symbols_old(ecoff, syms);
}
return syms;
}
RzPVector /*<RzBinImport *>*/ *ecoff_get_imports(const ECoff *ecoff) {
RzPVector *imports = rz_pvector_new((RzPVectorFree)rz_bin_import_free);
if (!imports) {
return NULL;
}
// TODO
return imports;
}
RzPVector /*<RzBinSection *>*/ *ecoff_get_sections(const ECoff *ecoff) {
if (ecoff_is_ecoff64(ecoff)) {
return ecoff_get_sections_64(&ecoff->ecoff64);
}
return ecoff_get_sections_32(&ecoff->ecoff32);
}
static ut64 ecoff_to_debug_info(const ECoff *ecoff) {
ut64 dbg_info = 0;
ut16 f_flags = 0;
st64 f_symptr = 0;
if (ecoff_is_ecoff64(ecoff)) {
f_flags = ecoff->ecoff64.header.f_flags;
f_symptr = ecoff->ecoff64.header.f_symptr;
} else {
f_flags = ecoff->ecoff32.header.f_flags;
f_symptr = ecoff->ecoff32.header.f_symptr;
}
if (f_flags & ECOFF_F_FLAGS_IS_STRIPPED || !f_symptr) {
return RZ_BIN_DBG_STRIPPED;
}
if (!(f_flags & ECOFF_F_FLAGS_RELFLG)) {
dbg_info |= RZ_BIN_DBG_RELOCS;
}
if (!(f_flags & ECOFF_F_FLAGS_LNNO)) {
dbg_info |= RZ_BIN_DBG_LINENUMS;
}
if (!(f_flags & ECOFF_F_FLAGS_LSYMS)) {
dbg_info |= RZ_BIN_DBG_SYMS;
}
return dbg_info;
}
RzBinInfo *ecoff_get_info(const ECoff *ecoff) {
RzBinInfo *ret = RZ_NEW0(RzBinInfo);
if (!ret) {
return NULL;
}
ret->rclass = rz_str_dup(ecoff_is_ecoff64(ecoff) ? "ecoff64" : "ecoff");
ret->bclass = rz_str_dup("coff");
ret->type = rz_str_dup("ECOFF (Executable file)");
ret->os = rz_str_dup("any");
ret->subsystem = rz_str_dup("any");
ret->has_va = true;
ret->dbg_info = ecoff_to_debug_info(ecoff);
ret->bits = 32;
const ut16 magic = ecoff_machine(ecoff);
switch (magic) {
case ECOFF_MACHINE_MIPS1:
ret->machine = rz_str_dup("MIPS I");
ret->arch = rz_str_dup("mips");
ret->cpu = rz_str_dup("mips1");
break;
case ECOFF_MACHINE_MIPS1_EL:
ret->machine = rz_str_dup("MIPS I LE");
ret->arch = rz_str_dup("mips");
ret->cpu = rz_str_dup("mips1");
break;
case ECOFF_MACHINE_MIPS1_BE:
ret->big_endian = true;
ret->machine = rz_str_dup("MIPS I BE");
ret->arch = rz_str_dup("mips");
ret->cpu = rz_str_dup("mips1");
break;
case ECOFF_MACHINE_MIPS2_EL:
ret->machine = rz_str_dup("MIPS II EL");
ret->arch = rz_str_dup("mips");
ret->cpu = rz_str_dup("mips2");
break;
case ECOFF_MACHINE_MIPS2_BE:
ret->big_endian = true;
ret->machine = rz_str_dup("MIPS II BE");
ret->arch = rz_str_dup("mips");
ret->cpu = rz_str_dup("mips2");
break;
case ECOFF_MACHINE_MIPS3_EL:
ret->machine = rz_str_dup("MIPS III EL");
ret->arch = rz_str_dup("mips");
ret->cpu = rz_str_dup("mips3");
break;
case ECOFF_MACHINE_MIPS3_BE:
ret->big_endian = true;
ret->machine = rz_str_dup("MIPS III BE");
ret->arch = rz_str_dup("mips");
ret->cpu = rz_str_dup("mips3");
break;
case ECOFF_MACHINE_ALPHA:
ret->machine = rz_str_dup("Alpha");
ret->arch = rz_str_dup("alpha");
break;
case ECOFF_MACHINE_ALPHA_BSD:
ret->machine = rz_str_dup("Alpha BSD");
ret->arch = rz_str_dup("alpha");
ret->bits = 64;
break;
default:
ret->machine = rz_str_dup("unknown");
ret->arch = rz_str_dup("mips");
ret->cpu = rz_str_dup("mips32");
break;
}
return ret;
}
static const char *ecoff_header_magic_to_string(const ECoff *ecoff) {
const ut16 magic = ecoff_machine(ecoff);
switch (magic) {
case ECOFF_MACHINE_MIPS1:
return "MIPS1 (" RZ_STR_DEF(ECOFF_MACHINE_MIPS1) ")";
case ECOFF_MACHINE_MIPS1_EL:
return "MIPS1 little endian (" RZ_STR_DEF(ECOFF_MACHINE_MIPS1_EL) ")";
case ECOFF_MACHINE_MIPS1_BE:
return "MIPS1 big endian (" RZ_STR_DEF(ECOFF_MACHINE_MIPS1_BE) ")";
case ECOFF_MACHINE_MIPS2_EL:
return "MIPS2 little endian (" RZ_STR_DEF(ECOFF_MACHINE_MIPS2_EL) ")";
case ECOFF_MACHINE_MIPS2_BE:
return "MIPS2 big endian (" RZ_STR_DEF(ECOFF_MACHINE_MIPS2_BE) ")";
case ECOFF_MACHINE_MIPS3_EL:
return "MIPS3 little endian (" RZ_STR_DEF(ECOFF_MACHINE_MIPS3_EL) ")";
case ECOFF_MACHINE_MIPS3_BE:
return "MIPS3 big endian (" RZ_STR_DEF(ECOFF_MACHINE_MIPS3_BE) ")";
case ECOFF_MACHINE_ALPHA:
return "ALPHA (" RZ_STR_DEF(ECOFF_MACHINE_ALPHA) ")";
case ECOFF_MACHINE_ALPHA_BSD:
return "ALPHA (" RZ_STR_DEF(ECOFF_MACHINE_ALPHA_BSD) ")";
default:
return "unknown magic";
}
}
static bool ecoff_header_timedate_to_string(const ECoff *ecoff, RzStructuredData *parent) {
if (!parent) {
return false;
}
st32 f_timedate = 0;
if (ecoff_is_ecoff64(ecoff)) {
f_timedate = ecoff->ecoff64.header.f_timedate;
} else {
f_timedate = ecoff->ecoff32.header.f_timedate;
}
if (f_timedate <= 0) {
// can be zero or negative, we ignore it.
return rz_structured_data_map_add_unsigned(parent, "f_timdat", f_timedate, true);
}
char *timestamp = rz_time_stamp_to_str(f_timedate);
if (!timestamp) {
return false;
}
bool res = rz_structured_data_map_add_string(parent, "f_timdat", timestamp);
free(timestamp);
return res;
}
static bool ecoff_header_flags_to_structure(const ECoff *ecoff, RzStructuredData *parent) {
if (!parent) {
return false;
}
ut16 flags = 0;
ut16 magic = 0;
if (ecoff_is_ecoff64(ecoff)) {
magic = ecoff->ecoff64.header.f_magic;
flags = ecoff->ecoff64.header.f_flags;
} else {
magic = ecoff->ecoff32.header.f_magic;
flags = ecoff->ecoff32.header.f_flags;
}
RzStructuredData *f_flags = rz_structured_data_map_add_map(parent, "f_flags");
if (!f_flags) {
return false;
}
rz_structured_data_map_add_unsigned(f_flags, "value", flags, true);
RzStructuredData *readable = rz_structured_data_map_add_array(f_flags, "readable");
if (!readable) {
return false;
}
#define HAS_FLAG(flag, name) \
if (flags & flag && !rz_structured_data_array_add_string(readable, name)) { \
return false; \
}
HAS_FLAG(ECOFF_F_FLAGS_RELFLG, "F_RELFLG");
HAS_FLAG(ECOFF_F_FLAGS_EXEC, "F_EXEC");
HAS_FLAG(ECOFF_F_FLAGS_LNNO, "F_LNNO");
HAS_FLAG(ECOFF_F_FLAGS_LSYMS, "F_LSYMS");
HAS_FLAG(ECOFF_F_FLAGS_NO_SHARED, "F_NO_SHARED");
HAS_FLAG(ECOFF_F_FLAGS_NO_CALL_SHARED, "F_NO_CALL_SHARED");
HAS_FLAG(ECOFF_F_FLAGS_LOMAP, "F_LOMAP");
HAS_FLAG(ECOFF_F_FLAGS_NO_REORG, "F_NO_REORG");
HAS_FLAG(ECOFF_F_FLAGS_NO_REMOVE, "F_NO_REMOVE");
#undef HAS_FLAG
if (magic == ECOFF_MACHINE_ALPHA ||
magic == ECOFF_MACHINE_ALPHA_BSD) {
const ut16 object = flags & ECOFF_F_FLAGS_ALPHA_OBJ_MASK;
if (object == ECOFF_F_FLAGS_ALPHA_NO_SHARED && !rz_structured_data_array_add_string(readable, "NO_SHARED")) {
return false;
} else if (object == ECOFF_F_FLAGS_ALPHA_SHARABLE && !rz_structured_data_array_add_string(readable, "SHARABLE")) {
return false;
} else if (object == ECOFF_F_FLAGS_ALPHA_CALL_SHARED && !rz_structured_data_array_add_string(readable, "CALL_SHARED")) {
return false;
}
}
return true;
}
static bool ecoff_header_to_structure(const ECoff *ecoff, RzStructuredData *parent) {
if (!parent) {
return false;
}
ut16 f_nscns = 0;
ut64 f_symptr = 0;
st32 f_nsyms = 0;
ut16 f_opthdr = 0;
if (ecoff_is_ecoff64(ecoff)) {
f_nscns = ecoff->ecoff64.header.f_nscns;
f_symptr = (ut64)ecoff->ecoff64.header.f_symptr;
f_nsyms = ecoff->ecoff64.header.f_nsyms;
f_opthdr = ecoff->ecoff64.header.f_opthdr;
} else {
f_nscns = ecoff->ecoff32.header.f_nscns;
f_symptr = (ut64)ecoff->ecoff32.header.f_symptr;
f_nsyms = ecoff->ecoff32.header.f_nsyms;
f_opthdr = ecoff->ecoff32.header.f_opthdr;
}
RzStructuredData *filehdr = rz_structured_data_map_add_map(parent, "filehdr");
if (!filehdr) {
return false;
}
const char *f_magic = ecoff_header_magic_to_string(ecoff);
return rz_structured_data_map_add_string(filehdr, "f_magic", f_magic) &&
rz_structured_data_map_add_unsigned(filehdr, "f_nscns", f_nscns, false) &&
ecoff_header_timedate_to_string(ecoff, filehdr) &&
rz_structured_data_map_add_unsigned(filehdr, "f_symptr", f_symptr, true) &&
rz_structured_data_map_add_signed(filehdr, "f_nsyms", f_nsyms) &&
rz_structured_data_map_add_unsigned(filehdr, "f_opthdr", f_opthdr, true) &&
ecoff_header_flags_to_structure(ecoff, filehdr);
}
static const char *ecoff_aouthdr_magic_to_string(const ut16 magic) {
switch (magic) {
case ECOFF_AOUTHDR_OMAGIC:
return "OMAGIC (" RZ_STR_DEF(ECOFF_AOUTHDR_OMAGIC) ")";
case ECOFF_AOUTHDR_NMAGIC:
return "NMAGIC (" RZ_STR_DEF(ECOFF_AOUTHDR_NMAGIC) ")";
case ECOFF_AOUTHDR_SMAGIC:
return "SMAGIC (" RZ_STR_DEF(ECOFF_AOUTHDR_SMAGIC) ")";
case ECOFF_AOUTHDR_ZMAGIC:
return "ZMAGIC (" RZ_STR_DEF(ECOFF_AOUTHDR_ZMAGIC) ")";
case ECOFF_AOUTHDR_LIBMAGIC:
return "LIBMAGIC (" RZ_STR_DEF(ECOFF_AOUTHDR_LIBMAGIC) ")";
default:
rz_warn_if_reached();
return "unknown";
}
}
static bool ecoff_aouthdr_alpha_to_structure(const ECoff_64 *ecoff, RzStructuredData *parent) {
if (!parent) {
return false;
}
char vstamp[16] = { 0 };
const ECoff_AOutHdr_Alpha *alpha = &ecoff->aouthdr.alpha;
const char *magic = ecoff_aouthdr_magic_to_string(alpha->magic);
rz_strf(vstamp, "v%u.%u", alpha->vstamp >> 8, alpha->vstamp & 0xff);
return rz_structured_data_map_add_string(parent, "magic", magic) &&
rz_structured_data_map_add_string(parent, "vstamp", vstamp) &&
rz_structured_data_map_add_unsigned(parent, "bldrev", alpha->bldrev, true) &&
rz_structured_data_map_add_unsigned(parent, "padding", alpha->padding, true) &&
rz_structured_data_map_add_unsigned(parent, "tsize", alpha->tsize, true) &&
rz_structured_data_map_add_unsigned(parent, "dsize", alpha->dsize, true) &&
rz_structured_data_map_add_unsigned(parent, "bsize", alpha->bsize, true) &&
rz_structured_data_map_add_unsigned(parent, "entry", alpha->entry, true) &&
rz_structured_data_map_add_unsigned(parent, "text_start", alpha->text_start, true) &&
rz_structured_data_map_add_unsigned(parent, "data_start", alpha->data_start, true) &&
rz_structured_data_map_add_unsigned(parent, "bss_start", alpha->bss_start, true) &&
rz_structured_data_map_add_unsigned(parent, "gpr_mask", alpha->gpr_mask, true) &&
rz_structured_data_map_add_unsigned(parent, "fpr_mask", alpha->fpr_mask, true) &&
rz_structured_data_map_add_unsigned(parent, "gp_value", alpha->gp_value, true);
}
static bool ecoff_aouthdr_mips_to_structure(const ECoff_32 *ecoff, RzStructuredData *parent) {
if (!parent) {
return false;
}
char vstamp[16] = { 0 };
const ECoff_AOutHdr_Mips *mips = &ecoff->aouthdr.mips;
const char *magic = ecoff_aouthdr_magic_to_string(mips->magic);
rz_strf(vstamp, "v%u.%u", mips->vstamp >> 8, mips->vstamp & 0xff);
bool res = rz_structured_data_map_add_string(parent, "magic", magic) &&
rz_structured_data_map_add_string(parent, "vstamp", vstamp) &&
rz_structured_data_map_add_unsigned(parent, "tsize", mips->tsize, true) &&
rz_structured_data_map_add_unsigned(parent, "dsize", mips->dsize, true) &&
rz_structured_data_map_add_unsigned(parent, "bsize", mips->bsize, true) &&
rz_structured_data_map_add_unsigned(parent, "entry", mips->entry, true) &&
rz_structured_data_map_add_unsigned(parent, "text_start", mips->text_start, true) &&
rz_structured_data_map_add_unsigned(parent, "data_start", mips->data_start, true) &&
rz_structured_data_map_add_unsigned(parent, "bss_start", mips->bss_start, true) &&
rz_structured_data_map_add_unsigned(parent, "gpr_mask", mips->gpr_mask, true);
if (!res) {
return false;
}
RzStructuredData *cpr_mask = rz_structured_data_map_add_array(parent, "cpr_mask");
if (!cpr_mask) {
return false;
}
return rz_structured_data_array_add_unsigned(cpr_mask, mips->cpr_mask[0], true) &&
rz_structured_data_array_add_unsigned(cpr_mask, mips->cpr_mask[1], true) &&
rz_structured_data_array_add_unsigned(cpr_mask, mips->cpr_mask[2], true) &&
rz_structured_data_array_add_unsigned(cpr_mask, mips->cpr_mask[3], true) &&
rz_structured_data_map_add_unsigned(parent, "gp_value", mips->gp_value, true);
}
static bool ecoff_aouthdr_to_structure(const ECoff *ecoff, RzStructuredData *parent) {
const ut16 magic = ecoff_machine(ecoff);
RzStructuredData *aouthdr = rz_structured_data_map_add_map(parent, "aouthdr");
if (!aouthdr) {
return false;
}
if (ecoff_is_alpha_magic(magic)) {
return ecoff_aouthdr_alpha_to_structure(&ecoff->ecoff64, aouthdr);
} else if (ecoff_is_mips_magic(magic)) {
return ecoff_aouthdr_mips_to_structure(&ecoff->ecoff32, aouthdr);
} else {
rz_warn_if_reached();
}
return true;
}
static bool ecoff_section_flags_to_structure(const ut32 flags, RzStructuredData *parent) {
const ut32 extflag = flags & ECOFF_SECTION_EXT_TYPE_MASK;
if (!parent) {
return false;
}
RzStructuredData *s_flags = rz_structured_data_map_add_map(parent, "s_flags");
if (!s_flags) {
return false;
}
rz_structured_data_map_add_unsigned(s_flags, "value", flags, true);
RzStructuredData *readable = rz_structured_data_map_add_array(s_flags, "readable");
if (!readable) {
return false;
}
if (flags == ECOFF_SECTION_TYPE_REG) {
// when zero is always this type.
return rz_structured_data_array_add_string(readable, "STYP_REG");
}
#define HAS_FLAG(flag, name) \
if (flags & flag && !rz_structured_data_array_add_string(readable, name)) { \
return false; \
}
HAS_FLAG(ECOFF_SECTION_TYPE_TEXT, "STYP_TEXT");
HAS_FLAG(ECOFF_SECTION_TYPE_DATA, "STYP_DATA");
HAS_FLAG(ECOFF_SECTION_TYPE_BSS, "STYP_BSS");
HAS_FLAG(ECOFF_SECTION_TYPE_RDATA, "STYP_RDATA");
HAS_FLAG(ECOFF_SECTION_TYPE_SDATA, "STYP_SDATA");
HAS_FLAG(ECOFF_SECTION_TYPE_SBSS, "STYP_SBSS");
HAS_FLAG(ECOFF_SECTION_TYPE_UCODE, "STYP_UCODE");
HAS_FLAG(ECOFF_SECTION_TYPE_GOT1, "STYP_GOT1");
HAS_FLAG(ECOFF_SECTION_TYPE_DYNAMIC1, "STYP_DYNAMIC1");
HAS_FLAG(ECOFF_SECTION_TYPE_DYNSYM1, "STYP_DYNSYM1");
HAS_FLAG(ECOFF_SECTION_TYPE_REL_DYN1, "STYP_REL_DYN1");
HAS_FLAG(ECOFF_SECTION_TYPE_DYNSTR1, "STYP_DYNSTR1");
HAS_FLAG(ECOFF_SECTION_TYPE_HASH1, "STYP_HASH1");
HAS_FLAG(ECOFF_SECTION_TYPE_DSOLIST1, "STYP_DSOLIST1");
HAS_FLAG(ECOFF_SECTION_TYPE_MSYM1, "STYP_MSYM1");
HAS_FLAG(ECOFF_SECTION_TYPE_LIT4, "STYP_LIT4");
HAS_FLAG(ECOFF_SECTION_TYPE_NRELOC_OVFL2, "STYP_NRELOC_OVFL2");
HAS_FLAG(ECOFF_SECTION_TYPE_LIB, "STYP_LIB");
HAS_FLAG(ECOFF_SECTION_TYPE_INIT, "STYP_INIT");
#undef HAS_FLAG
#define HAS_EXT_FLAG(flag, name) \
if (extflag == flag && !rz_structured_data_array_add_string(readable, name)) { \
return false; \
}
HAS_EXT_FLAG(ECOFF_SECTION_EXT_TYPE_CONFLICT1, "STYP_CONFLICT1");
HAS_EXT_FLAG(ECOFF_SECTION_EXT_TYPE_RESOURCE, "STYP_RESOURCE");
HAS_EXT_FLAG(ECOFF_SECTION_EXT_TYPE_FINI, "STYP_FINI");
HAS_EXT_FLAG(ECOFF_SECTION_EXT_TYPE_COMMENT1, "STYP_COMMENT1");
HAS_EXT_FLAG(ECOFF_SECTION_EXT_TYPE_COMMENT2, "STYP_COMMENT2");
HAS_EXT_FLAG(ECOFF_SECTION_EXT_TYPE_RCONST, "STYP_RCONST");
HAS_EXT_FLAG(ECOFF_SECTION_EXT_TYPE_XDATA, "STYP_XDATA");
HAS_EXT_FLAG(ECOFF_SECTION_EXT_TYPE_TLSDATA, "STYP_TLSDATA");
HAS_EXT_FLAG(ECOFF_SECTION_EXT_TYPE_TLSBSS, "STYP_TLSBSS");
HAS_EXT_FLAG(ECOFF_SECTION_EXT_TYPE_TLSINIT, "STYP_TLSINIT");
HAS_EXT_FLAG(ECOFF_SECTION_EXT_TYPE_PDATA, "STYP_PDATA");
HAS_EXT_FLAG(ECOFF_SECTION_EXT_TYPE_LITA, "STYP_LITA");
HAS_EXT_FLAG(ECOFF_SECTION_EXT_TYPE_LIT8, "STYP_LIT8");
#undef HAS_EXT_FLAG
return true;
}
static const char *ecoff_symbol_sclass(const ECoff_Symbol_Old *symbol) {
switch (symbol->e_sclass) {
case ECOFF_SYMBOL_OLD_SC_EFCN:
return "C_EFCN";
case ECOFF_SYMBOL_OLD_SC_NULL:
return "C_NULL";
case ECOFF_SYMBOL_OLD_SC_AUTO:
return "C_AUTO";
case ECOFF_SYMBOL_OLD_SC_EXT:
return "C_EXT";
case ECOFF_SYMBOL_OLD_SC_STAT:
return "C_STAT";
case ECOFF_SYMBOL_OLD_SC_REG:
return "C_REG";
case ECOFF_SYMBOL_OLD_SC_EXTDEF:
return "C_EXTDEF";
case ECOFF_SYMBOL_OLD_SC_LABEL:
return "C_LABEL";
case ECOFF_SYMBOL_OLD_SC_ULABEL:
return "C_ULABEL";
case ECOFF_SYMBOL_OLD_SC_MOS:
return "C_MOS";
case ECOFF_SYMBOL_OLD_SC_ARG:
return "C_ARG";
case ECOFF_SYMBOL_OLD_SC_STRTAG:
return "C_STRTAG";
case ECOFF_SYMBOL_OLD_SC_MOU:
return "C_MOU";
case ECOFF_SYMBOL_OLD_SC_UNTAG:
return "C_UNTAG";
case ECOFF_SYMBOL_OLD_SC_TPDEF:
return "C_TPDEF";
case ECOFF_SYMBOL_OLD_SC_USTATIC:
return "C_USTATIC";
case ECOFF_SYMBOL_OLD_SC_ENTAG:
return "C_ENTAG";
case ECOFF_SYMBOL_OLD_SC_MOE:
return "C_MOE";
case ECOFF_SYMBOL_OLD_SC_REGPARM:
return "C_REGPARM";
case ECOFF_SYMBOL_OLD_SC_FIELD:
return "C_FIELD";
case ECOFF_SYMBOL_OLD_SC_BLOCK:
return "C_BLOCK";
case ECOFF_SYMBOL_OLD_SC_FCN:
return "C_FCN";
case ECOFF_SYMBOL_OLD_SC_EOS:
return "C_EOS";
case ECOFF_SYMBOL_OLD_SC_FILE:
return "C_FILE";
case ECOFF_SYMBOL_OLD_SC_LINE:
return "C_LINE";
case ECOFF_SYMBOL_OLD_SC_ALIAS:
return "C_ALIAS";
case ECOFF_SYMBOL_OLD_SC_HIDDEN:
return "C_HIDDEN";
default:
rz_warn_if_reached();
return "unknown";
}
}
static bool ecoff_symbol_old_to_structure(const ECoff_32 *ecoff, const ECoff_Symbol_Old *symbol, RzStructuredData *parent) {
const char *e_scnum = "unknown";
const char *e_sclass = ecoff_symbol_sclass(symbol);
if (symbol->e_scnum == ECOFF_SYMBOL_SECT_NUM_DEBUG) {
// Special symbolic debugging symbol
e_scnum = "N_DEBUG";
} else if (symbol->e_scnum == ECOFF_SYMBOL_SECT_NUM_ABS) {
// Absolute symbol
e_scnum = "N_ABS";
} else if (symbol->e_scnum == ECOFF_SYMBOL_SECT_NUM_UNDEF) {
// Undefined external symbol
e_scnum = "N_UNDEF";
} else if (symbol->e_scnum > 0 && symbol->e_scnum < rz_vector_len(ecoff->sections)) {
const ECoff_Section_32 *esec = rz_vector_index_ptr(ecoff->sections, symbol->e_scnum);
if (esec) {
e_scnum = esec->resolved_name;
}
}
return rz_structured_data_map_add_string(parent, "e_name", symbol->resolved_name) &&
rz_structured_data_map_add_unsigned(parent, "e_value", symbol->e_value, true) &&
rz_structured_data_map_add_string(parent, "e_scnum", e_scnum) &&
rz_structured_data_map_add_unsigned(parent, "e_type", symbol->e_type, true) &&
rz_structured_data_map_add_string(parent, "e_sclass", e_sclass) &&
rz_structured_data_map_add_unsigned(parent, "e_numaux", symbol->e_numaux, true);
}
static bool ecoff_symbols_to_structure_old(const ECoff_32 *ecoff, RzStructuredData *parent) {
RzStructuredData *symbols = rz_structured_data_map_add_array(parent, "symbols");
if (!symbols) {
return false;
}
const ECoff_Symbol_Old *symbol;
rz_vector_foreach (ecoff->symbols_old, symbol) {
RzStructuredData *section = rz_structured_data_array_add_map(symbols);
if (!section) {
return false;
} else if (!ecoff_symbol_old_to_structure(ecoff, symbol, section)) {
return false;
}
}
return true;
}
static const char *ecoff_file_descr_entry_get_lang(const ut16 lang) {
switch (lang) {
case ECOFF_FDE_LANG_C:
return "C";
case ECOFF_FDE_LANG_PASCAL:
return "Pascal";
case ECOFF_FDE_LANG_FORTRAN:
return "Fortran";
case ECOFF_FDE_LANG_ASSEMBLER:
return "Assembly";
case ECOFF_FDE_LANG_MACHINE:
return "Machine";
case ECOFF_FDE_LANG_NIL:
return "Nil";
case ECOFF_FDE_LANG_ADA:
return "Ada";
case ECOFF_FDE_LANG_PL1:
return "Pl1";
case ECOFF_FDE_LANG_COBOL:
return "Cobol";
case ECOFF_FDE_LANG_STDC:
return "stdC";
case ECOFF_FDE_LANG_MIPS_CXX:
return "Mips C++";
case ECOFF_FDE_LANG_DEC_CXX:
return "Dec C++";
case ECOFF_FDE_LANG_CXX:
return "C++";
case ECOFF_FDE_LANG_FORTRAN90:
return "Fortran 90";
case ECOFF_FDE_LANG_BLISS:
return "Bliss";
case ECOFF_FDE_LANG_PTAL:
return "PTAL";
case ECOFF_FDE_LANG_CXX_V1:
return "C++v1";
case ECOFF_FDE_LANG_CXX_V2:
return "C++v2";
default:
return "unknown";
}
}
static const char *ecoff_file_descr_entry_get_glevel(const ut16 glevel) {
switch (glevel) {
case ECOFF_FDE_GLEVEL_0:
return "-g0";
case ECOFF_FDE_GLEVEL_1:
return "-g1";
case ECOFF_FDE_GLEVEL_2:
return "-g2";
case ECOFF_FDE_GLEVEL_3:
return "-g3";
default:
return "unknown";
}
}
static const char *ecoff_local_symbol_get_type(const ut32 st) {
switch (st) {
default: return "Unknown";
case ECOFF_LOCAL_SYM_ST_NIL: return "Nil";
case ECOFF_LOCAL_SYM_ST_GLOBAL: return "Global Var";
case ECOFF_LOCAL_SYM_ST_STATIC: return "Static Var";
case ECOFF_LOCAL_SYM_ST_PARAM: return "Procedure Arg";
case ECOFF_LOCAL_SYM_ST_LOCAL: return "Local Var";
case ECOFF_LOCAL_SYM_ST_LABEL: return "Label";
case ECOFF_LOCAL_SYM_ST_PROC: return "Global Procedure";
case ECOFF_LOCAL_SYM_ST_BLOCK: return "Block";
case ECOFF_LOCAL_SYM_ST_END: return "End";
case ECOFF_LOCAL_SYM_ST_MEMBER: return "Member";
case ECOFF_LOCAL_SYM_ST_TYPEDEF: return "Typedef";
case ECOFF_LOCAL_SYM_ST_FILE: return "Source File";
case ECOFF_LOCAL_SYM_ST_REGRELOC: return "Register Relocation";
case ECOFF_LOCAL_SYM_ST_FORWARD: return "Forwarding Address";
case ECOFF_LOCAL_SYM_ST_STATICPROC: return "Static Procedure";
case ECOFF_LOCAL_SYM_ST_CONSTANT: return "Constant";
case ECOFF_LOCAL_SYM_ST_STAPARAM: return "Static Param";
case ECOFF_LOCAL_SYM_ST_BASE: return "Base";
case ECOFF_LOCAL_SYM_ST_VIRTBASE: return "VirtBase";
case ECOFF_LOCAL_SYM_ST_TAG: return "Tag";
case ECOFF_LOCAL_SYM_ST_INTER: return "Interlude";
// case ECOFF_LOCAL_SYM_ST_MODULE: return "Module"; // Conflicts with namespace
case ECOFF_LOCAL_SYM_ST_NAMESPACE: return "Namespace";
// case ECOFF_LOCAL_SYM_ST_MODVIEW: return "Modview"; // Conflicts with using
case ECOFF_LOCAL_SYM_ST_USING: return "Using";
case ECOFF_LOCAL_SYM_ST_ALIAS: return "Alias";
case ECOFF_LOCAL_SYM_ST_STRUCT: return "Struct";
case ECOFF_LOCAL_SYM_ST_UNION: return "Union";
case ECOFF_LOCAL_SYM_ST_ENUM: return "Enum";
case ECOFF_LOCAL_SYM_ST_INDIRECT: return "Indirect";
case ECOFF_LOCAL_SYM_ST_STR: return "String";
case ECOFF_LOCAL_SYM_ST_NUMBER: return "Number";
case ECOFF_LOCAL_SYM_ST_EXPR: return "Expr";
case ECOFF_LOCAL_SYM_ST_TYPE: return "Type";
}
}
static const char *ecoff_local_symbol_get_storage_class(const ut32 sc) {
switch (sc) {
default: return "Unknown";
case ECOFF_LOCAL_SYM_SC_NIL: return "nil";
case ECOFF_LOCAL_SYM_SC_TEXT: return ".text";
case ECOFF_LOCAL_SYM_SC_DATA: return ".data";
case ECOFF_LOCAL_SYM_SC_BSS: return ".bss";
case ECOFF_LOCAL_SYM_SC_REGISTER: return "register";
case ECOFF_LOCAL_SYM_SC_ABS: return "abs";
case ECOFF_LOCAL_SYM_SC_UNDEFINED: return "undefined";
case ECOFF_LOCAL_SYM_SC_UNALLOCATED: return "unallocated";
case ECOFF_LOCAL_SYM_SC_TLSUNDEFINED: return "tls undefined";
case ECOFF_LOCAL_SYM_SC_INFO: return "debugger info";
case ECOFF_LOCAL_SYM_SC_SDATA: return ".sdata";
case ECOFF_LOCAL_SYM_SC_SBSS: return ".sbss";
case ECOFF_LOCAL_SYM_SC_RDATA: return ".rdata";
case ECOFF_LOCAL_SYM_SC_VAR: return "var";
case ECOFF_LOCAL_SYM_SC_COMMON: return "common";
case ECOFF_LOCAL_SYM_SC_SCOMMON: return "small common";
case ECOFF_LOCAL_SYM_SC_VARREGISTER: return "var register";
case ECOFF_LOCAL_SYM_SC_VARIANT: return "variant";
// case ECOFF_LOCAL_SYM_SC_FILEDESC: return "file descriptor"; // Conflicts with variant
case ECOFF_LOCAL_SYM_SC_SUNDEFINED: return "small undefined";
case ECOFF_LOCAL_SYM_SC_INIT: return ".init";
case ECOFF_LOCAL_SYM_SC_REPORTDESC: return "report descriptor";
case ECOFF_LOCAL_SYM_SC_XDATA: return ".xdata";
case ECOFF_LOCAL_SYM_SC_PDATA: return ".pdata";
case ECOFF_LOCAL_SYM_SC_FINI: return ".fini";
case ECOFF_LOCAL_SYM_SC_RCONST: return ".rconst";
case ECOFF_LOCAL_SYM_SC_TLS_COMMON: return "tls common";
case ECOFF_LOCAL_SYM_SC_TLS_DATA: return "tls .data";
case ECOFF_LOCAL_SYM_SC_TLS_BSS: return "tls .bss";
}
}
static bool ecoff_sections_to_structure(const ECoff *ecoff, RzStructuredData *parent) {
if (!parent) {
return false;
}
if (ecoff_is_ecoff64(ecoff)) {
return ecoff_sections_to_structure_64(&ecoff->ecoff64, parent);
}
return ecoff_sections_to_structure_32(&ecoff->ecoff32, parent);
}
static bool ecoff_symbols_to_structure(const ECoff *ecoff, RzStructuredData *parent) {
if (!parent) {
return false;
}
if (ecoff_is_ecoff64(ecoff)) {
return ecoff_symbols_to_structure_64(&ecoff->ecoff64, parent);
} else if (ecoff_has_symbolic_header_32(&ecoff->ecoff32)) {
return ecoff_symbols_to_structure_32(&ecoff->ecoff32, parent);
}
return ecoff_symbols_to_structure_old(&ecoff->ecoff32, parent);
}
bool ecoff_new_structure(const ECoff *ecoff, RzStructuredData *parent) {
return ecoff_header_to_structure(ecoff, parent) &&
ecoff_aouthdr_to_structure(ecoff, parent) &&
ecoff_sections_to_structure(ecoff, parent) &&
ecoff_symbols_to_structure(ecoff, parent);
}
RzList /*<char *>*/ *ecoff_resolve_section_flags(ut64 s_flags) {
const ut32 extflag = s_flags & ECOFF_SECTION_EXT_TYPE_MASK;
RzList *flags = rz_list_new();
if (!flags) {
return false;
}
if (s_flags == ECOFF_SECTION_TYPE_REG) {
// when zero is always this type.
rz_list_append(flags, "STYP_REG");
return flags;
}
#define HAS_FLAG(flag, name) \
if (s_flags & flag) { \
rz_list_append(flags, name); \
}
HAS_FLAG(ECOFF_SECTION_TYPE_TEXT, "STYP_TEXT");
HAS_FLAG(ECOFF_SECTION_TYPE_DATA, "STYP_DATA");
HAS_FLAG(ECOFF_SECTION_TYPE_BSS, "STYP_BSS");
HAS_FLAG(ECOFF_SECTION_TYPE_RDATA, "STYP_RDATA");
HAS_FLAG(ECOFF_SECTION_TYPE_SDATA, "STYP_SDATA");
HAS_FLAG(ECOFF_SECTION_TYPE_SBSS, "STYP_SBSS");
HAS_FLAG(ECOFF_SECTION_TYPE_UCODE, "STYP_UCODE");
HAS_FLAG(ECOFF_SECTION_TYPE_GOT1, "STYP_GOT1");
HAS_FLAG(ECOFF_SECTION_TYPE_DYNAMIC1, "STYP_DYNAMIC1");
HAS_FLAG(ECOFF_SECTION_TYPE_DYNSYM1, "STYP_DYNSYM1");
HAS_FLAG(ECOFF_SECTION_TYPE_REL_DYN1, "STYP_REL_DYN1");
HAS_FLAG(ECOFF_SECTION_TYPE_DYNSTR1, "STYP_DYNSTR1");
HAS_FLAG(ECOFF_SECTION_TYPE_HASH1, "STYP_HASH1");
HAS_FLAG(ECOFF_SECTION_TYPE_DSOLIST1, "STYP_DSOLIST1");
HAS_FLAG(ECOFF_SECTION_TYPE_MSYM1, "STYP_MSYM1");
HAS_FLAG(ECOFF_SECTION_TYPE_LIT4, "STYP_LIT4");
HAS_FLAG(ECOFF_SECTION_TYPE_NRELOC_OVFL2, "STYP_NRELOC_OVFL2");
HAS_FLAG(ECOFF_SECTION_TYPE_LIB, "STYP_LIB");
HAS_FLAG(ECOFF_SECTION_TYPE_INIT, "STYP_INIT");
#undef HAS_FLAG
#define HAS_EXT_FLAG(flag, name) \
if (extflag == flag) { \
rz_list_append(flags, name); \
}
HAS_EXT_FLAG(ECOFF_SECTION_EXT_TYPE_CONFLICT1, "STYP_CONFLICT1");
HAS_EXT_FLAG(ECOFF_SECTION_EXT_TYPE_RESOURCE, "STYP_RESOURCE");
HAS_EXT_FLAG(ECOFF_SECTION_EXT_TYPE_FINI, "STYP_FINI");
HAS_EXT_FLAG(ECOFF_SECTION_EXT_TYPE_COMMENT1, "STYP_COMMENT1");
HAS_EXT_FLAG(ECOFF_SECTION_EXT_TYPE_COMMENT2, "STYP_COMMENT2");
HAS_EXT_FLAG(ECOFF_SECTION_EXT_TYPE_RCONST, "STYP_RCONST");
HAS_EXT_FLAG(ECOFF_SECTION_EXT_TYPE_XDATA, "STYP_XDATA");
HAS_EXT_FLAG(ECOFF_SECTION_EXT_TYPE_TLSDATA, "STYP_TLSDATA");
HAS_EXT_FLAG(ECOFF_SECTION_EXT_TYPE_TLSBSS, "STYP_TLSBSS");
HAS_EXT_FLAG(ECOFF_SECTION_EXT_TYPE_TLSINIT, "STYP_TLSINIT");
HAS_EXT_FLAG(ECOFF_SECTION_EXT_TYPE_PDATA, "STYP_PDATA");
HAS_EXT_FLAG(ECOFF_SECTION_EXT_TYPE_LITA, "STYP_LITA");
HAS_EXT_FLAG(ECOFF_SECTION_EXT_TYPE_LIT8, "STYP_LIT8");
#undef HAS_EXT_FLAG
return flags;
}