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: */ |