1 /* p_ps1.cpp --
2 
3    This file is part of the UPX executable compressor.
4 
5    Copyright (C) 1996-2020 Markus Franz Xaver Johannes Oberhumer
6    Copyright (C) 1996-2020 Laszlo Molnar
7    Copyright (C) 2002-2020 Jens Medoch
8    All Rights Reserved.
9 
10    UPX and the UCL library are free software; you can redistribute them
11    and/or modify them under the terms of the GNU General Public License as
12    published by the Free Software Foundation; either version 2 of
13    the License, or (at your option) any later version.
14 
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19 
20    You should have received a copy of the GNU General Public License
21    along with this program; see the file COPYING.
22    If not, write to the Free Software Foundation, Inc.,
23    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 
25    Markus F.X.J. Oberhumer              Laszlo Molnar
26    <markus@oberhumer.com>               <ezerotven+github@gmail.com>
27 
28    Jens Medoch
29    <jssg@users.sourceforge.net>
30  */
31 
32 
33 #include "conf.h"
34 #include "file.h"
35 #include "filter.h"
36 #include "packer.h"
37 #include "p_ps1.h"
38 #include "linker.h"
39 
40 static const
41 #include "stub/mipsel.r3000-ps1.h"
42 
43 #define CD_SEC          2048
44 #define PS_HDR_SIZE     CD_SEC
45 #define PS_RAM_SIZE     ram_size
46 #define PS_MIN_SIZE     (PS_HDR_SIZE*3)
47 #define PS_MAX_SIZE     ((PS_RAM_SIZE*95) / 100)
48 #define PS_STACK_SIZE   (PS_RAM_SIZE / 256)
49 
50 #define SZ_IH_BKUP      (10 * sizeof(LE32))
51 #define HD_CODE_OFS     (sizeof(ps1_exe_t) + sz_cbh)
52 
53 #define K0_BS           (0x80000000)
54 #define K1_BS           (0xa0000000)
55 #define EXE_BS          (ih.epc & K0_BS)
56 #define FIX_PSVR        ((K1_BS - EXE_BS) + (PS_HDR_SIZE - HD_CODE_OFS))
57 
58 // lui / addiu
59 #define MIPS_HI(a)      (((a) >> 16) + (((a) & 0x8000) >> 15))
60 #define MIPS_LO(a)      ((a) & 0xffff)
61 #define MIPS_PC16(a)    ((a) >> 2)
62 #define MIPS_PC26(a)    (((a) & 0x0fffffff) >> 2)
63 
64 
65 /*************************************************************************
66 // ps1 exe looks like this:
67 // 1. <header>  2048 bytes
68 // 2. <body>    plain binary
69 //
70 // header:  contains the ps1_exe_t structure 188 bytes at offset zero
71 //          rest is filled with zeros to reach the required
72 //          cd mode 2 data sector size of 2048 bytes
73 // body:    contains the binary data / code of the executable
74 //          reqiures: executable code must be aligned to 4
75 //                    must be aligned to 2048 to run from a CD
76 //          optional: not aligned to 2048 (for console run only)
77 **************************************************************************/
78 
PackPs1(InputFile * f)79 PackPs1::PackPs1(InputFile *f) :
80     super(f),
81     isCon(!opt->ps1_exe.boot_only), is32Bit(!opt->ps1_exe.do_8bit),
82     buildPart2(0), foundBss(0), sa_cnt(0), overlap(0), sz_lunc(0), sz_lcpr(0),
83     pad_code(0), bss_start(0), bss_end(0)
84 {
85     bele = &N_BELE_RTP::le_policy;
86 
87     COMPILE_TIME_ASSERT(sizeof(ps1_exe_t) == 136)
88     COMPILE_TIME_ASSERT(sizeof(ps1_exe_hb_t) == 44)
89     COMPILE_TIME_ASSERT(sizeof(ps1_exe_chb_t) == 5)
90     COMPILE_TIME_ASSERT_ALIGNED1(ps1_exe_t)
91     COMPILE_TIME_ASSERT_ALIGNED1(ps1_exe_hb_t)
92     COMPILE_TIME_ASSERT_ALIGNED1(ps1_exe_chb_t)
93 
94     COMPILE_TIME_ASSERT(PS_HDR_SIZE > sizeof(ps1_exe_t))
95     COMPILE_TIME_ASSERT(SZ_IH_BKUP == 40)
96 
97     fdata_size = file_size - PS_HDR_SIZE;
98     ram_size = !opt->ps1_exe.do_8mib ? 0x200000 : 0x800000;
99 }
100 
getCompressionMethods(int method,int level) const101 const int *PackPs1::getCompressionMethods(int method, int level) const
102 {
103     if (is32Bit)
104         return Packer::getDefaultCompressionMethods_le32(method, level);
105     else
106         return Packer::getDefaultCompressionMethods_8(method, level);
107 }
108 
getFilters() const109 const int *PackPs1::getFilters() const
110 {
111     return NULL;
112 }
113 
newLinker() const114 Linker* PackPs1::newLinker() const
115 {
116     return new ElfLinkerMipsLE;
117 }
118 
119 
120 /*************************************************************************
121 // util
122 //   readFileHeader() reads ih and checks for illegal values
123 //   checkFileHeader() checks ih for legal but unsupported values
124 **************************************************************************/
125 
readFileHeader()126 int PackPs1::readFileHeader()
127 {
128     fi->seek(0, SEEK_SET);
129     fi->readx(&ih, sizeof(ih));
130     if (memcmp(&ih.id[0], "PS-X EXE", 8) != 0 &&
131         memcmp(&ih.id[0], "EXE X-SP", 8) != 0)
132         return 0;
133     if (ih.text != 0 || ih.data != 0)
134         return 0;
135     return UPX_F_PS1_EXE;
136 }
137 
readBkupHeader()138 bool PackPs1::readBkupHeader()
139 {
140     fi->seek(sizeof(ps1_exe_t)+8, SEEK_SET);
141     fi->readx(&bh, sizeof(bh));
142 
143     if (bh.ih_csum != upx_adler32(&bh, SZ_IH_BKUP))
144     {
145         unsigned char buf[sizeof(bh)];
146         fi->seek(sizeof(ps1_exe_t), SEEK_SET);
147         fi->readx(buf, sizeof(bh));
148         if (!getBkupHeader(buf, (unsigned char *)&bh))
149             return false;
150     }
151     return true;
152 }
153 
154 #define INIT_BH_BKUP(p, l)  {(p)->id = '1'; (p)->len = l;}
155 #define ADLER16(a)          (((a) >> 16) ^ ((a) & 0xffff))
156 
putBkupHeader(const unsigned char * src,unsigned char * dst,unsigned * len)157 void PackPs1::putBkupHeader(const unsigned char *src, unsigned char *dst, unsigned *len)
158 {
159     unsigned sz_cbh = MemBuffer::getSizeForCompression(SZ_IH_BKUP);
160 
161     if (src && dst)
162     {
163         unsigned char *cpr_bh = New(unsigned char, sz_cbh);
164 
165         memset(cpr_bh, 0, sizeof(bh));
166         ps1_exe_chb_t * p = (ps1_exe_chb_t * )cpr_bh;
167 
168         int r = upx_compress(src, SZ_IH_BKUP,
169                              &p->ih_bkup, &sz_cbh, NULL, M_NRV2E_8, 10, NULL, NULL );
170         if (r != UPX_E_OK || sz_cbh >= SZ_IH_BKUP)
171             throwInternalError("header compression failed");
172         INIT_BH_BKUP(p, sz_cbh);
173         *len = ALIGN_UP(sz_cbh + (unsigned) sizeof(ps1_exe_chb_t) - 1, 4u);
174         p->ih_csum = ADLER16(upx_adler32(&ih.epc, SZ_IH_BKUP));
175         memcpy(dst, cpr_bh, SZ_IH_BKUP);
176         delete [] cpr_bh;
177     }
178     else
179         throwInternalError("header compression failed");
180 }
181 
182 #define ADLER16_HI(a, b)    ((((a) & 0xffff) ^ (b)) << 16)
183 #define ADLER16_LO(a, b)    (((a) >> 16) ^ (b))
184 #define RE_ADLER16(a, b)    (ADLER16_HI(a,b) | ADLER16_LO(a,b))
185 
getBkupHeader(unsigned char * p,unsigned char * dst)186 bool PackPs1::getBkupHeader(unsigned char *p, unsigned char *dst)
187 {
188     ps1_exe_chb_t *src = (ps1_exe_chb_t*)p;
189 
190     if (src && (src->id == '1' && src->len < SZ_IH_BKUP) && dst)
191     {
192         unsigned char *unc_bh = New(unsigned char, MemBuffer::getSizeForUncompression(SZ_IH_BKUP));
193 
194         unsigned sz_bh = SZ_IH_BKUP;
195         int r = upx_decompress((const unsigned char *)&src->ih_bkup, src->len,
196                                unc_bh, &sz_bh, M_NRV2E_8, NULL );
197         if (r == UPX_E_OUT_OF_MEMORY)
198             throwOutOfMemoryException();
199         if (r != UPX_E_OK || sz_bh != SZ_IH_BKUP)
200             throwInternalError("header decompression failed");
201         unsigned ad = upx_adler32(unc_bh, SZ_IH_BKUP);
202         unsigned ch = src->ih_csum;
203         if (ad != RE_ADLER16(ad,ch))
204             throwInternalError("backup header damaged");
205         memcpy(dst, unc_bh, SZ_IH_BKUP);
206         delete [] unc_bh;
207     }
208     else
209         return false;
210     return true;
211 }
212 
checkFileHeader()213 bool PackPs1::checkFileHeader()
214 {
215     if (fdata_size != ih.tx_len || (ih.tx_len & 3))
216     {
217         if (!opt->force)
218             throwCantPack("file size entry damaged (try --force)");
219         else
220         {
221             opt->info_mode += !opt->info_mode ? 1 : 0;
222             infoWarning("fixing damaged header, keeping backup file");
223             opt->backup = 1;
224             ih.tx_len = fdata_size;
225         }
226     }
227     if (!opt->force &&
228        (ih.da_ptr != 0 || ih.da_len != 0 ||
229         ih.bs_ptr != 0 || ih.bs_len != 0))
230     {
231         infoWarning("unsupported header field entry");
232         return false;
233     }
234     if (ih.is_ptr < (EXE_BS | (PS_RAM_SIZE - PS_STACK_SIZE)))
235     {
236         if (!opt->force)
237             return false;
238         else
239             infoWarning("%s: stack pointer offset low", fi->getName());
240     }
241     return true;
242 }
243 
244 
245 /*************************************************************************
246 //
247 **************************************************************************/
248 
canPack()249 bool PackPs1::canPack()
250 {
251     unsigned char buf[PS_HDR_SIZE - sizeof(ps1_exe_t)];
252 
253     if (!readFileHeader())
254         return false;
255 
256     fi->readx(buf, sizeof(buf));
257     checkAlreadyPacked(buf, sizeof(buf));
258 
259     for (size_t i = 0; i < sizeof(buf); i++)
260         if (buf[i] != 0)
261         {
262             if (!opt->force)
263                 throwCantPack("unknown data in header (try --force)");
264             else
265             {
266                 opt->info_mode += !opt->info_mode ? 1 : 0;
267                 infoWarning("clearing header, keeping backup file");
268                 opt->backup = 1;
269                 break;
270             }
271         }
272     if (!checkFileHeader())
273         throwCantPack("unsupported header flags (try --force)");
274     if (!opt->force && file_size < PS_MIN_SIZE)
275         throwCantPack("file is too small (try --force)");
276     if (!opt->force && file_size > (off_t) PS_MAX_SIZE)
277         throwCantPack("file is too big (try --force)");
278     return true;
279 }
280 
281 
282 /*************************************************************************
283 //
284 **************************************************************************/
285 
buildLoader(const Filter *)286 void PackPs1::buildLoader(const Filter *)
287 {
288     const char *method = NULL;
289 
290     if (ph.method == M_NRV2B_8)
291         method = isCon ? "nrv2b.small,8bit.sub,nrv.done" :
292                     "nrv2b.8bit,nrv.done";
293     else if (ph.method == M_NRV2D_8)
294         method = isCon ? "nrv2d.small,8bit.sub,nrv.done" :
295                     "nrv2d.8bit,nrv.done";
296     else if (ph.method == M_NRV2E_8)
297         method = isCon ? "nrv2e.small,8bit.sub,nrv.done" :
298                     "nrv2e.8bit,nrv.done";
299     else if (ph.method == M_NRV2B_LE32)
300         method = isCon ? "nrv2b.small,32bit.sub,nrv.done" :
301                     "nrv2b.32bit,nrv.done";
302     else if (ph.method == M_NRV2D_LE32)
303         method = isCon ? "nrv2d.small,32bit.sub,nrv.done" :
304                     "nrv2d.32bit,nrv.done";
305     else if (ph.method == M_NRV2E_LE32)
306         method = isCon ? "nrv2e.small,32bit.sub,nrv.done" :
307                     "nrv2e.32bit,nrv.done";
308     else if (M_IS_LZMA(ph.method))
309         method = "nrv2b.small,8bit.sub,nrv.done,lzma.prep";
310     else
311         throwInternalError("unknown compression method");
312 
313     unsigned sa_tmp = sa_cnt;
314     if (ph.overlap_overhead > sa_cnt)
315     {
316         if (!opt->force)
317         {
318             infoWarning("not in-place decompressible");
319             throwCantPack("packed data overlap (try --force)");
320         }
321         else
322             sa_tmp += overlap = ALIGN_UP((ph.overlap_overhead - sa_tmp), 4u);
323     }
324 
325     if (isCon || M_IS_LZMA(ph.method))
326         foundBss = findBssSection();
327 
328     if (M_IS_LZMA(ph.method) && !buildPart2)
329     {
330         initLoader(stub_mipsel_r3000_ps1, sizeof(stub_mipsel_r3000_ps1));
331         addLoader("decompressor.start",
332                   isCon ? "LZMA_DEC20" : "LZMA_DEC10", "lzma.init", NULL);
333         addLoader(sa_tmp > (0x10000 << 2) ? "memset.long" : "memset.short",
334                   !foundBss ? "con.exit" : "bss.exit", NULL);
335     }
336     else
337     {
338         if (M_IS_LZMA(ph.method) && buildPart2)
339         {
340             sz_lcpr = MemBuffer::getSizeForCompression(sz_lunc);
341             unsigned char *cprLoader = New(unsigned char, sz_lcpr);
342             int r = upx_compress(getLoader(), sz_lunc, cprLoader, &sz_lcpr,
343                                  NULL, M_NRV2B_8, 10, NULL, NULL );
344             if (r != UPX_E_OK || sz_lcpr >= sz_lunc)
345                 throwInternalError("loader compression failed");
346             initLoader(stub_mipsel_r3000_ps1, sizeof(stub_mipsel_r3000_ps1),
347                       isCon || !M_IS_LZMA(ph.method) ? 0 : 1);
348             linker->addSection("lzma.exec", cprLoader, sz_lcpr, 0);
349             delete [] cprLoader;
350         }
351         else
352             initLoader(stub_mipsel_r3000_ps1, sizeof(stub_mipsel_r3000_ps1));
353 
354         pad_code = ALIGN_GAP((ph.c_len + (isCon ? sz_lcpr : 0)), 4u);
355         assert(pad_code < 4);
356         static const unsigned char pad_buffer[4] = { 0, 0, 0, 0 };
357         linker->addSection("pad.code", pad_buffer, pad_code, 0);
358 
359         if (isCon)
360         {
361             if (M_IS_LZMA(ph.method))
362                 addLoader(!foundBss ? "con.start" : "bss.con.start",
363                           method,
364                           ih.tx_ptr & 0xffff ?  "dec.ptr" : "dec.ptr.hi",
365                           "con.entry", "pad.code", "lzma.exec", NULL);
366             else
367                 addLoader(!foundBss ? "con.start" : "bss.con.start", "con.mcpy",
368                           ph.c_len & 3 ? "con.padcd" : "",
369                           ih.tx_ptr & 0xffff ?  "dec.ptr" : "dec.ptr.hi",
370                           "con.entry", method,
371                           sa_cnt ? sa_cnt > (0x10000 << 2) ? "memset.long" : "memset.short" : "",
372                           !foundBss ? "con.exit" : "bss.exit",
373                           "pad.code", NULL);
374         }
375         else
376         {
377             if (M_IS_LZMA(ph.method))
378                 addLoader(!foundBss ? "cdb.start.lzma" : "bss.cdb.start.lzma", "pad.code",
379                           !foundBss ? "cdb.entry.lzma" : "bss.cdb.entry.lzma",
380                           method, "cdb.lzma.cpr",
381                           ih.tx_ptr & 0xffff ?  "dec.ptr" : "dec.ptr.hi",
382                           "lzma.exec", NULL);
383             else
384             {
385                 assert(foundBss != true);
386                 addLoader("cdb.start", "pad.code", "cdb.entry",
387                           ih.tx_ptr & 0xffff ?  "cdb.dec.ptr" : "cdb.dec.ptr.hi",
388                           method,
389                           sa_cnt ? sa_cnt > (0x10000 << 2) ? "memset.long" : "memset.short" : "",
390                           "cdb.exit", NULL);
391             }
392         }
393         addLoader("UPX1HEAD", "IDENTSTR", NULL);
394     }
395 }
396 
397 #define OPTYPE(x)     (((x) >> 13) & 0x7)
398 #define OPCODE(x)     (((x) >> 10) & 0x7)
399 #define REG1(x)       (((x) >> 5) & 0x1f)
400 #define REG2(x)       ((x) & 0x1f)
401 
402 #define MIPS_IMM(a,b) ((((a) - (((b) & 0x8000) >> 15)) << 16) | (b))
403 
404 // Type
405 #define REGIMM  1
406 #define STORE   5
407 // Op
408 #define LUI     7
409 #define ADDIU   1
410 #define SW      3
411 
412 #define IS_LUI(a)     ((OPTYPE(a) == REGIMM && OPCODE(a) == LUI))
413 #define IS_ADDIU(a)   ((OPTYPE(a) == REGIMM && OPCODE(a) == ADDIU))
414 #define IS_SW_ZERO(a) ((OPTYPE(a) == STORE && OPCODE(a) == SW) && REG2(a) == 0)
415 
416 #define BSS_CHK_LIMIT (18)
417 
findBssSection()418 bool PackPs1::findBssSection()
419 {
420     unsigned char reg;
421     const LE32 * const p1 = ACC_CCAST(const LE32 *, ibuf + (ih.epc - ih.tx_ptr));
422 
423     if ((ih.epc - ih.tx_ptr + (BSS_CHK_LIMIT * 4)) > fdata_size)
424         return false;
425 
426     // check 18 opcodes for sw zero,0(x)
427     for (signed i = BSS_CHK_LIMIT; i >= 0; i--)
428     {
429         upx_uint16_t op = p1[i] >> 16;
430         if (IS_SW_ZERO(op))
431         {
432             // found! get reg (x) for bss_start
433             reg = REG1(op);
434             for (; i >= 0; i--)
435             {
436                 const bss_nfo * const p = ACC_CCAST(const bss_nfo *, &p1[i]);
437                 upx_uint16_t op1 = p->op1, op2 = p->op2;
438 
439                 // check for la (x),bss_start
440                 if ((IS_LUI(op1) && REG2(op1) == reg) &&
441                     (IS_ADDIU(op2) && REG1(op2) == reg))
442                 {
443                     op1 = p->op3, op2 = p->op4;
444 
445                     // check for la (y),bss_end
446                     if (IS_LUI(op1) && IS_ADDIU(op2))
447                     {
448                         // bss section info found!
449                         bss_start = MIPS_IMM(p->hi1, p->lo1);
450                         bss_end = MIPS_IMM(p->hi2, p->lo2);
451 
452                         if (0 < ALIGN_DOWN(bss_end - bss_start, 4u))
453                         {
454                             unsigned wkmem_sz = M_IS_LZMA(ph.method) ? 32768 : 800;
455                             unsigned end_offs = ih.tx_ptr + fdata_size + overlap;
456                             if (bss_end > (end_offs + wkmem_sz))
457                                 return isCon || (!isCon && M_IS_LZMA(ph.method));
458                             else
459                                 return false;
460                         }
461                     }
462                     else
463                         return false;
464                 }
465             }
466         }
467     }
468     return false;
469 }
470 
471 
472 /*************************************************************************
473 //
474 **************************************************************************/
475 
pack(OutputFile * fo)476 void PackPs1::pack(OutputFile *fo)
477 {
478     ibuf.alloc(fdata_size);
479     obuf.allocForCompression(fdata_size);
480     const upx_byte *p_scan = ibuf + fdata_size;
481 
482     // read file
483     fi->seek(PS_HDR_SIZE,SEEK_SET);
484     fi->readx(ibuf,fdata_size);
485 
486     // scan EOF for 2048 bytes sector alignment
487     // the removed space will secure in-place decompression
488     while (!(*--p_scan)) { if (sa_cnt++ > (0x10000 << 5) || sa_cnt >= fdata_size - 1024) break; }
489 
490     if (sa_cnt > (0x10000 << 2))
491         sa_cnt = ALIGN_DOWN(sa_cnt, 32u);
492     else
493         sa_cnt = ALIGN_DOWN(sa_cnt, 4u);
494 
495     // prepare packheader
496     ph.u_len = (fdata_size - sa_cnt);
497     ph.filter = 0;
498     Filter ft(ph.level);
499 
500     // compress (max_match = 65535)
501     upx_compress_config_t cconf; cconf.reset();
502     cconf.conf_ucl.max_match = 65535;
503     cconf.conf_lzma.max_num_probs = 1846 + (768 << 4); // ushort: ~28 KiB stack
504     compressWithFilters(&ft, sa_cnt, &cconf);
505 
506     if (overlap)
507     {
508         opt->info_mode += !opt->info_mode ? 1 : 0;
509         infoWarning("overlap - relocating load address (+%d bytes)", overlap);
510         sa_cnt += overlap;
511     }
512 
513 /*
514     if (bss_start && bss_end && !foundBss)
515         infoWarning("%s: .bss section too small - use stack", fi->getName());
516 */
517 
518     unsigned lzma_init = 0;
519 
520     if (M_IS_LZMA(ph.method))
521     {
522         sz_lunc = getLoaderSize();
523 
524         lzma_init = 0u - (sz_lunc - linker->getSymbolOffset("lzma.init"));
525         defineDecompressorSymbols();
526         linker->defineSymbol("entry", ih.epc);
527         linker->defineSymbol("SC",
528                              sa_cnt > (0x10000 << 2) ? sa_cnt >> 5 : sa_cnt >> 2);
529         relocateLoader();
530 
531         buildPart2 = true;
532         buildLoader(&ft);
533     }
534 
535     memcpy(&oh, &ih, sizeof(ih));
536 
537     unsigned sz_cbh;
538     putBkupHeader((const unsigned char *)&ih.epc, (unsigned char *)&bh, &sz_cbh);
539 
540     if (ih.is_ptr < (EXE_BS | (PS_RAM_SIZE - PS_STACK_SIZE)))
541         oh.is_ptr = (EXE_BS | (PS_RAM_SIZE - 16));
542 
543     if (ih.da_ptr != 0 || ih.da_len != 0 ||
544         ih.bs_ptr != 0 || ih.bs_len != 0)
545         oh.da_ptr = oh.da_len =
546         oh.bs_ptr = oh.bs_len = 0;
547 
548     const int lsize = getLoaderSize();
549 
550     unsigned filelen = ALIGN_UP(ih.tx_len, 4u);
551 
552     const unsigned decomp_data_start = ih.tx_ptr;
553     const unsigned comp_data_start = (decomp_data_start + filelen + overlap) - ph.c_len;
554 
555     const int h_len = lsize - getLoaderSectionStart("UPX1HEAD");
556     int d_len = 0;
557     int e_len = 0;
558 
559     if (isCon)
560     {
561         e_len = lsize - h_len;
562         d_len = e_len - getLoaderSectionStart("con.entry");
563     }
564     else
565     {
566         const char* entry_lzma = !foundBss ? "cdb.entry.lzma" : "bss.cdb.entry.lzma";
567 
568         d_len = (lsize - h_len) - getLoaderSectionStart(M_IS_LZMA(ph.method) ? entry_lzma : "cdb.entry");
569         e_len = (lsize - d_len) - h_len;
570     }
571 
572     linker->defineSymbol("entry", ih.epc);
573     linker->defineSymbol("SC", MIPS_LO(sa_cnt > (0x10000 << 2) ?
574                                        sa_cnt >> 5 : sa_cnt >> 2));
575     linker->defineSymbol("DECO", decomp_data_start);
576     linker->defineSymbol("ldr_sz", M_IS_LZMA(ph.method) ? sz_lunc + 16 : (d_len-pad_code));
577 
578     if (foundBss)
579     {
580         if (M_IS_LZMA(ph.method))
581             linker->defineSymbol("wrkmem", bss_end - 160 - getDecompressorWrkmemSize()
582                                            - (sz_lunc + 16));
583         else
584             linker->defineSymbol("wrkmem", bss_end - 16 - (d_len - pad_code));
585     }
586 
587 
588     const unsigned entry = comp_data_start - e_len;
589     oh.epc = oh.tx_ptr = entry;
590     oh.tx_len = ph.c_len + e_len;
591 
592     unsigned pad = 0;
593 
594     if (!opt->ps1_exe.no_align || !isCon)
595     {
596         pad = oh.tx_len;
597         oh.tx_len = ALIGN_UP(oh.tx_len, CD_SEC+0u);
598         pad = oh.tx_len - pad;
599         oh.tx_ptr -= pad;
600     }
601 
602     ibuf.clear(0,fdata_size);
603     upx_bytep paddata = ibuf;
604 
605     if (M_IS_LZMA(ph.method))
606     {
607         linker->defineSymbol("lzma_init_off", lzma_init);
608         linker->defineSymbol("gb_e", linker->getSymbolOffset("gb8_e"));
609     }
610     else
611         if (isCon)
612             linker->defineSymbol("gb_e", linker->getSymbolOffset(is32Bit ? "gb32_e" : "gb8_e"));
613 
614     if (isCon)
615     {
616         linker->defineSymbol("PAD", pad_code);
617         if (M_IS_LZMA(ph.method))
618             linker->defineSymbol("DCRT", (entry + getLoaderSectionStart("lzma.exec")));
619         else
620             linker->defineSymbol("DCRT", (entry + (e_len - d_len)));
621     }
622     else
623     {
624         linker->defineSymbol("PSVR", FIX_PSVR);
625         linker->defineSymbol("CPDO", comp_data_start);
626         if (M_IS_LZMA(ph.method))
627         {
628             unsigned entry_lzma = getLoaderSectionStart( !foundBss ? "cdb.entry.lzma" :
629                                                                      "bss.cdb.entry.lzma");
630 
631             linker->defineSymbol("lzma_cpr", getLoaderSectionStart("lzma.exec") - entry_lzma);
632         }
633     }
634 
635     relocateLoader();
636     //linker->dumpSymbols();
637     MemBuffer loader(lsize);
638     assert(lsize == getLoaderSize());
639     memcpy(loader, getLoader(), lsize);
640     patchPackHeader(loader, lsize);
641 
642     if (!isCon && M_IS_LZMA(ph.method) && (HD_CODE_OFS + d_len + h_len) > CD_SEC)
643         throwInternalError("lzma --boot-only loader > 2048");
644 
645     // ps1_exe_t structure
646     fo->write(&oh, sizeof(oh));
647     fo->write(&bh, sz_cbh);
648     // decompressor
649     fo->write(loader + e_len, isCon ? h_len : (d_len + h_len));
650 
651     // header size is 2048 bytes + sector alignment
652     fo->write(paddata, (pad + PS_HDR_SIZE) - fo->getBytesWritten());
653     // entry
654     fo->write(loader, e_len);
655     // compressed body
656     fo->write(obuf, ph.c_len);
657 
658     verifyOverlappingDecompression();
659     if (!checkFinalCompressionRatio(fo))
660         throwNotCompressible();
661 
662 #if 0
663     printf("%-13s: uncompressed  : %8ld bytes\n", getName(), (long) ph.u_len);
664     printf("%-13s: compressed    : %8ld bytes\n", getName(), (long) ph.c_len);
665     printf("%-13s: decompressor  : %8ld bytes\n", getName(), (long) lsize - h_len - pad_code);
666     printf("%-13s: header comp   : %8ld bytes\n", getName(), (long) sz_cbh);
667     printf("%-13s: overlap       : %8ld bytes\n", getName(), (long) overlap);
668     printf("%-13s: load address  : %08X bytes\n", getName(), (unsigned int) oh.tx_ptr);
669     printf("%-13s: code entry    : %08X bytes\n", getName(), (unsigned int) oh.epc);
670     printf("%-13s: bbs start     : %08X bytes\n", getName(), (unsigned int) bss_start);
671     printf("%-13s: bbs end       : %08X bytes\n", getName(), (unsigned int) bss_end);
672     printf("%-13s: eof in mem IF : %08X bytes\n", getName(), (unsigned int) ih.tx_ptr + ih.tx_len);
673     printf("%-13s: eof in mem OF : %08X bytes\n", getName(), (unsigned int) oh.tx_ptr + oh.tx_len);
674     unsigned char i = 0;
675     if (isCon) { if (foundBss) i = 1; }
676     else { i = 2; if (M_IS_LZMA(ph.method)) { if (!foundBss) i = 3; else i = 4; } }
677     const char *loader_method[] = { "con/stack", "con/bss", "cdb", "cdb/stack", "cdb/bss" };
678     char method_name[32+1]; set_method_name(method_name, sizeof(method_name), ph.method, ph.level);
679     printf("%-13s: methods       : %s, %s\n", getName(), method_name, loader_method[i]);
680 #endif
681 }
682 
683 
684 /*************************************************************************
685 //
686 **************************************************************************/
687 
canUnpack()688 int PackPs1::canUnpack()
689 {
690     if (!readFileHeader())
691         return false;
692     if (!readPackHeader(CD_SEC))
693         return false;
694     // check header as set by packer
695     if (!readBkupHeader() || ph.c_len >= fdata_size)
696         throwCantUnpack("header damaged");
697     // generic check
698     if (!checkFileHeader())
699         throwCantUnpack("unsupported header flags");
700     return true;
701 }
702 
703 
704 /*************************************************************************
705 //
706 **************************************************************************/
707 
unpack(OutputFile * fo)708 void PackPs1::unpack(OutputFile *fo)
709 {
710     // restore orig exec hdr
711     memcpy(&oh, &ih, sizeof(ih));
712     memcpy((void *) &oh.epc, &bh, SZ_IH_BKUP);
713 
714     // check for removed sector alignment
715     assert(oh.tx_len >= ph.u_len);
716     const unsigned pad = oh.tx_len - ph.u_len;
717 
718     ibuf.alloc(fdata_size > PS_HDR_SIZE ? fdata_size : PS_HDR_SIZE);
719     obuf.allocForUncompression(ph.u_len, pad);
720 
721     fi->seek(PS_HDR_SIZE, SEEK_SET);
722     fi->readx(ibuf, fdata_size);
723 
724     // decompress
725     decompress(ibuf + (fdata_size - ph.c_len), obuf);
726 
727     // write decompressed file
728     if (fo)
729     {
730         // write header
731         fo->write(&oh, sizeof(oh));
732         // align the ps exe header (mode 2 sector data size)
733         ibuf.clear();
734         fo->write(ibuf, PS_HDR_SIZE - fo->getBytesWritten());
735         // write uncompressed data + pad
736         obuf.clear(ph.u_len, pad);
737         fo->write(obuf, ph.u_len + pad);
738     }
739 }
740 
741 /* vim:set ts=4 sw=4 et: */
742