/* * Interactive disassembler (IDA). * Copyright (c) 1990-97 by Ilfak Guilfanov. * ALL RIGHTS RESERVED. * E-mail: ig@estar.msk.su * FIDO: 2:5020/209 * * ARM Image File (AIF) Loader * --------------------------- * This module allows IDA to load ARM image files into * its database and to disassemble them correctly. * * NOTE: Compressed image files are not supported * Self-relocating image files are not supported * Thumb image files are not supported * Only 32-bit image files are supported * * This module automatically detects the byte sex and sets inf.mf * variable accrodingly. * * The debug information is partially processed. * */ #include #include "../idaldr.h" #include "aif.h" #include "../aof/aof.h" // the following function is defined to be used by aifcmn.cpp // included below (see also efd/aif.cpp) inline bool is_mf() { return inf_is_be(); } #include "aifcmn.cpp" //-------------------------------------------------------------------------- // // 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 *) { if ( !is_aif_file(li) ) return 0; *fileformatname = "ARM Image File"; *processor = "arm"; return 1; } //-------------------------------------------------------------------------- // Create a section. static void create_section( ushort sel, ea_t start_ea, ea_t end_ea, const char *name, const char *classname) { set_selector(sel, 0); segment_t s; s.sel = sel; s.start_ea = start_ea; s.end_ea = end_ea; s.align = saRelByte; s.comb = scPub; s.bitness = 1; // 32-bit int flags = ADDSEG_SPARSE | ADDSEG_NOSREG | ADDSEG_NOTRUNC; if ( !add_segm_ex(&s, name, classname, flags) ) loader_failure(); segment_t *sptr = getseg(start_ea); set_arm_segm_flags(start_ea, 2 << 10); // alignment sptr->update(); } //-------------------------------------------------------------------------- // The assembler and the compiler generate lots of meaningless symbols. // We will ignore them. static bool special_name(const char *name) { int i; if ( name[0] == '\0' ) return true; if ( name[0] == '$' ) return true; const char *ptr = strchr(name,'$'); if ( ptr != NULL && ptr[1] == '$' ) return true; static const char *const ex[] = { "_etext", "_edata", "_end", "!!!" }; for ( i=0; i < qnumber(ex); i++ ) if ( strcmp(ex[i],name) == 0 ) return true; static const char *const data_names[] = { "x$constdata", "x$litpool" }; for ( i=0; i < qnumber(data_names); i++ ) if ( strncmp(name, data_names[i], strlen(data_names[i])) == 0 ) return true; return false; } //-------------------------------------------------------------------------- // The debug information says that "xlitpool" symbols have "CODE" type. // We cannot base on this because doing so we would convert // xlitpools to instructions. // So, we will look at the names and if a location has // "xlitpool" or similar name, we will not convert it to instructions // even it is marked as "CODE". // // Later: I decided not to use all those names at all. static bool is_true_text_symbol(dsym_t *ds,const char *name) { if ( ds->is_text() ) { static const char *const data_names[] = { "x$constdata", "x$litpool" }; for ( int i=0; i < qnumber(data_names); i++ ) if ( strncmp(name,data_names[i],strlen(data_names[i])) == 0 ) return false; return true; } return false; } //-------------------------------------------------------------------------- // Process debugging information item and try to incorporate it into // the database. // NOTE: This function does not process all debugging information. // It knows only about some types of debugingo. static size_t process_item(uchar *di, size_t disize, section_t *sect) { uchar *const end = di + disize; if ( disize < 4 ) return 0; uint32 fw = *(uint32 *)di; if ( inf_is_be() ) fw = swap32(fw); size_t len = fw >> 16; if ( len == 0 || len > disize ) return 0; switch ( fw & 0xFFFF ) { case AIF_DEB_SECT: // section if ( disize < sizeof(section_t) ) return 0; sect = (section_t *)di; if ( inf_is_be() ) swap_section(sect); if ( sect->debugsize != 0 ) { len = sect->debugsize; if ( len > disize ) return 0; } switch ( sect->lang ) { case LANG_C: add_extra_cmt(sect->codestart, true, "C source level debugging data is present"); break; case LANG_PASCAL: add_extra_cmt(sect->codestart, true, "Pascal source level debugging data is present"); break; case LANG_FORTRAN: add_extra_cmt(sect->codestart, true, "Fortran-77 source level debugging data is present"); break; case LANG_ASM: add_extra_cmt(sect->codestart, true, "ARM assembler line number data is present"); break; } if ( sect->lang == LANG_NONE ) { size_t nsyms = size_t(sect->name); dsym_t *ds = (dsym_t *)(sect+1); char *str = (char *)(ds+nsyms); if ( !is_mul_ok(nsyms, sizeof(dsym_t)) || ds+nsyms < ds || str >= (char *)end ) return 0; bool use_pascal = swap_symbols(ds, str, end, nsyms); for ( int i=0; i < nsyms; i++,ds++ ) { if ( ds->sym & ASD_16BITSYM ) continue; size_t off = size_t(ds->sym & ASD_SYMOFF); char *name = str + off + use_pascal; if ( name < str || name >= (char *)end ) continue; if ( special_name(name) ) continue; if ( ds->sym == ASD_ABSSYM ) // if the symbol is absolute { add_pgm_cmt("%s = 0x%X", name, ds->value); } else if ( is_mapped(ds->value) ) { if ( ds->sym & ASD_GLOBSYM ) { add_entry(ds->value, ds->value, name, is_true_text_symbol(ds, name), AEF_IDBENC); } else { force_name(ds->value, name, SN_IDBENC); if ( is_true_text_symbol(ds, name) ) auto_make_code(ds->value); } } } } else { char name[64]; const uchar *nptr = (const uchar *)§->name; size_t namelen = *nptr++; if ( namelen > end-nptr || namelen >= sizeof(name) ) return 0; qstrncpy(name, (const char *)nptr, sizeof(name)); name[namelen] = '\0'; if ( sect->codestart != 0 ) add_extra_cmt(sect->codestart, true, "Section \"%s\", size 0x%X",name,sect->codesize); if ( sect->datastart != 0 ) add_extra_cmt(sect->datastart, true, "Section \"%s\", size 0x%X",name,sect->datasize); } #if 0 if ( sect->fileinfo != 0 ) // fileinfo is present? process_item(di+size_t(sect->fileinfo),sect); #endif break; case AIF_DEB_FDEF: // procedure/function definition deb(IDA_DEBUG_LDR, "procedure/function definition\n"); break; case AIF_DEB_ENDP: // endproc deb(IDA_DEBUG_LDR, "endproc\n"); break; case AIF_DEB_VAR: // variable deb(IDA_DEBUG_LDR, "variable\n"); break; case AIF_DEB_TYPE: // type deb(IDA_DEBUG_LDR, "type\n"); break; case AIF_DEB_STRU: // struct deb(IDA_DEBUG_LDR, "struct\n"); break; case AIF_DEB_ARRAY: // array deb(IDA_DEBUG_LDR, "array\n"); break; case AIF_DEB_RANGE: // subrange deb(IDA_DEBUG_LDR, "subrange\n"); break; case AIF_DEB_SET: // set deb(IDA_DEBUG_LDR, "set\n"); break; case AIF_DEB_FILE: // fileinfo deb(IDA_DEBUG_LDR, "fileinfo\n"); break; case AIF_DEB_CENUM: // contiguous enumeration deb(IDA_DEBUG_LDR, "contiguous enumeration\n"); break; case AIF_DEB_DENUM: // discontiguous enumeration deb(IDA_DEBUG_LDR, "discontiguous enumeration\n"); break; case AIF_DEB_FDCL: // procedure/function declaration deb(IDA_DEBUG_LDR, "procedure/function declaration\n"); break; case AIF_DEB_SCOPE: // begin naming scope deb(IDA_DEBUG_LDR, "begin naming scope\n"); break; case AIF_DEB_ENDS: // end naming scope deb(IDA_DEBUG_LDR, "end naming scope\n"); break; case AIF_DEB_BITF: // bitfield deb(IDA_DEBUG_LDR, "bitfield\n"); break; case AIF_DEB_MACRO: // macro definition deb(IDA_DEBUG_LDR, "macro definition\n"); break; case AIF_DEB_ENDM: // macro undefinition deb(IDA_DEBUG_LDR, "macro undefinition\n"); break; case AIF_DEB_CLASS: // class deb(IDA_DEBUG_LDR, "class\n"); break; case AIF_DEB_UNION: // union deb(IDA_DEBUG_LDR, "union\n"); break; case AIF_DEB_FPMAP: // FP map fragment deb(IDA_DEBUG_LDR, "FP map fragment\n"); break; default: msg("unknown (0x%u.)!!!\n", fw & 0xFFFF); break; } return len; } //-------------------------------------------------------------------------- // // load file into the database. // void idaapi load_file(linput_t *li, ushort /*neflag*/, const char * /*fileformatname*/) { aif_header_t hd; set_processor_type("arm", SETPROC_LOADER); lread(li, &hd, sizeof(hd)); inf_set_be(match_zero_code(&hd) != 1); if ( (hd.address_mode & 0xFF) != 32 ) { if ( (hd.address_mode & 0xFF) != 0 ) loader_failure("26-bit modules are not supported"); msg("Old AIF format file..."); } if ( hd.decompress_code != NOP ) loader_failure("Compressed modules are not supported"); if ( hd.self_reloc_code != NOP ) loader_failure("Self-relocating modules are not supported"); inf_set_baseaddr(0); int isexec = is_bl(hd.entry_point); qoff64_t offset = sizeof(aif_header_t); ea_t start = hd.image_base; if ( isexec ) { start += sizeof(aif_header_t); hd.readonly_size -= sizeof(aif_header_t); } uint64 rest = qlsize(li) - offset; if ( rest < hd.readonly_size ) BAD_FILE: loader_failure("Corrupted file"); ea_t end = start + hd.readonly_size; file2base(li, offset, start, end, FILEREG_PATCHABLE); create_section(1, start, end, NAME_CODE, CLASS_CODE); offset += hd.readonly_size; if ( hd.readwrite_size != 0 ) { rest = qlsize(li) - offset; if ( rest < hd.readwrite_size ) goto BAD_FILE; start = (hd.address_mode & AIF_SEP_DATA) ? hd.data_base : end; end = start + hd.readwrite_size; file2base(li, offset, start, end, FILEREG_PATCHABLE); create_section(2, start, end, NAME_DATA, CLASS_DATA); offset += hd.readwrite_size; } if ( hd.zero_init_size != 0 ) { start = end; end = start + hd.zero_init_size; create_section(3, start, end, NAME_BSS, CLASS_BSS); } create_filename_cmt(); if ( isexec ) hd.entry_point = hd.image_base + offsetof(aif_header_t,entry_point) + ((hd.entry_point & ~BLMASK) << 2) + 8; inf_set_start_cs(1); inf_set_start_ip(hd.entry_point); inf_set_start_ea(hd.entry_point); validate_array_count(li, &hd.debug_size, 1, "Size of debug info", offset); if ( hd.debug_size != 0 ) { msg("Debugging information is present (%u bytes at file offset 0x%" FMT_64 "X)...\n", hd.debug_size, offset); uchar *di = qalloc_array(size_t(hd.debug_size)); if ( di == NULL ) nomem("AIF debugging info"); qlseek(li, offset); lread(li, di, size_t(hd.debug_size)); uchar *ptr = di; uchar *diend = di + size_t(hd.debug_size); section_t *sect = NULL; while ( ptr < diend ) { size_t len = process_item(ptr, diend-ptr, sect); if ( len == 0 ) { warning("Corrupted debug info"); break; } ptr += len; } qfree(di); } } //---------------------------------------------------------------------- // // 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, };