diff --git a/src/p_lx_elf.cpp b/src/p_lx_elf.cpp index 6f4407ab..5cdb7742 100644 --- a/src/p_lx_elf.cpp +++ b/src/p_lx_elf.cpp @@ -738,14 +738,7 @@ void PackLinuxElf32::pack2(OutputFile *fo, Filter &ft) unsigned total_in = 0; unsigned total_out = 0; - ui_pass = -1; // Compressing Elf headers is invisible to UI. - x.offset = 0; - x.size = sizeof(Elf32_Ehdr) + sz_phdrs; - { - int const old_level = ph.level; ph.level = 10; - packExtent(x, total_in, total_out, 0, fo); - ph.level = old_level; - } + unsigned hdr_ulen = sizeof(Elf32_Ehdr) + sz_phdrs; ui_pass = 0; ft.addvalue = 0; @@ -770,7 +763,8 @@ void PackLinuxElf32::pack2(OutputFile *fo, Filter &ft) // sometimes marks as PF_X anyway. So filter only first segment. packExtent(x, total_in, total_out, ((0==nx && (Elf32_Phdr::PF_X & get_native32(&phdri[k].p_flags))) - ? &ft : 0 ), fo ); + ? &ft : 0 ), fo, hdr_ulen); + hdr_ulen = 0; ++nx; } if (0!=ptload1sz && ptload0hi < ptload1lo) { // alignment hole? @@ -819,14 +813,7 @@ void PackLinuxElf64::pack2(OutputFile *fo, Filter &ft) unsigned total_in = 0; unsigned total_out = 0; - ui_pass = -1; // Compressing Elf headers is invisible to UI. - x.offset = 0; - x.size = sizeof(Elf64_Ehdr) + sz_phdrs; - { - int const old_level = ph.level; ph.level = 10; - packExtent(x, total_in, total_out, 0, fo); - ph.level = old_level; - } + unsigned hdr_ulen = sizeof(Elf64_Ehdr) + sz_phdrs; ui_pass = 0; ft.addvalue = 0; @@ -851,7 +838,8 @@ void PackLinuxElf64::pack2(OutputFile *fo, Filter &ft) // sometimes marks as PF_X anyway. So filter only first segment. packExtent(x, total_in, total_out, ((0==nx && (Elf64_Phdr::PF_X & get_native64(&phdri[k].p_flags))) - ? &ft : 0 ), fo ); + ? &ft : 0 ), fo, hdr_ulen); + hdr_ulen = 0; ++nx; } if (0!=ptload1sz && ptload0hi < ptload1lo) { // alignment hole? @@ -1513,14 +1501,7 @@ void PackLinuxI386elf::pack2(OutputFile *fo, Filter &ft) unsigned total_in = 0; unsigned total_out = 0; - ui_pass = -1; // Compressing Elf headers is invisible to UI. - x.offset = 0; - x.size = sizeof(Elf32_Ehdr) + sz_phdrs; - { - int const old_level = ph.level; ph.level = 10; - packExtent(x, total_in, total_out, 0, fo); - ph.level = old_level; - } + unsigned hdr_ulen = sizeof(Elf32_Ehdr) + sz_phdrs; ui_pass = 0; ft.addvalue = 0; @@ -1542,7 +1523,8 @@ void PackLinuxI386elf::pack2(OutputFile *fo, Filter &ft) } packExtent(x, total_in, total_out, ((Elf32_Phdr::PF_X & phdri[k].p_flags) - ? &ft : 0 ), fo ); + ? &ft : 0 ), fo, hdr_ulen); + hdr_ulen = 0; ++nx; } if (0!=ptload1sz && ptload0hi < ptload1lo) { // alignment hole? diff --git a/src/p_unix.cpp b/src/p_unix.cpp index 57fcef93..ef41e949 100644 --- a/src/p_unix.cpp +++ b/src/p_unix.cpp @@ -304,9 +304,16 @@ void PackUnix::packExtent( unsigned &total_in, unsigned &total_out, Filter *ft, - OutputFile *fo + OutputFile *fo, + unsigned hdr_ulen ) { + MemBuffer hdr_ibuf; + if (hdr_ulen) { + hdr_ibuf.alloc(hdr_ulen); + fi->seek(0, SEEK_SET); + int l = fi->readx(hdr_ibuf, hdr_ulen); + } fi->seek(x.offset, SEEK_SET); for (off_t rest = x.size; 0 != rest; ) { int const strategy = getStrategy(*ft); @@ -336,7 +343,9 @@ void PackUnix::packExtent( ft->id = 0; ft->cto = 0; - compressWithFilters(ft, OVERHEAD, strategy); + compressWithFilters(ft, OVERHEAD, strategy, + NULL, 0, 0, 0, 0, // those 5 args are the defaults + hdr_ibuf, hdr_ulen); } else { (void) compress(ibuf, obuf); // ignore return value @@ -358,6 +367,24 @@ void PackUnix::packExtent( // write block sizes b_info tmp; + if (hdr_ulen) { + unsigned hdr_clen; + MemBuffer hdr_obuf; + unsigned result[16]; + upx_compress_config_t conf; + memset(&conf, 0xff, sizeof(conf)); + hdr_obuf.allocForCompression(hdr_ulen); + int r = upx_compress(hdr_ibuf, hdr_ulen, hdr_obuf, &hdr_clen, 0, + ph.method, 10, &conf, result); + memset(&tmp, 0, sizeof(tmp)); + set_native32(&tmp.sz_unc, hdr_ulen); + set_native32(&tmp.sz_cpr, hdr_clen); + tmp.b_method = (unsigned char) ph.method; + fo->write(&tmp, sizeof(tmp)); + b_len += sizeof(b_info); + fo->write(hdr_obuf, hdr_clen); + hdr_ulen = 0; // compress hdr one time only + } memset(&tmp, 0, sizeof(tmp)); set_native32(&tmp.sz_unc, ph.u_len); set_native32(&tmp.sz_cpr, ph.c_len); diff --git a/src/p_unix.h b/src/p_unix.h index 00d84232..b8227bec 100644 --- a/src/p_unix.h +++ b/src/p_unix.h @@ -81,7 +81,8 @@ protected: off_t size; }; virtual void packExtent(const Extent &x, - unsigned &total_in, unsigned &total_out, Filter *, OutputFile *); + unsigned &total_in, unsigned &total_out, Filter *, OutputFile *, + unsigned hdr_len = 0); virtual void unpackExtent(unsigned wanted, OutputFile *fo, unsigned &total_in, unsigned &total_out, unsigned &c_adler, unsigned &u_adler, diff --git a/src/packer.cpp b/src/packer.cpp index dbf78cf9..2b459e90 100644 --- a/src/packer.cpp +++ b/src/packer.cpp @@ -1258,13 +1258,32 @@ const char *Packer::getDecompressor() const // executable formats. // // It will replace the tryFilters() / compress() call sequence. + +// 2006-02-15: hdr_buf and hdr_u_len are default empty input "header" array +// to fix a 2-pass problem with Elf headers. As of today there can be +// only one decompression method per executable output file, and that method +// is the one that gives best compression for .text and loader. However, +// the Elf headers precede .text in the output file, and are written first. +// "--brute" compression often compressed the Elf headers using nrv2b +// but the .text (and loader) with nrv2e. This often resulted in SIGSEGV +// during decompression. +// The workaround is for hdr_buf and hdr_u_len to describe the Elf headers +// (typically less than 512 bytes) when .text is passed in, and include +// them in the calculation of shortest output. Then the result +// this->ph.method will say which [single] method to use for everthing. +// The Elf headers are never filtered. They are short enough (< 512 bytes) +// that compressing them more than once per method (once here when choosing, +// once again just before writing [because compressWithFilters discards]) +// is OK because of the simplicity of not having two output arrays. **************************************************************************/ void Packer::compressWithFilters(Filter *parm_ft, const unsigned overlap_range, int strategy, const int *parm_filters, unsigned max_offset, unsigned max_match, - unsigned filter_off, unsigned compress_buf_off) + unsigned filter_off, unsigned compress_buf_off, + unsigned char *hdr_buf, + unsigned hdr_u_len) { const int *f; // @@ -1280,6 +1299,7 @@ void Packer::compressWithFilters(Filter *parm_ft, best_ph.c_len = orig_ph.u_len; best_ph.overlap_overhead = 0; unsigned best_ph_lsize = 0; + unsigned best_hdr_clen = 0; // preconditions assert(orig_ph.filter == 0); @@ -1373,6 +1393,14 @@ void Packer::compressWithFilters(Filter *parm_ft, int nfilters_success = 0; for (int m = 0; m < nmethods; m++) // for all methods { + unsigned hdr_clen = 0; + if (hdr_buf && hdr_u_len) { + unsigned result[16]; + upx_compress_config_t conf; + memset(&conf, 0xff, sizeof(conf)); + int r = upx_compress(hdr_buf, hdr_u_len, obuf, &hdr_clen, + 0, methods[m], 10, &conf, result); + } for (int i = 0; i < nfilters; i++) // for all filters { ibuf.checkState(); @@ -1428,19 +1456,19 @@ void Packer::compressWithFilters(Filter *parm_ft, lsize = buildLoader(&ft); } #if 0 - printf("\n%2d %02x: %d +%4d = %d (best: %d +%4d = %d)\n", ph.method, ph.filter, - ph.c_len, lsize, ph.c_len + lsize, - best_ph.c_len, best_ph_lsize, best_ph.c_len + best_ph_lsize); + printf("\n%2d %02x: %d +%4d +%3d = %d (best: %d +%4d +%3d = %d)\n", ph.method, ph.filter, + ph.c_len, lsize, hdr_clen, ph.c_len + lsize + hdr_clen, + best_ph.c_len, best_ph_lsize, best_hdr_clen, best_ph.c_len + best_ph_lsize + best_hdr_clen); #endif bool update = false; - if (ph.c_len + lsize < best_ph.c_len + best_ph_lsize) + if (ph.c_len + lsize + hdr_clen < best_ph.c_len + best_ph_lsize + best_hdr_clen) update = true; - else if (ph.c_len + lsize == best_ph.c_len + best_ph_lsize) + else if (ph.c_len + lsize + hdr_clen == best_ph.c_len + best_ph_lsize + best_hdr_clen) { // prefer smaller loaders - if (lsize < best_ph_lsize) + if (lsize + hdr_clen < best_ph_lsize + best_hdr_clen) update = true; - else if (lsize == best_ph_lsize) + else if (lsize + hdr_clen == best_ph_lsize + best_hdr_clen) { // prefer less overlap_overhead if (ph.overlap_overhead < best_ph.overlap_overhead) @@ -1455,6 +1483,7 @@ void Packer::compressWithFilters(Filter *parm_ft, // save compression results best_ph = ph; best_ph_lsize = lsize; + best_hdr_clen = hdr_clen; best_ft = ft; } } diff --git a/src/packer.h b/src/packer.h index 4eda5e25..618f5f19 100644 --- a/src/packer.h +++ b/src/packer.h @@ -172,7 +172,9 @@ protected: const int *filters = NULL, unsigned max_offset = 0, unsigned max_match = 0, unsigned filter_buf_off = 0, - unsigned compress_buf_off = 0); + unsigned compress_buf_off = 0, + unsigned char *header_buffer = 0, + unsigned header_length = 0); // util for verifying overlapping decompresion // non-destructive test