Coverage Report

Created: 2026-09-28 06:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/upx/src/p_djgpp2.cpp
Line
Count
Source
1
/* p_djgpp2.cpp --
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_djgpp2.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/i386-dos32.djgpp2.h"
39
static const CLANG_FORMAT_DUMMY_STATEMENT
40
#include "stub/i386-dos32.djgpp2-stubify.h"
41
42
/*************************************************************************
43
//
44
**************************************************************************/
45
46
42.0k
PackDjgpp2::PackDjgpp2(InputFile *f) : super(f), coff_offset(0) {
47
42.0k
    bele = &N_BELE_RTP::le_policy;
48
42.0k
    COMPILE_TIME_ASSERT(sizeof(dos_header_t) == 64)
49
42.0k
    COMPILE_TIME_ASSERT(sizeof(external_scnhdr_t) == 40)
50
42.0k
    COMPILE_TIME_ASSERT(sizeof(coff_header_t) == 0xa8)
51
42.0k
    COMPILE_TIME_ASSERT_ALIGNED1(dos_header_t)
52
42.0k
    COMPILE_TIME_ASSERT_ALIGNED1(external_scnhdr_t)
53
42.0k
    COMPILE_TIME_ASSERT_ALIGNED1(coff_header_t)
54
42.0k
    COMPILE_TIME_ASSERT(sizeof(stub_i386_dos32_djgpp2_stubify) == 2048)
55
42.0k
    COMPILE_TIME_ASSERT(STUB_I386_DOS32_DJGPP2_STUBIFY_ADLER32 == 0xbf689ba8)
56
42.0k
    COMPILE_TIME_ASSERT(STUB_I386_DOS32_DJGPP2_STUBIFY_CRC32 == 0x2ae982b2)
57
    // printf("0x%08x\n", upx_adler32(stubify_stub, sizeof(stubify_stub)));
58
    // assert(upx_adler32(stubify_stub, sizeof(stubify_stub)) == STUBIFY_STUB_ADLER32);
59
42.0k
}
60
61
0
Linker *PackDjgpp2::newLinker() const { return new ElfLinkerX86; }
62
63
0
const int *PackDjgpp2::getCompressionMethods(int method, int level) const {
64
0
    return Packer::getDefaultCompressionMethods_le32(method, level);
65
0
}
66
67
0
const int *PackDjgpp2::getFilters() const {
68
0
    static const int filters[] = {0x26, 0x24,           0x49, 0x46, 0x16, 0x13,  0x14,
69
0
                                  0x11, FT_ULTRA_BRUTE, 0x25, 0x15, 0x12, FT_END};
70
0
    return filters;
71
0
}
72
73
unsigned PackDjgpp2::findOverlapOverhead(const byte *buf, const byte *tbuf, unsigned range,
74
0
                                         unsigned upper_limit) const {
75
0
    unsigned o = super::findOverlapOverhead(buf, tbuf, range, upper_limit);
76
0
    o = (o + 0x3ff) & ~0x1ff;
77
0
    return o;
78
0
}
79
80
0
void PackDjgpp2::buildLoader(const Filter *ft) {
81
    // prepare loader
82
0
    initLoader(EM_386, stub_i386_dos32_djgpp2, sizeof(stub_i386_dos32_djgpp2));
83
0
    addLoader("IDENTSTR,DJ2MAIN1", ft->id ? "DJCALLT1" : "",
84
0
              ph.first_offset_found == 1 ? "DJ2MAIN2" : "",
85
0
              M_IS_LZMA(ph.method) ? "LZMA_INIT_STACK" : "", getDecompressorSections(),
86
0
              M_IS_LZMA(ph.method) ? "LZMA_DONE_STACK" : "", "DJ2BSS00");
87
0
    if (ft->id) {
88
0
        assert(ft->calls > 0);
89
0
        addLoader("DJCALLT2");
90
0
        addFilter32(ft->id);
91
0
    }
92
0
    addLoader("DJRETURN,+40C,UPX1HEAD");
93
0
}
94
95
/*************************************************************************
96
// util
97
**************************************************************************/
98
99
18
void PackDjgpp2::handleStub(OutputFile *fo) {
100
18
    if (fo && !opt->djgpp2_coff.coff) {
101
13
        if (coff_offset > 0) {
102
            // copy stub from exe
103
9
            Packer::handleStub(fi, fo, coff_offset);
104
9
        } else {
105
            // "stubify" stub
106
4
            info("Adding stub: %zd bytes", sizeof(stub_i386_dos32_djgpp2_stubify));
107
4
            fo->write(stub_i386_dos32_djgpp2_stubify, sizeof(stub_i386_dos32_djgpp2_stubify));
108
4
        }
109
13
    }
110
18
}
111
112
61
static bool is_dlm(InputFile *fi, unsigned coff_offset) {
113
61
    byte buf[4];
114
61
    unsigned off;
115
116
61
    try {
117
61
        fi->seek(coff_offset, SEEK_SET);
118
61
        fi->readx(buf, 4);
119
61
        off = get_le32(buf);
120
61
        if (off > coff_offset + 4)
121
33
            return false;
122
28
        fi->seek(off, SEEK_SET);
123
28
        fi->readx(buf, 4);
124
28
        if (memcmp(buf, "DLMF", 4) == 0)
125
3
            return true;
126
28
    } catch (const IOException &) {
127
0
    }
128
25
    return false;
129
61
}
130
131
2
static void handle_allegropak(InputFile *fi, OutputFile *fo) {
132
2
    byte b[8];
133
2
    int pfsize = 0;
134
135
2
    try {
136
2
        fi->seek(-8, SEEK_END);
137
2
        fi->readx(b, 8);
138
2
        if (memcmp(b, "slh+", 4) != 0)
139
2
            return;
140
0
        pfsize = get_be32_signed(b + 4);
141
0
        if (pfsize <= 8 || pfsize >= fi->st.st_size)
142
0
            return;
143
0
        fi->seek(-pfsize, SEEK_END);
144
0
    } catch (const IOException &) {
145
0
        return;
146
0
    }
147
0
    MemBuffer buf(0x4000);
148
0
    while (pfsize > 0) {
149
0
        const int len = UPX_MIN(pfsize, (int) buf.getSize());
150
0
        fi->readx(buf, len);
151
0
        fo->write(buf, len);
152
0
        pfsize -= len;
153
0
    }
154
0
}
155
156
42.0k
int PackDjgpp2::readFileHeader() {
157
42.0k
    dos_header_t dos_hdr;
158
159
42.0k
    fi->seek(0, SEEK_SET);
160
42.0k
    fi->readx(&dos_hdr, sizeof(dos_hdr));
161
42.0k
    if (get_le16(&dos_hdr.e_magic) == 0x5a4d) { // MZ exe signature, stubbed?
162
1.41k
        byte magic[8];
163
1.41k
        fi->seek(16 * get_le16(&dos_hdr.e_cparhdr), SEEK_SET);
164
1.41k
        fi->readx(magic, 8);
165
1.41k
        if (memcmp("go32stub", magic, 8) != 0)
166
1.01k
            return 0; // not V2 image
167
1.41k
    }
168
41.0k
    coff_offset = 512 * get_le16(&dos_hdr.e_cp);
169
41.0k
    if (get_le16(&dos_hdr.e_cblp) != 0)
170
39.5k
        coff_offset += get_le16(&dos_hdr.e_cblp) - 512;
171
41.0k
    fi->seek(coff_offset, SEEK_SET);
172
41.0k
    if (fi->read(&coff_hdr, sizeof(coff_hdr)) != sizeof(coff_hdr))
173
15
        throwCantPack("skipping djgpp symlink");
174
40.9k
    if (coff_hdr.f_magic != 0x014c) // I386MAGIC
175
1.52k
        return 0;
176
39.4k
    if ((coff_hdr.f_flags & 2) == 0) // F_EXEC - COFF executable
177
3
        return 0;
178
39.4k
    if (coff_hdr.a_magic != 0413) // ZMAGIC - demand load format
179
9
        return 0;
180
    // FIXME: check for Linux etc.
181
182
39.4k
    text = &coff_hdr.sh[0];
183
39.4k
    data = &coff_hdr.sh[1];
184
39.4k
    bss = &coff_hdr.sh[2];
185
39.4k
    return UPX_F_DJGPP2_COFF;
186
39.4k
}
187
188
// "strip" debug info
189
0
void PackDjgpp2::stripDebug() {
190
0
    coff_hdr.f_symptr = 0;
191
0
    coff_hdr.f_nsyms = 0;
192
0
    coff_hdr.f_flags = 0x10f; // 0x100: "32 bit machine: LSB first"
193
0
    memset(text->misc, 0, 12);
194
0
}
195
196
/*************************************************************************
197
//
198
**************************************************************************/
199
200
0
tribool PackDjgpp2::canPack() {
201
0
    if (!readFileHeader())
202
0
        return false;
203
0
    if (is_dlm(fi, coff_offset))
204
0
        throwCantPack("can't handle DLM");
205
206
0
    if (opt->force == 0)
207
0
        if (text->size != coff_hdr.a_tsize || data->size != coff_hdr.a_dsize)
208
0
            throwAlreadyPacked();
209
210
    // Check for gap in vaddr between text and data, or between data and bss.
211
0
    if (text->vaddr + text->size != data->vaddr || data->vaddr + data->size != bss->vaddr) {
212
        // "Non-standard" layout of text,data,bss: not contiguous in vaddr.
213
        // But should be OK if no overlap.
214
215
        // Check for no overlap of text and data:
216
        // neither by vaddr, nor by image data
217
0
        if (text->vaddr + text->size <= data->vaddr &&
218
0
            data->scnptr - text->scnptr <= data->vaddr - text->vaddr) {
219
            // Examples: Quake1; FreePascal(DOS) install.exe (github-issue45)
220
            // Hack: enlarge text image data to eliminate the gap.
221
0
            text->size = coff_hdr.a_tsize = data->scnptr - text->scnptr;
222
            // But complain if this causes overlap in vaddr
223
0
            if (text->vaddr + text->size > data->vaddr)
224
0
                throwAlreadyPacked();
225
0
        } else
226
0
            throwAlreadyPacked();
227
0
    }
228
    // FIXME: check for Linux etc.
229
0
    return true;
230
0
}
231
232
/*************************************************************************
233
//
234
**************************************************************************/
235
236
0
void PackDjgpp2::pack(OutputFile *fo) {
237
0
    handleStub(fo);
238
239
    // patch coff header #1: "strip" debug info
240
0
    stripDebug();
241
242
    // read file
243
0
    const unsigned size = text->size + data->size;
244
0
    const unsigned tpos = text->scnptr;
245
0
    const unsigned hdrsize = 20 + 28 + mem_size(sizeof(external_scnhdr_t), coff_hdr.f_nscns);
246
0
    const unsigned usize = size + hdrsize;
247
0
    if (hdrsize < sizeof(coff_hdr) || hdrsize > tpos)
248
0
        throwCantPack("coff header error");
249
250
0
    ibuf.alloc(usize);
251
0
    obuf.allocForCompression(usize);
252
253
0
    fi->seek(coff_offset, SEEK_SET);
254
0
    fi->readx(ibuf, hdrsize); // orig. coff header
255
0
    fi->seek(coff_offset + tpos, SEEK_SET);
256
0
    fi->readx(ibuf + hdrsize, size);
257
258
    // prepare packheader
259
0
    ph.u_len = usize;
260
    // prepare filter
261
0
    Filter ft(ph.level);
262
0
    ft.buf_len = usize - data->size;
263
0
    ft.addvalue = text->vaddr - hdrsize;
264
    // compress
265
0
    upx_compress_config_t cconf;
266
0
    cconf.reset();
267
    // limit stack size needed for runtime decompression
268
0
    cconf.conf_lzma.max_num_probs = 1846 + (768 << 4); // ushort: ~28 KiB stack
269
0
    compressWithFilters(&ft, 512, &cconf);
270
271
    // patch coff header #2
272
0
    const unsigned lsize = getLoaderSize();
273
0
    assert(lsize % 4 == 0);
274
0
    text->size = lsize;    // new size of .text
275
0
    data->size = ph.c_len; // new size of .data
276
277
0
    unsigned stack = 1024 + ph.overlap_overhead + getDecompressorWrkmemSize();
278
0
    stack = ALIGN_UP(stack, 16u);
279
0
    if (bss->size < stack) // give it a .bss
280
0
        bss->size = stack;
281
282
0
    text->scnptr = sizeof(coff_hdr);
283
0
    data->scnptr = text->scnptr + text->size;
284
0
    data->vaddr = bss->vaddr + ((data->scnptr + data->size) & 0x1ff) - data->size +
285
0
                  ph.overlap_overhead - 0x200;
286
0
    coff_hdr.f_nscns = 3;
287
288
0
    linker->defineSymbol("original_entry", coff_hdr.a_entry);
289
0
    linker->defineSymbol("length_of_bss", ph.overlap_overhead / 4);
290
0
    defineDecompressorSymbols();
291
    // Just need no overlap; non-contiguous (gap length > 0)) is OK
292
0
    assert(bss->vaddr >= ((size + 0x1ff) & ~0x1ff) + (text->vaddr & ~0x1ff));
293
0
    linker->defineSymbol("stack_for_lzma", bss->vaddr + bss->size);
294
0
    linker->defineSymbol("start_of_uncompressed", text->vaddr - hdrsize);
295
0
    linker->defineSymbol("start_of_compressed", data->vaddr);
296
0
    defineFilterSymbols(&ft);
297
298
    // we should not overwrite our decompressor during unpacking
299
    // the original coff header (which is put just before the
300
    // beginning of the original .text section)
301
0
    assert(text->vaddr > hdrsize + lsize + sizeof(coff_hdr));
302
303
    // patch coff header #3
304
0
    text->vaddr = sizeof(coff_hdr);
305
0
    coff_hdr.a_entry = sizeof(coff_hdr) + getLoaderSection("DJ2MAIN1");
306
0
    bss->vaddr += ph.overlap_overhead;
307
0
    bss->size -= ph.overlap_overhead;
308
309
    // because of a feature (bug?) in stub.asm we need some padding
310
0
    memcpy(obuf + data->size, "UPX", 3);
311
0
    data->size = ALIGN_UP(data->size, 4u);
312
313
0
    linker->defineSymbol("DJ2MAIN1", coff_hdr.a_entry);
314
0
    relocateLoader();
315
316
    // prepare loader
317
0
    MemBuffer loader(lsize);
318
0
    memcpy(loader, getLoader(), lsize);
319
0
    patchPackHeader(loader, lsize);
320
321
    // write coff header, loader and compressed file
322
0
    fo->write(&coff_hdr, sizeof(coff_hdr));
323
0
    fo->write(loader, lsize);
324
0
    if (opt->debug.dump_stub_loader)
325
0
        OutputFile::dump(opt->debug.dump_stub_loader, loader, lsize);
326
0
    fo->write(obuf, data->size);
327
#if 0
328
    printf("%-13s: coff hdr   : %8d bytes\n", getName(), (int) sizeof(coff_hdr));
329
    printf("%-13s: loader     : %8d bytes\n", getName(), (int) lsize);
330
    printf("%-13s: compressed : %8d bytes\n", getName(), (int) data->size);
331
#endif
332
333
    // verify
334
0
    verifyOverlappingDecompression();
335
336
    // handle overlay
337
    // FIXME: only Allegro pakfiles are supported
338
0
    handle_allegropak(fi, fo);
339
340
    // finally check the compression ratio
341
0
    if (!checkFinalCompressionRatio(fo))
342
0
        throwNotCompressible();
343
0
}
344
345
/*************************************************************************
346
//
347
**************************************************************************/
348
349
42.0k
tribool PackDjgpp2::canUnpack() {
350
42.0k
    if (!readFileHeader())
351
2.55k
        return false;
352
39.4k
    if (is_dlm(fi, coff_offset))
353
3
        throwCantUnpack("can't handle DLM");
354
39.4k
    fi->seek(coff_offset, SEEK_SET);
355
39.4k
    return readPackHeader(4096) ? 1 : -1;
356
39.4k
}
357
358
/*************************************************************************
359
//
360
**************************************************************************/
361
362
18
void PackDjgpp2::unpack(OutputFile *fo) {
363
18
    handleStub(fo);
364
365
18
    ibuf.alloc(ph.c_len);
366
18
    obuf.allocForDecompression(ph.u_len);
367
368
18
    fi->seek(coff_offset + ph.buf_offset + ph.getPackHeaderSize(), SEEK_SET);
369
18
    fi->readx(ibuf, ph.c_len);
370
371
    // decompress
372
18
    decompress(ibuf, obuf);
373
374
18
    coff_header_t *const chdr = (coff_header_t *) raw_bytes(obuf, sizeof(coff_header_t));
375
18
    text = &chdr->sh[0];
376
18
    data = &chdr->sh[1];
377
18
    bss = &chdr->sh[2];
378
379
18
    const unsigned hdrsize = 20 + 28 + mem_size(sizeof(external_scnhdr_t), chdr->f_nscns);
380
18
    if (hdrsize < sizeof(coff_hdr) || hdrsize > text->scnptr || hdrsize > ph.u_len)
381
0
        throwCantUnpack("coff header error");
382
383
18
    unsigned addvalue;
384
18
    if (ph.version >= 14)
385
4
        addvalue = text->vaddr - hdrsize;
386
14
    else
387
14
        addvalue = text->vaddr & ~0x1ff; // for old versions
388
389
    // unfilter
390
18
    if (ph.filter) {
391
4
        Filter ft(ph.level);
392
4
        ft.init(ph.filter, addvalue);
393
4
        ft.cto = (byte) ph.filter_cto;
394
4
        if (ph.version < 11) {
395
0
            byte ctobuf[4];
396
0
            fi->readx(ctobuf, 4);
397
0
            ft.cto = (byte) (get_le32(ctobuf) >> 24);
398
0
        }
399
4
        ft.unfilter(obuf, ph.u_len - data->size);
400
4
    }
401
402
18
    if (ph.version < 14) {
403
        // fixup for the aligning bug in strip 2.8+
404
0
        text->scnptr &= 0x1ff;
405
0
        data->scnptr = text->scnptr + text->size;
406
        // write decompressed file
407
0
        if (fo)
408
0
            fo->write(obuf, ph.u_len);
409
18
    } else {
410
        // write the header
411
        // some padding might be required between the end
412
        // of the header and the start of the .text section
413
414
18
        const unsigned padding = text->scnptr - hdrsize;
415
18
        ibuf.clear(0, padding);
416
417
18
        if (fo) {
418
2
            fo->write(obuf, hdrsize);
419
2
            fo->write(ibuf, padding);
420
2
            fo->write(obuf + hdrsize, ph.u_len - hdrsize);
421
2
        }
422
18
    }
423
424
18
    if (fo)
425
2
        handle_allegropak(fi, fo);
426
18
}
427
428
/* vim:set ts=4 sw=4 et: */