From 663d6b466b80790e465346d3cea05fe0ec2d49dc Mon Sep 17 00:00:00 2001 From: John Reiser Date: Mon, 21 Mar 2022 13:49:46 -0700 Subject: [PATCH] WIP: clarify compressWithFilters; reduce size of obuf for PackLinuxElf64 modified: p_lx_elf.cpp modified: packer.cpp modified: packer.h --- src/p_lx_elf.cpp | 57 +++++++++++++++++++++++++++++++----------------- src/packer.cpp | 57 +++++++++++++++++++++++++++++++++--------------- src/packer.h | 22 ++++++++++++++----- 3 files changed, 92 insertions(+), 44 deletions(-) diff --git a/src/p_lx_elf.cpp b/src/p_lx_elf.cpp index 6050153d..dc7e3e22 100644 --- a/src/p_lx_elf.cpp +++ b/src/p_lx_elf.cpp @@ -2276,8 +2276,8 @@ bool PackLinuxElf32::canPack() if (r_va == user_init_ava) { // found the Elf32_Rel unsigned r_info = get_te32(&rp->r_info); unsigned r_type = ELF32_R_TYPE(r_info); - if (Elf32_Ehdr::EM_ARM == e_machine - && R_ARM_RELATIVE == r_type) { + if ((Elf32_Ehdr::EM_ARM == e_machine && R_ARM_RELATIVE == r_type) + || (Elf32_Ehdr::EM_386 == e_machine && R_386_RELATIVE == r_type) ) { user_init_va = get_te32(&file_image[user_init_off]); } else { @@ -2464,7 +2464,7 @@ PackLinuxElf64::canPack() return false; } - // The first PT_LOAD64 must cover the beginning of the file (0==p_offset). + upx_uint64_t max_LOADsz = 0, max_offset = 0; Elf64_Phdr const *phdr = phdri; for (unsigned j=0; j < e_phnum; ++phdr, ++j) { if (j >= 14) { @@ -2472,23 +2472,27 @@ PackLinuxElf64::canPack() return false; } unsigned const p_type = get_te32(&phdr->p_type); - if (1!=exetype && PT_LOAD64 == p_type) { // 1st PT_LOAD - exetype = 1; - load_va = get_te64(&phdr->p_vaddr); // class data member - upx_uint64_t const p_offset = get_te64(&phdr->p_offset); - upx_uint64_t const off = ~page_mask & load_va; - if (off && off == p_offset) { // specific hint - throwCantPack("Go-language PT_LOAD: try hemfix.c, or try '--force-execve'"); - // Fixing it inside upx fails because packExtent() reads original file. - return false; + if (PT_LOAD64 == p_type) { + // The first PT_LOAD64 must cover the beginning of the file (0==p_offset). + if (1!= exetype) { + exetype = 1; + load_va = get_te64(&phdr->p_vaddr); // class data member + upx_uint64_t const p_offset = get_te64(&phdr->p_offset); + upx_uint64_t const off = ~page_mask & load_va; + if (off && off == p_offset) { // specific hint + throwCantPack("Go-language PT_LOAD: try hemfix.c, or try '--force-execve'"); + // Fixing it inside upx fails because packExtent() reads original file. + return false; + } + if (0 != p_offset) { // 1st PT_LOAD must cover Ehdr and Phdr + throwCantPack("first PT_LOAD.p_offset != 0; try '--force-execve'"); + return false; + } + // FIXME: bad for shlib! + hatch_off = ~3ul & (3+ get_te64(&phdr->p_memsz)); } - if (0 != p_offset) { // 1st PT_LOAD must cover Ehdr and Phdr - throwCantPack("first PT_LOAD.p_offset != 0; try '--force-execve'"); - return false; - } - // FIXME: bad for shlib! - hatch_off = ~3ul & (3+ get_te64(&phdr->p_memsz)); - break; + max_LOADsz = UPX_MAX(max_LOADsz, get_te64(&phdr->p_filesz)); + max_offset = UPX_MAX(max_offset, get_te64(&phdr->p_filesz) + get_te64(&phdr->p_offset)); } } // We want to compress position-independent executable (gcc -pie) @@ -2804,7 +2808,8 @@ proceed: ; exetype = 0; // set options - opt->o_unix.blocksize = blocksize = file_size; + // .blocksize: avoid over-allocating + opt->o_unix.blocksize = blocksize = UPX_MAX(max_LOADsz, file_size - max_offset); return true; } @@ -3689,6 +3694,18 @@ void PackLinuxElf64::pack1(OutputFile *fo, Filter & /*ft*/) assert(e_phoff == sizeof(Elf64_Ehdr)); // checked by canPack() sz_phdrs = e_phnum * get_te16(&ehdri.e_phentsize); +// We compress separate pieces (usually each PT_LOAD, plus the gaps in the file +// that are not covered by any PT_LOAD), but currently at run time there can be +// only one decompressor method. +// Therefore we must plan ahead because Packer::compressWithFilters tries +// to find the smallest result among the available methods. +// In the future we may allow more than one decompression method at run time, +// but for now we must choose just one, and force compressWithFilters to use it. + int methods[256]; + int nmethods = prepareMethods(methods, ph.method, getCompressionMethods(M_ALL, ph.level)); + if (1 < nmethods) { + } + Elf64_Phdr *phdr = phdri; note_size = 0; for (unsigned j=0; j < e_phnum; ++phdr, ++j) { diff --git a/src/packer.cpp b/src/packer.cpp index 9604810b..dd9a74d3 100644 --- a/src/packer.cpp +++ b/src/packer.cpp @@ -1091,6 +1091,7 @@ void Packer::relocateLoader() { } /************************************************************************* +// void Packer::compressWithFilters(): // Try compression with several methods and filters, choose the best / or first working one. Needs buildLoader(). // @@ -1137,7 +1138,7 @@ void Packer::relocateLoader() { // is OK because of the simplicity of not having two output arrays. **************************************************************************/ -static int prepareMethods(int *methods, int ph_method, const int *all_methods) { +int Packer::prepareMethods(int *methods, int ph_method, const int *all_methods) const { int nmethods = 0; if (!opt->all_methods || all_methods == nullptr) { methods[nmethods++] = ph_method; @@ -1212,11 +1213,18 @@ done: return nfilters; } -void Packer::compressWithFilters(upx_bytep i_ptr, unsigned i_len, upx_bytep o_ptr, upx_bytep f_ptr, - unsigned f_len, const upx_bytep hdr_ptr, unsigned hdr_len, - Filter *parm_ft, const unsigned overlap_range, - const upx_compress_config_t *cconf, int filter_strategy, - bool inhibit_compression_check) { +void Packer::compressWithFilters( + upx_bytep i_ptr, unsigned const i_len, // written and restored by filters + upx_bytep const o_ptr, // where to put compressed output + upx_bytep f_ptr, unsigned const f_len, // subset of [*i_ptr, +i_len) + upx_bytep const hdr_ptr, unsigned const hdr_len, + Filter *const parm_ft, // updated + unsigned const overlap_range, + upx_compress_config_t const *const cconf, + int filter_strategy, // in+out for prepareFilters + bool const inhibit_compression_check +) +{ parm_ft->buf_len = f_len; // struct copies const PackHeader orig_ph = this->ph; @@ -1404,18 +1412,26 @@ void Packer::compressWithFilters(upx_bytep i_ptr, unsigned i_len, upx_bytep o_pt // **************************************************************************/ -void Packer::compressWithFilters(Filter *ft, const unsigned overlap_range, - const upx_compress_config_t *cconf, int filter_strategy, - bool inhibit_compression_check) { - compressWithFilters(ft, overlap_range, cconf, filter_strategy, 0, 0, 0, nullptr, 0, - inhibit_compression_check); +void Packer::compressWithFilters( + Filter *ft, const unsigned overlap_range, + const upx_compress_config_t *cconf, int filter_strategy, + bool inhibit_compression_check) +{ + compressWithFilters( // call the subroutine immediately below + ft, overlap_range, + cconf, filter_strategy, + 0, 0, 0, + nullptr, 0, + inhibit_compression_check); } -void Packer::compressWithFilters(Filter *ft, const unsigned overlap_range, - const upx_compress_config_t *cconf, int filter_strategy, - unsigned filter_off, unsigned ibuf_off, unsigned obuf_off, - const upx_bytep hdr_ptr, unsigned hdr_len, - bool inhibit_compression_check) { +void Packer::compressWithFilters( + Filter *ft, const unsigned overlap_range, + upx_compress_config_t const *cconf, int filter_strategy, + unsigned filter_off, unsigned ibuf_off, unsigned obuf_off, + upx_bytep const hdr_ptr, unsigned hdr_len, + bool inhibit_compression_check) +{ ibuf.checkState(); obuf.checkState(); @@ -1427,8 +1443,13 @@ void Packer::compressWithFilters(Filter *ft, const unsigned overlap_range, assert(f_ptr + f_len <= i_ptr + i_len); - compressWithFilters(i_ptr, i_len, o_ptr, f_ptr, f_len, hdr_ptr, hdr_len, ft, overlap_range, - cconf, filter_strategy, inhibit_compression_check); + compressWithFilters( // call the first one in this file + i_ptr, i_len, + o_ptr, + f_ptr, f_len, + hdr_ptr, hdr_len, + ft, overlap_range, + cconf, filter_strategy, inhibit_compression_check); ibuf.checkState(); obuf.checkState(); diff --git a/src/packer.h b/src/packer.h index ea4ef6fb..ad166279 100644 --- a/src/packer.h +++ b/src/packer.h @@ -177,14 +177,23 @@ protected: bool inhibit_compression_check = false); void compressWithFilters(Filter *ft, const unsigned overlap_range, const upx_compress_config_t *cconf, int filter_strategy, - unsigned filter_buf_off, unsigned compress_ibuf_off, - unsigned compress_obuf_off, const upx_bytep hdr_ptr, unsigned hdr_len, + unsigned filter_buf_off, + unsigned compress_ibuf_off, + unsigned compress_obuf_off, + upx_bytep const hdr_ptr, unsigned hdr_len, bool inhibit_compression_check = false); // real compression driver - void compressWithFilters(upx_bytep i_ptr, unsigned i_len, upx_bytep o_ptr, upx_bytep f_ptr, - unsigned f_len, const upx_bytep hdr_ptr, unsigned hdr_len, Filter *ft, - const unsigned overlap_range, const upx_compress_config_t *cconf, - int filter_strategy, bool inhibit_compression_check = false); + void compressWithFilters( + upx_bytep i_ptr, unsigned i_len, // written and restored by filters + upx_bytep o_ptr, + upx_bytep f_ptr, unsigned f_len, // subset of [*i_ptr, +i_len) + upx_bytep const hdr_ptr, unsigned hdr_len, + Filter *parm_ft, // updated + unsigned overlap_range, + upx_compress_config_t const *cconf, + int filter_strategy, + bool inhibit_compression_check = false + ); // util for verifying overlapping decompresion // non-destructive test @@ -239,6 +248,7 @@ public: protected: const int *getDefaultCompressionMethods_8(int method, int level, int small = -1) const; const int *getDefaultCompressionMethods_le32(int method, int level, int small = -1) const; + int prepareMethods(int *methods, int ph_method, const int *all_methods) const; virtual const char *getDecompressorSections() const; virtual unsigned getDecompressorWrkmemSize() const; virtual void defineDecompressorSymbols();