Line | Count | Source |
1 | | /* p_tos.cpp -- atari/tos executable format |
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 | | // atari/tos: lots of micro-optimizations because this was written at a time |
29 | | // where bytes and CPU cycles really mattered |
30 | | |
31 | | #include "conf.h" |
32 | | #include "file.h" |
33 | | #include "filter.h" |
34 | | #include "packer.h" |
35 | | #include "p_tos.h" |
36 | | #include "linker.h" |
37 | | |
38 | | static const CLANG_FORMAT_DUMMY_STATEMENT |
39 | | #include "stub/m68k-atari.tos.h" |
40 | | |
41 | | // #define TESTING 1 |
42 | | |
43 | | /************************************************************************* |
44 | | // |
45 | | **************************************************************************/ |
46 | | |
47 | 7.28k | #define FH_SIZE sizeof(tos_header_t) |
48 | | |
49 | 7.08k | PackTos::PackTos(InputFile *f) : super(f) { |
50 | 7.08k | bele = &N_BELE_RTP::be_policy; |
51 | 7.08k | COMPILE_TIME_ASSERT(FH_SIZE == 28) |
52 | 7.08k | COMPILE_TIME_ASSERT_ALIGNED1(tos_header_t) |
53 | 7.08k | } |
54 | | |
55 | 0 | Linker *PackTos::newLinker() const { return new ElfLinkerM68k; } |
56 | | |
57 | 0 | const int *PackTos::getCompressionMethods(int method, int level) const { |
58 | 0 | bool small = ih.fh_text + ih.fh_data <= 256 * 1024; |
59 | 0 | return Packer::getDefaultCompressionMethods_8(method, level, small); |
60 | 0 | } |
61 | | |
62 | 0 | const int *PackTos::getFilters() const { return nullptr; } |
63 | | |
64 | 0 | void PackTos::LinkerSymbols::LoopInfo::init(unsigned count_, bool allow_dbra) { |
65 | 0 | count = value = count_; |
66 | 0 | if (count == 0) |
67 | 0 | mode = LOOP_NONE; |
68 | 0 | else if (count <= 65536 && allow_dbra) { |
69 | 0 | mode = LOOP_DBRA; |
70 | 0 | value -= 1; |
71 | 0 | value &= 0xffff; |
72 | 0 | } else if (count <= 65536) { |
73 | 0 | mode = LOOP_SUBQ_W; |
74 | 0 | value &= 0xffff; |
75 | 0 | } else |
76 | 0 | mode = LOOP_SUBQ_L; |
77 | 0 | } |
78 | | |
79 | 0 | unsigned PackTos::getDecomprOffset(int method, int small) const { |
80 | 0 | UNUSED(small); |
81 | 0 | if (M_IS_NRV2B(method)) |
82 | 0 | return 2; // FIXME: do not hardcode this value |
83 | 0 | else if (M_IS_NRV2D(method)) |
84 | 0 | return 2; // FIXME: do not hardcode this value |
85 | 0 | else if (M_IS_NRV2E(method)) |
86 | 0 | return 2; // FIXME: do not hardcode this value |
87 | 0 | else if (M_IS_LZMA(method)) |
88 | 0 | return linker->getSectionSize("__mulsi3"); |
89 | 0 | else |
90 | 0 | throwBadLoader(); |
91 | 0 | return 0; |
92 | 0 | } |
93 | | |
94 | 0 | void PackTos::buildLoader(const Filter *ft) { |
95 | 0 | assert(ft->id == 0); |
96 | | |
97 | 0 | initLoader(stub_m68k_atari_tos, sizeof(stub_m68k_atari_tos)); |
98 | | // linker->dumpSymbols(); |
99 | | |
100 | | // |
101 | | // part 1a |
102 | | // |
103 | |
|
104 | 0 | addLoader("entry"); |
105 | |
|
106 | 0 | if (symbols.up21_a6 <= 32767) |
107 | 0 | addLoader("set_up21_a6.w"); |
108 | 0 | else if (symbols.up21_d4 <= 32767) |
109 | 0 | addLoader("set_up21_d4.w"); |
110 | 0 | else |
111 | 0 | addLoader("set_up21_d4.l"); |
112 | |
|
113 | 0 | assert(symbols.loop1.count || symbols.loop2.count); |
114 | 0 | if (symbols.loop1.count) { |
115 | 0 | if (symbols.loop1.value <= 127) |
116 | 0 | addLoader("loop1_set_count.b"); |
117 | 0 | else if (symbols.loop1.value <= 65535) |
118 | 0 | addLoader("loop1_set_count.w"); |
119 | 0 | else |
120 | 0 | addLoader("loop1_set_count.l"); |
121 | 0 | addLoader("loop1_label"); |
122 | 0 | addLoader(opt->small ? "loop1.small" : "loop1.fast"); |
123 | 0 | if (symbols.loop1.mode == symbols.LOOP_SUBQ_L) |
124 | 0 | addLoader("loop1_subql"); |
125 | 0 | else if (symbols.loop1.mode == symbols.LOOP_SUBQ_W) |
126 | 0 | addLoader("loop1_subqw"); |
127 | 0 | else if (symbols.loop1.mode == symbols.LOOP_DBRA) |
128 | 0 | addLoader("loop1_dbra"); |
129 | 0 | else |
130 | 0 | throwBadLoader(); |
131 | 0 | } |
132 | 0 | if (symbols.loop2.count) { |
133 | 0 | assert(symbols.loop2.mode == symbols.LOOP_DBRA); |
134 | 0 | addLoader(opt->small ? "loop2.small" : "loop2.fast"); |
135 | 0 | } |
136 | | |
137 | 0 | addLoader("copy_to_stack"); |
138 | |
|
139 | 0 | if (M_IS_NRV2B(ph.method)) |
140 | 0 | addLoader("nrv2b.init"); |
141 | 0 | else if (M_IS_NRV2D(ph.method)) |
142 | 0 | addLoader("nrv2d.init"); |
143 | 0 | else if (M_IS_NRV2E(ph.method)) |
144 | 0 | addLoader("nrv2e.init"); |
145 | 0 | else if (M_IS_LZMA(ph.method)) |
146 | 0 | addLoader("lzma.init"); |
147 | 0 | else |
148 | 0 | throwBadLoader(); |
149 | | |
150 | 0 | symbols.up31_d4 = symbols.up31_base_d4 + getDecomprOffset(ph.method, opt->small); |
151 | 0 | symbols.up31_a6 = symbols.up31_base_a6 + getDecomprOffset(ph.method, opt->small); |
152 | 0 | if (symbols.up31_a6 <= 32767) |
153 | 0 | addLoader("jmp_decompressor_a6.w"); |
154 | 0 | else if (symbols.up31_d4 <= 32767) |
155 | 0 | addLoader("jmp_decompressor_d4.w"); |
156 | 0 | else if (symbols.up31_a6 <= 65534) |
157 | 0 | addLoader("jmp_decompressor_a6.w2"); |
158 | 0 | else |
159 | 0 | addLoader("jmp_decompressor_d4.l"); |
160 | | |
161 | | // |
162 | | // part 1b |
163 | | // |
164 | |
|
165 | 0 | addLoader("code_on_stack"); |
166 | |
|
167 | 0 | addLoader("clear_dirty_bss"); |
168 | 0 | addLoader("loop3_label"); |
169 | 0 | addLoader(opt->small ? "loop3.small" : "loop3.fast"); |
170 | 0 | if (symbols.loop3.mode == symbols.LOOP_SUBQ_L) |
171 | 0 | addLoader("loop3_subql"); |
172 | 0 | else if (symbols.loop3.mode == symbols.LOOP_SUBQ_W) |
173 | 0 | addLoader("loop3_subqw"); |
174 | 0 | else if (symbols.loop3.mode == symbols.LOOP_DBRA) |
175 | 0 | addLoader("loop3_dbra"); |
176 | 0 | else |
177 | 0 | throwBadLoader(); |
178 | | |
179 | 0 | addLoader("flush_cache"); |
180 | 0 | addLoader("restore_stack"); |
181 | | #if 0 |
182 | | addLoader("clear_dirty_stack"); |
183 | | #endif |
184 | 0 | addLoader("start_program"); |
185 | |
|
186 | 0 | addLoader("IDENTSTR,+40D,UPX1HEAD,CUTPOINT"); |
187 | | |
188 | | // |
189 | | // part 2 |
190 | | // |
191 | |
|
192 | 0 | if (M_IS_NRV2B(ph.method)) { |
193 | 0 | addLoader(opt->small ? "nrv2b_8.small" : "nrv2b_8.fast"); |
194 | 0 | } else if (M_IS_NRV2D(ph.method)) { |
195 | 0 | addLoader(opt->small ? "nrv2d_8.small" : "nrv2d_8.fast"); |
196 | 0 | } else if (M_IS_NRV2E(ph.method)) { |
197 | 0 | addLoader(opt->small ? "nrv2e_8.small" : "nrv2e_8.fast"); |
198 | 0 | } else if (M_IS_LZMA(ph.method)) { |
199 | 0 | addLoader("__mulsi3"); |
200 | 0 | addLoader(opt->small ? "lzma.small" : "lzma.fast"); |
201 | 0 | addLoader("lzma.finish"); |
202 | 0 | } else |
203 | 0 | throwBadLoader(); |
204 | | |
205 | 0 | if (symbols.need_reloc) |
206 | 0 | addLoader("reloc"); |
207 | |
|
208 | 0 | assert(symbols.loop3.count); |
209 | 0 | if (symbols.loop3.value <= 127) |
210 | 0 | addLoader("loop3_set_count.b"); |
211 | 0 | else if (symbols.loop3.value <= 65535) |
212 | 0 | addLoader("loop3_set_count.w"); |
213 | 0 | else |
214 | 0 | addLoader("loop3_set_count.l"); |
215 | |
|
216 | 0 | addLoader("jmp_stack"); |
217 | 0 | } |
218 | | |
219 | | /************************************************************************* |
220 | | // |
221 | | **************************************************************************/ |
222 | | |
223 | | /* flags for curproc->memflags */ |
224 | | /* also used for program headers fh_flag */ |
225 | 0 | #define F_FASTLOAD 0x01 // don't zero heap |
226 | | #define F_ALTLOAD 0x02 // OK to load in alternate ram |
227 | 0 | #define F_ALTALLOC 0x04 // OK to malloc from alt. ram |
228 | 0 | #define F_SMALLTPA 0x08 |
229 | | // used in MagiC: TPA can be allocated as specified in the program header |
230 | | // rather than the biggest free memory block |
231 | | #define F_MEMFLAGS 0xf0 // reserved for future use |
232 | 20 | #define F_SHTEXT 0x800 // program's text may be shared |
233 | | |
234 | | #define F_MINALT 0xf0000000 // used to decide which type of RAM to load in |
235 | | |
236 | 0 | #define F_ALLOCZERO 0x2000 // zero mem, for bugged (GEM...) programs |
237 | | |
238 | | /* Bit in Mxalloc's arg for "don't auto-free this memory" */ |
239 | 0 | #define F_KEEP 0x4000 |
240 | | |
241 | 30 | #define F_OS_SPECIAL 0x8000 // mark as a special process |
242 | | |
243 | | /* flags for curproc->memflags (that is, fh_flag) and also Mxalloc mode. */ |
244 | | /* (Actually, when users call Mxalloc, they add 0x10 to what you see here) */ |
245 | 52 | #define F_PROTMODE 0xf0 // protection mode bits |
246 | 24 | #define F_PROT_P 0x00 // no read or write |
247 | | #define F_PROT_G 0x10 // any access OK |
248 | | #define F_PROT_S 0x20 // any super access OK |
249 | | #define F_PROT_PR 0x30 // any read OK, no write |
250 | 28 | #define F_PROT_I 0x40 // invalid page |
251 | | |
252 | | /************************************************************************* |
253 | | // util |
254 | | // readFileHeader() reads ih and checks for illegal values |
255 | | // checkFileHeader() checks ih for legal but unsupported values |
256 | | **************************************************************************/ |
257 | | |
258 | 7.08k | int PackTos::readFileHeader() { |
259 | 7.08k | fi->seek(0, SEEK_SET); |
260 | 7.08k | fi->readx(&ih, FH_SIZE); |
261 | 7.08k | if (ih.fh_magic != 0x601a) |
262 | 6.88k | return 0; |
263 | 194 | if (0ull + FH_SIZE + ih.fh_text + ih.fh_data + ih.fh_sym > file_size_u) |
264 | 65 | return 0; |
265 | 129 | return UPX_F_ATARI_TOS; |
266 | 194 | } |
267 | | |
268 | 30 | bool PackTos::checkFileHeader() { |
269 | 30 | const unsigned f = ih.fh_flag; |
270 | | // printf("flags: 0x%x, text: %d, data: %d, bss: %d, sym: %d\n", f, (int) ih.fh_text, |
271 | | // (int) ih.fh_data, (int) ih.fh_bss, (int) ih.fh_sym); |
272 | 30 | if ((ih.fh_text & 1) || (ih.fh_data & 1)) |
273 | 0 | throwCantPack("odd size values in text/data"); |
274 | 30 | if (f & F_OS_SPECIAL) |
275 | 2 | throwCantPack("I won't pack F_OS_SPECIAL programs"); |
276 | 28 | if ((f & F_PROTMODE) > F_PROT_I) |
277 | 4 | throwCantPack("invalid protection mode"); |
278 | 24 | if ((f & F_PROTMODE) != F_PROT_P) { |
279 | 4 | if (opt->force < 1) |
280 | 4 | throwCantPack("no private memory protection; use option '-f' to force packing"); |
281 | 4 | } |
282 | 20 | if (f & F_SHTEXT) { |
283 | 3 | if (opt->force < 1) |
284 | 3 | throwCantPack("shared text segment; use option '-f' to force packing"); |
285 | 3 | } |
286 | | #if 0 |
287 | | // fh_reserved seems to be unused |
288 | | if (ih.fh_reserved != 0) { |
289 | | if (opt->force < 1) |
290 | | throwCantPack("reserved header field set; use option '-f' to force packing"); |
291 | | } |
292 | | #endif |
293 | 17 | return true; |
294 | 20 | } |
295 | | |
296 | | /************************************************************************* |
297 | | // relocs |
298 | | **************************************************************************/ |
299 | | |
300 | | // Check relocations for errors to make sure our loader can handle them |
301 | | static bool check_relocs(const byte *relocs, unsigned rsize, unsigned image_size, |
302 | 0 | unsigned *relocnum, unsigned *relocsize, unsigned *overlay) { |
303 | 0 | assert(rsize >= 4); |
304 | 0 | assert(image_size >= 4); |
305 | 0 | unsigned fixup = get_be32(relocs); |
306 | 0 | if (fixup == 0 || fixup >= image_size) |
307 | 0 | return false; |
308 | 0 | unsigned last_fixup = fixup; |
309 | 0 | unsigned i = 4; |
310 | |
|
311 | 0 | *relocnum = 1; |
312 | 0 | for (;;) { |
313 | 0 | if (fixup & 1) // must be word-aligned |
314 | 0 | return false; |
315 | 0 | if (fixup + 4 > image_size) // out of bounds |
316 | 0 | return false; |
317 | 0 | if (i >= rsize) // premature EOF in relocs |
318 | 0 | return false; |
319 | 0 | unsigned c = relocs[i++]; |
320 | 0 | if (c == 0) // EOF end marker |
321 | 0 | break; |
322 | 0 | else if (c == 1) // increase fixup, no reloc |
323 | 0 | fixup += 254; |
324 | 0 | else if (c & 1) // must be word-aligned |
325 | 0 | return false; |
326 | 0 | else // next reloc is here |
327 | 0 | { |
328 | 0 | fixup += c; |
329 | 0 | if (fixup - last_fixup < 4) // overlapping relocation |
330 | 0 | return false; |
331 | 0 | last_fixup = fixup; |
332 | 0 | *relocnum += 1; |
333 | 0 | } |
334 | 0 | } |
335 | | |
336 | 0 | *relocsize = i; |
337 | 0 | *overlay = rsize - i; |
338 | 0 | return true; |
339 | 0 | } |
340 | | |
341 | | /************************************************************************* |
342 | | // |
343 | | **************************************************************************/ |
344 | | |
345 | 0 | tribool PackTos::canPack() { |
346 | 0 | if (!readFileHeader()) |
347 | 0 | return false; |
348 | | |
349 | 0 | byte buf[768]; |
350 | 0 | fi->readx(buf, sizeof(buf)); |
351 | 0 | checkAlreadyPacked(buf, sizeof(buf)); |
352 | |
|
353 | 0 | if (!checkFileHeader()) |
354 | 0 | throwCantPack("unsupported header flags"); |
355 | 0 | if (file_size < 1024) |
356 | 0 | throwCantPack("program is too small for atari/tos"); |
357 | | |
358 | 0 | return true; |
359 | 0 | } |
360 | | |
361 | 0 | void PackTos::fileInfo() { |
362 | 0 | if (!readFileHeader()) |
363 | 0 | return; |
364 | 0 | con_fprintf(stdout, " text: %d, data: %d, sym: %d, bss: %d, flags=0x%x\n", (int) ih.fh_text, |
365 | 0 | (int) ih.fh_data, (int) ih.fh_sym, (int) ih.fh_bss, (int) ih.fh_flag); |
366 | 0 | } |
367 | | |
368 | | /************************************************************************* |
369 | | // |
370 | | **************************************************************************/ |
371 | | |
372 | 0 | void PackTos::pack(OutputFile *fo) { |
373 | 0 | unsigned t; |
374 | 0 | unsigned relocnum = 0; |
375 | 0 | unsigned relocsize = 0; |
376 | 0 | unsigned overlay = 0; |
377 | |
|
378 | 0 | const unsigned i_text = ih.fh_text; |
379 | 0 | const unsigned i_data = ih.fh_data; |
380 | 0 | const unsigned i_sym = ih.fh_sym; |
381 | 0 | const unsigned i_bss = ih.fh_bss; |
382 | |
|
383 | 0 | symbols.reset(); |
384 | 0 | symbols.need_reloc = false; |
385 | | // prepare symbols for buildLoader() - worst case |
386 | 0 | symbols.loop1.init(65536 + 1); |
387 | 0 | symbols.loop2.init((160 - 1) / 4); |
388 | 0 | symbols.loop3.init(65536 + 1); |
389 | 0 | symbols.up21_d4 = 65536 + 1; |
390 | 0 | symbols.up21_a6 = 65536 + 1; |
391 | 0 | symbols.up31_base_d4 = 65536 + 1; |
392 | 0 | symbols.up31_base_a6 = 65536 + 1; |
393 | | |
394 | | // read file |
395 | 0 | const unsigned isize = file_size_u - i_sym; |
396 | 0 | ibuf.alloc(isize); |
397 | 0 | fi->seek(FH_SIZE, SEEK_SET); |
398 | | // read text + data |
399 | 0 | t = i_text + i_data; |
400 | 0 | fi->readx(ibuf, t); |
401 | | // skip symbols |
402 | 0 | if (i_sym && opt->exact) |
403 | 0 | throwCantPackExact(); |
404 | 0 | fi->seek(i_sym, SEEK_CUR); |
405 | | // read relocations + overlay |
406 | 0 | overlay = file_size_u - (FH_SIZE + i_text + i_data + i_sym); |
407 | 0 | fi->readx(ibuf + t, overlay); |
408 | |
|
409 | | #if TESTING |
410 | | printf("text: %d, data: %d, sym: %d, bss: %d, flags=0x%x\n", i_text, i_data, i_sym, i_bss, |
411 | | (int) ih.fh_flag); |
412 | | printf("xx1 reloc: %d, overlay: %d, fixup: %d\n", relocsize, overlay, |
413 | | overlay >= 4 ? (int) get_be32(ibuf + t) : -1); |
414 | | #endif |
415 | | |
416 | | // Check relocs (see load_and_reloc() in freemint/sys/memory.c). |
417 | | // Must work around TOS bugs and lots of broken programs. |
418 | 0 | if (overlay < 4) { |
419 | | // Bug workaround: Whatever this is, silently keep it in |
420 | | // the (unused) relocations for byte-identical unpacking. |
421 | 0 | relocsize = overlay; |
422 | 0 | overlay = 0; |
423 | 0 | } else if (get_be32(ibuf + t) == 0) { |
424 | | // Bug workaround - check the empty fixup before testing fh_reloc. |
425 | 0 | relocsize = 4; |
426 | 0 | overlay -= 4; |
427 | 0 | } else if (ih.fh_reloc != 0) |
428 | 0 | relocsize = 0; |
429 | 0 | else { |
430 | 0 | if (!check_relocs(ibuf + t, overlay, t, &relocnum, &relocsize, &overlay)) |
431 | 0 | throwCantPack("bad relocation table"); |
432 | 0 | symbols.need_reloc = true; |
433 | 0 | } |
434 | | |
435 | | #if TESTING |
436 | | printf("xx2: %d relocs: %d, overlay: %d, t: %d\n", relocnum, relocsize, overlay, t); |
437 | | #endif |
438 | | |
439 | 0 | checkOverlay(overlay); |
440 | | |
441 | | // Append original fileheader. |
442 | 0 | t += relocsize; |
443 | 0 | ih.fh_sym = 0; // we stripped all symbols |
444 | 0 | memcpy(ibuf + t, &ih, FH_SIZE); |
445 | 0 | t += FH_SIZE; |
446 | | #if TESTING |
447 | | printf("xx3 reloc: %d, overlay: %d, t: %d\n", relocsize, overlay, t); |
448 | | #endif |
449 | 0 | assert(t <= isize); |
450 | | |
451 | | // Now the data in ibuf[0..t] looks like this: |
452 | | // text + data + relocs + original file header |
453 | | // After compression this will become the first part of the |
454 | | // data segment. The second part will be the decompressor. |
455 | | |
456 | | // alloc buffer (4096 is for decompressor and the various alignments) |
457 | 0 | obuf.allocForCompression(t, 4096); |
458 | | |
459 | | // prepare packheader |
460 | 0 | ph.u_len = t; |
461 | | // prepare filter |
462 | 0 | Filter ft(ph.level); |
463 | | // compress (max_match = 65535) |
464 | 0 | upx_compress_config_t cconf; |
465 | 0 | cconf.reset(); |
466 | 0 | cconf.conf_ucl.max_match = 65535; |
467 | 0 | cconf.conf_lzma.max_num_probs = 1846 + (768 << 4); // ushort: ~28 KiB stack |
468 | 0 | compressWithFilters(&ft, 512, &cconf); |
469 | | |
470 | | // |
471 | | // multipass buildLoader() |
472 | | // |
473 | | |
474 | | // save initial loader |
475 | 0 | const unsigned initial_lsize = getLoaderSize(); |
476 | 0 | unsigned last_lsize = initial_lsize; |
477 | 0 | MemBuffer last_loader(last_lsize); |
478 | 0 | memcpy(last_loader, getLoader(), last_lsize); |
479 | |
|
480 | 0 | unsigned o_text, o_data, o_bss; |
481 | 0 | unsigned e_len, d_len, d_off; |
482 | 0 | for (;;) { |
483 | | // The decompressed data will now get placed at this offset: |
484 | 0 | unsigned offset = (ph.u_len + ph.overlap_overhead) - ph.c_len; |
485 | | |
486 | | // get loader |
487 | 0 | const unsigned lsize = getLoaderSize(); |
488 | 0 | e_len = getLoaderSectionStart("CUTPOINT"); |
489 | 0 | d_len = lsize - e_len; |
490 | 0 | assert((e_len & 3) == 0 && (d_len & 1) == 0); |
491 | | |
492 | | // compute section sizes |
493 | 0 | o_text = e_len; |
494 | 0 | o_data = ph.c_len; |
495 | 0 | o_bss = i_bss; |
496 | | |
497 | | // word align len of compressed data |
498 | 0 | while (o_data & 1) { |
499 | 0 | obuf[o_data++] = 0; |
500 | 0 | offset++; |
501 | 0 | } |
502 | | |
503 | | // append decompressor (part 2 of loader) |
504 | 0 | d_off = o_data; |
505 | | ////memcpy(obuf + d_off, getLoader() + e_len, d_len); // must be done after relocation |
506 | 0 | o_data += d_len; |
507 | | |
508 | | // dword align the len of the final data segment |
509 | 0 | while (o_data & 3) { |
510 | 0 | obuf[o_data++] = 0; |
511 | 0 | offset++; |
512 | 0 | } |
513 | | // dword align offset |
514 | 0 | while (offset & 3) |
515 | 0 | offset++; |
516 | | |
517 | | // new bss |
518 | 0 | if (i_text + i_data + i_bss > o_text + o_data + o_bss) |
519 | 0 | o_bss = (i_text + i_data + i_bss) - (o_text + o_data); |
520 | | |
521 | | // dirty bss |
522 | 0 | unsigned dirty_bss = (o_data + offset) - (i_text + i_data); |
523 | | // printf("real dirty_bss: %d\n", dirty_bss); |
524 | | // dword align (or 16 - for speedup when clearing the dirty bss) |
525 | 0 | const unsigned dirty_bss_align = opt->small ? 4 : 16; |
526 | 0 | while (dirty_bss & (dirty_bss_align - 1)) |
527 | 0 | dirty_bss++; |
528 | | // adjust bss, assert room for some stack |
529 | 0 | unsigned stack = 512 + getDecompressorWrkmemSize(); |
530 | 0 | if (dirty_bss + stack > o_bss) |
531 | 0 | o_bss = dirty_bss + stack; |
532 | | |
533 | | // dword align the len of the final bss segment |
534 | 0 | while (o_bss & 3) |
535 | 0 | o_bss++; |
536 | | |
537 | | // update symbols for buildLoader() |
538 | 0 | if (opt->small) { |
539 | 0 | symbols.loop1.init(o_data / 4); |
540 | 0 | symbols.loop2.init(0); |
541 | 0 | } else { |
542 | 0 | symbols.loop1.init(o_data / 160); |
543 | 0 | symbols.loop2.init((o_data % 160) / 4); |
544 | 0 | } |
545 | 0 | symbols.loop3.init(dirty_bss / dirty_bss_align); |
546 | |
|
547 | 0 | symbols.up21_d4 = o_data + offset; |
548 | 0 | symbols.up31_base_d4 = d_off + offset; |
549 | 0 | symbols.up21_a6 = symbols.up21_d4 - (i_text + i_data); |
550 | 0 | symbols.up31_base_a6 = symbols.up31_base_d4 - (i_text + i_data); |
551 | 0 | assert((int) symbols.up21_a6 > 0); |
552 | 0 | assert((int) symbols.up31_base_a6 > 0); |
553 | | |
554 | 0 | const unsigned c = linker->getSymbolOffset("code_on_stack"); |
555 | 0 | unsigned d; |
556 | 0 | d = linker->getSymbolOffset("flush_cache_rts") - c; |
557 | 0 | symbols.flush_cache_rts_offset = d; |
558 | 0 | d = linker->getSymbolOffset("clear_dirty_stack_loop") - c; |
559 | 0 | symbols.clear_dirty_stack_len = (d + 3) / 4 + 32 - 1; |
560 | 0 | d = linker->getSymbolOffset("code_on_stack_end") - c; |
561 | 0 | symbols.copy_to_stack_len = d / 2 - 1; |
562 | | |
563 | | // now re-build loader |
564 | 0 | buildLoader(&ft); |
565 | 0 | unsigned new_lsize = getLoaderSize(); |
566 | | // printf("buildLoader %d %d\n", new_lsize, initial_lsize); |
567 | 0 | assert(new_lsize <= initial_lsize); |
568 | 0 | if (new_lsize == last_lsize && memcmp(getLoader(), last_loader, last_lsize) == 0) |
569 | 0 | break; |
570 | 0 | last_lsize = new_lsize; |
571 | 0 | memcpy(last_loader, getLoader(), last_lsize); |
572 | 0 | } |
573 | | |
574 | | // |
575 | | // define symbols and reloc |
576 | | // |
577 | |
|
578 | 0 | defineDecompressorSymbols(); |
579 | |
|
580 | 0 | linker->defineSymbol("loop1_count", symbols.loop1.value); |
581 | 0 | linker->defineSymbol("loop2_count", symbols.loop2.value); |
582 | 0 | linker->defineSymbol("loop3_count", symbols.loop3.value); |
583 | |
|
584 | 0 | linker->defineSymbol("orig_p_tlen", i_text); |
585 | 0 | linker->defineSymbol("orig_p_dlen", i_data); |
586 | 0 | linker->defineSymbol("orig_p_blen", i_bss); |
587 | |
|
588 | 0 | if (symbols.up21_a6 <= 32767) |
589 | 0 | linker->defineSymbol("up21_a6", symbols.up21_a6); |
590 | 0 | else |
591 | 0 | linker->defineSymbol("up21_d4", symbols.up21_d4); |
592 | |
|
593 | 0 | if (symbols.up31_a6 <= 32767) |
594 | 0 | linker->defineSymbol("up31_a6", symbols.up31_a6); |
595 | 0 | else if (symbols.up31_d4 <= 32767) |
596 | 0 | linker->defineSymbol("up31_d4", symbols.up31_d4); |
597 | 0 | else if (symbols.up31_a6 <= 65534) |
598 | 0 | linker->defineSymbol("up31_a6", symbols.up31_a6 - 32767); |
599 | 0 | else |
600 | 0 | linker->defineSymbol("up31_d4", symbols.up31_d4); |
601 | | #if 0 |
602 | | printf("relocsize = %d\n", relocsize); |
603 | | printf("upx21(d4) = %d\n", symbols.up21_d4); |
604 | | printf("upx21(a6) = %d\n", symbols.up21_a6); |
605 | | printf("upx31(d4) = %d\n", symbols.up31_d4); |
606 | | printf("upx31(a6) = %d\n", symbols.up31_a6); |
607 | | #endif |
608 | |
|
609 | 0 | linker->defineSymbol("flush_cache_rts_offset", symbols.flush_cache_rts_offset); |
610 | 0 | linker->defineSymbol("copy_to_stack_len", symbols.copy_to_stack_len); |
611 | 0 | linker->defineSymbol("clear_dirty_stack_len", symbols.clear_dirty_stack_len); |
612 | |
|
613 | 0 | relocateLoader(); |
614 | | |
615 | | // |
616 | | // write |
617 | | // |
618 | | |
619 | | // set new file_hdr |
620 | 0 | memcpy(&oh, &ih, FH_SIZE); |
621 | 0 | if (opt->atari_tos.split_segments) { |
622 | 0 | oh.fh_text = o_text; |
623 | 0 | oh.fh_data = o_data; |
624 | 0 | } else { |
625 | | // put everything into the text segment |
626 | 0 | oh.fh_text = o_text + o_data; |
627 | 0 | oh.fh_data = 0; |
628 | 0 | } |
629 | 0 | oh.fh_bss = o_bss; |
630 | 0 | oh.fh_sym = 0; |
631 | 0 | oh.fh_reserved = 0; |
632 | | // only keep the following flags: |
633 | 0 | oh.fh_flag = ih.fh_flag & (F_FASTLOAD | F_ALTALLOC | F_SMALLTPA | F_ALLOCZERO | F_KEEP); |
634 | | // add an empty relocation fixup to workaround a bug in some TOS versions |
635 | 0 | oh.fh_reloc = 0; |
636 | |
|
637 | | #if TESTING |
638 | | printf("old text: %6d, data: %6d, bss: %6d, reloc: %d, overlay: %d\n", i_text, i_data, i_bss, |
639 | | relocsize, overlay); |
640 | | printf("new text: %6d, data: %6d, bss: %6d, flag=0x%x\n", o_text, o_data, o_bss, |
641 | | (int) oh.fh_flag); |
642 | | linker->dumpSymbols(); |
643 | | #endif |
644 | | |
645 | | // prepare loader |
646 | 0 | MemBuffer loader(o_text); |
647 | 0 | memcpy(loader, getLoader(), o_text); |
648 | 0 | patchPackHeader(loader, o_text); |
649 | | |
650 | | // write new file header, loader and compressed file |
651 | 0 | fo->write(&oh, FH_SIZE); |
652 | 0 | fo->write(loader, o_text); // entry |
653 | 0 | if (opt->debug.dump_stub_loader) |
654 | 0 | OutputFile::dump(opt->debug.dump_stub_loader, loader, o_text); |
655 | 0 | memcpy(obuf + d_off, getLoader() + e_len, d_len); // copy decompressor |
656 | 0 | fo->write(obuf, o_data); // compressed + decompressor |
657 | | |
658 | | // write empty relocation fixup |
659 | 0 | fo->write("\x00\x00\x00\x00", 4); |
660 | | |
661 | | // verify |
662 | 0 | verifyOverlappingDecompression(); |
663 | | |
664 | | // copy the overlay |
665 | 0 | copyOverlay(fo, overlay, obuf); |
666 | | |
667 | | // finally check the compression ratio |
668 | 0 | if (!checkFinalCompressionRatio(fo)) |
669 | 0 | throwNotCompressible(); |
670 | 0 | } |
671 | | |
672 | | /************************************************************************* |
673 | | // |
674 | | **************************************************************************/ |
675 | | |
676 | 7.08k | tribool PackTos::canUnpack() { |
677 | 7.08k | if (!readFileHeader()) |
678 | 6.95k | return false; |
679 | 129 | if (!readPackHeader(768)) |
680 | 15 | return false; |
681 | | // check header as set by packer |
682 | 114 | if ((ih.fh_text & 3) != 0 || (ih.fh_data & 3) != 0 || (ih.fh_bss & 3) != 0 || ih.fh_sym != 0 || |
683 | 78 | ih.fh_reserved != 0 || ih.fh_reloc > 1) |
684 | 70 | throwCantUnpack("program header damaged"); |
685 | | // generic check |
686 | 44 | if (!checkFileHeader()) |
687 | 0 | throwCantUnpack("unsupported header flags"); |
688 | 44 | return true; |
689 | 44 | } |
690 | | |
691 | | /************************************************************************* |
692 | | // |
693 | | **************************************************************************/ |
694 | | |
695 | 4 | void PackTos::unpack(OutputFile *fo) { |
696 | 4 | ibuf.alloc(ph.c_len); |
697 | 4 | obuf.allocForDecompression(ph.u_len); |
698 | | |
699 | 4 | fi->seek(FH_SIZE + ph.buf_offset + ph.getPackHeaderSize(), SEEK_SET); |
700 | 4 | fi->readx(ibuf, ph.c_len); |
701 | | |
702 | | // decompress |
703 | 4 | decompress(ibuf, obuf); |
704 | | |
705 | | // write original header & decompressed file |
706 | 4 | if (fo) { |
707 | 1 | unsigned overlay = file_size_u - (FH_SIZE + ih.fh_text + ih.fh_data); |
708 | 1 | if (ih.fh_reloc == 0 && overlay >= 4) |
709 | 1 | overlay -= 4; // this is our empty fixup |
710 | 1 | checkOverlay(overlay); |
711 | | |
712 | 1 | fo->write(obuf + ph.u_len - FH_SIZE, FH_SIZE); // orig. file_hdr |
713 | 1 | fo->write(obuf, ph.u_len - FH_SIZE); // orig. text+data+relocs |
714 | | |
715 | | // copy any overlay |
716 | 1 | copyOverlay(fo, overlay, obuf); |
717 | 1 | } |
718 | 4 | } |
719 | | |
720 | | /* vim:set ts=4 sw=4 et: */ |