763 lines
21 KiB
C++
763 lines
21 KiB
C++
/*
|
|
* This Loader Module is written by Ilfak Guilfanov and
|
|
* rewriten by Yury Haron
|
|
*
|
|
*/
|
|
/*
|
|
L O A D E R for MS-DOS file format's
|
|
*/
|
|
|
|
#include "../idaldr.h"
|
|
#include <exehdr.h>
|
|
#include <setjmp.h>
|
|
#include <typeinf.hpp>
|
|
#include "dos_ovr.h"
|
|
#include "cv.hpp"
|
|
|
|
static const char fn_ovr[] = "MS-DOS executable (perhaps overlayed)",
|
|
fn_exe[] = "MS-DOS executable (EXE)",
|
|
fn_drv[] = "MS-DOS SYS-file (perhaps device driver)";
|
|
const char e_exe[] = "exe";
|
|
|
|
static jmp_buf jmpb;
|
|
|
|
#define R_ss 18 // this comes from intel.hpp
|
|
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
// 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 *filename)
|
|
{
|
|
static int order = 0;
|
|
if ( order >= 4 )
|
|
return 0;
|
|
|
|
uint32 fLen = qlsize(li);
|
|
const char *file_ext = get_file_ext(filename);
|
|
if ( file_ext == NULL )
|
|
file_ext = "";
|
|
|
|
exehdr E;
|
|
*processor = "metapc";
|
|
switch ( order )
|
|
{
|
|
case 0:
|
|
if ( fLen <= sizeof(E) )
|
|
{
|
|
order = 3; // check for com
|
|
break;
|
|
}
|
|
|
|
CASSERT(sizeof(E) >= 16);
|
|
lread(li, &E, sizeof(E));
|
|
if ( E.exe_ident != EXE_ID && E.exe_ident != EXE_ID2
|
|
|| E.HdrSize*16 < sizeof(E) )
|
|
{
|
|
order = 2; // check for drv
|
|
break;
|
|
}
|
|
if ( fLen < E.HdrSize*16 )
|
|
return 0;
|
|
if ( E.ReloCnt != 0 )
|
|
{
|
|
if ( E.TablOff + (E.ReloCnt*4) > fLen
|
|
|| E.TablOff != 0 && E.TablOff < sizeof(E)
|
|
|| E.TablOff == 0 )
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
if ( E.CalcEXE_Length() < fLen - E.HdrSize*16
|
|
&& PrepareOverlayType(li, &E) != ovr_noexe )
|
|
{
|
|
*fileformatname = fn_ovr;
|
|
++order;
|
|
return f_EXE | ACCEPT_CONTINUE;
|
|
}
|
|
// no break
|
|
case 1:
|
|
*fileformatname = fn_exe;
|
|
order = 5; // done
|
|
return f_EXE;
|
|
|
|
case 2:
|
|
case 3:
|
|
break;
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
if ( ++order == 3 )
|
|
{
|
|
if ( strieq(file_ext, "sys") || strieq(file_ext, "drv") )
|
|
{
|
|
*fileformatname = fn_drv;
|
|
return f_DRV | ACCEPT_CONTINUE;
|
|
}
|
|
order++; // 4
|
|
}
|
|
|
|
if ( strieq(file_ext, "com") )
|
|
{ // com files must be readable
|
|
// on wince, file .exe files are unreadable. we do not want them to
|
|
// be detected as com files
|
|
qlseek(li, 0);
|
|
if ( qlread(li, &fLen, 1) == 1 )
|
|
{
|
|
*fileformatname = "MS-DOS COM-file";
|
|
return f_COM;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
NORETURN void errstruct(void)
|
|
{
|
|
if ( ask_yn(ASKBTN_CANCEL,
|
|
"HIDECANCEL\n"
|
|
"Bad file structure or read error.\n"
|
|
"Proceed with the loaded infomration?") <= ASKBTN_NO )
|
|
{
|
|
loader_failure();
|
|
}
|
|
longjmp(jmpb, 1);
|
|
#ifdef __CODEGEARC__
|
|
exit(0); // suppress compiler error
|
|
#endif
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
int CheckCtrlBrk(void)
|
|
{
|
|
if ( user_cancelled() )
|
|
{
|
|
if ( ask_yn(ASKBTN_NO,
|
|
"HIDECANCEL\n"
|
|
"Do you really want to abort loading?") > ASKBTN_NO )
|
|
{
|
|
loader_failure();
|
|
}
|
|
clr_cancelled();
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
void add_segm_by_selector(sel_t base, const char *sclass)
|
|
{
|
|
segment_t *ptr = get_segm_by_sel(base);
|
|
|
|
if ( ptr == NULL || ptr->sel != base )
|
|
{
|
|
ea_t ea = sel2ea(base);
|
|
if ( ea > inf_get_omax_ea() )
|
|
inf_set_omax_ea(ea);
|
|
|
|
segment_t s;
|
|
s.sel = base;
|
|
s.start_ea = sel2ea(base);
|
|
s.end_ea = inf_get_omax_ea();
|
|
s.align = saRelByte;
|
|
s.comb = sclass != NULL && strcmp(sclass, "STACK") == 0 ? scStack : scPub;
|
|
add_segm_ex(&s, NULL, sclass, ADDSEG_SPARSE | ADDSEG_NOSREG);
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
//
|
|
// For all addresses in relocation table:
|
|
// add 'delta'
|
|
// if ( dosegs ) then make segments
|
|
//
|
|
static void doRelocs(int16 delta, bool dosegs, netnode ovr_info)
|
|
{
|
|
|
|
if ( ovr_info == BADNODE )
|
|
return;
|
|
|
|
fixup_data_t fd(FIXUP_SEG16);
|
|
for ( ea_t xEA = ovr_info.altfirst(); xEA != BADADDR; xEA = ovr_info.altnext(xEA) )
|
|
{
|
|
show_addr(xEA);
|
|
|
|
uint16 curval = get_word(xEA);
|
|
uint16 base = curval + delta;
|
|
if ( base < curval && delta > 0 )
|
|
{
|
|
ask_for_feedback("%a: fixup overflow; skipping fixup processing", xEA);
|
|
break;
|
|
}
|
|
put_word(xEA, base);
|
|
fd.sel = base;
|
|
fd.set(xEA);
|
|
if ( dosegs )
|
|
add_segm_by_selector(base, NULL);
|
|
CheckCtrlBrk();
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
static void create_msdos_segments(bool com_mode, netnode ovr_info)
|
|
{
|
|
// msg("Creating segments...\n");
|
|
add_segm_by_selector(find_selector(inf_get_start_cs()), CLASS_CODE);
|
|
if ( com_mode ) // COM/DRV
|
|
{
|
|
set_segm_start(inf_get_omin_ea(), inf_get_omin_ea(), SEGMOD_KILL);
|
|
inf_set_min_ea(inf_get_omin_ea());
|
|
|
|
segment_t *s = getseg(inf_get_min_ea());
|
|
if ( s )
|
|
{
|
|
s->set_comorg(); // i display ORG directive
|
|
s->update();
|
|
}
|
|
}
|
|
if ( inf_get_start_ss() != BADSEL && inf_get_start_ss() != inf_get_start_cs() )
|
|
add_segm_by_selector(inf_get_start_ss(), CLASS_STACK);
|
|
else // specify the sp value for the first segment
|
|
set_default_sreg_value(get_segm_by_sel(inf_get_start_cs()), R_ss, inf_get_start_cs());
|
|
doRelocs(inf_get_baseaddr(), true, ovr_info);
|
|
|
|
ea_t ea = inf_get_omin_ea();
|
|
ea_t omea = inf_get_omax_ea();
|
|
for ( int i = 0; ea < omea; )
|
|
{
|
|
segment_t *sptr = getnseg(i);
|
|
if ( sptr == NULL || ea < sptr->start_ea )
|
|
{
|
|
msg("Dummy segment at 0x%a (next segment at 0x%a)\n",
|
|
ea,
|
|
sptr == NULL ? BADADDR : sptr->start_ea);
|
|
add_segm_by_selector(unsigned(ea>>4), "DUMMY");
|
|
}
|
|
else
|
|
{
|
|
ea = sptr->end_ea;
|
|
if ( !is_mapped(ea) )
|
|
ea = next_addr(ea);
|
|
++i;
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
bool pos_read(linput_t *li, uint32 pos, void *buf, size_t size)
|
|
{
|
|
qlseek(li, pos);
|
|
return qlread(li, buf, size) != size;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
static ea_t FindDseg(void)
|
|
{
|
|
ea_t dea = to_ea(inf_get_start_cs(), inf_get_start_ip());
|
|
|
|
if ( get_byte(dea) == 0x9A ) // call far
|
|
{
|
|
dea = to_ea(sel2para(get_word(dea+3)), get_word(dea+1));
|
|
inf_set_strtype(STRTYPE_PASCAL);
|
|
}
|
|
//
|
|
// Borland startup
|
|
//
|
|
uchar code = get_byte(dea);
|
|
uchar reg = code & 7;
|
|
if ( (code & ~7) == 0xB8 // mov reg, ????
|
|
&& ((get_byte(dea+3) == 0x8E
|
|
&& ((code=get_byte(dea+4)) & ~7) == 0xD8 // mov ds, reg
|
|
&& (code & 7) == reg)
|
|
|| (get_byte(dea+3) == 0x2E // mov cs:@DGROUP, reg
|
|
&& get_byte(dea+4) == 0x89
|
|
&& ((code = get_byte(dea+5)) & 0x8F) == 6
|
|
&& ((code>>3) & 7) == reg)) )
|
|
{
|
|
segment_t *s = get_segm_by_sel(get_word(dea + 1));
|
|
return s == NULL ? BADADDR : s->start_ea;
|
|
}
|
|
//
|
|
// Watcom startup
|
|
//
|
|
if ( get_byte(dea) == 0xE9 ) // jmp ???
|
|
{
|
|
dea = dea + 3 + get_word(dea + 1);
|
|
if ( get_byte(dea + 0) == 0xFB // sti
|
|
&& get_byte(dea + 1) == 0xB9 ) // mov cx, ???
|
|
{
|
|
segment_t *s = get_segm_by_sel(get_word(dea + 2));
|
|
return s == NULL ? BADADDR : s->start_ea;
|
|
}
|
|
}
|
|
//
|
|
// Generic: find copyright notice
|
|
//
|
|
static const char *const copyr[] =
|
|
{
|
|
" - Copyright",
|
|
// "Borland C++ - Copyright 1991 Borland Intl.",
|
|
// "Turbo-C - Copyright (c) 1988 Borland Intl.",
|
|
// "Turbo C - Copyright 1989 Borland Intl.",
|
|
// "Turbo C++ - Copyright 1990 Borland Intl.",
|
|
// "MS Run-Time Library - Copyright (c)",
|
|
NULL
|
|
};
|
|
for ( const char *const *p = copyr; *p != NULL; ++p )
|
|
{
|
|
msg("Looking for '%s'...\n", *p);
|
|
ea_t dataea = bin_search2(inf_get_min_ea(),
|
|
inf_get_max_ea(),
|
|
(uchar *)*p,
|
|
NULL,
|
|
strlen(*p),
|
|
BIN_SEARCH_CASE|BIN_SEARCH_FORWARD);
|
|
if ( dataea != BADADDR )
|
|
return dataea;
|
|
}
|
|
return BADADDR;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
static void setup_default_ds_register(sel_t ds_value)
|
|
{
|
|
segment_t *dseg;
|
|
|
|
if ( ds_value != BADSEL )
|
|
{
|
|
dseg = get_segm_by_sel(ds_value);
|
|
goto setname;
|
|
}
|
|
msg("Searching for the data segment...\n");
|
|
switch ( inf_get_filetype() )
|
|
{
|
|
case f_EXE: // Find default data seg
|
|
{
|
|
ea_t dataea = FindDseg();
|
|
if ( dataea == BADADDR )
|
|
return;
|
|
dseg = getseg(dataea);
|
|
if ( dseg == NULL )
|
|
return;
|
|
}
|
|
dseg->align = saRelPara;
|
|
ds_value = dseg->sel;
|
|
setname:
|
|
set_segm_class(dseg, CLASS_DATA);
|
|
set_segm_name(dseg, "dseg");
|
|
break;
|
|
case f_COM:
|
|
ds_value = find_selector(inf_get_start_cs());
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
msg("Default DS register: 0x%*a\n", 4, ds_value);
|
|
set_default_dataseg(ds_value);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
// load file into the database.
|
|
//
|
|
void idaapi load_file(linput_t *li, ushort neflag, const char *fileformatname)
|
|
{
|
|
exehdr E;
|
|
netnode ovr_info = BADNODE;
|
|
volatile int type = 0; // volatile because of setjmp()
|
|
volatile sel_t dseg = BADSEL;
|
|
volatile o_type ovr_type = ovr_noexe;
|
|
|
|
processor_t &ph = PH;
|
|
if ( setjmp(jmpb) == 0 )
|
|
{
|
|
set_processor_type("metapc", SETPROC_LOADER);
|
|
|
|
type = strieq(fileformatname, fn_ovr) ? 3
|
|
: strieq(fileformatname, fn_exe) ? 2
|
|
: strieq(fileformatname, fn_drv) ? 1
|
|
: 0;
|
|
|
|
clr_cancelled();
|
|
|
|
uval_t start_off;
|
|
uval_t fcoresize;
|
|
cm_t cm = inf_get_cc_cm() & CM_CC_MASK;
|
|
if ( type < 2 ) // COM/DRV
|
|
{
|
|
inf_set_cc_cm(cm | C_PC_SMALL);
|
|
if ( !type ) // f_COM
|
|
{
|
|
inf_set_start_ip(0x100);
|
|
inf_set_min_ea(to_ea(inf_get_baseaddr(), inf_get_start_ip()));
|
|
}
|
|
else
|
|
{ // f_DRV
|
|
inf_set_start_ip(BADADDR);
|
|
inf_set_min_ea(to_ea(inf_get_baseaddr(), 0 /*binoff*/));
|
|
// binoff has no sense for COM/DRV
|
|
}
|
|
inf_set_start_cs(inf_get_baseaddr());
|
|
start_off = 0;
|
|
fcoresize = qlsize(li);
|
|
inf_set_max_ea(inf_get_min_ea() + fcoresize);
|
|
}
|
|
else
|
|
{ // EXE (/OVR)
|
|
inf_set_cc_cm(cm | C_PC_LARGE);
|
|
lread(li, &E, sizeof(E));
|
|
if ( !E.ReloCnt
|
|
&& ask_yn(ASKBTN_YES,
|
|
"HIDECANCEL\nPossibly packed file, continue?") <= ASKBTN_NO )
|
|
{
|
|
loader_failure();
|
|
}
|
|
inf_set_start_ss(E.ReloSS);
|
|
inf_set_start_cs(E.ReloCS);
|
|
inf_set_start_sp(E.ExeSP);
|
|
inf_set_start_ip(E.ExeIP);
|
|
// take into account pointers like FFF0:0100
|
|
// FFF0 should be treated as signed in this case
|
|
if ( inf_get_start_cs() >= 0xFFF0 || inf_get_start_ss() >= 0xFFF0 )
|
|
{
|
|
if ( inf_get_baseaddr() < 0x10 )
|
|
inf_set_baseaddr(0x10);
|
|
if ( inf_get_start_cs() >= 0xFFF0 )
|
|
inf_set_start_cs(short(inf_get_start_cs()));
|
|
if ( inf_get_start_ss() >= 0xFFF0 )
|
|
inf_set_start_ss(short(inf_get_start_ss()));
|
|
}
|
|
inf_set_start_ss(inf_get_start_ss() + inf_get_baseaddr());
|
|
inf_set_start_cs(inf_get_start_cs() + inf_get_baseaddr());
|
|
inf_set_min_ea(to_ea(inf_get_baseaddr(), 0));
|
|
fcoresize = E.CalcEXE_Length();
|
|
|
|
ovr_info.create(LDR_INFO_NODE);
|
|
ovr_info.set((char *)&E, sizeof(E));
|
|
|
|
// i Check for file size
|
|
uint32 fsize = qlsize(li) - E.HdrSize*16;
|
|
if ( fcoresize > fsize )
|
|
fcoresize = fsize;
|
|
if ( type == 2
|
|
&& fcoresize < fsize
|
|
&& ask_yn(ASKBTN_YES,
|
|
"HIDECANCEL\n"
|
|
"The input file has extra information at the end\n"
|
|
"(tail %Xh, loaded %ah), continue?",
|
|
fsize,
|
|
fcoresize) <= ASKBTN_NO )
|
|
{
|
|
loader_failure();
|
|
}
|
|
inf_set_max_ea(inf_get_min_ea() + fcoresize);
|
|
|
|
ea_t stackEA = to_ea(inf_get_start_ss(), inf_get_start_sp());
|
|
if ( inf_get_max_ea() < stackEA )
|
|
inf_set_max_ea(stackEA);
|
|
msg("Reading relocation table...\n");
|
|
if ( E.ReloCnt )
|
|
{
|
|
qlseek(li, E.TablOff);
|
|
for ( int i = 0; i < E.ReloCnt; ++i )
|
|
{
|
|
ushort buf[2];
|
|
|
|
lread(li, buf, sizeof(buf));
|
|
|
|
ea_t xEA = to_ea((ushort)(inf_get_baseaddr() + buf[1]), buf[0]); // we need ushort() here!
|
|
if ( xEA >= inf_get_max_ea() )
|
|
errstruct();
|
|
ovr_info.altset(xEA, 1);
|
|
}
|
|
}
|
|
start_off = E.HdrSize * 16;
|
|
// i preset variable for overlay loading
|
|
if ( type == 3 )
|
|
ovr_type = PrepareOverlayType(li, &E);
|
|
}
|
|
// next 2 strings for create_msdos_segments & CppOverlays
|
|
inf_set_omin_ea(inf_get_min_ea());
|
|
inf_set_omax_ea(inf_get_max_ea());
|
|
|
|
file2base(li, start_off, inf_get_min_ea(), inf_get_min_ea() + fcoresize,
|
|
FILEREG_PATCHABLE);
|
|
|
|
if ( ovr_type != ovr_cpp )
|
|
{
|
|
if ( type == 3 || (neflag & NEF_SEGS) )
|
|
create_msdos_segments((type <= 1), ovr_info);
|
|
else
|
|
doRelocs(inf_get_baseaddr(), false, ovr_info);
|
|
}
|
|
|
|
create_filename_cmt();
|
|
add_pgm_cmt("Base Address: %ah Range: %ah-%ah Loaded length: %ah",
|
|
inf_get_baseaddr(), inf_get_min_ea(), inf_get_max_ea(), fcoresize);
|
|
if ( type >= 2 )
|
|
{ // f_EXE
|
|
linput_t *volatile lio = NULL;
|
|
add_pgm_cmt("Entry Point : %a:%a", inf_get_start_cs(), inf_get_start_ip());
|
|
if ( type == 2 // && E.CalcEXE_Length() < qlsize(li) - E.HdrSize*16
|
|
&& (lio = CheckExternOverlays()) != NULL )
|
|
{
|
|
++type;
|
|
}
|
|
if ( type != 3 )
|
|
{
|
|
ovr_info.altset(-1, type); // EXE without overlays
|
|
}
|
|
else
|
|
{
|
|
switch ( ovr_type )
|
|
{
|
|
case ovr_pascal:
|
|
lio = li;
|
|
// fallthrough
|
|
case ovr_noexe:
|
|
LoadPascalOverlays(lio);
|
|
if ( ovr_type == ovr_noexe )
|
|
close_linput(lio);
|
|
break;
|
|
|
|
case ovr_cpp:
|
|
dseg = LoadCppOverlays(li);
|
|
doRelocs(inf_get_baseaddr(), false, ovr_info);
|
|
break;
|
|
|
|
case ovr_ms:
|
|
dseg = LoadMsOverlays(li, E.Overlay == 0);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
setup_default_ds_register(dseg); // former SRcreate()
|
|
if ( dseg != BADSEL && ovr_type == ovr_ms )
|
|
{
|
|
segment_t *s = get_segm_by_sel(find_selector(inf_get_start_cs()));
|
|
if ( s != NULL )
|
|
set_default_sreg_value(s, ph.reg_data_sreg, s->sel);
|
|
}
|
|
inf_set_start_ea((inf_get_start_ip() == BADADDR)
|
|
? BADADDR
|
|
: to_ea(sel2para(inf_get_start_cs()), inf_get_start_ip()));
|
|
if ( inf_get_start_ip() != BADADDR )
|
|
{
|
|
uval_t val;
|
|
if ( type < 2 )
|
|
val = find_selector(inf_get_start_cs()); // COM/DRV
|
|
else if ( get_str_type_code(inf_get_strtype()) == STRTYPE_PASCAL )
|
|
val = get_sreg(inf_get_start_ea(), ph.reg_data_sreg); // i set in [srareaovl.cpp]FindDseg
|
|
else
|
|
val = inf_get_baseaddr() - 0x10;
|
|
split_sreg_range(inf_get_start_ea(), ph.reg_data_sreg, val, SR_autostart, true);
|
|
}
|
|
|
|
if ( inf_get_filetype() == f_COM )
|
|
{
|
|
inf_set_lowoff(0x100);
|
|
}
|
|
else
|
|
{
|
|
// check for the debug information
|
|
char debug_magic[4];
|
|
qlseek(li, -8, SEEK_END);
|
|
lread(li, debug_magic, 4);
|
|
if ( is_codeview_magic(debug_magic) )
|
|
load_dbg_dbginfo(NULL, li);
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
static int expand_file(FILE *fp, uint32 pos)
|
|
{
|
|
// return chsize(li, pos) || qfseek(fp, pos, SEEK_SET);
|
|
// but qchsize(), which does not fill with zeroes.
|
|
uint32 curpos = qftell(fp);
|
|
QASSERT(20041, curpos <= pos);
|
|
while ( curpos < pos )
|
|
{
|
|
if ( qfputc(0, fp) == EOF )
|
|
return 0;
|
|
++curpos;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
// generate binary file.
|
|
//
|
|
int idaapi save_file(FILE *fp, const char * /*fileformatname*/)
|
|
{
|
|
int retcode;
|
|
uint32 codeoff;
|
|
netnode ovr_info(LDR_INFO_NODE, 0, 0);
|
|
|
|
if ( fp == NULL )
|
|
return ovr_info == BADNODE || ovr_info.altval(-1) == 2;
|
|
|
|
if ( ovr_info != BADNODE ) // f_EXE
|
|
{
|
|
exehdr E;
|
|
ovr_info.valobj(&E, sizeof(E));
|
|
|
|
if ( qfwrite(fp, &E, sizeof(E)) != sizeof(E) )
|
|
return 0;
|
|
if ( E.ReloCnt )
|
|
{
|
|
if ( !expand_file(fp, E.TablOff) )
|
|
return 0;
|
|
|
|
for ( uval_t x = ovr_info.altfirst();
|
|
x != BADADDR;
|
|
x = ovr_info.altnext(x) )
|
|
{
|
|
ushort buf[2];
|
|
|
|
buf[1] = ushort((x >> 4) - inf_get_baseaddr());
|
|
buf[0] = ushort(x) & 0xF;
|
|
if ( qfwrite(fp, buf, sizeof(buf)) != sizeof(buf) )
|
|
return 0;
|
|
}
|
|
}
|
|
codeoff = E.HdrSize * 16;
|
|
if ( !expand_file(fp, codeoff) )
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
codeoff = 0; // f_COM, f_DRV
|
|
}
|
|
|
|
doRelocs(-inf_get_baseaddr(), 0, ovr_info);
|
|
retcode = base2file(fp, codeoff, inf_get_omin_ea(), inf_get_omax_ea());
|
|
doRelocs(inf_get_baseaddr(), 0, ovr_info);
|
|
return retcode;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
static int idaapi move_segm(ea_t from, ea_t to, asize_t /*size*/, const char * /*fileformatname*/)
|
|
{
|
|
// Before relocating, we need all of the relocation entries, which were
|
|
// part of the original executable file and consequently stored in our
|
|
// private loader node.
|
|
netnode ovr_info(LDR_INFO_NODE, 0, 0);
|
|
if ( ovr_info == BADNODE )
|
|
{
|
|
// Can't find our private loader node.
|
|
msg("Couldn't find dos.ldr node, assuming file has no relocations.\n");
|
|
return 1;
|
|
}
|
|
|
|
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.
|
|
int32 delta = to;
|
|
|
|
// If the delta is not a multiple of 16 bytes, we can't reliably
|
|
// relocate the executable.
|
|
if ( (delta % 16) != 0 )
|
|
{
|
|
warning("DOS images can only be relocated to 16-byte boundaries.");
|
|
return 0;
|
|
}
|
|
|
|
// Fixup the relocation entry netnode. It contains entries that point
|
|
// to locations that needed fixups when the image was located at its
|
|
// old address. Change the entries so that they point to the appropriate
|
|
// places in the new image location.
|
|
ea_t current_base = uint32(inf_get_baseaddr() << 4);
|
|
ea_t new_base = current_base + delta;
|
|
ovr_info.altshift(current_base, new_base, inf_get_privrange_start_ea());
|
|
|
|
// remember bases for later remapping of segment regs
|
|
std::map<ea_t, ea_t> segmap;
|
|
|
|
// Now that the relocation entries point to the correct spots, go fix
|
|
// those spots up so that they point to the correct places.
|
|
doRelocs(delta >> 4, false, ovr_info);
|
|
|
|
// IDA has adjusted all segment start and end addresses to cover their
|
|
// new effective address ranges, but we, the loader, must finish the
|
|
// job by rebasing each segment.
|
|
for ( int i = 0; i < get_segm_qty(); ++i )
|
|
{
|
|
segment_t *seg = getnseg(i);
|
|
ea_t curbase = get_segm_base(seg); // Returns base in EA
|
|
ea_t newbase = curbase + delta;
|
|
set_segm_base(seg, newbase >> 4); // Expects base in Paragraphs
|
|
segmap[curbase >> 4] = newbase >> 4;
|
|
seg->update();
|
|
}
|
|
|
|
// fix up segment registers
|
|
// rebase segment registers
|
|
processor_t &ph = PH;
|
|
for ( int sr = 0; sr < SREG_NUM; ++sr )
|
|
{
|
|
int sra_num = get_sreg_ranges_qty(ph.reg_first_sreg + sr);
|
|
for ( int i = 0; i < sra_num; ++i )
|
|
{
|
|
sreg_range_t sra;
|
|
if ( !getn_sreg_range(&sra, sr, i) )
|
|
break;
|
|
sel_t reg = sra.val;
|
|
if ( reg != BADSEL )
|
|
{
|
|
std::map<ea_t, ea_t>::const_iterator p = segmap.find(reg);
|
|
if ( p != segmap.end() )
|
|
split_sreg_range(sra.start_ea, ph.reg_first_sreg + sr, p->second, SR_auto, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Record the new image base address.
|
|
inf_set_baseaddr(new_base >> 4);
|
|
set_imagebase(new_base);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
//
|
|
// LOADER DESCRIPTION BLOCK
|
|
//
|
|
//----------------------------------------------------------------------
|
|
loader_t LDSC =
|
|
{
|
|
IDP_INTERFACE_VERSION,
|
|
// loader flags
|
|
0,
|
|
|
|
// 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.
|
|
save_file,
|
|
|
|
// take care of a moved segment (fix up relocations, for example)
|
|
move_segm,
|
|
NULL,
|
|
};
|