Files
sigmaker-ida/idasdk76/ldr/amiga/amiga.cpp
2021-10-31 21:20:46 +02:00

611 lines
17 KiB
C++

/*
* Interactive disassembler (IDA).
* Copyright (c) 1990-2020 by Ilfak Guilfanov, <ig@datarescue.com>
* 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,
};