Coverage Report

Created: 2025-10-10 06:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/upx/src/p_tos.cpp
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: */