/* * Interactive disassembler (IDA). * Copyright (c) 1990-2020 by Ilfak Guilfanov, * ALL RIGHTS RESERVED. * * AMIGA hunk file loader * */ #include "../idaldr.h" #include "amiga.hpp" #define SkipLong(Longs) do { if ( qlseek(li, 4 * qoff64_t(Longs), SEEK_CUR) == -1 ) goto TRUNCATED_INPUT; } while ( 0 ) #define SkipWord(Words) do { if ( qlseek(li, 2 * qoff64_t(Words), SEEK_CUR) == -1 ) goto TRUNCATED_INPUT; } while ( 0 ) #define SkipByte(Bytes) do { if ( qlseek(li, 1 * qoff64_t(Bytes), SEEK_CUR) == -1 ) goto TRUNCATED_INPUT; } while ( 0 ) //------------------------------------------------------------------------------ static void ask_for_help(void) { ask_for_feedback("This file contains some untested records"); } //------------------------------------------------------------------------------ static char *read_name(linput_t *li, char *buf, size_t bufsize, int Longs) { if ( ssize_t(bufsize) > 0 ) { size_t sz = Longs; if ( sz != 0 ) { sz *= 4; if ( sz >= bufsize ) sz = bufsize-1; lread(li, buf, sz); } buf[sz] = '\0'; } return buf; } //------------------------------------------------------------------------------ void idaapi load_file(linput_t *li,ushort /*_neflags*/,const char * /*fileformatname*/) { set_processor_type("68040", SETPROC_LOADER); uint32 Type, Data, i; int nums; char NameString[MAXSTR]; bool has_header = false; bool shortreloc = false; ea_t start = to_ea(inf_get_baseaddr(), 0); ea_t end = start; // // The first pass // qoff64_t fsize = qlsize(li); qlseek(li, 0); while ( true ) { i = (uint32)qlread(li, &Type, sizeof(Type)); if ( i != sizeof(Type) ) { if ( i != 0 ) warning("There %s %u extra byte%s at end of file.", i == 1 ? "is" : "are", i, i == 1 ? "" : "s"); break; } Type = swap32(Type); if ( Type == HUNK_DREL32 && has_header ) Type = HUNK_DREL32EXE; switch ( Type & 0xFFFF ) { case HUNK_UNIT: read_name(li, NameString, sizeof(NameString), mf_readlong(li)); break; case HUNK_NAME: read_name(li, NameString, sizeof(NameString), mf_readlong(li)); break; case HUNK_LIB: SkipLong(1); break; case HUNK_INDEX: SkipLong(mf_readlong(li)); break; case HUNK_CODE: case HUNK_PPC_CODE: case HUNK_DATA: case HUNK_BSS: { Data = mf_readlong(li); Data <<= 2; Data &= 0x7FFFFFFF; start = free_chunk(end, Data, -0xF); end = start + Data; if ( end < start ) loader_failure("Segment address overlow: %a..%a", start, end); const char *sname = NULL; sel_t sel = get_segm_qty() + 1; set_selector(sel, 0); switch ( Type & 0xFFFF ) { case HUNK_PPC_CODE: set_processor_type("ppc", SETPROC_LOADER); sname = "PPC_CODE"; break; case HUNK_CODE: sname = "CODE"; if ( inf_get_start_cs() == BADSEL ) { inf_set_start_cs(sel); inf_set_start_ip(start); } break; case HUNK_DATA: sname = "DATA"; break; case HUNK_BSS: sname = "BSS"; break; } if ( (Type & 0xFFFF) != HUNK_BSS ) { uint64 rest = fsize - qltell(li); if ( end-start > rest ) loader_failure("Too big segment %a..%a", start, end); file2base(li, qltell(li), start, end, FILEREG_PATCHABLE); } segment_t s; s.sel = setup_selector(sel); s.start_ea = start; s.end_ea = end; s.align = saRelByte; s.comb = scPub; s.bitness = PH.get_segm_bitness(); add_segm_ex(&s, sname, sname, ADDSEG_NOSREG|ADDSEG_SPARSE); } break; case HUNK_RELOC32SHORT: case HUNK_DREL32EXE: shortreloc = true; // no break case HUNK_RELRELOC32: case HUNK_ABSRELOC16: case HUNK_RELRELOC26: case HUNK_RELOC32: case HUNK_RELOC16: case HUNK_RELOC8: case HUNK_DREL32: case HUNK_DREL16: case HUNK_DREL8: nums = 0; while ( true ) { if ( qltell(li) >= fsize ) TRUNCATED_INPUT: loader_failure("Truncated file"); Data = shortreloc ? mf_readshort(li) : mf_readlong(li); if ( Data == 0 ) break; shortreloc ? mf_readshort(li) : mf_readlong(li); if ( shortreloc ) SkipWord(Data); else SkipLong(Data); nums += Data; } if ( (nums & 1) == 0 && shortreloc ) SkipWord(1); shortreloc = false; break; case HUNK_EXT: while ( true ) { if ( qltell(li) >= fsize ) goto TRUNCATED_INPUT; Data = mf_readlong(li); if ( Data == 0 ) break; /* Is it followed by a symbol name? */ if ( Data & 0xFFFFFF ) read_name(li, NameString, sizeof(NameString), Data & 0xFFFFFF); /* Remember extension type. */ int32 exttype = (Data >> 24) & 0xFF; /* Display value of symbol. */ if ( exttype == EXT_DEF || exttype == EXT_ABS || exttype == EXT_RES ) mf_readlong(li); /* Skip relocation information. */ if ( exttype == EXT_REF32 || exttype == EXT_REF16 || exttype == EXT_REF8 // || exttype == EXT_DEXT32 // || exttype == EXT_DEXT16 // || exttype == EXT_DEXT8 // || exttype == EXT_RELREF32 || exttype == EXT_RELREF26 ) { SkipLong(mf_readlong(li)); } /* Display size of common block. */ if ( exttype == EXT_COMMON ) { mf_readlong(li); SkipLong(mf_readlong(li)); } } break; case HUNK_SYMBOL: while ( true ) { if ( qltell(li) >= fsize ) goto TRUNCATED_INPUT; Data = mf_readlong(li); if ( Data == 0 ) break; read_name(li, NameString, sizeof(NameString), Data & 0xFFFFFF); mf_readlong(li); } break; case HUNK_DEBUG: SkipLong(mf_readlong(li)); break; case HUNK_END: break; case HUNK_HEADER: { has_header = true; while ( true ) { if ( qltell(li) >= fsize ) goto TRUNCATED_INPUT; Data = mf_readlong(li); if ( Data == 0 ) break; read_name(li, NameString, sizeof(NameString), Data); } mf_readlong(li); int32 From = mf_readlong(li); int32 To = mf_readlong(li); SkipLong(To-From+1); } break; case HUNK_OVERLAY: { int32 TabSize = mf_readlong(li); if ( TabSize ) { mf_readlong(li); SkipLong(TabSize); } int32 hunktype = mf_readlong(li); if ( TabSize && hunktype >= HUNK_UNIT && hunktype <= HUNK_ABSRELOC16 ) qlseek(li, -4, SEEK_CUR); } break; case HUNK_BREAK: break; default: warning("Unknown hunk type %04X - Aborting!", Type & 0xFFFF); return; } } // // The second pass // qlseek(li, 0); int nseg = 0; while ( true ) { i = (uint32)qlread(li, &Type, sizeof(Type)); if ( i != sizeof(Type) ) break; Type = swap32(Type); if ( Type == HUNK_DREL32 && has_header ) Type = HUNK_DREL32EXE; switch ( Type & 0xFFFF ) { case HUNK_UNIT: read_name(li, NameString, sizeof(NameString), mf_readlong(li)); add_pgm_cmt("Unit: %s", NameString); break; case HUNK_NAME: read_name(li, NameString, sizeof(NameString), mf_readlong(li)); add_pgm_cmt("Title: %s", NameString); break; case HUNK_LIB: mf_readlong(li); break; case HUNK_INDEX: SkipLong(mf_readlong(li)); break; case HUNK_CODE: case HUNK_PPC_CODE: case HUNK_DATA: case HUNK_BSS: Data = mf_readlong(li); Data <<= 2; Data &= 0x7FFFFFFF; if ( (Type & 0xFFFF) != HUNK_BSS ) SkipByte(Data); nseg++; break; case HUNK_RELOC32SHORT: case HUNK_DREL32EXE: shortreloc = true; // no break case HUNK_RELRELOC32: case HUNK_ABSRELOC16: case HUNK_RELRELOC26: case HUNK_RELOC32: case HUNK_RELOC16: case HUNK_RELOC8: case HUNK_DREL32: case HUNK_DREL16: case HUNK_DREL8: nums = 0; while ( (Data=(shortreloc ? mf_readshort(li) : mf_readlong(li))) != 0 ) { uint32 dat2 = shortreloc ? mf_readshort(li) : mf_readlong(li); segment_t *s = get_segm_by_sel(dat2+1); segment_t *ssrc = get_segm_by_sel(nseg); ea_t base = BADADDR; if ( ssrc != NULL ) base = ssrc->start_ea; else s = NULL; int elsize = shortreloc ? 2 : 4; validate_array_count_or_die(li, Data, elsize, "Number of relocations"); for ( uint32 dat3 = Data; dat3; --dat3 ) { uint32 off = shortreloc ? mf_readshort(li) : mf_readlong(li); if ( s != NULL ) { ea_t src = base + off; ea_t dst = s->start_ea; ea_t target = BADADDR; fixup_type_t fd_type = 0; switch ( Type & 0xFFFF ) { case HUNK_RELRELOC32: case HUNK_RELOC32: case HUNK_DREL32: case HUNK_RELOC32SHORT: case HUNK_DREL32EXE: target = get_dword(src)+dst; put_dword(src, target); fd_type = FIXUP_OFF32; break; case HUNK_ABSRELOC16: case HUNK_RELRELOC26: case HUNK_RELOC16: case HUNK_DREL16: target = get_word(src)+dst; put_word(src, target); fd_type = FIXUP_OFF16; break; case HUNK_RELOC8: case HUNK_DREL8: target = get_byte(src)+dst; put_byte(src, (uint32)target); fd_type = FIXUP_OFF8; break; } if ( fd_type != 0 ) { fixup_data_t fd(fd_type); fd.sel = dat2 + 1; fd.off = target; fd.set(src); } } else { ask_for_help(); } } nums += Data; } if ( (nums & 1) == 0 && shortreloc ) SkipWord(1); shortreloc = false; break; case HUNK_EXT: ask_for_help(); while ( (Data=mf_readlong(li)) != 0 ) { switch ( (Data >> 24) & 0xFF ) { case EXT_DEF: msg(" EXT_DEF"); break; case EXT_ABS: msg(" EXT_ABS"); break; case EXT_RES: msg(" EXT_RES"); break; case EXT_REF32: msg(" EXT_REF32"); break; case EXT_COMMON: msg(" EXT_COMMON"); break; case EXT_REF16: msg(" EXT_REF16"); break; case EXT_REF8: msg(" EXT_REF8"); break; // case EXT_DEXT32: msg(" EXT_DEXT32"); break; // case EXT_DEXT16: msg(" EXT_DEXT16"); break; // case EXT_DEXT8: msg(" EXT_DEXT8"); break; // case EXT_RELREF32: msg(" EXT_RELREF32"); break; // case EXT_RELREF26: msg(" EXT_RELREF26"); break; // case EXT_RELCOMMON: msg(" EXT_RELCOMMON"); break; default: msg(" EXT_??? (%02x)\n",(Data >> 24) & 0xFF); break; } /* Is it followed by a symbol name? */ if ( Data & 0xFFFFFF ) { read_name(li, NameString, sizeof(NameString), Data & 0xFFFFFF); msg(" %s", NameString); } /* Remember extension type. */ int32 exttype = (Data >> 24) & 0xFF; /* Display value of symbol. */ if ( exttype == EXT_DEF || exttype == EXT_ABS || exttype == EXT_RES ) { if ( !(Data & 0xFFFFFF) ) msg(" ???"); Data = mf_readlong(li); msg("%08X", Data); } /* Skip relocation information. */ if ( exttype == EXT_REF32 || exttype == EXT_REF16 || exttype == EXT_REF8 // || exttype == EXT_DEXT32 // || exttype == EXT_DEXT16 // || exttype == EXT_DEXT8 // || exttype == EXT_RELREF32 || exttype == EXT_RELREF26 ) { Data = mf_readlong(li); msg("= %u entr%s", Data, Data == 1 ? "y" : "ies"); SkipLong(Data); } /* Display size of common block. */ if ( exttype == EXT_COMMON ) { Data = mf_readlong(li); msg(" Size = %u bytes", Data << 2); Data = mf_readlong(li); SkipLong(Data); } msg("\n"); } break; case HUNK_SYMBOL: while ( (Data=mf_readlong(li)) != 0 ) { /* Display name. */ read_name(li, NameString, sizeof(NameString), Data & 0xFFFFFF); /* Display value. */ Data = mf_readlong(li); segment_t *ssrc = get_segm_by_sel(nseg); if ( ssrc == NULL ) ask_for_help(); else set_name(ssrc->start_ea+Data, NameString, SN_NOCHECK | SN_NOWARN | SN_IDBENC); } break; case HUNK_DEBUG: SkipLong(mf_readlong(li)); break; case HUNK_END: break; case HUNK_HEADER: { has_header = true; while ( (Data=mf_readlong(li)) != 0 ) read_name(li, NameString, sizeof(NameString), Data); mf_readlong(li); int32 From = mf_readlong(li); int32 To = mf_readlong(li); SkipLong(To-From+1); } break; case HUNK_OVERLAY: { int32 TabSize = mf_readlong(li); if ( TabSize ) { mf_readlong(li); SkipLong(TabSize); } int32 hunktype = mf_readlong(li); if ( TabSize && hunktype >= HUNK_UNIT && hunktype <= HUNK_ABSRELOC16 ) qlseek(li, -4, SEEK_CUR); } break; case HUNK_BREAK: break; default: warning("Unknown hunk type %04X - Aborting!", Type & 0xFFFF); return; } } create_filename_cmt(); } //-------------------------------------------------------------------------- static int idaapi accept_file( qstring *fileformatname, qstring *processor, linput_t *li, const char *) { qlseek(li, 0); uint32 type; if ( qlread(li, &type, sizeof(uint32)) == sizeof(uint32) && swap32(type) == HUNK_HEADER ) { *fileformatname = "Amiga hunk file"; *processor = "68040"; return 1; } return 0; } //-------------------------------------------------------------------------- int idaapi move_segm_relocs(ea_t from, ea_t to, asize_t size, const char *fileformatname) { qnotused(size); qnotused(fileformatname); if ( from == BADADDR ) { // The entire program is being rebased. // In this case, 'to' actually contains a delta value; the number of bytes // forward (positive) or backward (negative) that the whole database is // being moved. ea_t delta = to; // fix up relocations fixup_data_t fd; for ( ea_t xEA = get_first_fixup_ea(); xEA != BADADDR; xEA = get_next_fixup_ea(xEA) ) { show_addr(xEA); get_fixup(&fd, xEA); fd.off += delta; switch ( fd.get_type() ) { case FIXUP_OFF8: put_byte(xEA, fd.off); break; case FIXUP_OFF16: put_word(xEA, fd.off); break; case FIXUP_OFF32: put_dword(xEA, fd.off); break; } set_fixup(xEA, fd); } // Record the new image base address. inf_set_baseaddr(inf_get_baseaddr() + delta); // set_imagebase(new_base); } return 1; } //-------------------------------------------------------------------------- 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) move_segm_relocs, NULL, };