1 /* p_unix.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) 2000-2020 John F. Reiser
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    John F. Reiser
29    <jreiser@users.sourceforge.net>
30  */
31 
32 
33 #include "conf.h"
34 
35 #include "file.h"
36 #include "filter.h"
37 #include "packer.h"
38 #include "p_unix.h"
39 #include "p_elf.h"
40 
41 // do not change
42 #define BLOCKSIZE       (512*1024)
43 
44 
45 /*************************************************************************
46 //
47 **************************************************************************/
48 
PackUnix(InputFile * f)49 PackUnix::PackUnix(InputFile *f) :
50     super(f), exetype(0), blocksize(0), overlay_offset(0), lsize(0)
51 {
52     COMPILE_TIME_ASSERT(sizeof(Elf32_Ehdr) == 52);
53     COMPILE_TIME_ASSERT(sizeof(Elf32_Phdr) == 32);
54     COMPILE_TIME_ASSERT(sizeof(b_info) == 12);
55     COMPILE_TIME_ASSERT(sizeof(l_info) == 12);
56     COMPILE_TIME_ASSERT(sizeof(p_info) == 12);
57 }
58 
59 
60 // common part of canPack(), enhanced by subclasses
canPack()61 bool PackUnix::canPack()
62 {
63     if (exetype == 0)
64         return false;
65 
66 #if defined(__unix__) && !defined(__MSYS2__)
67     // must be executable by owner
68     if ((fi->st.st_mode & S_IXUSR) == 0)
69         throwCantPack("file not executable; try 'chmod +x'");
70 #endif
71     if (file_size < 4096)
72         throwCantPack("file is too small");
73 
74     // info: currently the header is 36 (32+4) bytes before EOF
75     unsigned char buf[256];
76     fi->seek(-(off_t)sizeof(buf), SEEK_END);
77     fi->readx(buf, sizeof(buf));
78     checkAlreadyPacked(buf, sizeof(buf));
79 
80     return true;
81 }
82 
83 
writePackHeader(OutputFile * fo)84 void PackUnix::writePackHeader(OutputFile *fo)
85 {
86     unsigned char buf[32];
87     memset(buf, 0, sizeof(buf));
88 
89     const int hsize = ph.getPackHeaderSize();
90     assert((unsigned)hsize <= sizeof(buf));
91 
92     // note: magic constants are always le32
93     set_le32(buf+0, UPX_MAGIC_LE32);
94     set_le32(buf+4, UPX_MAGIC2_LE32);
95 
96     checkPatch(NULL, 0, 0, 0);  // reset
97     patchPackHeader(buf, hsize);
98     checkPatch(NULL, 0, 0, 0);  // reset
99 
100     fo->write(buf, hsize);
101 }
102 
103 
104 /*************************************************************************
105 // Generic Unix pack(). Subclasses must provide patchLoader().
106 //
107 // A typical compressed Unix executable looks like this:
108 //   - loader stub
109 //   - 12 bytes header info
110 //   - the compressed blocks, each with a 8 byte header for block sizes
111 //   - 4 bytes block end marker (uncompressed size 0)
112 //   - 32 bytes UPX packheader
113 //   - 4 bytes overlay offset (needed for decompression)
114 **************************************************************************/
115 
116 // see note below and Packer::compress()
checkCompressionRatio(unsigned,unsigned) const117 bool PackUnix::checkCompressionRatio(unsigned, unsigned) const
118 {
119     return true;
120 }
121 
pack1(OutputFile *,Filter &)122 void PackUnix::pack1(OutputFile * /*fo*/, Filter & /*ft*/)
123 {
124     // derived class usually provides this
125 }
126 
getStrategy(Filter &)127 int PackUnix::getStrategy(Filter &/*ft*/)
128 {
129     // Called just before reading and compressing each block.
130     // Might want to adjust blocksize, etc.
131 
132     // If user specified the filter, then use it (-2==filter_strategy).
133     // Else try the first two filters, and pick the better (2==filter_strategy).
134     return (opt->no_filter ? -3 : ((opt->filter > 0) ? -2 : 2));
135 }
136 
pack2(OutputFile * fo,Filter & ft)137 int PackUnix::pack2(OutputFile *fo, Filter &ft)
138 {
139     // compress blocks
140     unsigned total_in = 0;
141     unsigned total_out = 0;
142 
143 // FIXME: ui_total_passes is not correct with multiple blocks...
144 //    ui_total_passes = (file_size + blocksize - 1) / blocksize;
145 //    if (ui_total_passes == 1)
146 //        ui_total_passes = 0;
147 
148     unsigned remaining = file_size;
149     unsigned n_block = 0;
150     while (remaining > 0)
151     {
152         // FIXME: disable filters if we have more than one block.
153         // FIXME: There is only 1 un-filter in the stub [as of 2002-11-10].
154         // So the next block really has no choice!
155         // This merely prevents an assert() in compressWithFilters(),
156         // which assumes it has free choice on each call [block].
157         // And if the choices aren't the same on each block,
158         // then un-filtering will give incorrect results.
159         int filter_strategy = getStrategy(ft);
160         if (file_size > (off_t)blocksize)
161             filter_strategy = -3;      // no filters
162 
163         int l = fi->readx(ibuf, UPX_MIN(blocksize, remaining));
164         remaining -= l;
165 
166         // Note: compression for a block can fail if the
167         //       file is e.g. blocksize + 1 bytes long
168 
169         // compress
170         ph.overlap_overhead = 0;
171         ph.c_len = ph.u_len = l;
172         ft.buf_len = l;
173 
174         // compressWithFilters() updates u_adler _inside_ compress();
175         // that is, AFTER filtering.  We want BEFORE filtering,
176         // so that decompression checks the end-to-end checksum.
177         unsigned const end_u_adler = upx_adler32(ibuf, ph.u_len, ph.u_adler);
178         compressWithFilters(&ft, OVERHEAD, NULL_cconf, filter_strategy,
179             !!n_block++);  // check compression ratio only on first block
180 
181         if (ph.c_len < ph.u_len) {
182             const upx_bytep tbuf = NULL;
183             if (ft.id == 0) tbuf = ibuf;
184             ph.overlap_overhead = OVERHEAD;
185             if (!testOverlappingDecompression(obuf, tbuf, ph.overlap_overhead)) {
186                 // not in-place compressible
187                 ph.c_len = ph.u_len;
188             }
189         }
190         if (ph.c_len >= ph.u_len) {
191             // block is not compressible
192             ph.c_len = ph.u_len;
193             // must manually update checksum of compressed data
194             ph.c_adler = upx_adler32(ibuf, ph.u_len, ph.saved_c_adler);
195         }
196 
197         // write block header
198         b_info blk_info;
199         memset(&blk_info, 0, sizeof(blk_info));
200         set_te32(&blk_info.sz_unc, ph.u_len);
201         set_te32(&blk_info.sz_cpr, ph.c_len);
202         if (ph.c_len < ph.u_len) {
203             blk_info.b_method = (unsigned char) ph.method;
204             blk_info.b_ftid = (unsigned char) ph.filter;
205             blk_info.b_cto8 = (unsigned char) ph.filter_cto;
206         }
207         fo->write(&blk_info, sizeof(blk_info));
208         b_len += sizeof(b_info);
209 
210         // write compressed data
211         if (ph.c_len < ph.u_len) {
212             fo->write(obuf, ph.c_len);
213             verifyOverlappingDecompression();  // uses ph.u_adler
214         }
215         else {
216             fo->write(ibuf, ph.u_len);
217         }
218         ph.u_adler = end_u_adler;
219 
220         total_in += ph.u_len;
221         total_out += ph.c_len;
222     }
223 
224     // update header with totals
225     ph.u_len = total_in;
226     ph.c_len = total_out;
227 
228     if ((off_t)total_in != file_size) {
229         throwEOFException();
230     }
231 
232     return 1;  // default: write end-of-compression bhdr next
233 }
234 
235 void
patchLoaderChecksum()236 PackUnix::patchLoaderChecksum()
237 {
238     unsigned char *const ptr = getLoader();
239     l_info *const lp = &linfo;
240     // checksum for loader; also some PackHeader info
241     lp->l_magic = UPX_MAGIC_LE32;  // LE32 always
242     set_te16(&lp->l_lsize, (upx_uint16_t) lsize);
243     lp->l_version = (unsigned char) ph.version;
244     lp->l_format  = (unsigned char) ph.format;
245     // INFO: lp->l_checksum is currently unused
246     set_te32(&lp->l_checksum, upx_adler32(ptr, lsize));
247 }
248 
pack3(OutputFile * fo,Filter & ft)249 off_t PackUnix::pack3(OutputFile *fo, Filter &ft)
250 {
251     if (0==linker) {
252         // If no filter, then linker is not constructed by side effect
253         // of packExtent calling compressWithFilters.
254         // This is typical after "/usr/bin/patchelf --set-rpath".
255         buildLoader(&ft);
256     }
257     upx_byte *p = getLoader();
258     lsize = getLoaderSize();
259     updateLoader(fo);
260     patchLoaderChecksum();
261     fo->write(p, lsize);
262     return fo->getBytesWritten();
263 }
264 
pack4(OutputFile * fo,Filter &)265 void PackUnix::pack4(OutputFile *fo, Filter &)
266 {
267     writePackHeader(fo);
268 
269     unsigned tmp;
270     set_te32(&tmp, overlay_offset);
271     fo->write(&tmp, sizeof(tmp));
272 }
273 
pack(OutputFile * fo)274 void PackUnix::pack(OutputFile *fo)
275 {
276     Filter ft(ph.level);
277     ft.addvalue = 0;
278     b_len = 0;
279     progid = 0;
280 
281     // set options
282     blocksize = opt->o_unix.blocksize;
283     if (blocksize <= 0)
284         blocksize = BLOCKSIZE;
285     if ((off_t)blocksize > file_size)
286         blocksize = file_size;
287 
288     // init compression buffers
289     ibuf.alloc(blocksize);
290     obuf.allocForCompression(blocksize);
291 
292     fi->seek(0, SEEK_SET);
293     pack1(fo, ft);  // generate Elf header, etc.
294 
295     p_info hbuf;
296     set_te32(&hbuf.p_progid, progid);
297     set_te32(&hbuf.p_filesize, file_size);
298     set_te32(&hbuf.p_blocksize, blocksize);
299     fo->write(&hbuf, sizeof(hbuf));
300 
301     // append the compressed body
302     if (pack2(fo, ft)) {
303         // write block end marker (uncompressed size 0)
304         b_info hdr; memset(&hdr, 0, sizeof(hdr));
305         set_le32(&hdr.sz_cpr, UPX_MAGIC_LE32);
306         fo->write(&hdr, sizeof(hdr));
307     }
308 
309     pack3(fo, ft);  // append loader
310 
311     pack4(fo, ft);  // append PackHeader and overlay_offset; update Elf header
312 
313     // finally check the compression ratio
314     if (!checkFinalCompressionRatio(fo))
315         throwNotCompressible();
316 }
317 
318 
packExtent(const Extent & x,unsigned & total_in,unsigned & total_out,Filter * ft,OutputFile * fo,unsigned hdr_u_len)319 void PackUnix::packExtent(
320     const Extent &x,
321     unsigned &total_in,
322     unsigned &total_out,
323     Filter *ft,
324     OutputFile *fo,
325     unsigned hdr_u_len
326 )
327 {
328     unsigned const init_u_adler = ph.u_adler;
329     unsigned const init_c_adler = ph.c_adler;
330     MemBuffer hdr_ibuf;
331     if (hdr_u_len) {
332         hdr_ibuf.alloc(hdr_u_len);
333         fi->seek(0, SEEK_SET);
334         int l = fi->readx(hdr_ibuf, hdr_u_len);
335         (void)l;
336     }
337     fi->seek(x.offset, SEEK_SET);
338     for (off_t rest = x.size; 0 != rest; ) {
339         int const filter_strategy = ft ? getStrategy(*ft) : 0;
340         int l = fi->readx(ibuf, UPX_MIN(rest, (off_t)blocksize));
341         if (l == 0) {
342             break;
343         }
344         rest -= l;
345 
346         // Note: compression for a block can fail if the
347         //       file is e.g. blocksize + 1 bytes long
348 
349         // compress
350         ph.c_len = ph.u_len = l;
351         ph.overlap_overhead = 0;
352         unsigned end_u_adler = 0;
353         if (ft) {
354             // compressWithFilters() updates u_adler _inside_ compress();
355             // that is, AFTER filtering.  We want BEFORE filtering,
356             // so that decompression checks the end-to-end checksum.
357             end_u_adler = upx_adler32(ibuf, ph.u_len, ph.u_adler);
358             ft->buf_len = l;
359 
360                 // compressWithFilters() requirements?
361             ph.filter = 0;
362             ph.filter_cto = 0;
363             ft->id = 0;
364             ft->cto = 0;
365 
366             compressWithFilters(ft, OVERHEAD, NULL_cconf, filter_strategy,
367                                 0, 0, 0, hdr_ibuf, hdr_u_len);
368         }
369         else {
370             (void) compress(ibuf, ph.u_len, obuf);    // ignore return value
371         }
372 
373         if (ph.c_len < ph.u_len) {
374             const upx_bytep tbuf = NULL;
375             if (ft == NULL || ft->id == 0) tbuf = ibuf;
376             ph.overlap_overhead = OVERHEAD;
377             if (!testOverlappingDecompression(obuf, tbuf, ph.overlap_overhead)) {
378                 // not in-place compressible
379                 ph.c_len = ph.u_len;
380             }
381         }
382         if (ph.c_len >= ph.u_len) {
383             // block is not compressible
384             ph.c_len = ph.u_len;
385             memcpy(obuf, ibuf, ph.c_len);
386             // must update checksum of compressed data
387             ph.c_adler = upx_adler32(ibuf, ph.u_len, ph.saved_c_adler);
388         }
389 
390         // write block sizes
391         b_info tmp;
392         if (hdr_u_len) {
393             unsigned hdr_c_len = 0;
394             MemBuffer hdr_obuf;
395             hdr_obuf.allocForCompression(hdr_u_len);
396             int r = upx_compress(hdr_ibuf, hdr_u_len, hdr_obuf, &hdr_c_len, 0,
397                 ph.method, 10, NULL, NULL);
398             if (r != UPX_E_OK)
399                 throwInternalError("header compression failed");
400             if (hdr_c_len >= hdr_u_len)
401                 throwInternalError("header compression size increase");
402             ph.saved_u_adler = upx_adler32(hdr_ibuf, hdr_u_len, init_u_adler);
403             ph.saved_c_adler = upx_adler32(hdr_obuf, hdr_c_len, init_c_adler);
404             ph.u_adler = upx_adler32(ibuf, ph.u_len, ph.saved_u_adler);
405             ph.c_adler = upx_adler32(obuf, ph.c_len, ph.saved_c_adler);
406             end_u_adler = ph.u_adler;
407             memset(&tmp, 0, sizeof(tmp));
408             set_te32(&tmp.sz_unc, hdr_u_len);
409             set_te32(&tmp.sz_cpr, hdr_c_len);
410             tmp.b_method = (unsigned char) ph.method;
411             fo->write(&tmp, sizeof(tmp));
412             b_len += sizeof(b_info);
413             fo->write(hdr_obuf, hdr_c_len);
414             total_out += hdr_c_len;
415             total_in  += hdr_u_len;
416             hdr_u_len = 0;  // compress hdr one time only
417         }
418         memset(&tmp, 0, sizeof(tmp));
419         set_te32(&tmp.sz_unc, ph.u_len);
420         set_te32(&tmp.sz_cpr, ph.c_len);
421         if (ph.c_len < ph.u_len) {
422             tmp.b_method = (unsigned char) ph.method;
423             if (ft) {
424                 tmp.b_ftid = (unsigned char) ft->id;
425                 tmp.b_cto8 = ft->cto;
426             }
427         }
428         fo->write(&tmp, sizeof(tmp));
429         b_len += sizeof(b_info);
430 
431         if (ft) {
432             ph.u_adler = end_u_adler;
433         }
434         // write compressed data
435         if (ph.c_len < ph.u_len) {
436             fo->write(obuf, ph.c_len);
437             // Checks ph.u_adler after decompression, after unfiltering
438             verifyOverlappingDecompression(ft);
439         }
440         else {
441             fo->write(ibuf, ph.u_len);
442         }
443 
444         total_in += ph.u_len;
445         total_out += ph.c_len;
446     }
447 }
448 
unpackExtent(unsigned wanted,OutputFile * fo,unsigned & total_in,unsigned & total_out,unsigned & c_adler,unsigned & u_adler,bool first_PF_X,unsigned szb_info,bool is_rewrite)449 void PackUnix::unpackExtent(unsigned wanted, OutputFile *fo,
450     unsigned &total_in, unsigned &total_out,
451     unsigned &c_adler, unsigned &u_adler,
452     bool first_PF_X, unsigned szb_info, bool is_rewrite
453 )
454 {
455     b_info hdr; memset(&hdr, 0, sizeof(hdr));
456     while (wanted) {
457         fi->readx(&hdr, szb_info);
458         int const sz_unc = ph.u_len = get_te32(&hdr.sz_unc);
459         int const sz_cpr = ph.c_len = get_te32(&hdr.sz_cpr);
460         ph.filter_cto = hdr.b_cto8;
461 
462         if (sz_unc == 0) { // must never happen while 0!=wanted
463             throwCantUnpack("corrupt b_info");
464             break;
465         }
466         if (sz_unc <= 0 || sz_cpr <= 0)
467             throwCantUnpack("corrupt b_info");
468         if (sz_cpr > sz_unc || sz_unc > (int)blocksize)
469             throwCantUnpack("corrupt b_info");
470 
471         int j = blocksize + OVERHEAD - sz_cpr;
472         fi->readx(ibuf+j, sz_cpr);
473         // update checksum of compressed data
474         c_adler = upx_adler32(ibuf + j, sz_cpr, c_adler);
475         // decompress
476         if (sz_cpr < sz_unc)
477         {
478             decompress(ibuf+j, ibuf, false);
479             if (12==szb_info) { // modern per-block filter
480                 if (hdr.b_ftid) {
481                     Filter ft(ph.level);  // FIXME: ph.level for b_info?
482                     ft.init(hdr.b_ftid, 0);
483                     ft.cto = hdr.b_cto8;
484                     ft.unfilter(ibuf, sz_unc);
485                 }
486             }
487             else { // ancient per-file filter
488                 if (first_PF_X) { // Elf32_Ehdr is never filtered
489                     first_PF_X = false;  // but everything else might be
490                 }
491                 else if (ph.filter) {
492                     Filter ft(ph.level);
493                     ft.init(ph.filter, 0);
494                     ft.cto = (unsigned char) ph.filter_cto;
495                     ft.unfilter(ibuf, sz_unc);
496                 }
497             }
498             j = 0;
499         }
500         // update checksum of uncompressed data
501         u_adler = upx_adler32(ibuf + j, sz_unc, u_adler);
502         total_in  += sz_cpr;
503         total_out += sz_unc;
504         // write block
505         if (fo) {
506             if (is_rewrite) {
507                 fo->rewrite(ibuf + j, sz_unc);
508             }
509             else {
510                 fo->write(ibuf + j, sz_unc);
511             }
512         }
513         if (wanted < (unsigned)sz_unc)
514             throwCantUnpack("corrupt b_info");
515         wanted -= sz_unc;
516     }
517 }
518 
519 /*************************************************************************
520 // Generic Unix canUnpack().
521 **************************************************************************/
522 
canUnpack()523 int PackUnix::canUnpack()
524 {
525     int const small = 32 + sizeof(overlay_offset);
526     // Allow zero-filled last page, for Mac OS X code signing.
527     int bufsize = 2*4096 + 2*small +1;
528     if (bufsize > fi->st_size())
529         bufsize = fi->st_size();
530     MemBuffer buf(bufsize);
531 
532     fi->seek(-(off_t)bufsize, SEEK_END);
533     fi->readx(buf, bufsize);
534     int i = bufsize;
535     while (i > small && 0 == buf[--i]) { }
536     i -= small;
537     // allow incompressible extents
538     if (i < 0 || !getPackHeader(buf + i, bufsize - i, true))
539         return false;
540 
541     int l = ph.buf_offset + ph.getPackHeaderSize();
542     if (l < 0 || l + 4 > bufsize)
543         throwCantUnpack("file corrupted");
544     overlay_offset = get_te32(buf + i + l);
545     if ((off_t)overlay_offset >= file_size)
546         throwCantUnpack("file corrupted");
547 
548     return true;
549 }
550 
551 
552 /*************************************************************************
553 // Generic Unix unpack().
554 //
555 // This code looks much like the one in stub/l_linux.c
556 // See notes there.
557 **************************************************************************/
558 
unpack(OutputFile * fo)559 void PackUnix::unpack(OutputFile *fo)
560 {
561     b_info bhdr;
562     unsigned const szb_info = (ph.version <= 11)
563         ? sizeof(bhdr.sz_unc) + sizeof(bhdr.sz_cpr)  // old style
564         : sizeof(bhdr);
565 
566     unsigned c_adler = upx_adler32(NULL, 0);
567     unsigned u_adler = upx_adler32(NULL, 0);
568 
569     // defaults for ph.version == 8
570     unsigned orig_file_size = 0;
571     blocksize = 512 * 1024;
572 
573     fi->seek(overlay_offset, SEEK_SET);
574     if (ph.version > 8)
575     {
576         p_info hbuf;
577         fi->readx(&hbuf, sizeof(hbuf));
578         orig_file_size = get_te32(&hbuf.p_filesize);
579         blocksize = get_te32(&hbuf.p_blocksize);
580 
581         if (file_size > (off_t)orig_file_size || blocksize > orig_file_size)
582             throwCantUnpack("file header corrupted");
583     }
584     else
585     {
586         // skip 4 bytes (program id)
587         fi->seek(4, SEEK_CUR);
588     }
589 
590     if ((int)(blocksize + OVERHEAD) < 0)
591         throwCantUnpack("blocksize corrupted");
592     ibuf.alloc(blocksize + OVERHEAD);
593 
594     // decompress blocks
595     unsigned total_in = 0;
596     unsigned total_out = 0;
597     memset(&bhdr, 0, sizeof(bhdr));
598     for (;;)
599     {
600 #define buf ibuf
601         int i;
602         unsigned sz_unc, sz_cpr;
603 
604         fi->readx(&bhdr, szb_info);
605         ph.u_len = sz_unc = get_te32(&bhdr.sz_unc);
606         ph.c_len = sz_cpr = get_te32(&bhdr.sz_cpr);
607 
608         if (sz_unc == 0)                   // uncompressed size 0 -> EOF
609         {
610             // note: must reload sz_cpr as magic is always stored le32
611             sz_cpr = get_le32(&bhdr.sz_cpr);
612             if (sz_cpr != UPX_MAGIC_LE32)  // sz_cpr must be h->magic
613                 throwCompressedDataViolation();
614             break;
615         }
616         if (sz_unc <= 0 || sz_cpr <= 0)
617             throwCompressedDataViolation();
618         if (sz_cpr > sz_unc || sz_unc > blocksize)
619             throwCompressedDataViolation();
620 
621         i = blocksize + OVERHEAD - sz_cpr;
622         if (i < 0)
623             throwCantUnpack("corrupt b_info");
624         fi->readx(buf+i, sz_cpr);
625         // update checksum of compressed data
626         c_adler = upx_adler32(buf + i, sz_cpr, c_adler);
627         // decompress
628         if (sz_cpr < sz_unc) {
629             decompress(buf+i, buf, false);
630             if (0!=bhdr.b_ftid) {
631                 Filter ft(ph.level);
632                 ft.init(bhdr.b_ftid);
633                 ft.cto = bhdr.b_cto8;
634                 ft.unfilter(buf, sz_unc);
635             }
636             i = 0;
637         }
638         // update checksum of uncompressed data
639         u_adler = upx_adler32(buf + i, sz_unc, u_adler);
640         total_in  += sz_cpr;
641         total_out += sz_unc;
642         // write block
643         if (fo)
644             fo->write(buf + i, sz_unc);
645 #undef buf
646     }
647 
648     // update header with totals
649     ph.c_len = total_in;
650     ph.u_len = total_out;
651 
652     // all bytes must be written
653     if (ph.version > 8 && total_out != orig_file_size)
654         throwEOFException();
655 
656     // finally test the checksums
657     if (ph.c_adler != c_adler || ph.u_adler != u_adler)
658         throwChecksumError();
659 }
660 
661 /* vim:set ts=4 sw=4 et: */
662