/* * This Loader Module is written by Yury Haron * */ /* L O A D E R for a.out (Linux) */ #include "../idaldr.h" //#define DEBUG #include "aout.h" #include "common.cpp" #include "../../module/sparc/notify_codes.hpp" class aout_t { public: exec ex; nlist *symtab; char *strtab; uint32 symcount; // number of symbols in symtab uint32 text; // first address of each section uint32 data; uint32 bss; uint32 extrn; uint32 top; // next available address uint32 treloff; // file offset of each section uint32 dreloff; uint32 symoff; uint32 stroff; bool msb; aout_t(void) { memset((void*) this, 0, sizeof(*this)); } ~aout_t() { if ( symtab != NULL ) { qfree(symtab); symtab = NULL; } if ( strtab != NULL ) { qfree(strtab); strtab = NULL; } } }; //-------------------------------------------------------------------------- static const char *guess_processor(const exec &ex) { if ( N_MACHTYPE(ex) ) { switch ( N_MACHTYPE(ex) ) { case M_386: case M_386_NETBSD: return "metapc"; case M_ARM: case M_ARM6_NETBSD: return "arm"; case M_SPARC: return "sparcb"; default: break; } } return NULL; } //-------------------------------------------------------------------------- // // check input file format. if recognized, then return 1 // and fill 'fileformatname'. // otherwise return 0 // static int idaapi accept_file( qstring *fileformatname, qstring *processor, linput_t *li, const char *) { exec ex; int i = get_aout_file_format_index(li, &ex); #ifdef DEBUG msg("getfmtindex=%d\n", i); #endif if ( i == 0 ) return 0; static const char *const ff[] = { "demand-paged executable with NULL-ptr check", // q "object or impure executable", // o "demand-paged executable", // z "core", // c "pure executable", // n "OpenBSD demand-paged executable", // zo }; fileformatname->sprnt("a.out (%s)", ff[i-1]); *processor = guess_processor(ex); #ifdef DEBUG msg("%s\n", fileformatname.c_str()); #endif return f_AOUT; } //-------------------------------------------------------------------------- static void create32( processor_t &ph, ushort sel, ea_t start_ea, ea_t end_ea, const char *name, const char *classname) { set_selector(sel, 0); if ( !add_segm(sel, start_ea, end_ea, name, classname, ADDSEG_SPARSE) ) loader_failure(); if ( ph.id == PLFM_386 ) set_segm_addressing(getseg(start_ea), 1); } //-------------------------------------------------------------------------- bool ana_hdr(processor_t &ph, linput_t *li, exec *_ex) { bool msb = false; exec &ex = *_ex; lread(li, &ex, sizeof(ex)); if ( N_BADMAG(ex) ) { swap_exec(ex); msb = true; msg("Assuming big-endian...\n"); } const char *proc = guess_processor(ex); if ( proc != NULL ) { set_processor_type(proc, SETPROC_LOADER); if ( N_MACHTYPE(ex) == M_SPARC ) sparc_module_t::set_v8(true); // set SPARC_V8 parameter } else { if ( ask_yn(ASKBTN_YES, "HIDECANCEL\n" "AUTOHIDE REGISTRY\n" "Missing machine type. Continue?") <= ASKBTN_NO ) { loader_failure(); } if ( ph.id == -1 ) set_processor_type(inf_get_procname().c_str(), SETPROC_LOADER); } return msb; } //-------------------------------------------------------------------------- inline fixup_type_t get_hi22_reltype() { static fixup_type_t hi22_reltype = FIXUP_CUSTOM; if ( hi22_reltype == FIXUP_CUSTOM ) hi22_reltype = find_custom_fixup("HI22"); return hi22_reltype; } //-------------------------------------------------------------------------- inline fixup_type_t get_lo10_reltype() { static fixup_type_t lo10_reltype = FIXUP_CUSTOM; if ( lo10_reltype == FIXUP_CUSTOM ) lo10_reltype = find_custom_fixup("LO10"); return lo10_reltype; } //-------------------------------------------------------------------------- static void do_fixup(uint32 where, uint32 delta, uint32 target, fixup_type_t type, int external) { fixup_data_t fd(type); if ( external ) fd.set_extdef(); fd.displacement = delta; if ( external ) target -= delta; segment_t *s = getseg(target); if ( s != NULL ) { fd.sel = s->sel; fd.off = target - get_segm_base(s); } else { fd.off = target; } fd.set(where); } #define S_MASK(x) ((1 << (x)) - 1) //---------------------------------------------------------------------- static void do_relocation_sparc( linput_t *li, const aout_t &ctx, uint32 off, uint32 len, int seg) { const segment_t *seg_p = getnseg(seg); if ( seg_p == NULL ) { msg("relocation data for missing segment %d ignored\n", seg); return; } uint32 base = seg == 1 ? ctx.text : ctx.data; #ifdef DEBUG msg("seg %d base 0x%08X\n", seg, base); #endif // load relocation table uint32 relcount = len / sizeof(reloc_info_sparc); validate_array_count(li, &relcount, sizeof(reloc_info_sparc), "Relocation count", off); if ( relcount == 0 ) return; reloc_info_sparc *reltab = qalloc_array(relcount); if ( reltab == NULL ) loader_failure("Unable to allocate relocation table for %u entries\n", relcount); qlseek(li, off); for ( reloc_info_sparc *rel = reltab; rel < reltab + relcount; rel++ ) { uint32 temp = 0; lread4bytes(li, &rel->r_address, ctx.msb); lread4bytes(li, &temp, ctx.msb); lread4bytes(li, &rel->r_addend, ctx.msb); rel->r_index = (temp >> 8) & 0x00FFFFFF; rel->r_extern = (temp >> 7) & 1; rel->r_type = (reloc_type_sparc) ((temp) & 0x1F); #ifdef DEBUG if ( rel->r_address >= 0x80C8 && rel->r_address <= 0x8200 ) msg("%08X: index=0x%06X extern=%d type=%02X addend=0x%08X\n", rel->r_address, rel->r_index, rel->r_extern, rel->r_type, rel->r_addend); #endif } // perform relocation for ( reloc_info_sparc *rel = reltab; rel < reltab + relcount; rel++ ) { uint32 where = base + rel->r_address; uint32 instr = get_dword(where); fixup_type_t type = FIXUP_OFF32; uint32 target = where; uint32 value; uint32 merged; #ifdef DEBUG value = instr; merged = instr; #endif if ( rel->r_extern ) { if ( rel->r_index >= ctx.symcount ) { msg("%08X: relocation to extern symbol idx %08X out of bounds, ignored\n", where, rel->r_index); continue; } nlist *sym = &ctx.symtab[rel->r_index]; // The in-database address for this symbol was set when loading the symtab. target = sym->n_value; target += rel->r_addend; /* if ( (sym->n_type & N_TYPE ) != N_ABS && (sym->n_type & N_TYPE) != N_COMM) target += where; if ( (rel->r_type == SPARC_RELOC_PC10 ) || (rel->r_type == SPARC_RELOC_PC22)) target -= where; */ } else { if ( rel->r_type == SPARC_RELOC_HI22 || rel->r_type == SPARC_RELOC_LO10 ) target = rel->r_addend; if ( seg == 2 && rel->r_type == SPARC_RELOC_32 ) target = rel->r_addend; /* if ( (rel->r_index == N_TEXT ) || (rel->r_index == N_DATA) || (rel->r_index == N_BSS)) target = rel->r_addend; */ } uint32 delta = 0; switch ( rel->r_type ) { case SPARC_RELOC_32: value = instr; target += value; merged = target; break; case SPARC_RELOC_WDISP30: value = (instr & S_MASK(30)); target += value; merged = (instr & ~S_MASK(30)) | ((target >> 2) & S_MASK(30)); break; case SPARC_RELOC_WDISP22: value = (instr & S_MASK(22)); target += value; merged = (instr & ~S_MASK(22)) | ((target >> 2) & S_MASK(22)); break; case SPARC_RELOC_HI22: value = (instr & S_MASK(22)) << 10; target += value; merged = (instr & ~S_MASK(22)) | ((target >> 10) & S_MASK(22)); delta = 0; //-(target & S_MASK(10)); type = get_hi22_reltype(); break; case SPARC_RELOC_LO10: value = (instr & S_MASK(10)); target += value; merged = (instr & ~S_MASK(10)) | ((target) & S_MASK(10)); type = get_lo10_reltype(); break; default: msg("Unsupported sparc relocation type 0x%02X, ignored\n", rel->r_type); continue; } #ifdef DEBUG // if ( rel->r_address < 0x300 ) // msg("%08X: %08X -> %08X (%08X -> %08X)\n", where, instr, merged, value, target); #endif put_dword(where, merged); do_fixup(where, rel->r_extern ? rel->r_addend : delta, target, type, rel->r_extern); } qfree(reltab); } //---------------------------------------------------------------------- static void do_relocation(linput_t *li, const aout_t &ctx, uint32 off, uint32 len, int seg) { switch ( N_MACHTYPE(ctx.ex) ) { case M_SPARC: do_relocation_sparc(li, ctx, off, len, seg); break; default: msg("Warning: Relocation in image file not supported yet for this processor\n"); break; } } //---------------------------------------------------------------------- void load_syms(linput_t *li, aout_t &ctx) { // get string table length uint32 tabsize = 0; qlseek(li, ctx.stroff); lread4bytes(li, &tabsize, ctx.msb); #ifdef DEBUG msg("symoff=0x%08x symlen=0x%08x stroff=0x%08x strlen=0x%08x\n", ctx.symoff, ctx.ex.a_syms, ctx.stroff, tabsize); #endif // load string table char *strtab = (char *)qalloc(tabsize+1); if ( strtab == NULL ) loader_failure("Unable to allocate string table for %u bytes\n", tabsize+1); qlseek(li, ctx.stroff); lreadbytes(li, strtab, tabsize, false); strtab[tabsize] = '\0'; // ensure trailing zero // load symbol table ctx.symcount = ctx.ex.a_syms / sizeof(nlist); validate_array_count_or_die(li, ctx.symcount, sizeof(nlist), "Number of symbols", ctx.symoff); nlist *symtab = qalloc_array(ctx.symcount); if ( symtab == NULL ) loader_failure("Unable to allocate symbol table for %u entries\n", ctx.symcount); qlseek(li, ctx.symoff); uint32 extern_count = 0; for ( nlist *sym = symtab; sym < symtab + ctx.symcount; sym++ ) { lread4bytes(li, &sym->n_un.n_strx, ctx.msb); lreadbytes(li, &sym->n_type, 1, ctx.msb); lreadbytes(li, &sym->n_other, 1, ctx.msb); lread2bytes(li, &sym->n_desc, ctx.msb); lread4bytes(li, &sym->n_value, ctx.msb); if ( sym->n_type == N_EXT ) extern_count++; } // create extern section uint32 extern_base = ctx.top; if ( extern_count != 0 ) { // create new segment add_segm(0, extern_base, extern_base + (extern_count * 4), "extern", "XTRN", ADDSEG_SPARSE); ctx.extrn = extern_base; ctx.top += extern_count * 4; } // import symbols #ifdef DEBUG int i = 0; #endif uint32 i_extern = 0; for ( nlist *sym = symtab; sym < symtab + ctx.symcount; sym++ ) { if ( sym->n_type & N_STAB ) // debug stab info, not a symbol continue; if ( sym->n_type == N_EXT ) { sym->n_value = extern_base + (i_extern * 4); if ( getseg(sym->n_value) ) put_dword(sym->n_value, 0); i_extern++; } if ( getseg(sym->n_value) != NULL ) { if ( sym->n_un.n_strx < tabsize ) { set_name(sym->n_value, strtab + sym->n_un.n_strx, (sym->n_type & N_EXT ? SN_PUBLIC : SN_NON_PUBLIC)|SN_NOCHECK|SN_NOWARN|SN_IDBENC); } else { msg("%08X: type=0x%02X other=0x%02X desc=0x%04X: bad str offset %08X\n", sym->n_value, sym->n_type, sym->n_other, sym->n_desc, sym->n_un.n_strx); } } if ( (sym->n_type & N_TYPE) == N_ABS ) { #ifdef DEBUG msg("%04X: %08X: type=0x%02X other=0x%02X desc=0x%04X: %s\n", i, sym->n_value, sym->n_type, sym->n_other, sym->n_desc, strtab + sym->n_un.n_strx); #endif } #ifdef DEBUG i++; #endif } ctx.strtab = strtab; ctx.symtab = symtab; } //-------------------------------------------------------------------------- static void handle_ld_info(linput_t *li, int diroff, int base) { lddir_t lddir; qlseek(li, diroff); lread(li, &lddir, sizeof(lddir)); ld_info_t ldinfo; qlseek(li, lddir.ldinfo-base); lread(li, &ldinfo, sizeof(ldinfo)); qoff64_t fpos = ldinfo.symbols - base; int nsyms = (ldinfo.strings - ldinfo.symbols) / sizeof(ld_symbol_t); validate_array_count(li, &nsyms, sizeof(ld_symbol_t), "Symbol count", fpos); qlseek(li, fpos); for ( int i=0; i < nsyms; i++ ) { ld_symbol_t sym; lread(li, &sym, sizeof(sym)); char name[MAXSTR]; qlgetz(li, ldinfo.strings + sym.nameoff - base, name, sizeof(name)); set_name(sym.addr, name, SN_IDBENC); if ( (sym.flags & (AOUT_LD_FUNC|AOUT_LD_DEF)) == AOUT_LD_FUNC ) { // imported function put_byte(sym.addr, 0xC3); // return if ( sym.addr <= ldinfo.ldentry ) warning("interr: symbol #%d (%s) is not in the plt", i, name); } } } //-------------------------------------------------------------------------- // // load file into the database. // void idaapi load_file(linput_t *li, ushort /*neflag*/, const char * /*fileformatname*/) { processor_t &ph = PH; aout_t ctx; exec &ex = ctx.ex; ctx.msb = ana_hdr(ph, li, &ex); ctx.symtab = NULL; ctx.symcount = 0; ctx.strtab = NULL; ctx.treloff = N_TRELOFF(ex); ctx.dreloff = N_DRELOFF(ex); ctx.symoff = N_SYMOFF(ex); ctx.stroff = N_STROFF(ex); int txtoff = N_TXTOFF(ex); int txtadr; switch ( ph.id ) { case PLFM_SPARC: txtoff = N_TXTOFF_SPARC(ex); txtadr = N_TXTADDR_SPARC(ex); ctx.treloff = N_TRELOFF_SPARC(ex); ctx.dreloff = N_DRELOFF_SPARC(ex); ctx.symoff = N_SYMOFF_SPARC(ex); ctx.stroff = N_STROFF_SPARC(ex); break; case PLFM_ARM: txtadr = N_TXTADDR_ARM(ex); break; default: txtadr = N_TXTADDR(ex); switch ( N_MAGIC(ex) ) { // case NMAGIC: // case CMAGIC: default: loader_failure("This image type is not supported yet"); case ZMAGIC: if ( qlsize(li) < ex.a_text + ex.a_data + N_SYMSIZE(ex) + txtoff ) { txtoff = 0; txtadr = 0x1000; } else if ( txtoff < 512 ) { loader_failure("Size of demand page < size of block"); } // fallthrough case QMAGIC: if ( ex.a_text & 0xFFF || ex.a_data & 0xFFF ) loader_failure("Executable is not page aligned"); break; case OMAGIC: txtoff = sizeof(ex); break; } break; } inf_set_baseaddr(0); uint32 base, top; top = base = txtadr; if ( ex.a_text || ex.a_data ) { top += ex.a_text; // msg("txtoff=%d, base=%d top=%d end=%d\n", txtoff, base, top, top+ex.a_data); file2base(li, txtoff, base, top + ex.a_data, FILEREG_PATCHABLE); if ( ex.a_text ) { create32(ph, 1, base, top, NAME_CODE, CLASS_CODE); inf_set_start_cs(1); inf_set_start_ip(ex.a_entry); ctx.text = base; } if ( ex.a_data ) { base = top; create32(ph, 2, base, top += ex.a_data, NAME_DATA, CLASS_DATA); set_default_dataseg(2); ctx.data = base; } } if ( ex.a_bss ) { create32(ph, 3, top, top + ex.a_bss, NAME_BSS, CLASS_BSS); ctx.bss = top; } ctx.top = top + ex.a_bss; if ( ex.a_syms ) load_syms(li, ctx); if ( N_TRSIZE(ex) ) do_relocation(li, ctx, ctx.treloff, N_TRSIZE(ex), 1); if ( N_DRSIZE(ex) ) do_relocation(li, ctx, ctx.dreloff, N_DRSIZE(ex), 2); // We come in here for the regular a.out style of shared libraries */ // ((ex.a_entry & 0xfff) && N_MAGIC(ex) == ZMAGIC) || // return -ENOEXEC; // } // For QMAGIC, the starting address is 0x20 into the page. We mask // this off to get the starting address for the page */ // start_addr = ex.a_entry & 0xfffff000; ////// if ( ph.id != PLFM_SPARC && N_FLAGS(ex) & EX_PIC ) handle_ld_info(li, ex.a_text, txtadr); create_filename_cmt(); add_pgm_cmt("Flag value: %Xh", N_FLAGS(ex)); } //---------------------------------------------------------------------- // // LOADER DESCRIPTION BLOCK // //---------------------------------------------------------------------- loader_t LDSC = { IDP_INTERFACE_VERSION, 0, // loader flags // // check input file format. if recognized, then return 1 // and fill 'fileformatname'. // otherwise return 0 // accept_file, // // load file into the database. // load_file, // // create output file from the database. // this function may be absent. // NULL, // take care of a moved segment (fix up relocations, for example) NULL, NULL, };