Coverage Report

Created: 2026-09-28 06:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/upx/src/p_exe.cpp
Line
Count
Source
1
/* p_exe.cpp -- dos/exe executable format
2
3
   This file is part of the UPX executable compressor.
4
5
   Copyright (C) Markus Franz Xaver Johannes Oberhumer
6
   Copyright (C) Laszlo Molnar
7
   All Rights Reserved.
8
9
   UPX and the UCL library are free software; you can redistribute them
10
   and/or modify them under the terms of the GNU General Public License as
11
   published by the Free Software Foundation; either version 2 of
12
   the License, or (at your option) any later version.
13
14
   This program is distributed in the hope that it will be useful,
15
   but WITHOUT ANY WARRANTY; without even the implied warranty of
16
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
   GNU General Public License for more details.
18
19
   You should have received a copy of the GNU General Public License
20
   along with this program; see the file COPYING.
21
   If not, write to the Free Software Foundation, Inc.,
22
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23
24
   Markus F.X.J. Oberhumer              Laszlo Molnar
25
   <markus@oberhumer.com>               <ezerotven+github@gmail.com>
26
 */
27
28
#include "conf.h"
29
#include "file.h"
30
#include "filter.h"
31
#include "packer.h"
32
#include "p_exe.h"
33
#define WANT_EHDR_ENUM 1
34
#include "p_elf_enum.h"
35
#include "linker.h"
36
37
static const CLANG_FORMAT_DUMMY_STATEMENT
38
#include "stub/i086-dos16.exe.h"
39
40
0
#define MAXMATCH     0x2000
41
0
#define MAXRELOCSIZE (0x8000 - MAXMATCH)
42
43
0
#define DI_LIMIT 0xff00 // see the assembly why
44
45
/*************************************************************************
46
//
47
**************************************************************************/
48
49
39.4k
PackExe::PackExe(InputFile *f) : super(f) {
50
39.4k
    bele = &N_BELE_RTP::le_policy;
51
39.4k
    COMPILE_TIME_ASSERT(sizeof(exe_header_t) == 32)
52
39.4k
    COMPILE_TIME_ASSERT_ALIGNED1(exe_header_t)
53
39.4k
}
54
55
0
Linker *PackExe::newLinker() const { return new ElfLinkerX86(); }
56
57
0
const int *PackExe::getCompressionMethods(int method, int level) const {
58
0
    bool small = ih_imagesize <= 256 * 1024;
59
    // disable lzma for "--brute" unless explicitly given "--lzma"
60
    // (note that class PackMaster creates per-file local options)
61
0
    if (opt->all_methods_use_lzma == 1 && !opt->method_lzma_seen)
62
0
        opt->all_methods_use_lzma = 0;
63
0
    return Packer::getDefaultCompressionMethods_8(method, level, small);
64
0
}
65
66
0
const int *PackExe::getFilters() const { return nullptr; }
67
68
0
int PackExe::fillExeHeader(struct exe_header_t *eh) const {
69
0
#define oh (*eh)
70
    // fill new exe header
71
0
    int flag = 0;
72
0
    if (!opt->dos_exe.no_reloc && !M_IS_LZMA(ph.method))
73
0
        flag |= USEJUMP;
74
0
    if (ih.relocs == 0)
75
0
        flag |= NORELOC;
76
77
0
    mem_clear(&oh);
78
0
    oh.ident = 'M' + 'Z' * 256;
79
0
    oh.headsize16 = 2;
80
81
0
    unsigned minsp = 0x200;
82
0
    if (M_IS_LZMA(ph.method))
83
0
        minsp = stack_for_lzma;
84
0
    minsp = ALIGN_UP(minsp, 16u);
85
0
    assert(minsp < 0xff00);
86
0
    if (oh.sp > minsp)
87
0
        minsp = oh.sp;
88
0
    if (minsp < 0xff00 - 2)
89
0
        minsp = ALIGN_UP(minsp, 2u);
90
0
    oh.sp = minsp;
91
92
0
    unsigned destpara = (ph.u_len + ph.overlap_overhead - ph.c_len + 31) / 16;
93
0
    oh.ss = ph.c_len / 16 + destpara;
94
0
    if (ih.ss * 16 + ih.sp < 0x100000 && ih.ss > oh.ss && ih.sp > 0x200)
95
0
        oh.ss = ih.ss;
96
0
    if (oh.ss * 16 + 0x50 < ih.ss * 16 + ih.sp && oh.ss * 16 + 0x200 > ih.ss * 16 + ih.sp)
97
0
        oh.ss += 0x20;
98
99
0
    if (oh.ss != ih.ss)
100
0
        flag |= SS;
101
0
    if (oh.sp != ih.sp || M_IS_LZMA(ph.method))
102
0
        flag |= SP;
103
0
    return flag;
104
0
#undef oh
105
0
}
106
107
0
void PackExe::addLoaderEpilogue(int flag) {
108
0
    addLoader("EXEMAIN5");
109
0
    if (relocsize)
110
0
        addLoader(ph.u_len <= DI_LIMIT || (ph.u_len & 0x7fff) >= relocsize ? "EXENOADJ"
111
0
                                                                           : "EXEADJUS",
112
0
                  "EXERELO1", has_9a ? "EXEREL9A" : "", "EXERELO2",
113
0
                  ih_exesize > 0xFE00 ? "EXEREBIG" : "", "EXERELO3");
114
0
    addLoader("EXEMAIN8", device_driver ? "DEVICEEND" : "", (flag & SS) ? "EXESTACK" : "",
115
0
              (flag & SP) ? "EXESTASP" : "", (flag & USEJUMP) ? "EXEJUMPF" : "");
116
0
    if (!(flag & USEJUMP))
117
0
        addLoader(ih.cs ? "EXERCSPO" : "", "EXERETIP");
118
119
0
    linker->defineSymbol("original_cs", ih.cs);
120
0
    linker->defineSymbol("original_ip", ih.ip);
121
0
    linker->defineSymbol("original_sp", ih.sp);
122
0
    linker->defineSymbol("original_ss", ih.ss);
123
0
    linker->defineSymbol(
124
0
        "reloc_size",
125
0
        (ph.u_len <= DI_LIMIT || (ph.u_len & 0x7fff) >= relocsize ? 0 : MAXRELOCSIZE) - relocsize);
126
0
}
127
128
0
void PackExe::buildLoader(const Filter *) {
129
    // get flag
130
0
    exe_header_t dummy_oh;
131
0
    int flag = fillExeHeader(&dummy_oh);
132
133
0
    initLoader(EM_386, stub_i086_dos16_exe, sizeof(stub_i086_dos16_exe));
134
135
0
    if (M_IS_LZMA(ph.method)) {
136
0
        addLoader("LZMA_DEC00", opt->small ? "LZMA_DEC10" : "LZMA_DEC20", "LZMA_DEC30",
137
0
                  use_clear_dirty_stack ? "LZMA_DEC31" : "", "LZMA_DEC32",
138
0
                  ph.u_len > 0xffff ? "LZMA_DEC33" : "");
139
140
0
        addLoaderEpilogue(flag);
141
0
        defineDecompressorSymbols();
142
0
        const unsigned lsize0 = getLoaderSize();
143
144
        // Lzma decompression code starts at ss:0x10, and its size is
145
        // lsize bytes. It also needs getDecompressorWrkmemSize() bytes
146
        // during uncompression. It also uses some stack, so 0x100
147
        // more bytes are allocated
148
0
        stack_for_lzma = 0x10 + lsize0 + getDecompressorWrkmemSize() + 0x100;
149
0
        stack_for_lzma = ALIGN_UP(stack_for_lzma, 16u);
150
151
0
        unsigned clear_dirty_stack_low = 0x10 + lsize0;
152
0
        clear_dirty_stack_low = ALIGN_UP(clear_dirty_stack_low, 2u);
153
0
        if (use_clear_dirty_stack)
154
0
            linker->defineSymbol("clear_dirty_stack_low", clear_dirty_stack_low);
155
156
0
        relocateLoader();
157
0
        const unsigned lsize = getLoaderSize();
158
0
        assert(lsize0 == lsize);
159
0
        MemBuffer loader(lsize);
160
0
        memcpy(loader, getLoader(), lsize);
161
162
0
        MemBuffer compressed_lzma;
163
0
        compressed_lzma.allocForCompression(lsize);
164
0
        unsigned c_len_lzma = MemBuffer::getSizeForCompression(lsize);
165
0
        int r = upx_compress(loader, lsize, compressed_lzma, &c_len_lzma, nullptr, M_NRV2B_LE16, 9,
166
0
                             nullptr, nullptr);
167
0
        assert(r == UPX_E_OK);
168
0
        assert(c_len_lzma < lsize);
169
170
0
        info("lzma+relocator code compressed: %u -> %u", lsize, c_len_lzma);
171
        // reinit the loader
172
0
        initLoader(EM_386, stub_i086_dos16_exe, sizeof(stub_i086_dos16_exe));
173
        // prepare loader
174
0
        if (device_driver)
175
0
            addLoader("DEVICEENTRY,LZMADEVICE,DEVICEENTRY2");
176
177
0
        linker->addSection("COMPRESSED_LZMA", compressed_lzma, c_len_lzma, 0);
178
0
        addLoader("LZMAENTRY,NRV2B160,NRVDDONE,NRVDECO1,NRVGTD00,NRVDECO2");
179
180
0
    } else if (device_driver)
181
0
        addLoader("DEVICEENTRY,DEVICEENTRY2");
182
183
0
    addLoader("EXEENTRY", M_IS_LZMA(ph.method) && device_driver ? "LONGSUB" : "SHORTSUB",
184
0
              "JNCDOCOPY", relocsize ? "EXERELPU" : "", "EXEMAIN4",
185
0
              M_IS_LZMA(ph.method) ? "" : "EXEMAIN4B", "EXEMAIN4C",
186
0
              M_IS_LZMA(ph.method) ? "COMPRESSED_LZMA_START,COMPRESSED_LZMA" : "",
187
0
              "+G5DXXXX,UPX1HEAD,EXECUTPO");
188
0
    if (ph.method == M_NRV2B_8)
189
0
        addLoader("NRV2B16S", // decompressor
190
0
                  ph.u_len > DI_LIMIT ? "N2B64K01" : "", "NRV2BEX1",
191
0
                  opt->cpu_x86 == opt->CPU_8086 ? "N2BX8601" : "N2B28601", "NRV2BEX2",
192
0
                  opt->cpu_x86 == opt->CPU_8086 ? "N2BX8602" : "N2B28602", "NRV2BEX3",
193
0
                  ph.c_len > 0xffff ? "N2B64K02" : "", "NRV2BEX9");
194
0
    else if (ph.method == M_NRV2D_8)
195
0
        addLoader("NRV2D16S", ph.u_len > DI_LIMIT ? "N2D64K01" : "", "NRV2DEX1",
196
0
                  opt->cpu_x86 == opt->CPU_8086 ? "N2DX8601" : "N2D28601", "NRV2DEX2",
197
0
                  opt->cpu_x86 == opt->CPU_8086 ? "N2DX8602" : "N2D28602", "NRV2DEX3",
198
0
                  ph.c_len > 0xffff ? "N2D64K02" : "", "NRV2DEX9");
199
0
    else if (ph.method == M_NRV2E_8)
200
0
        addLoader("NRV2E16S", ph.u_len > DI_LIMIT ? "N2E64K01" : "", "NRV2EEX1",
201
0
                  opt->cpu_x86 == opt->CPU_8086 ? "N2EX8601" : "N2E28601", "NRV2EEX2",
202
0
                  opt->cpu_x86 == opt->CPU_8086 ? "N2EX8602" : "N2E28602", "NRV2EEX3",
203
0
                  ph.c_len > 0xffff ? "N2E64K02" : "", "NRV2EEX9");
204
0
    else if M_IS_LZMA (ph.method)
205
0
        return;
206
0
    else
207
0
        throwInternalError("unknown compression method");
208
209
0
    addLoaderEpilogue(flag);
210
0
}
211
212
/*************************************************************************
213
//
214
**************************************************************************/
215
216
39.4k
int PackExe::readFileHeader() {
217
39.4k
    ih_exesize = ih_imagesize = ih_overlay = 0;
218
39.4k
    fi->readx(&ih, sizeof(ih));
219
39.4k
    if (ih.ident != 'M' + 'Z' * 256 && ih.ident != 'Z' + 'M' * 256)
220
38.7k
        return 0;
221
755
    ih_exesize = ih.m512 + ih.p512 * 512 - (ih.m512 ? 512 : 0);
222
755
    if (ih_exesize == 0)
223
246
        ih_exesize = file_size;
224
755
    ih_imagesize = ih_exesize - ih.headsize16 * 16;
225
755
    ih_overlay = file_size - ih_exesize;
226
755
    if (file_size_u < sizeof(ih) || ((ih.m512 | ih.p512) && ih.m512 + ih.p512 * 512u < sizeof(ih)))
227
22
        throwCantPack("illegal exe header");
228
733
    if (ih_exesize > file_size_u || ih_imagesize < 4 || ih_imagesize > ih_exesize)
229
350
        throwCantPack("exe header corrupted");
230
383
    NO_printf("dos/exe header: %d %d %d\n", ih_exesize, ih_imagesize, ih_overlay);
231
383
    return UPX_F_DOS_EXE;
232
733
}
233
234
0
tribool PackExe::canPack() {
235
0
    if (fn_has_ext(fi->getName(), "sys")) // dos/sys
236
0
        return false;
237
0
    if (!readFileHeader())
238
0
        return false;
239
0
    if (file_size < 1024 || ih_imagesize < 512)
240
0
        throwCantPack("file is too small for dos/exe");
241
0
    fi->seek(0x3c, SEEK_SET);
242
0
    LE32 offs;
243
0
    fi->readx(&offs, sizeof(offs));
244
0
    if (ih.relocoffs >= 0x40 && offs) {
245
0
        if (opt->dos_exe.force_stub)
246
0
            opt->overlay = opt->COPY_OVERLAY;
247
0
        else
248
0
            throwCantPack("dos/exe: can't pack new-exe");
249
0
    }
250
0
    return true;
251
0
}
252
253
/*************************************************************************
254
//
255
**************************************************************************/
256
257
static unsigned optimize_relocs(SPAN_S(byte) image, const unsigned image_size,
258
                                SPAN_S(const byte) relocs, const unsigned relocnum,
259
0
                                SPAN_S(byte) crel, bool *has_9a) {
260
0
#if WITH_XSPAN >= 2
261
0
    ptr_check_no_overlap(image.data(image_size), image_size, relocs.data(), relocs.size_bytes(),
262
0
                         crel.data(), crel.size_bytes());
263
0
#endif
264
0
    if (opt->exact)
265
0
        throwCantPackExact();
266
267
0
    SPAN_S_VAR(byte, const crel_start, crel);
268
0
    unsigned seg_high = 0;
269
#if 0
270
    unsigned seg_low = 0xffffffff;
271
    unsigned off_low = 0xffffffff;
272
    unsigned off_high = 0;
273
    unsigned linear_low = 0xffffffff;
274
    unsigned linear_high = 0;
275
#endif
276
277
    // pass 1 - find 0x9a bounds in image
278
0
    for (unsigned i = 0; i < relocnum; i++) {
279
0
        unsigned addr = get_le32(relocs + 4 * i);
280
0
        if (addr >= image_size - 1)
281
0
            throwCantPack("unexpected relocation 1");
282
0
        if (addr >= 3 && image[addr - 3] == 0x9a) {
283
0
            unsigned seg = get_le16(image + addr);
284
0
            if (seg > seg_high)
285
0
                seg_high = seg;
286
#if 0
287
            if (seg < seg_low)
288
                seg_low = seg;
289
            unsigned off = get_le16(image + addr - 2);
290
            if (off < off_low)
291
                off_low = off;
292
            if (off > off_high)
293
                off_high = off;
294
            unsigned l = (seg << 4) + off;
295
            if (l < linear_low)
296
                linear_low = l;
297
            if (l > linear_high)
298
                linear_high = l;
299
#endif
300
0
        }
301
0
    }
302
    // printf("%d %d\n", seg_low, seg_high);
303
    // printf("%d %d\n", off_low, off_high);
304
    // printf("%d %d\n", linear_low, linear_high);
305
306
    // pass 2 - reloc
307
308
0
    crel += 4; // to be filled in later
309
310
0
    unsigned ones = 0;
311
0
    unsigned es = 0;
312
0
    for (unsigned i = 0; i < relocnum;) {
313
0
        unsigned addr = get_le32(relocs + 4 * i);
314
0
        unsigned di = addr & 0x0f;
315
0
        set_le16(crel + 0, di);
316
0
        set_le16(crel + 2, (addr >> 4) - es);
317
0
        crel += 4;
318
0
        es = addr >> 4;
319
320
0
        for (++i; i < relocnum; i++) {
321
0
            unsigned t;
322
0
            addr = get_le32(relocs + 4 * i);
323
0
            NO_printf("%x\n", es * 16 + di);
324
0
            if ((addr - es * 16 > 0xfffe) || (i == relocnum - 1 && addr - es * 16 > 0xff00)) {
325
                // segment change
326
0
                t = 1 + (0xffff - di) / 254;
327
0
                memset(crel, 1, t);
328
0
                crel += t;
329
0
                ones += t - 1; // -1 is used to help the assembly stuff
330
0
                break;
331
0
            }
332
0
            unsigned offs = addr - es * 16;
333
0
            if (offs >= 3 && image[es * 16 + offs - 3] == 0x9a && offs > di + 3) {
334
0
                for (t = di; t < offs - 3; t++)
335
0
                    if (image[es * 16 + t] == 0x9a && get_le16(image + es * 16 + t + 3) <= seg_high)
336
0
                        break;
337
0
                if (t == offs - 3) {
338
                    // code 0: search for 0x9a
339
0
                    *crel++ = 0;
340
0
                    di = offs;
341
0
                    *has_9a = true;
342
0
                    continue;
343
0
                }
344
0
            }
345
0
            t = offs - di;
346
0
            if ((int) t < 2)
347
0
                throwCantPack("unexpected relocation 2");
348
0
            while (t >= 256) {
349
                // code 1: add 254, don't reloc
350
0
                *crel++ = 1;
351
0
                t -= 254;
352
0
                ones++;
353
0
            }
354
0
            *crel++ = (byte) t;
355
0
            di = offs;
356
0
        }
357
0
    }
358
0
    *crel++ = 1;
359
0
    ones++;
360
0
    set_le16(crel_start, ones);
361
0
    set_le16(crel_start + 2, seg_high);
362
363
    // OutputFile::dump("x.rel", crel_start, ptr_udiff_bytes(crel, crel_start));
364
0
    return ptr_udiff_bytes(crel, crel_start);
365
0
}
366
367
/*************************************************************************
368
//
369
**************************************************************************/
370
371
0
void PackExe::pack(OutputFile *fo) {
372
0
    unsigned ic;
373
374
0
    const unsigned relocnum = ih.relocs;
375
0
    if (relocnum > MAXRELOCSIZE) // early check
376
0
        throwCantPack("too many relocations");
377
0
    checkOverlay(ih_overlay);
378
379
    // read image
380
    // image + space for optimized relocs + safety/alignments
381
0
    ibuf.alloc(ih_imagesize + 4 * relocnum + 1024);
382
0
    fi->seek(ih.headsize16 * 16, SEEK_SET);
383
0
    fi->readx(ibuf, ih_imagesize);
384
385
0
    checkAlreadyPacked(ibuf, UPX_MIN(ih_imagesize, 127u));
386
387
0
    device_driver = get_le32(ibuf) == 0xffffffffu;
388
389
    // relocations
390
0
    relocsize = 0;
391
0
    has_9a = false;
392
0
    if (relocnum) {
393
0
        MemBuffer mb_relocs(4 * relocnum);
394
0
        SPAN_S_VAR(byte, relocs, mb_relocs);
395
0
        fi->seek(ih.relocoffs, SEEK_SET);
396
0
        fi->readx(relocs, 4 * relocnum);
397
398
        // dos/exe runs in real-mode, so convert to linear addresses
399
0
        for (ic = 0; ic < relocnum; ic++) {
400
0
            unsigned jc = get_le32(relocs + 4 * ic);
401
0
            set_le32(relocs + 4 * ic, ((jc >> 16) * 16 + (jc & 0xffff)) & 0xfffff);
402
0
        }
403
0
        upx_qsort(raw_bytes(relocs, 4 * relocnum), relocnum, 4, le32_compare);
404
405
0
        SPAN_S_VAR(byte, image, ibuf + 0, ih_imagesize);
406
0
        SPAN_S_VAR(byte, crel, ibuf + ih_imagesize, ibuf);
407
0
        relocsize = optimize_relocs(image, ih_imagesize, relocs, relocnum, crel, &has_9a);
408
0
        set_le16(crel + relocsize, relocsize + 2);
409
0
        relocsize += 2;
410
0
        assert(relocsize >= 11);
411
0
        if (relocsize > MAXRELOCSIZE) // optimize_relocs did not help
412
0
            throwCantPack("too many relocations");
413
#if TESTING && 0
414
        unsigned rout_len = MemBuffer::getSizeForCompression(relocsize);
415
        MemBuffer rout(rout_len);
416
        ucl_nrv2b_99_compress(raw_bytes(crel, relocsize), relocsize, rout, &rout_len, nullptr, 9,
417
                              nullptr, nullptr);
418
        printf("dos/exe reloc compress: %d -> %d\n", relocsize, rout_len);
419
#endif
420
0
    }
421
422
    // prepare packheader
423
0
    ph.u_len = ih_imagesize + relocsize;
424
0
    obuf.allocForCompression(ph.u_len);
425
    // prepare filter
426
0
    Filter ft(ph.level);
427
    // compress (max_match = 8192)
428
0
    upx_compress_config_t cconf;
429
0
    cconf.reset();
430
0
    cconf.conf_ucl.max_match = MAXMATCH;
431
0
    cconf.conf_lzma.max_num_probs = 1846 + (768 << 4); // ushort: ~28 KiB stack
432
0
    compressWithFilters(&ft, 32, &cconf);
433
434
0
    if (M_IS_NRV2B(ph.method) || M_IS_NRV2D(ph.method) || M_IS_NRV2E(ph.method))
435
0
        if (ph.max_run_found + ph.max_match_found > 0x8000)
436
0
            throwCantPack("decompressor limit exceeded, send a bugreport");
437
438
#if TESTING
439
    if (opt->debug.debug_level) {
440
        printf("image+relocs %d -> %d\n", ih_imagesize + relocsize, ph.c_len);
441
        printf("offsets: %d - %d\nmatches: %d - %d\nruns: %d - %d\n", 0 /*ph.min_offset_found*/,
442
               ph.max_offset_found, 0 /*ph.min_match_found*/, ph.max_match_found,
443
               0 /*ph.min_run_found*/, ph.max_run_found);
444
    }
445
#endif
446
447
0
    int flag = fillExeHeader(&oh);
448
449
0
    const unsigned lsize = getLoaderSize();
450
0
    MemBuffer loader(lsize);
451
0
    memcpy(loader, getLoader(), lsize);
452
    // OutputFile::dump("xxloader.dat", loader, lsize);
453
454
    // patch loader
455
0
    const unsigned packedsize = ph.c_len;
456
0
    const unsigned e_len = getLoaderSectionStart("EXECUTPO");
457
0
    const unsigned d_len = lsize - e_len;
458
0
    assert((e_len & 15) == 0);
459
460
0
    const unsigned copysize = (1 + packedsize + d_len) & ~1;
461
0
    const unsigned firstcopy = copysize % 0x10000 ? copysize % 0x10000 : 0x10000;
462
463
    // set oh.min & oh.max
464
0
    ic = ih.min * 16 + ih_imagesize;
465
0
    if (ic < oh.ss * 16u + oh.sp)
466
0
        ic = oh.ss * 16u + oh.sp;
467
0
    oh.min = (ic - (packedsize + lsize)) / 16;
468
0
    ic = oh.min + (ih.max - ih.min);
469
0
    oh.max = ic < 0xffff && ih.max != 0xffff ? ic : 0xffff;
470
471
    // set extra info
472
0
    byte extra_info[9];
473
0
    unsigned eisize = 0;
474
0
    if (oh.ss != ih.ss) {
475
0
        set_le16(extra_info + eisize, ih.ss);
476
0
        eisize += 2;
477
0
        assert((flag & SS) != 0); // set in fillExeHeader()
478
0
    }
479
0
    if (oh.sp != ih.sp) {
480
0
        set_le16(extra_info + eisize, ih.sp);
481
0
        eisize += 2;
482
0
        assert((flag & SP) != 0); // set in fillExeHeader()
483
0
    }
484
0
    if (ih.min != oh.min) {
485
0
        set_le16(extra_info + eisize, ih.min);
486
0
        eisize += 2;
487
0
        flag |= MINMEM;
488
0
    }
489
0
    if (ih.max != oh.max) {
490
0
        set_le16(extra_info + eisize, ih.max);
491
0
        eisize += 2;
492
0
        flag |= MAXMEM;
493
0
    }
494
0
    extra_info[eisize++] = (byte) flag;
495
496
0
    if (M_IS_NRV2B(ph.method) || M_IS_NRV2D(ph.method) || M_IS_NRV2E(ph.method))
497
0
        linker->defineSymbol("bx_magic", 0x7FFF + 0x10 * ((packedsize & 15) + 1));
498
499
0
    unsigned decompressor_entry = 1 + (packedsize & 15);
500
0
    if (M_IS_LZMA(ph.method))
501
0
        decompressor_entry = 0x10;
502
0
    linker->defineSymbol("decompressor_entry", decompressor_entry);
503
504
    // patch loader
505
0
    if (flag & USEJUMP) {
506
        // I use a relocation entry to set the original cs
507
0
        unsigned n = getLoaderSectionStart("EXEJUMPF") + 1;
508
0
        n += packedsize + 2;
509
0
        oh.relocs = 1;
510
0
        oh.firstreloc = (n & 0xf) + ((n >> 4) << 16);
511
0
    } else {
512
0
        oh.relocs = 0;
513
0
        oh.firstreloc = ih.cs * 0x10000 + ih.ip;
514
0
    }
515
516
0
    oh.relocoffs = offsetof(exe_header_t, firstreloc);
517
518
0
    linker->defineSymbol("destination_segment", oh.ss - ph.c_len / 16 - e_len / 16);
519
0
    linker->defineSymbol("source_segment", e_len / 16 + (copysize - firstcopy) / 16);
520
0
    linker->defineSymbol("copy_offset", firstcopy - 2);
521
0
    linker->defineSymbol("words_to_copy", firstcopy / 2);
522
523
0
    linker->defineSymbol("exe_stack_sp", oh.sp);
524
0
    linker->defineSymbol("exe_stack_ss", oh.ss);
525
0
    linker->defineSymbol("interrupt", get_le16(ibuf + 8));
526
0
    linker->defineSymbol("attribute", get_le16(ibuf + 4));
527
0
    linker->defineSymbol("orig_strategy", get_le16(ibuf + 6));
528
529
0
    const unsigned outputlen = sizeof(oh) + e_len + packedsize + d_len + eisize;
530
0
    oh.m512 = outputlen & 511;
531
0
    oh.p512 = (outputlen + 511) >> 9;
532
533
0
    const char *exeentry = M_IS_LZMA(ph.method) ? "LZMAENTRY" : "EXEENTRY";
534
0
    oh.ip = device_driver ? getLoaderSection(exeentry) - 2 : 0;
535
536
0
    defineDecompressorSymbols();
537
0
    relocateLoader();
538
0
    memcpy(loader, getLoader(), lsize);
539
0
    patchPackHeader(loader, e_len);
540
541
0
    NO_fprintf(stderr, "\ne_len=%x d_len=%x c_len=%x oo=%x ulen=%x copysize=%x imagesize=%x", e_len,
542
0
               d_len, packedsize, ph.overlap_overhead, ph.u_len, copysize, ih_imagesize);
543
544
    // write header + write loader + compressed file
545
#if TESTING
546
    if (opt->debug.debug_level)
547
        printf("\n%d %d %d %d\n", (int) sizeof(oh), e_len, packedsize, d_len);
548
#endif
549
0
    fo->write(&oh, sizeof(oh));       // program header
550
0
    fo->write(loader, e_len);         // entry code
551
0
    fo->write(obuf, packedsize);      // compressed data
552
0
    fo->write(loader + e_len, d_len); // decompressor code
553
0
    fo->write(extra_info, eisize);    // extra info for unpacking
554
0
    assert(eisize <= 9);
555
0
    NO_printf("%-13s: program hdr  : %8u bytes\n", getName(), usizeof(oh));
556
0
    NO_printf("%-13s: entry        : %8u bytes\n", getName(), e_len);
557
0
    NO_printf("%-13s: compressed   : %8u bytes\n", getName(), packedsize);
558
0
    NO_printf("%-13s: decompressor : %8u bytes\n", getName(), d_len);
559
0
    NO_printf("%-13s: extra info   : %8u bytes\n", getName(), eisize);
560
561
    // verify
562
0
    verifyOverlappingDecompression();
563
564
    // copy the overlay
565
0
    copyOverlay(fo, ih_overlay, obuf);
566
0
    NO_fprintf(stderr, "dos/exe %x %x\n", relocsize, ph.u_len);
567
568
    // finally check the compression ratio
569
0
    if (!checkFinalCompressionRatio(fo))
570
0
        throwNotCompressible();
571
0
}
572
573
/*************************************************************************
574
//
575
**************************************************************************/
576
577
39.4k
tribool PackExe::canUnpack() {
578
39.4k
    if (!readFileHeader())
579
38.7k
        return false;
580
755
    const unsigned off = ih.headsize16 * 16;
581
755
    fi->seek(off, SEEK_SET);
582
755
    bool b = readPackHeader(4096);
583
755
    return b && (off + ph.c_len <= file_size_u);
584
39.4k
}
585
586
/*************************************************************************
587
//
588
**************************************************************************/
589
590
159
void PackExe::unpack(OutputFile *fo) {
591
159
    ibuf.alloc(file_size);
592
159
    obuf.allocForDecompression(ph.u_len);
593
594
    // read the file
595
159
    fi->seek(ih.headsize16 * 16, SEEK_SET);
596
159
    fi->readx(ibuf, ih_imagesize);
597
598
    // get compressed data offset
599
159
    unsigned e_len = ph.buf_offset + ph.getPackHeaderSize();
600
159
    if (ih_imagesize <= e_len + ph.c_len)
601
14
        throwCantUnpack("file damaged");
602
603
145
    checkOverlay(ih_overlay);
604
605
    // decompress
606
145
    decompress(ibuf + e_len, obuf);
607
608
145
    unsigned imagesize = ih_imagesize;
609
145
    imagesize -= 1;
610
145
    const byte flag = ibuf[imagesize];
611
612
    // relocations
613
145
    unsigned relocnum = 0;
614
145
    SPAN_S_VAR(const byte, relocstart, obuf + ph.u_len, obuf);
615
145
    MemBuffer mb_relocs;
616
145
    SPAN_0_VAR(byte, relocs, nullptr);
617
145
    if (!(flag & NORELOC)) {
618
0
        mb_relocs.alloc(4 * MAXRELOCSIZE);
619
0
        relocs = SPAN_S_MAKE(byte, mb_relocs); // => now a SPAN_S
620
621
0
        relocsize = get_le16(obuf + ph.u_len - 2);
622
0
        ph.u_len -= 2;
623
0
        if (relocsize < 11 || relocsize > MAXRELOCSIZE || relocsize >= imagesize)
624
0
            throwCantUnpack("bad relocations");
625
0
        relocstart -= relocsize;
626
627
        // unoptimize_relocs
628
0
        unsigned ones = get_le16(relocstart);
629
0
        const unsigned seg_high = get_le16(relocstart + 2);
630
0
        SPAN_S_VAR(const byte, p, relocstart + 4);
631
0
        unsigned es = 0;
632
0
        while (ones) {
633
0
            unsigned di = get_le16(p);
634
0
            es += get_le16(p + 2);
635
0
            bool dorel = true;
636
0
            for (p += 4; ones && di < 0x10000; p++) {
637
0
                if (dorel) {
638
0
                    set_le16(relocs + (4 * relocnum + 0), di);
639
0
                    set_le16(relocs + (4 * relocnum + 2), es);
640
0
                    NO_printf("dos/exe unreloc %4d %6x\n", relocnum, es * 16 + di);
641
0
                    relocnum++;
642
0
                }
643
0
                dorel = true;
644
0
                if (*p == 0) {
645
0
                    SPAN_S_VAR(const byte, q, obuf + (es * 16 + di), obuf);
646
0
                    while (!(*q == 0x9a && get_le16(q + 3) <= seg_high))
647
0
                        q++;
648
0
                    di = ptr_udiff_bytes(q, obuf + (es * 16)) + 3;
649
0
                } else if (*p == 1) {
650
0
                    di += 254;
651
0
                    if (di < 0x10000)
652
0
                        ones--;
653
0
                    dorel = false;
654
0
                } else
655
0
                    di += *p;
656
0
            }
657
0
        }
658
0
    }
659
660
    // fill new exe header
661
145
    mem_clear(&oh);
662
145
    oh.ident = 'M' + 'Z' * 256;
663
664
145
    if (relocnum) {
665
0
        oh.relocs = relocnum;
666
0
        while (relocnum & 3) // paragraph align
667
0
            set_le32(relocs + (4 * relocnum++), 0);
668
0
    }
669
670
145
    unsigned outputlen = sizeof(oh) + 4 * relocnum + ptr_udiff_bytes(relocstart, obuf);
671
145
    oh.m512 = outputlen & 511;
672
145
    oh.p512 = (outputlen + 511) >> 9;
673
145
    oh.headsize16 = 2 + relocnum / 4;
674
675
145
    oh.max = ih.max;
676
145
    oh.min = ih.min;
677
145
    oh.sp = ih.sp;
678
145
    oh.ss = ih.ss;
679
680
145
    if (flag & MAXMEM) {
681
0
        imagesize -= 2;
682
0
        oh.max = get_le16(ibuf + imagesize);
683
0
    }
684
145
    if (flag & MINMEM) {
685
0
        imagesize -= 2;
686
0
        oh.min = get_le16(ibuf + imagesize);
687
0
    }
688
145
    if (flag & SP) {
689
0
        imagesize -= 2;
690
0
        oh.sp = get_le16(ibuf + imagesize);
691
0
    }
692
145
    if (flag & SS) {
693
0
        imagesize -= 2;
694
0
        oh.ss = get_le16(ibuf + imagesize);
695
0
    }
696
697
145
    unsigned ip = (flag & USEJUMP) ? get_le32(ibuf + imagesize - 4) : (unsigned) ih.firstreloc;
698
145
    oh.ip = ip & 0xffff;
699
145
    oh.cs = ip >> 16;
700
701
145
    oh.relocoffs = sizeof(oh);
702
145
    oh.firstreloc = 0;
703
145
    if (!fo)
704
0
        return;
705
706
    // write header + relocations + uncompressed file
707
145
    fo->write(&oh, sizeof(oh));
708
145
    if (relocnum)
709
0
        fo->write(relocs, 4 * relocnum);
710
145
    fo->write(obuf, ptr_udiff_bytes(relocstart, obuf));
711
712
    // copy the overlay
713
145
    copyOverlay(fo, ih_overlay, obuf);
714
145
}
715
716
/*
717
718
memory layout at decompression time
719
===================================
720
721
normal exe
722
----------
723
724
a, at load time
725
726
(e - copying code, C - compressed data, d - decompressor+relocator,
727
 x - not specified, U - uncompressed code+data, R uncompressed relocation)
728
729
eeCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCdddd
730
^ CS:0                                       ^ SS:0
731
732
b, after copying
733
734
xxxxxxxxxxxxxxxCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCdddd
735
^ES:DI=0       ^ DS:SI=0                     ^ CS=SS, IP in range 0..0xf
736
737
c, after uncompression
738
739
UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUURRdddd
740
                                           ^ ES:DI
741
742
device driver
743
-------------
744
745
the file has 2 entry points, CS:0 in device driver mode, and
746
CS:exe_as_device_entry in normal mode. the code in section DEVICEENTRY
747
sets up the same environment for section EXEENTRY, as it would see in normal
748
execution mode.
749
750
lzma uncompression for normal exes
751
----------------------------------
752
753
(n - nrv2b uncompressor, l - nrv2b compressed lzma + relocator code)
754
755
a, at load time
756
757
nneelllCCCCCCCCCCCCCCCCCCCCCCCCC
758
759
^ CS:0                                       ^ SS:0
760
761
b, after nrv2b
762
763
nneelllCCCCCCCCCCCCCCCCCCCCCCCCC             dddd
764
^ CS:0                                       ^ SS:0x10
765
766
after this, normal ee code runs
767
768
lzma + device driver
769
--------------------
770
771
(D - device driver adapter)
772
773
a, at load time
774
775
DDnneelllCCCCCCCCCCCCCCCCCCCCCCCCC
776
777
*/
778
779
/* vim:set ts=4 sw=4 et: */