Coverage Report

Created: 2025-11-24 06:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/upx/src/lefile.cpp
Line
Count
Source
1
/* lefile.cpp --
2
3
   This file is part of the UPX executable compressor.
4
5
   Copyright (C) 1996-2025 Markus Franz Xaver Johannes Oberhumer
6
   Copyright (C) 1996-2025 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 "lefile.h"
31
32
7.16k
LeFile::LeFile(InputFile *f) noexcept : fif(f) {
33
7.16k
    COMPILE_TIME_ASSERT(sizeof(le_header_t) == 196)
34
7.16k
    COMPILE_TIME_ASSERT(sizeof(le_object_table_entry_t) == 24)
35
7.16k
    COMPILE_TIME_ASSERT(sizeof(le_pagemap_entry_t) == 4)
36
7.16k
    mem_clear(&ih);
37
7.16k
    mem_clear(&oh);
38
7.16k
}
39
40
7.16k
LeFile::~LeFile() noexcept {
41
7.16k
    delete[] iobject_table;
42
7.16k
    delete[] oobject_table;
43
7.16k
    delete[] ifpage_table;
44
7.16k
    delete[] ofpage_table;
45
7.16k
    delete[] ipm_entries;
46
7.16k
    delete[] opm_entries;
47
7.16k
    delete[] ires_names;
48
7.16k
    delete[] ores_names;
49
7.16k
    delete[] ifixups;
50
7.16k
    delete[] ofixups;
51
7.16k
    delete[] inonres_names;
52
7.16k
    delete[] ononres_names;
53
7.16k
    delete[] ientries;
54
7.16k
    delete[] oentries;
55
7.16k
}
56
57
150
#define objects ih.object_table_entries
58
757
#define pages   ih.memory_pages
59
1.06k
#define mps     ih.memory_page_size
60
61
75
void LeFile::readObjectTable() {
62
75
    soobject_table = objects;
63
75
    iobject_table = New(le_object_table_entry_t, soobject_table);
64
75
    fif->seek(le_offset + ih.object_table_offset, SEEK_SET);
65
75
    fif->readx(iobject_table, sizeof(*iobject_table) * objects);
66
75
}
67
68
0
void LeFile::writeObjectTable() {
69
0
    if (fof && oobject_table)
70
0
        fof->write(oobject_table, sizeof(*iobject_table) * soobject_table);
71
0
}
72
73
52
void LeFile::readPageMap() {
74
52
    sopm_entries = pages;
75
52
    ipm_entries = New(le_pagemap_entry_t, sopm_entries);
76
52
    fif->seek(le_offset + ih.object_pagemap_offset, SEEK_SET);
77
52
    fif->readx(ipm_entries, sizeof(*ipm_entries) * pages);
78
79
301
    for (unsigned ic = 0; ic < pages; ic++)
80
254
        if ((ipm_entries[ic].type & 0xC0) != 0 && (ipm_entries[ic].type & 0xC0) != 0xC0)
81
5
            throwCantPack("unexpected value in page map table");
82
52
}
83
84
0
void LeFile::writePageMap() {
85
0
    if (fof && opm_entries)
86
0
        fof->write(opm_entries, sizeof(*ipm_entries) * sopm_entries);
87
0
}
88
89
47
void LeFile::readResidentNames() {
90
47
    sores_names = ih.entry_table_offset - ih.resident_names_offset;
91
47
    ires_names = New(byte, sores_names);
92
47
    fif->seek(le_offset + ih.resident_names_offset, SEEK_SET);
93
47
    fif->readx(ires_names, sores_names);
94
47
}
95
96
0
void LeFile::writeResidentNames() {
97
0
    if (fof && ores_names)
98
0
        fof->write(ores_names, sores_names);
99
0
}
100
101
36
void LeFile::readEntryTable() {
102
36
    soentries = ih.fixup_page_table_offset - ih.entry_table_offset;
103
36
    fif->seek(le_offset + ih.entry_table_offset, SEEK_SET);
104
36
    ientries = New(byte, soentries);
105
36
    fif->readx(ientries, soentries);
106
36
}
107
108
0
void LeFile::writeEntryTable() {
109
0
    if (fof && oentries)
110
0
        fof->write(oentries, soentries);
111
0
}
112
113
30
void LeFile::readFixupPageTable() {
114
30
    sofpage_table = 1 + pages;
115
30
    ifpage_table = New(unsigned, sofpage_table);
116
30
    fif->seek(le_offset + ih.fixup_page_table_offset, SEEK_SET);
117
30
    fif->readx(ifpage_table, 4 * sofpage_table);
118
30
}
119
120
0
void LeFile::writeFixupPageTable() {
121
0
    if (fof && ofpage_table)
122
0
        fof->write(ofpage_table, 4 * sofpage_table);
123
0
}
124
125
30
void LeFile::readFixups() {
126
30
    sofixups = get_le32(ifpage_table + pages) - get_le32(ifpage_table);
127
30
    ifixups = New(byte, sofixups);
128
30
    fif->seek(le_offset + ih.fixup_record_table_offset, SEEK_SET);
129
30
    fif->readx(ifixups, sofixups);
130
30
}
131
132
0
void LeFile::writeFixups() {
133
0
    if (fof && ofixups)
134
0
        fof->write(ofixups, sofixups);
135
0
}
136
137
88
unsigned LeFile::getImageSize() const {
138
88
    unsigned n = 0;
139
88
    if (ih.memory_pages > 0) {
140
88
        n = (ih.memory_pages - 1) * ih.memory_page_size;
141
88
        n += ih.bytes_on_last_page;
142
88
    }
143
88
    return n;
144
88
}
145
146
20
void LeFile::readImage() {
147
20
    soimage = mem_size(mps, pages); // assert size
148
20
    if (!soimage)                   // late detection, but protect against .alloc(0)
149
0
        throwCantPack("no soimage");
150
20
    mb_iimage.alloc(soimage);
151
20
    mb_iimage.clear();
152
20
    iimage = mb_iimage; // => now a SPAN_S
153
154
20
    unsigned ic, jc;
155
41
    for (ic = jc = 0; ic < pages; ic++) {
156
21
        if ((ipm_entries[ic].type & 0xC0) == 0) {
157
4
            fif->seek(ih.data_pages_offset + exe_offset +
158
4
                          (ipm_entries[ic].m * 0x100 + ipm_entries[ic].l - 1) * mps,
159
4
                      SEEK_SET);
160
4
            unsigned bytes = ic != pages - 1 ? mps : ih.bytes_on_last_page;
161
4
            fif->readx(iimage + jc, bytes);
162
4
        }
163
21
        jc += mps;
164
21
    }
165
20
}
166
167
0
void LeFile::writeImage() {
168
0
    if (fof && oimage != nullptr)
169
0
        fof->write(oimage, soimage);
170
0
}
171
172
18
void LeFile::readNonResidentNames() {
173
18
    if (ih.non_resident_name_table_length) {
174
14
        sononres_names = ih.non_resident_name_table_length;
175
14
        inonres_names = New(byte, sononres_names);
176
14
        fif->seek(exe_offset + ih.non_resident_name_table_offset, SEEK_SET);
177
14
        fif->readx(inonres_names, sononres_names);
178
14
    }
179
18
}
180
181
0
void LeFile::writeNonResidentNames() {
182
0
    if (fof && ononres_names)
183
0
        fof->write(ononres_names, sononres_names);
184
0
}
185
186
7.16k
bool LeFile::readFileHeader() {
187
7.16k
#define H(x) get_le16(header + (2 * (x)))
188
7.16k
    byte header[0x40];
189
7.16k
    le_offset = exe_offset = 0;
190
7.16k
    int ic;
191
192
8.09k
    for (ic = 0; ic < 20; ic++) {
193
8.06k
        fif->seek(le_offset, SEEK_SET);
194
8.06k
        fif->readx(header, sizeof(header));
195
196
8.06k
        if (memcmp(header, "MZ", 2) == 0) // normal dos exe
197
920
        {
198
920
            exe_offset = le_offset;
199
920
            if (H(0x18 / 2) >= 0x40 && memcmp(header + 0x19, "TIPPACH", 7)) // new format exe
200
747
                le_offset += H(0x3c / 2) + H(0x3e / 2) * 65536;
201
173
            else {
202
173
                le_offset += H(2) * 512 + H(1);
203
173
                if (H(1))
204
133
                    le_offset -= 512;
205
40
                else if (H(2) == 0)
206
21
                    return false;
207
173
            }
208
7.14k
        } else if (memcmp(header, "BW", 2) == 0) // used in dos4gw.exe
209
28
            le_offset += H(2) * 512 + H(1);
210
7.11k
        else if (memcmp(header, "LE", 2) == 0)
211
166
            break; // success
212
6.94k
        else if (memcmp(header, "PMW1", 4) == 0)
213
13
            throwCantPack("already packed with PMWLITE");
214
6.93k
        else
215
6.93k
            return false;
216
8.06k
    }
217
196
    if (ic == 20)
218
30
        return false;
219
166
    fif->seek(le_offset, SEEK_SET);
220
166
    fif->readx(&ih, sizeof(ih));
221
166
    if (mps < 512 || mps > 2097152 || (mps & (mps - 1)) != 0)
222
34
        throwCantPack("LE file header invalid page size %u", (unsigned) mps);
223
132
    if (ih.bytes_on_last_page > mps || pages == 0)
224
19
        throwCantPack("bad LE file header");
225
113
    if (!mem_size_valid(mps, pages) || exe_offset > le_offset)
226
24
        throwCantPack("bad LE file header");
227
89
    return true;
228
113
#undef H
229
113
}
230
231
0
void LeFile::writeFile(OutputFile *f, bool le) {
232
0
    fof = f;
233
0
    memcpy(&oh, &ih,
234
0
           (charptr) &oh.memory_pages - (charptr) &oh); // copy some members of the orig. header
235
0
    oh.memory_page_size = mps;
236
0
    oh.object_table_offset = sizeof(oh);
237
0
    oh.object_table_entries = soobject_table;
238
0
    oh.object_pagemap_offset = oh.object_table_offset + soobject_table * sizeof(*iobject_table);
239
0
    oh.resident_names_offset = oh.object_pagemap_offset + sopm_entries * sizeof(*ipm_entries);
240
0
    oh.entry_table_offset = oh.resident_names_offset + sores_names;
241
0
    oh.fixup_page_table_offset = oh.entry_table_offset + soentries;
242
0
    oh.fixup_record_table_offset = oh.fixup_page_table_offset + sofpage_table * 4;
243
0
    oh.imported_modules_name_table_offset = oh.fixup_record_table_offset + sofixups - FIXUP_EXTRA;
244
0
    oh.imported_procedures_name_table_offset = oh.imported_modules_name_table_offset;
245
0
    oh.data_pages_offset =
246
0
        oh.fixup_record_table_offset + sofixups + (le ? 0 : le_offset - exe_offset);
247
0
    if (ih.non_resident_name_table_length) {
248
0
        oh.non_resident_name_table_offset = oh.data_pages_offset + soimage;
249
0
        oh.non_resident_name_table_length = sononres_names;
250
0
    }
251
0
    oh.fixup_size = sofixups + 4 * sofpage_table;
252
0
    oh.loader_size = oh.fixup_size + oh.fixup_page_table_offset - sizeof(oh);
253
254
0
    fof->write(&oh, sizeof(oh));
255
0
    writeObjectTable();
256
0
    writePageMap();
257
0
    writeResidentNames();
258
0
    writeEntryTable();
259
0
    writeFixupPageTable();
260
0
    writeFixups();
261
0
    writeImage();
262
0
    writeNonResidentNames();
263
0
}
264
265
0
void LeFile::countFixups(unsigned *counts) const {
266
0
    const unsigned o = objects;
267
0
    memset(counts, 0, mem_size(sizeof(unsigned), o + 2));
268
    // counts[0..objects-1] - # of 32-bit offset relocations in for that objects
269
    // counts[objects]      - # of selector fixups
270
    // counts[objects+1]    - # of self-relative fixups
271
272
0
    const byte *fix = ifixups;
273
0
    const unsigned sfixups = get_le32(ifpage_table + pages);
274
0
    unsigned ll;
275
276
0
    while (ptr_udiff_bytes(fix, ifixups) < sfixups) {
277
0
        if ((fix[1] & ~0x10) != 0)
278
0
            throwCantPack("unsupported fixup record");
279
0
        switch (*fix) {
280
0
        case 2: // selector fixup
281
0
            counts[o] += 9;
282
0
            fix += 5;
283
0
            break;
284
0
        case 0x12: // alias selector
285
0
            throwCantPack("16-bit selector alias fixup not yet supported");
286
0
        case 5: // 16-bit offset
287
0
            fix += (fix[1] & 0x10) ? 9 : 7;
288
0
            break;
289
0
        case 6: // 16:32 pointer
290
0
            counts[o] += 9;
291
            // fall through
292
0
        case 7: // 32-bit offset
293
0
            counts[fix[4] - 1] += 4;
294
0
            fix += (fix[1] & 0x10) ? 9 : 7;
295
0
            break;
296
0
        case 0x27: // 32-bit offset list
297
0
            ll = fix[2];
298
0
            counts[fix[3] - 1] += ll * 4;
299
0
            fix += (fix[1] & 0x10) ? 6 : 4;
300
0
            fix += ll * 2;
301
0
            break;
302
0
        case 8: // 32-bit self relative fixup
303
0
            counts[o + 1] += 4;
304
0
            fix += (fix[1] & 0x10) ? 9 : 7;
305
0
            break;
306
0
        default:
307
0
            throwCantPack("unsupported fixup record");
308
0
        }
309
0
    }
310
0
    counts[o]++;        // extra space for 'ret'
311
0
    counts[o + 1] += 4; // extra space for 0xFFFFFFFF
312
0
}
313
314
/* vim:set ts=4 sw=4 et: */