mirror of
https://github.com/upx/upx
synced 2025-09-28 19:06:07 +08:00
778 lines
26 KiB
C++
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: */
|