1
0
mirror of https://github.com/upx/upx synced 2025-09-28 19:06:07 +08:00
upx/src/p_exe.cpp
Markus F.X.J. Oberhumer 62dbf8485f src: sort cleanups
2023-09-04 07:28:48 +02:00

778 lines
26 KiB
C++

/* p_exe.cpp -- dos/exe executable format
This file is part of the UPX executable compressor.
Copyright (C) 1996-2023 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 1996-2023 Laszlo Molnar
All Rights Reserved.
UPX and the UCL library are free software; you can redistribute them
and/or modify them under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING.
If not, write to the Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Markus F.X.J. Oberhumer Laszlo Molnar
<markus@oberhumer.com> <ezerotven+github@gmail.com>
*/
#include "conf.h"
#include "file.h"
#include "filter.h"
#include "packer.h"
#include "p_exe.h"
#include "linker.h"
static const CLANG_FORMAT_DUMMY_STATEMENT
#include "stub/i086-dos16.exe.h"
#define MAXMATCH 0x2000
#define MAXRELOCSIZE (0x8000 - MAXMATCH)
#define DI_LIMIT 0xff00 // see the assembly why
/*************************************************************************
//
**************************************************************************/
PackExe::PackExe(InputFile *f) : super(f) {
bele = &N_BELE_RTP::le_policy;
COMPILE_TIME_ASSERT(sizeof(exe_header_t) == 32)
COMPILE_TIME_ASSERT_ALIGNED1(exe_header_t)
}
Linker *PackExe::newLinker() const { return new ElfLinkerX86(); }
const int *PackExe::getCompressionMethods(int method, int level) const {
bool small = ih_imagesize <= 256 * 1024;
// disable lzma for "--brute" unless explicitly given "--lzma"
// (note that class PackMaster creates per-file local options)
if (opt->all_methods_use_lzma == 1 && !opt->method_lzma_seen)
opt->all_methods_use_lzma = 0;
return Packer::getDefaultCompressionMethods_8(method, level, small);
}
const int *PackExe::getFilters() const { return nullptr; }
int PackExe::fillExeHeader(struct exe_header_t *eh) const {
#define oh (*eh)
// fill new exe header
int flag = 0;
if (!opt->dos_exe.no_reloc && !M_IS_LZMA(ph.method))
flag |= USEJUMP;
if (ih.relocs == 0)
flag |= NORELOC;
mem_clear(&oh);
oh.ident = 'M' + 'Z' * 256;
oh.headsize16 = 2;
unsigned minsp = 0x200;
if (M_IS_LZMA(ph.method))
minsp = stack_for_lzma;
minsp = ALIGN_UP(minsp, 16u);
assert(minsp < 0xff00);
if (oh.sp > minsp)
minsp = oh.sp;
if (minsp < 0xff00 - 2)
minsp = ALIGN_UP(minsp, 2u);
oh.sp = minsp;
unsigned destpara = (ph.u_len + ph.overlap_overhead - ph.c_len + 31) / 16;
oh.ss = ph.c_len / 16 + destpara;
if (ih.ss * 16 + ih.sp < 0x100000 && ih.ss > oh.ss && ih.sp > 0x200)
oh.ss = ih.ss;
if (oh.ss * 16 + 0x50 < ih.ss * 16 + ih.sp && oh.ss * 16 + 0x200 > ih.ss * 16 + ih.sp)
oh.ss += 0x20;
if (oh.ss != ih.ss)
flag |= SS;
if (oh.sp != ih.sp || M_IS_LZMA(ph.method))
flag |= SP;
return flag;
#undef oh
}
void PackExe::addLoaderEpilogue(int flag) {
addLoader("EXEMAIN5");
if (relocsize)
addLoader(ph.u_len <= DI_LIMIT || (ph.u_len & 0x7fff) >= relocsize ? "EXENOADJ"
: "EXEADJUS",
"EXERELO1", has_9a ? "EXEREL9A" : "", "EXERELO2",
ih_exesize > 0xFE00 ? "EXEREBIG" : "", "EXERELO3");
addLoader("EXEMAIN8", device_driver ? "DEVICEEND" : "", (flag & SS) ? "EXESTACK" : "",
(flag & SP) ? "EXESTASP" : "", (flag & USEJUMP) ? "EXEJUMPF" : "");
if (!(flag & USEJUMP))
addLoader(ih.cs ? "EXERCSPO" : "", "EXERETIP");
linker->defineSymbol("original_cs", ih.cs);
linker->defineSymbol("original_ip", ih.ip);
linker->defineSymbol("original_sp", ih.sp);
linker->defineSymbol("original_ss", ih.ss);
linker->defineSymbol(
"reloc_size",
(ph.u_len <= DI_LIMIT || (ph.u_len & 0x7fff) >= relocsize ? 0 : MAXRELOCSIZE) - relocsize);
}
void PackExe::buildLoader(const Filter *) {
// get flag
exe_header_t dummy_oh;
int flag = fillExeHeader(&dummy_oh);
initLoader(stub_i086_dos16_exe, sizeof(stub_i086_dos16_exe));
if (M_IS_LZMA(ph.method)) {
addLoader("LZMA_DEC00", opt->small ? "LZMA_DEC10" : "LZMA_DEC20", "LZMA_DEC30",
use_clear_dirty_stack ? "LZMA_DEC31" : "", "LZMA_DEC32",
ph.u_len > 0xffff ? "LZMA_DEC33" : "");
addLoaderEpilogue(flag);
defineDecompressorSymbols();
const unsigned lsize0 = getLoaderSize();
// Lzma decompression code starts at ss:0x10, and its size is
// lsize bytes. It also needs getDecompressorWrkmemSize() bytes
// during uncompression. It also uses some stack, so 0x100
// more bytes are allocated
stack_for_lzma = 0x10 + lsize0 + getDecompressorWrkmemSize() + 0x100;
stack_for_lzma = ALIGN_UP(stack_for_lzma, 16u);
unsigned clear_dirty_stack_low = 0x10 + lsize0;
clear_dirty_stack_low = ALIGN_UP(clear_dirty_stack_low, 2u);
if (use_clear_dirty_stack)
linker->defineSymbol("clear_dirty_stack_low", clear_dirty_stack_low);
relocateLoader();
const unsigned lsize = getLoaderSize();
assert(lsize0 == lsize);
MemBuffer loader(lsize);
memcpy(loader, getLoader(), lsize);
MemBuffer compressed_lzma;
compressed_lzma.allocForCompression(lsize);
unsigned c_len_lzma = MemBuffer::getSizeForCompression(lsize);
int r = upx_compress(loader, lsize, compressed_lzma, &c_len_lzma, nullptr, M_NRV2B_LE16, 9,
nullptr, nullptr);
assert(r == UPX_E_OK);
assert(c_len_lzma < lsize);
info("lzma+relocator code compressed: %u -> %u", lsize, c_len_lzma);
// reinit the loader
initLoader(stub_i086_dos16_exe, sizeof(stub_i086_dos16_exe));
// prepare loader
if (device_driver)
addLoader("DEVICEENTRY,LZMADEVICE,DEVICEENTRY2");
linker->addSection("COMPRESSED_LZMA", compressed_lzma, c_len_lzma, 0);
addLoader("LZMAENTRY,NRV2B160,NRVDDONE,NRVDECO1,NRVGTD00,NRVDECO2");
} else if (device_driver)
addLoader("DEVICEENTRY,DEVICEENTRY2");
addLoader("EXEENTRY", M_IS_LZMA(ph.method) && device_driver ? "LONGSUB" : "SHORTSUB",
"JNCDOCOPY", relocsize ? "EXERELPU" : "", "EXEMAIN4",
M_IS_LZMA(ph.method) ? "" : "EXEMAIN4B", "EXEMAIN4C",
M_IS_LZMA(ph.method) ? "COMPRESSED_LZMA_START,COMPRESSED_LZMA" : "",
"+G5DXXXX,UPX1HEAD,EXECUTPO");
if (ph.method == M_NRV2B_8)
addLoader("NRV2B16S", // decompressor
ph.u_len > DI_LIMIT ? "N2B64K01" : "", "NRV2BEX1",
opt->cpu_x86 == opt->CPU_8086 ? "N2BX8601" : "N2B28601", "NRV2BEX2",
opt->cpu_x86 == opt->CPU_8086 ? "N2BX8602" : "N2B28602", "NRV2BEX3",
ph.c_len > 0xffff ? "N2B64K02" : "", "NRV2BEX9");
else if (ph.method == M_NRV2D_8)
addLoader("NRV2D16S", ph.u_len > DI_LIMIT ? "N2D64K01" : "", "NRV2DEX1",
opt->cpu_x86 == opt->CPU_8086 ? "N2DX8601" : "N2D28601", "NRV2DEX2",
opt->cpu_x86 == opt->CPU_8086 ? "N2DX8602" : "N2D28602", "NRV2DEX3",
ph.c_len > 0xffff ? "N2D64K02" : "", "NRV2DEX9");
else if (ph.method == M_NRV2E_8)
addLoader("NRV2E16S", ph.u_len > DI_LIMIT ? "N2E64K01" : "", "NRV2EEX1",
opt->cpu_x86 == opt->CPU_8086 ? "N2EX8601" : "N2E28601", "NRV2EEX2",
opt->cpu_x86 == opt->CPU_8086 ? "N2EX8602" : "N2E28602", "NRV2EEX3",
ph.c_len > 0xffff ? "N2E64K02" : "", "NRV2EEX9");
else if M_IS_LZMA (ph.method)
return;
else
throwInternalError("unknown compression method");
addLoaderEpilogue(flag);
}
/*************************************************************************
//
**************************************************************************/
int PackExe::readFileHeader() {
ih_exesize = ih_imagesize = ih_overlay = 0;
fi->readx(&ih, sizeof(ih));
if (ih.ident != 'M' + 'Z' * 256 && ih.ident != 'Z' + 'M' * 256)
return 0;
ih_exesize = ih.m512 + ih.p512 * 512 - (ih.m512 ? 512 : 0);
if (ih_exesize == 0)
ih_exesize = file_size;
ih_imagesize = ih_exesize - ih.headsize16 * 16;
ih_overlay = file_size - ih_exesize;
if (file_size_u < sizeof(ih) || ((ih.m512 | ih.p512) && ih.m512 + ih.p512 * 512u < sizeof(ih)))
throwCantPack("illegal exe header");
if (ih_exesize > file_size_u || ih_imagesize < 4 || ih_imagesize > ih_exesize)
throwCantPack("exe header corrupted");
NO_printf("dos/exe header: %d %d %d\n", ih_exesize, ih_imagesize, ih_overlay);
return UPX_F_DOS_EXE;
}
tribool PackExe::canPack() {
if (fn_has_ext(fi->getName(), "sys")) // dos/sys
return false;
if (!readFileHeader())
return false;
if (file_size < 1024 || ih_imagesize < 512)
throwCantPack("file is too small for dos/exe");
fi->seek(0x3c, SEEK_SET);
LE32 offs;
fi->readx(&offs, sizeof(offs));
if (ih.relocoffs >= 0x40 && offs) {
if (opt->dos_exe.force_stub)
opt->overlay = opt->COPY_OVERLAY;
else
throwCantPack("dos/exe: can't pack new-exe");
}
return true;
}
/*************************************************************************
//
**************************************************************************/
static unsigned optimize_relocs(SPAN_S(byte) image, const unsigned image_size,
SPAN_S(const byte) relocs, const unsigned relocnum,
SPAN_S(byte) crel, bool *has_9a) {
#if WITH_XSPAN >= 2
ptr_check_no_overlap(image.data(image_size), image_size, relocs.data(), relocs.size_bytes(),
crel.data(), crel.size_bytes());
#endif
if (opt->exact)
throwCantPackExact();
SPAN_S_VAR(byte, const crel_start, crel);
unsigned seg_high = 0;
#if 0
unsigned seg_low = 0xffffffff;
unsigned off_low = 0xffffffff;
unsigned off_high = 0;
unsigned linear_low = 0xffffffff;
unsigned linear_high = 0;
#endif
// pass 1 - find 0x9a bounds in image
for (unsigned i = 0; i < relocnum; i++) {
unsigned addr = get_le32(relocs + 4 * i);
if (addr >= image_size - 1)
throwCantPack("unexpected relocation 1");
if (addr >= 3 && image[addr - 3] == 0x9a) {
unsigned seg = get_le16(image + addr);
if (seg > seg_high)
seg_high = seg;
#if 0
if (seg < seg_low)
seg_low = seg;
unsigned off = get_le16(image + addr - 2);
if (off < off_low)
off_low = off;
if (off > off_high)
off_high = off;
unsigned l = (seg << 4) + off;
if (l < linear_low)
linear_low = l;
if (l > linear_high)
linear_high = l;
#endif
}
}
// printf("%d %d\n", seg_low, seg_high);
// printf("%d %d\n", off_low, off_high);
// printf("%d %d\n", linear_low, linear_high);
// pass 2 - reloc
crel += 4; // to be filled in later
unsigned ones = 0;
unsigned es = 0;
for (unsigned i = 0; i < relocnum;) {
unsigned addr = get_le32(relocs + 4 * i);
unsigned di = addr & 0x0f;
set_le16(crel + 0, di);
set_le16(crel + 2, (addr >> 4) - es);
crel += 4;
es = addr >> 4;
for (++i; i < relocnum; i++) {
unsigned t;
addr = get_le32(relocs + 4 * i);
NO_printf("%x\n", es * 16 + di);
if ((addr - es * 16 > 0xfffe) || (i == relocnum - 1 && addr - es * 16 > 0xff00)) {
// segment change
t = 1 + (0xffff - di) / 254;
memset(crel, 1, t);
crel += t;
ones += t - 1; // -1 is used to help the assembly stuff
break;
}
unsigned offs = addr - es * 16;
if (offs >= 3 && image[es * 16 + offs - 3] == 0x9a && offs > di + 3) {
for (t = di; t < offs - 3; t++)
if (image[es * 16 + t] == 0x9a && get_le16(image + es * 16 + t + 3) <= seg_high)
break;
if (t == offs - 3) {
// code 0: search for 0x9a
*crel++ = 0;
di = offs;
*has_9a = true;
continue;
}
}
t = offs - di;
if ((int) t < 2)
throwCantPack("unexpected relocation 2");
while (t >= 256) {
// code 1: add 254, don't reloc
*crel++ = 1;
t -= 254;
ones++;
}
*crel++ = (byte) t;
di = offs;
}
}
*crel++ = 1;
ones++;
set_le16(crel_start, ones);
set_le16(crel_start + 2, seg_high);
// OutputFile::dump("x.rel", crel_start, ptr_udiff_bytes(crel, crel_start));
return ptr_udiff_bytes(crel, crel_start);
}
/*************************************************************************
//
**************************************************************************/
void PackExe::pack(OutputFile *fo) {
unsigned ic;
const unsigned relocnum = ih.relocs;
if (relocnum > MAXRELOCSIZE) // early check
throwCantPack("too many relocations");
checkOverlay(ih_overlay);
// read image
// image + space for optimized relocs + safety/alignments
ibuf.alloc(ih_imagesize + 4 * relocnum + 1024);
fi->seek(ih.headsize16 * 16, SEEK_SET);
fi->readx(ibuf, ih_imagesize);
checkAlreadyPacked(ibuf, UPX_MIN(ih_imagesize, 127u));
device_driver = get_le32(ibuf) == 0xffffffffu;
// relocations
relocsize = 0;
has_9a = false;
if (relocnum) {
MemBuffer mb_relocs(4 * relocnum);
SPAN_S_VAR(byte, relocs, mb_relocs);
fi->seek(ih.relocoffs, SEEK_SET);
fi->readx(relocs, 4 * relocnum);
// dos/exe runs in real-mode, so convert to linear addresses
for (ic = 0; ic < relocnum; ic++) {
unsigned jc = get_le32(relocs + 4 * ic);
set_le32(relocs + 4 * ic, ((jc >> 16) * 16 + (jc & 0xffff)) & 0xfffff);
}
upx_qsort(raw_bytes(relocs, 4 * relocnum), relocnum, 4, le32_compare);
SPAN_S_VAR(byte, image, ibuf + 0, ih_imagesize);
SPAN_S_VAR(byte, crel, ibuf + ih_imagesize, ibuf);
relocsize = optimize_relocs(image, ih_imagesize, relocs, relocnum, crel, &has_9a);
set_le16(crel + relocsize, relocsize + 2);
relocsize += 2;
assert(relocsize >= 11);
if (relocsize > MAXRELOCSIZE) // optimize_relocs did not help
throwCantPack("too many relocations");
#if TESTING && 0
unsigned rout_len = MemBuffer::getSizeForCompression(relocsize);
MemBuffer rout(rout_len);
ucl_nrv2b_99_compress(raw_bytes(crel, relocsize), relocsize, rout, &rout_len, nullptr, 9,
nullptr, nullptr);
printf("dos/exe reloc compress: %d -> %d\n", relocsize, rout_len);
#endif
}
// prepare packheader
ph.u_len = ih_imagesize + relocsize;
obuf.allocForCompression(ph.u_len);
// prepare filter
Filter ft(ph.level);
// compress (max_match = 8192)
upx_compress_config_t cconf;
cconf.reset();
cconf.conf_ucl.max_match = MAXMATCH;
cconf.conf_lzma.max_num_probs = 1846 + (768 << 4); // ushort: ~28 KiB stack
compressWithFilters(&ft, 32, &cconf);
if (M_IS_NRV2B(ph.method) || M_IS_NRV2D(ph.method) || M_IS_NRV2E(ph.method))
if (ph.max_run_found + ph.max_match_found > 0x8000)
throwCantPack("decompressor limit exceeded, send a bugreport");
#if TESTING
if (opt->debug.debug_level) {
printf("image+relocs %d -> %d\n", ih_imagesize + relocsize, ph.c_len);
printf("offsets: %d - %d\nmatches: %d - %d\nruns: %d - %d\n", 0 /*ph.min_offset_found*/,
ph.max_offset_found, 0 /*ph.min_match_found*/, ph.max_match_found,
0 /*ph.min_run_found*/, ph.max_run_found);
}
#endif
int flag = fillExeHeader(&oh);
const unsigned lsize = getLoaderSize();
MemBuffer loader(lsize);
memcpy(loader, getLoader(), lsize);
// OutputFile::dump("xxloader.dat", loader, lsize);
// patch loader
const unsigned packedsize = ph.c_len;
const unsigned e_len = getLoaderSectionStart("EXECUTPO");
const unsigned d_len = lsize - e_len;
assert((e_len & 15) == 0);
const unsigned copysize = (1 + packedsize + d_len) & ~1;
const unsigned firstcopy = copysize % 0x10000 ? copysize % 0x10000 : 0x10000;
// set oh.min & oh.max
ic = ih.min * 16 + ih_imagesize;
if (ic < oh.ss * 16u + oh.sp)
ic = oh.ss * 16u + oh.sp;
oh.min = (ic - (packedsize + lsize)) / 16;
ic = oh.min + (ih.max - ih.min);
oh.max = ic < 0xffff && ih.max != 0xffff ? ic : 0xffff;
// set extra info
byte extra_info[9];
unsigned eisize = 0;
if (oh.ss != ih.ss) {
set_le16(extra_info + eisize, ih.ss);
eisize += 2;
assert((flag & SS) != 0); // set in fillExeHeader()
}
if (oh.sp != ih.sp) {
set_le16(extra_info + eisize, ih.sp);
eisize += 2;
assert((flag & SP) != 0); // set in fillExeHeader()
}
if (ih.min != oh.min) {
set_le16(extra_info + eisize, ih.min);
eisize += 2;
flag |= MINMEM;
}
if (ih.max != oh.max) {
set_le16(extra_info + eisize, ih.max);
eisize += 2;
flag |= MAXMEM;
}
extra_info[eisize++] = (byte) flag;
if (M_IS_NRV2B(ph.method) || M_IS_NRV2D(ph.method) || M_IS_NRV2E(ph.method))
linker->defineSymbol("bx_magic", 0x7FFF + 0x10 * ((packedsize & 15) + 1));
unsigned decompressor_entry = 1 + (packedsize & 15);
if (M_IS_LZMA(ph.method))
decompressor_entry = 0x10;
linker->defineSymbol("decompressor_entry", decompressor_entry);
// patch loader
if (flag & USEJUMP) {
// I use a relocation entry to set the original cs
unsigned n = getLoaderSectionStart("EXEJUMPF") + 1;
n += packedsize + 2;
oh.relocs = 1;
oh.firstreloc = (n & 0xf) + ((n >> 4) << 16);
} else {
oh.relocs = 0;
oh.firstreloc = ih.cs * 0x10000 + ih.ip;
}
oh.relocoffs = offsetof(exe_header_t, firstreloc);
linker->defineSymbol("destination_segment", oh.ss - ph.c_len / 16 - e_len / 16);
linker->defineSymbol("source_segment", e_len / 16 + (copysize - firstcopy) / 16);
linker->defineSymbol("copy_offset", firstcopy - 2);
linker->defineSymbol("words_to_copy", firstcopy / 2);
linker->defineSymbol("exe_stack_sp", oh.sp);
linker->defineSymbol("exe_stack_ss", oh.ss);
linker->defineSymbol("interrupt", get_le16(ibuf + 8));
linker->defineSymbol("attribute", get_le16(ibuf + 4));
linker->defineSymbol("orig_strategy", get_le16(ibuf + 6));
const unsigned outputlen = sizeof(oh) + e_len + packedsize + d_len + eisize;
oh.m512 = outputlen & 511;
oh.p512 = (outputlen + 511) >> 9;
const char *exeentry = M_IS_LZMA(ph.method) ? "LZMAENTRY" : "EXEENTRY";
oh.ip = device_driver ? getLoaderSection(exeentry) - 2 : 0;
defineDecompressorSymbols();
relocateLoader();
memcpy(loader, getLoader(), lsize);
patchPackHeader(loader, e_len);
NO_fprintf(stderr, "\ne_len=%x d_len=%x c_len=%x oo=%x ulen=%x copysize=%x imagesize=%x", e_len,
d_len, packedsize, ph.overlap_overhead, ph.u_len, copysize, ih_imagesize);
// write header + write loader + compressed file
#if TESTING
if (opt->debug.debug_level)
printf("\n%d %d %d %d\n", (int) sizeof(oh), e_len, packedsize, d_len);
#endif
fo->write(&oh, sizeof(oh)); // program header
fo->write(loader, e_len); // entry code
fo->write(obuf, packedsize); // compressed data
fo->write(loader + e_len, d_len); // decompressor code
fo->write(extra_info, eisize); // extra info for unpacking
assert(eisize <= 9);
NO_printf("%-13s: program hdr : %8u bytes\n", getName(), usizeof(oh));
NO_printf("%-13s: entry : %8u bytes\n", getName(), e_len);
NO_printf("%-13s: compressed : %8u bytes\n", getName(), packedsize);
NO_printf("%-13s: decompressor : %8u bytes\n", getName(), d_len);
NO_printf("%-13s: extra info : %8u bytes\n", getName(), eisize);
// verify
verifyOverlappingDecompression();
// copy the overlay
copyOverlay(fo, ih_overlay, obuf);
NO_fprintf(stderr, "dos/exe %x %x\n", relocsize, ph.u_len);
// finally check the compression ratio
if (!checkFinalCompressionRatio(fo))
throwNotCompressible();
}
/*************************************************************************
//
**************************************************************************/
tribool PackExe::canUnpack() {
if (!readFileHeader())
return false;
const unsigned off = ih.headsize16 * 16;
fi->seek(off, SEEK_SET);
bool b = readPackHeader(4096);
return b && (off + ph.c_len <= file_size_u);
}
/*************************************************************************
//
**************************************************************************/
void PackExe::unpack(OutputFile *fo) {
ibuf.alloc(file_size);
obuf.allocForDecompression(ph.u_len);
// read the file
fi->seek(ih.headsize16 * 16, SEEK_SET);
fi->readx(ibuf, ih_imagesize);
// get compressed data offset
unsigned e_len = ph.buf_offset + ph.getPackHeaderSize();
if (ih_imagesize <= e_len + ph.c_len)
throwCantUnpack("file damaged");
checkOverlay(ih_overlay);
// decompress
decompress(ibuf + e_len, obuf);
unsigned imagesize = ih_imagesize;
imagesize -= 1;
const byte flag = ibuf[imagesize];
// relocations
unsigned relocnum = 0;
SPAN_S_VAR(const byte, relocstart, obuf + ph.u_len, obuf);
MemBuffer mb_relocs;
SPAN_0_VAR(byte, relocs, nullptr);
if (!(flag & NORELOC)) {
mb_relocs.alloc(4 * MAXRELOCSIZE);
relocs = mb_relocs; // => now a SPAN_S
relocsize = get_le16(obuf + ph.u_len - 2);
ph.u_len -= 2;
if (relocsize < 11 || relocsize > MAXRELOCSIZE || relocsize >= imagesize)
throwCantUnpack("bad relocations");
relocstart -= relocsize;
// unoptimize_relocs
unsigned ones = get_le16(relocstart);
const unsigned seg_high = get_le16(relocstart + 2);
SPAN_S_VAR(const byte, p, relocstart + 4);
unsigned es = 0;
while (ones) {
unsigned di = get_le16(p);
es += get_le16(p + 2);
bool dorel = true;
for (p += 4; ones && di < 0x10000; p++) {
if (dorel) {
set_le16(relocs + (4 * relocnum + 0), di);
set_le16(relocs + (4 * relocnum + 2), es);
NO_printf("dos/exe unreloc %4d %6x\n", relocnum, es * 16 + di);
relocnum++;
}
dorel = true;
if (*p == 0) {
SPAN_S_VAR(const byte, q, obuf + (es * 16 + di), obuf);
while (!(*q == 0x9a && get_le16(q + 3) <= seg_high))
q++;
di = ptr_udiff_bytes(q, obuf + (es * 16)) + 3;
} else if (*p == 1) {
di += 254;
if (di < 0x10000)
ones--;
dorel = false;
} else
di += *p;
}
}
}
// fill new exe header
mem_clear(&oh);
oh.ident = 'M' + 'Z' * 256;
if (relocnum) {
oh.relocs = relocnum;
while (relocnum & 3) // paragraph align
set_le32(relocs + (4 * relocnum++), 0);
}
unsigned outputlen = sizeof(oh) + 4 * relocnum + ptr_udiff_bytes(relocstart, obuf);
oh.m512 = outputlen & 511;
oh.p512 = (outputlen + 511) >> 9;
oh.headsize16 = 2 + relocnum / 4;
oh.max = ih.max;
oh.min = ih.min;
oh.sp = ih.sp;
oh.ss = ih.ss;
if (flag & MAXMEM) {
imagesize -= 2;
oh.max = get_le16(ibuf + imagesize);
}
if (flag & MINMEM) {
imagesize -= 2;
oh.min = get_le16(ibuf + imagesize);
}
if (flag & SP) {
imagesize -= 2;
oh.sp = get_le16(ibuf + imagesize);
}
if (flag & SS) {
imagesize -= 2;
oh.ss = get_le16(ibuf + imagesize);
}
unsigned ip = (flag & USEJUMP) ? get_le32(ibuf + imagesize - 4) : (unsigned) ih.firstreloc;
oh.ip = ip & 0xffff;
oh.cs = ip >> 16;
oh.relocoffs = sizeof(oh);
oh.firstreloc = 0;
if (!fo)
return;
// write header + relocations + uncompressed file
fo->write(&oh, sizeof(oh));
if (relocnum)
fo->write(relocs, 4 * relocnum);
fo->write(obuf, ptr_udiff_bytes(relocstart, obuf));
// copy the overlay
copyOverlay(fo, ih_overlay, obuf);
}
/*
memory layout at decompression time
===================================
normal exe
----------
a, at load time
(e - copying code, C - compressed data, d - decompressor+relocator,
x - not specified, U - uncompressed code+data, R uncompressed relocation)
eeCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCdddd
^ CS:0 ^ SS:0
b, after copying
xxxxxxxxxxxxxxxCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCdddd
^ES:DI=0 ^ DS:SI=0 ^ CS=SS, IP in range 0..0xf
c, after uncompression
UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUURRdddd
^ ES:DI
device driver
-------------
the file has 2 entry points, CS:0 in device driver mode, and
CS:exe_as_device_entry in normal mode. the code in section DEVICEENTRY
sets up the same environment for section EXEENTRY, as it would see in normal
execution mode.
lzma uncompression for normal exes
----------------------------------
(n - nrv2b uncompressor, l - nrv2b compressed lzma + relocator code)
a, at load time
nneelllCCCCCCCCCCCCCCCCCCCCCCCCC
^ CS:0 ^ SS:0
b, after nrv2b
nneelllCCCCCCCCCCCCCCCCCCCCCCCCC dddd
^ CS:0 ^ SS:0x10
after this, normal ee code runs
lzma + device driver
--------------------
(D - device driver adapter)
a, at load time
DDnneelllCCCCCCCCCCCCCCCCCCCCCCCCC
*/
/* vim:set ts=4 sw=4 et: */