diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f2a3819e..7f0492a1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,8 +13,8 @@ env: CTEST_OUTPUT_ON_FAILURE: "ON" DEBIAN_FRONTEND: noninteractive UPX_CMAKE_BUILD_FLAGS: --verbose - # 2023-10-27 - ZIG_DIST_VERSION: 0.12.0-dev.1297+a9e66ed73 + # 2023-10-29 + ZIG_DIST_VERSION: 0.12.0-dev.1327+256ab68a9 jobs: job-rebuild-and-verify-stubs: @@ -317,7 +317,8 @@ jobs: where cl & where link set RUN_CL=cl ${{ matrix.cl_machine_flags }} -MT set RUN_LIB=link -lib ${{ matrix.link_machine_flags }} - set DEFS=-D_CRT_NONSTDC_NO_WARNINGS -D_CRT_SECURE_NO_WARNINGS -DWIN32_LEAN_AND_MEAN + @rem UPX only uses the very basic Windows API + set DEFS=-D_CRT_NONSTDC_NO_WARNINGS -D_CRT_SECURE_NO_WARNINGS -DWIN32_LEAN_AND_MEAN -D_WIN32_WINNT=0x0400 set BDIR=%H%\build\%C%\%B% git rev-parse --short=12 HEAD > %BDIR%\upx\.GITREV.txt @REM ===== build bzip2 ===== diff --git a/.github/workflows/weekly-ci-bs-cmake-macos-xcode.yml b/.github/workflows/weekly-ci-bs-cmake-macos-xcode.yml index 59354309..e2f68c4f 100644 --- a/.github/workflows/weekly-ci-bs-cmake-macos-xcode.yml +++ b/.github/workflows/weekly-ci-bs-cmake-macos-xcode.yml @@ -27,9 +27,9 @@ jobs: uses: actions/checkout@v4 with: { submodules: true } - { name: 'Config cmake Xcode', run: 'cmake -S . -B build/xcode -G Xcode' } - - { name: 'Build cmake Xcode default', run: 'cmake --build build/xcode' } - - { name: 'Build cmake Xcode Debug', run: 'cmake --build build/xcode --config Debug' } - - { name: 'Build cmake Xcode Release', run: 'cmake --build build/xcode --config Release' } + - { name: 'Build cmake Xcode default', run: 'cmake --build build/xcode --parallel --verbose' } + - { name: 'Build cmake Xcode Debug', run: 'cmake --build build/xcode --parallel --verbose --config Debug' } + - { name: 'Build cmake Xcode Release', run: 'cmake --build build/xcode --parallel --verbose --config Release' } - name: 'Make artifact' run: | N=$(echo "upx-${GITHUB_REF_NAME}-${GITHUB_SHA:0:7}-weekly-ci-xcode-${{ matrix.os }}" | sed 's/[^0-9a-zA-Z_.-]/-/g') diff --git a/.github/workflows/weekly-ci-cc-llvm-mingw.yml b/.github/workflows/weekly-ci-cc-llvm-mingw.yml index 46db8c8f..3c86a78e 100644 --- a/.github/workflows/weekly-ci-cc-llvm-mingw.yml +++ b/.github/workflows/weekly-ci-cc-llvm-mingw.yml @@ -11,6 +11,7 @@ env: CMAKE_REQUIRED_QUIET: "OFF" CTEST_OUTPUT_ON_FAILURE: "ON" DEBIAN_FRONTEND: noninteractive + UPX_CMAKE_CONFIG_FLAGS: -DCMAKE_VERBOSE_MAKEFILE=ON jobs: job-llvm-mingw: # uses cmake + make @@ -51,18 +52,22 @@ jobs: - name: 'Build clang aarch64' run: | export CC="aarch64-w64-mingw32-clang -static" CXX="aarch64-w64-mingw32-clang++ -static" + CC="$CC -D_WIN32_WINNT=0x0400"; CXX="$CXX -D_WIN32_WINNT=0x0400" make UPX_XTARGET=aarch64-w64-mingw32-clang xtarget/debug xtarget/release - name: 'Build clang armv7' run: | export CC="armv7-w64-mingw32-clang -static" CXX="armv7-w64-mingw32-clang++ -static" + CC="$CC -D_WIN32_WINNT=0x0400"; CXX="$CXX -D_WIN32_WINNT=0x0400" make UPX_XTARGET=armv7-w64-mingw32-clang xtarget/debug xtarget/release - name: 'Build clang i686' run: | export CC="i686-w64-mingw32-clang -static" CXX="i686-w64-mingw32-clang++ -static" + CC="$CC -D_WIN32_WINNT=0x0400"; CXX="$CXX -D_WIN32_WINNT=0x0400" make UPX_XTARGET=i686-w64-mingw32-clang xtarget/debug xtarget/release - name: 'Build clang x86_64' run: | export CC="x86_64-w64-mingw32-clang -static" CXX="x86_64-w64-mingw32-clang++ -static" + CC="$CC -D_WIN32_WINNT=0x0400"; CXX="$CXX -D_WIN32_WINNT=0x0400" make UPX_XTARGET=x86_64-w64-mingw32-clang xtarget/debug xtarget/release - name: 'Make artifact' run: | diff --git a/.github/workflows/weekly-ci-cc-zigcc.yml b/.github/workflows/weekly-ci-cc-zigcc.yml index cca08370..b8e4525e 100644 --- a/.github/workflows/weekly-ci-cc-zigcc.yml +++ b/.github/workflows/weekly-ci-cc-zigcc.yml @@ -11,8 +11,8 @@ env: CMAKE_REQUIRED_QUIET: "OFF" CTEST_OUTPUT_ON_FAILURE: "ON" DEBIAN_FRONTEND: noninteractive - # 2023-10-27 - ZIG_DIST_VERSION: 0.12.0-dev.1297+a9e66ed73 + # 2023-10-29 + ZIG_DIST_VERSION: 0.12.0-dev.1327+256ab68a9 jobs: job-linux-zigcc: # uses cmake + make diff --git a/CMakeLists.txt b/CMakeLists.txt index bb8db87b..4cbd91a5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,9 +11,23 @@ if(DEFINED UPX_CONFIG_CMAKE_MINIMUM_REQUIRED_VERSION) cmake_minimum_required(VERSION "${UPX_CONFIG_CMAKE_MINIMUM_REQUIRED_VERSION}" FATAL_ERROR) else() - cmake_minimum_required(VERSION 3.8 FATAL_ERROR) # CMake >= 3.8 is needed for CXX_STANDARD 17 + cmake_minimum_required(VERSION "3.8" FATAL_ERROR) # CMake >= 3.8 is needed for CXX_STANDARD 17 endif() +# Sections of this CMakeLists.txt: +# - options +# - init +# - common compilation flags +# - targets +# - target compilation flags +# - test +# - install +# - print summary + +#*********************************************************************** +# options +#*********************************************************************** + # compilation config options if(NOT IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/.git") # permissive config defaults when building from source code tarball @@ -281,12 +295,23 @@ if(NOT CMAKE_C_COMPILER_ID MATCHES "^MSVC") endif() endif() -# compile a source file with -O2 even in Debug build +# compile a target with -O2 optimization even in Debug build +function(upx_compile_target_debug_with_O2) + foreach(t ${ARGV}) + if(MSVC_FRONTEND) + # MSVC uses some Debug compilation options like -RTC1 that are incompatible with -O2 + else() + target_compile_options(${t} PRIVATE $<$:-O2>) + endif() + endforeach() +endfunction() + +# compile a source file with -O2 optimization even in Debug build; messy because of CMake limitations function(upx_compile_source_debug_with_O2) set(flags "$<$:-O2>") - if (CMAKE_VERSION VERSION_LESS 3.8) + if(${CMAKE_VERSION} VERSION_LESS "3.8") # 3.8: The COMPILE_FLAGS source file property learned to support generator expressions - if (is_multi_config OR NOT CMAKE_BUILD_TYPE MATCHES "^Debug$") + if(is_multi_config OR NOT CMAKE_BUILD_TYPE MATCHES "^Debug$") return() endif() set(flags "-O2") @@ -309,17 +334,6 @@ function(upx_compile_source_debug_with_O2) endforeach() endfunction() -# compile a target with -O2 even in Debug build -function(upx_compile_target_debug_with_O2) - foreach(t ${ARGV}) - if(MSVC_FRONTEND) - # MSVC uses some Debug compilation options like -RTC1 that are incompatible with -O2 - else() - target_compile_options(${t} PRIVATE $<$:-O2>) - endif() - endforeach() -endfunction() - # sanitize a target function(upx_sanitize_target) foreach(t ${ARGV}) @@ -484,7 +498,7 @@ if(HAVE_UTIMENSAT) target_compile_definitions(${t} PRIVATE HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC=1) endif() endif() -# improve speed of the debug versions +# improve speed of the Debug versions upx_compile_source_debug_with_O2(src/compress/compress_lzma.cpp) upx_compile_source_debug_with_O2(src/filter/filter_impl.cpp) #upx_compile_target_debug_with_O2(${t}) @@ -496,9 +510,10 @@ else() endif() #*********************************************************************** -# ctest -# make test -# ninja test +# test +# ctest +# make test +# ninja test #*********************************************************************** if(NOT UPX_CONFIG_CMAKE_DISABLE_TEST) @@ -536,9 +551,10 @@ endif() endif() # UPX_CONFIG_CMAKE_DISABLE_TEST #*********************************************************************** -# cmake --install . -# make install -# ninja install +# install +# cmake --install . +# make install +# ninja install #*********************************************************************** if(NOT UPX_CONFIG_CMAKE_DISABLE_INSTALL) @@ -563,13 +579,16 @@ endif() endif() # UPX_CONFIG_CMAKE_DISABLE_INSTALL #*********************************************************************** -# finally print some info about the build configuration +# show summary +# print some info about the build configuration #*********************************************************************** function(print_var) foreach(var ${ARGV}) - if(${var}) - message(STATUS "${var} = ${${var}}") + if(DEFINED ${var} AND NOT ",${${var}}," STREQUAL ",,") + if(${var}) + message(STATUS "${var} = ${${var}}") + endif() endif() endforeach() endfunction() diff --git a/Makefile b/Makefile index 6dd4f0c0..b6ef3bab 100644 --- a/Makefile +++ b/Makefile @@ -56,7 +56,11 @@ build/%/all: $$(dir $$@)debug $$(dir $$@)release ; # # extra pre-defined build configurations and some utility; optional +ifneq ($(MAKEFILE_LIST),) +include $(dir $(lastword $(MAKEFILE_LIST)))/misc/make/Makefile-extra.mk +else include ./misc/make/Makefile-extra.mk +endif # developer convenience ifneq ($(wildcard /usr/bin/env),) # needs bash, perl, xargs, etc. diff --git a/misc/make/Makefile-extra.mk b/misc/make/Makefile-extra.mk index 42e6da29..612898e4 100644 --- a/misc/make/Makefile-extra.mk +++ b/misc/make/Makefile-extra.mk @@ -3,6 +3,9 @@ # Copyright (C) Markus Franz Xaver Johannes Oberhumer # +ifeq ($(UPX_MAKEFILE_EXTRA_MK_INCLUDED),) +UPX_MAKEFILE_EXTRA_MK_INCLUDED := 1 + #*********************************************************************** # extra builds: some pre-defined build configurations #*********************************************************************** @@ -131,14 +134,14 @@ build/extra/cross-linux-gnu-arm-eabihf/%: export CXX = arm-linux-gnueabihf-g++ - # cross compiler: Windows x86 win32 MinGW (i386) build/extra/cross-windows-mingw32/debug: PHONY; $(call run_config_and_build,$@,Debug) build/extra/cross-windows-mingw32/release: PHONY; $(call run_config_and_build,$@,Release) -build/extra/cross-windows-mingw32/%: export CC = i686-w64-mingw32-gcc -static -build/extra/cross-windows-mingw32/%: export CXX = i686-w64-mingw32-g++ -static +build/extra/cross-windows-mingw32/%: export CC = i686-w64-mingw32-gcc -static -D_WIN32_WINNT=0x0400 +build/extra/cross-windows-mingw32/%: export CXX = i686-w64-mingw32-g++ -static -D_WIN32_WINNT=0x0400 # cross compiler: Windows x64 win64 MinGW (amd64) build/extra/cross-windows-mingw64/debug: PHONY; $(call run_config_and_build,$@,Debug) build/extra/cross-windows-mingw64/release: PHONY; $(call run_config_and_build,$@,Release) -build/extra/cross-windows-mingw64/%: export CC = x86_64-w64-mingw32-gcc -static -build/extra/cross-windows-mingw64/%: export CXX = x86_64-w64-mingw32-g++ -static +build/extra/cross-windows-mingw64/%: export CC = x86_64-w64-mingw32-gcc -static -D_WIN32_WINNT=0x0400 +build/extra/cross-windows-mingw64/%: export CXX = x86_64-w64-mingw32-g++ -static -D_WIN32_WINNT=0x0400 # cross compiler: macOS arm64 (aarch64) build/extra/cross-darwin-arm64/debug: PHONY; $(call run_config_and_build,$@,Debug) @@ -249,3 +252,5 @@ SUBMODULES = doctest lzma-sdk ucl valgrind zlib dummy := $(foreach m,$(SUBMODULES),$(if $(wildcard vendor/$m/[CL]*),$m,\ $(error ERROR: missing git submodule '$m'; run 'git submodule update --init'))) + +endif # UPX_MAKEFILE_EXTRA_MK_INCLUDED diff --git a/src/check/dt_cxxlib.cpp b/src/check/dt_cxxlib.cpp index 6014d86f..40beda60 100644 --- a/src/check/dt_cxxlib.cpp +++ b/src/check/dt_cxxlib.cpp @@ -106,7 +106,8 @@ TEST_CASE("noncopyable") { } /************************************************************************* -// TriBool +// TriBool checks +// (modern compilers will optimize away most of this code) **************************************************************************/ namespace { diff --git a/src/main.cpp b/src/main.cpp index 48f039a3..f97da203 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -855,8 +855,8 @@ int main_get_options(int argc, char **argv) { {"fake-stub-version", 0x31, N, 542}, // for internal debugging {"fake-stub-year", 0x31, N, 543}, // for internal debugging {"disable-random-id", 0x90, N, 545}, // for internal debugging - {"debug-use-random-method", 0x90, N, 546}, // for internal debugging - {"debug-use-random-filter", 0x90, N, 547}, // for internal debugging + {"debug-use-random-method", 0x90, N, 546}, // for internal debugging / fuzz testing + {"debug-use-random-filter", 0x90, N, 547}, // for internal debugging / fuzz testing // backup options {"backup", 0x10, N, 'k'}, diff --git a/src/options.h b/src/options.h index 3430c14c..c93b611b 100644 --- a/src/options.h +++ b/src/options.h @@ -54,10 +54,12 @@ enum { }; struct Options final { - int cmd; + void reset() noexcept; + + int cmd; // CMD_xxx // compression options - int method; + int method; // M_xxx bool method_lzma_seen; bool method_nrv2b_seen; bool method_nrv2d_seen; @@ -90,6 +92,20 @@ struct Options final { int verbose; bool to_stdout; + // overlay handling + enum { SKIP_OVERLAY = 0, COPY_OVERLAY = 1, STRIP_OVERLAY = 2 }; + int overlay; + + // CPU options for i086/i386 + enum { + CPU_DEFAULT = 0, + CPU_8086 = 1, + CPU_286 = 2, + CPU_386 = 3, + CPU_486 = 4, + }; + int cpu_x86; + // debug options struct { int debug_level; @@ -98,14 +114,10 @@ struct Options final { char fake_stub_version[4 + 1]; // for internal debugging char fake_stub_year[4 + 1]; // for internal debugging bool getopt_throw_instead_of_exit; // for internal doctest checks - bool use_random_method; // for internal debugging - bool use_random_filter; // for internal debugging + bool use_random_method; // for internal debugging / fuzz testing + bool use_random_filter; // for internal debugging / fuzz testing } debug; - // overlay handling - enum { SKIP_OVERLAY = 0, COPY_OVERLAY = 1, STRIP_OVERLAY = 2 }; - int overlay; - // CRP - Compression Runtime Parameters (undocumented and subject to change) struct { lzma_compress_config_t crp_lzma; @@ -120,16 +132,6 @@ struct Options final { } } crp; - // CPU options for i086/i386 - enum { - CPU_DEFAULT = 0, - CPU_8086 = 1, - CPU_286 = 2, - CPU_386 = 3, - CPU_486 = 4, - }; - int cpu_x86; - // options for various executable formats struct { bool split_segments; @@ -173,8 +175,6 @@ struct Options final { int strip_relocs; const char *keep_resource; } win32_pe; - - void reset() noexcept; }; /* vim:set ts=4 sw=4 et: */ diff --git a/src/pefile.cpp b/src/pefile.cpp index 5df82760..31241f91 100644 --- a/src/pefile.cpp +++ b/src/pefile.cpp @@ -49,8 +49,8 @@ static void xcheck(const void *p) { throwCantUnpack("xcheck unexpected nullptr pointer; take care!"); } static void xcheck(const void *p, size_t plen, const void *b, size_t blen) { - const char *pp = (const char *) p; - const char *bb = (const char *) b; + const charptr pp = (const charptr) p; + const charptr bb = (const charptr) b; if very_unlikely (pp < bb || pp > bb + blen || pp + plen > bb + blen) throwCantUnpack("xcheck pointer out of range; take care!"); } @@ -154,7 +154,7 @@ bool PeFile::testUnpackVersion(int version) const { } int PeFile::readFileHeader() { - struct alignas(1) exe_header_t { + struct alignas(1) ExeHeader final { LE16 mz; LE16 m512; LE16 p512; @@ -164,12 +164,12 @@ int PeFile::readFileHeader() { LE32 nexepos; }; - COMPILE_TIME_ASSERT(sizeof(exe_header_t) == 64) - COMPILE_TIME_ASSERT_ALIGNED1(exe_header_t) - COMPILE_TIME_ASSERT(sizeof(((exe_header_t *) nullptr)->_) == 18) - COMPILE_TIME_ASSERT(sizeof(((exe_header_t *) nullptr)->__) == 34) + COMPILE_TIME_ASSERT(sizeof(ExeHeader) == 64) + COMPILE_TIME_ASSERT_ALIGNED1(ExeHeader) + COMPILE_TIME_ASSERT(sizeof(((ExeHeader *) nullptr)->_) == 18) + COMPILE_TIME_ASSERT(sizeof(((ExeHeader *) nullptr)->__) == 34) - exe_header_t h; + ExeHeader h; int ic; pe_offset = 0; @@ -179,11 +179,11 @@ int PeFile::readFileHeader() { if (h.mz == 'M' + 'Z' * 256) // dos exe { - if (h.nexepos && h.nexepos < sizeof(exe_header_t)) { + if (h.nexepos && h.nexepos < sizeof(ExeHeader)) { // Overlapping MZ and PE headers by 'leanify', etc. char buf[64]; snprintf(buf, sizeof(buf), "PE and MZ header overlap: %#x < %#x", - (unsigned) h.nexepos, (unsigned) sizeof(exe_header_t)); + (unsigned) h.nexepos, (unsigned) sizeof(ExeHeader)); throwCantPack(buf); } const unsigned delta = (h.relocoffs >= 0x40) @@ -283,11 +283,11 @@ void PeFile::Interval::dump() const { **************************************************************************/ namespace { -struct FixDeleter { // don't leak memory on exceptions +struct FixDeleter final { // helper so we don't leak memory on exceptions LE32 **fix; - size_t n; + size_t count; ~FixDeleter() noexcept { - for (size_t i = 0; i < n; i++) { + for (size_t i = 0; i < count; i++) { delete[] fix[i]; fix[i] = nullptr; } @@ -492,7 +492,7 @@ void PeFile32::processRelocs() // pass1 FixDeleter fixdel{fix, 0}; // don't leak memory for (ic = 0; ic < 4; ic++) { fix[ic] = New(LE32, counts[ic]); - fixdel.n += 1; + fixdel.count += 1; } unsigned xcounts[4]; @@ -594,7 +594,7 @@ void PeFile64::processRelocs() // pass1 FixDeleter fixdel{fix, 0}; // don't leak memory for (ic = 0; ic < 16; ic++) { fix[ic] = New(LE32, counts[ic]); - fixdel.n += 1; + fixdel.count += 1; } unsigned xcounts[16]; @@ -686,16 +686,18 @@ const LE32 &PeFile::IDADDR(unsigned x) const { return iddirs[x].vaddr; } */ class PeFile::ImportLinker final : public ElfLinkerAMD64 { - struct tstr : private ::noncopyable { - char *s = nullptr; - explicit tstr(char *str) : s(str) {} - ~tstr() noexcept { delete[] s; } - operator char *() const { return s; } + struct TStr final : private ::noncopyable { // temporary string owner, deletes on destruction + explicit TStr(char *str) noexcept : s(str) {} + ~TStr() noexcept { delete[] s; } // delete! + operator char *() noexcept { return s; } + operator const char *() const noexcept { return s; } + private: + char *s; }; // encoding of dll and proc names are required, so that our special // control characters in the name of sections can work as intended - static void encode_name(const char *name, char *buf) { + static void encode_name(SPAN_P(const char) name, SPAN_S(char) buf) { while (*name) { *buf++ = 'a' + ((*name >> 4) & 0xf); *buf++ = 'a' + (*name & 0xf); @@ -705,27 +707,29 @@ class PeFile::ImportLinker final : public ElfLinkerAMD64 { } static char *name_for_dll(const char *dll, char first_char) { - assert(dll); - unsigned l = strlen(dll); + assert(dll != nullptr); + const unsigned l = strlen(dll); assert(l > 0); - - char *name = New(char, 3 * l + 2); + const unsigned new_size = 1 + 3 * l + 1; + char *const new_name = New(char, new_size); + SPAN_S_VAR(char, const name, new_name, new_size); name[0] = first_char; - char *n = name + 1 + 2 * l; + SPAN_S_VAR(char, n, name + (1 + 2 * l)); do { *n++ = tolower((uchar) *dll); } while (*dll++); - encode_name(name + 1 + 2 * l, name + 1); - return name; + encode_name(new_name + (1 + 2 * l), name + 1); + return new_name; } static char *name_for_proc(const char *dll, const char *proc, char first_char, char separator) { - unsigned len = 1 + 2 * strlen(dll) + 1 + 2 * strlen(proc) + 1 + 1; - tstr dlln(name_for_dll(dll, first_char)); - char *procn = New(char, len); - upx_safe_snprintf(procn, len, "%s%c", (const char *) dlln, separator); - encode_name(proc, procn + strlen(procn)); - return procn; + const unsigned new_size = 1 + 2 * strlen(dll) + 1 + 2 * strlen(proc) + 1 + 1; + TStr dll_name(name_for_dll(dll, first_char)); + char *const new_name = New(char, new_size); + SPAN_S_VAR(char, const name, new_name, new_size); + upx_safe_snprintf(new_name, new_size, "%s%c", (const char *) dll_name, separator); + encode_name(proc, name + strlen(name)); + return new_name; } static const char zeros[sizeof(import_desc)]; @@ -747,8 +751,8 @@ class PeFile::ImportLinker final : public ElfLinkerAMD64 { unsigned thunk_size; // 4 or 8 bytes void add(const char *dll, const char *proc, unsigned ordinal) { - tstr sdll(name_for_dll(dll, dll_name_id)); - tstr desc_name(name_for_dll(dll, descriptor_id)); + TStr sdll(name_for_dll(dll, dll_name_id)); + TStr desc_name(name_for_dll(dll, descriptor_id)); char tsep = thunk_separator; if (findSection(sdll, false) == nullptr) { @@ -759,7 +763,7 @@ class PeFile::ImportLinker final : public ElfLinkerAMD64 { addSection(desc_name, zeros, sizeof(zeros), 0); // descriptor addRelocation(desc_name, offsetof(import_desc, dllname), "R_X86_64_32", sdll, 0); } - tstr thunk(proc == nullptr ? name_for_dll(dll, thunk_id) + TStr thunk(proc == nullptr ? name_for_dll(dll, thunk_id) : name_for_proc(dll, proc, thunk_id, tsep)); if (findSection(thunk, false) != nullptr) @@ -769,7 +773,7 @@ class PeFile::ImportLinker final : public ElfLinkerAMD64 { if (tsep == thunk_separator_first) { addRelocation(desc_name, offsetof(import_desc, iat), "R_X86_64_32", thunk, 0); - tstr last_thunk(name_for_proc(dll, "X", thunk_id, thunk_separator_last)); + TStr last_thunk(name_for_proc(dll, "X", thunk_id, thunk_separator_last)); addSection(last_thunk, zeros, thunk_size, 0); } @@ -777,7 +781,7 @@ class PeFile::ImportLinker final : public ElfLinkerAMD64 { if (ordinal != 0u) { addRelocation(thunk, 0, reltype, "*UND*", ordinal | (1ull << (thunk_size * 8 - 1))); } else if (proc != nullptr) { - tstr proc_name(name_for_proc(dll, proc, proc_name_id, procname_separator)); + TStr proc_name(name_for_proc(dll, proc, proc_name_id, procname_separator)); addSection(proc_name, zeros, 2, 1); // 2 bytes of word aligned "hint" addSymbol(proc_name, proc_name, 0); addRelocation(thunk, 0, reltype, proc_name, 0); @@ -806,7 +810,7 @@ class PeFile::ImportLinker final : public ElfLinkerAMD64 { const Section *getThunk(const char *dll, const char *proc, char tsep) const { assert(dll); assert(proc); - tstr thunk(name_for_proc(dll, proc, thunk_id, tsep)); + TStr thunk(name_for_proc(dll, proc, thunk_id, tsep)); return findSection(thunk, false); } @@ -887,7 +891,6 @@ public: assert(ordinal > 0 && ordinal < 0x10000); char ord[1 + 5 + 1]; upx_safe_snprintf(ord, sizeof(ord), "%c%05u", ordinal_id, ordinal); - const Section *s = getThunk((const char *) dll, ord, thunk_separator_first); if (s == nullptr && (s = getThunk((const char *) dll, ord, thunk_separator)) == nullptr) throwInternalError("entry not found"); @@ -897,18 +900,18 @@ public: template upx_uint64_t getAddress(const C *dll) const { ACC_COMPILE_TIME_ASSERT(sizeof(C) == 1) // "char" or "byte" - tstr sdll(name_for_dll((const char *) dll, dll_name_id)); + TStr sdll(name_for_dll((const char *) dll, dll_name_id)); return findSection(sdll, true)->offset; } template upx_uint64_t hasDll(const C *dll) const { ACC_COMPILE_TIME_ASSERT(sizeof(C) == 1) // "char" or "byte" - tstr sdll(name_for_dll((const char *) dll, dll_name_id)); + TStr sdll(name_for_dll((const char *) dll, dll_name_id)); return findSection(sdll, false) != nullptr; } }; -const char PeFile::ImportLinker::zeros[sizeof(import_desc)] = {0}; +/*static*/ const char PeFile::ImportLinker::zeros[sizeof(import_desc)] = {0}; void PeFile::addKernelImport(const char *name) { ilinker->add(kernelDll(), name); } @@ -923,10 +926,8 @@ void PeFile::addStubImports() { void PeFile::processImports2(unsigned myimport, unsigned) // pass 2 { COMPILE_TIME_ASSERT(sizeof(import_desc) == 20) - - if (!ilinker) + if (ilinker == nullptr) return; - ilinker->relocate_import(myimport); int len; oimpdlls = ilinker->getLoader(&len); @@ -958,7 +959,7 @@ unsigned PeFile::processImports0(ord_mask_t ord_mask) // pass 1 if (dllnum > 4096) // just some arbitrary limit/sanity check throwCantPack("too many DLL imports %u", dllnum); - struct udll { + struct UDll final { const byte *name; const byte *shname; unsigned ordinal; @@ -968,8 +969,8 @@ unsigned PeFile::processImports0(ord_mask_t ord_mask) // pass 1 bool isk32; static int __acc_cdecl_qsort compare(const void *aa, const void *bb) { - const udll *a = *(const udll *const *) aa; - const udll *b = *(const udll *const *) bb; + const UDll *a = *(const UDll *const *) aa; + const UDll *b = *(const UDll *const *) bb; if (a->original_position == b->original_position) // identical object, poor qsort() return 0; if (a->isk32 != b->isk32) @@ -997,8 +998,8 @@ unsigned PeFile::processImports0(ord_mask_t ord_mask) // pass 1 }; // +1 for dllnum=0 - Array(struct udll, dlls, dllnum + 1); - Array(struct udll *, idlls, dllnum + 1); + Array(UDll, dlls, dllnum + 1); + Array(UDll *, idlls, dllnum + 1); soimport = 1024; // safety @@ -1036,7 +1037,7 @@ unsigned PeFile::processImports0(ord_mask_t ord_mask) // pass 1 mb_oimport.clear(); oimport = mb_oimport; - upx_qsort(idlls, dllnum, sizeof(*idlls), udll::compare); + upx_qsort(idlls, dllnum, sizeof(*idlls), UDll::compare); info("Processing imports: %d DLLs", dllnum); for (unsigned ic = 0; ic < dllnum; ic++) { @@ -1081,7 +1082,7 @@ unsigned PeFile::processImports0(ord_mask_t ord_mask) // pass 1 ppi += 8; for (; *tarr; tarr++) if (*tarr & ord_mask) { - unsigned ord = *tarr & 0xffff; + const unsigned ord = *tarr & 0xffff; if (idlls[ic]->isk32 && kernel32ordinal) { *ppi++ = 0xfe; // signed + odd parity set_le32(ppi, ilinker->getAddress(idlls[ic]->name, ord)); @@ -1101,7 +1102,7 @@ unsigned PeFile::processImports0(ord_mask_t ord_mask) // pass 1 } ppi++; - unsigned esize = ptr_udiff_bytes(tarr, idlls[ic]->lookupt); + const unsigned esize = ptr_udiff_bytes(tarr, idlls[ic]->lookupt); lookups.add(idlls[ic]->lookupt, esize); if (ptr_diff_bytes(ibuf.subref("bad import name %#x", idlls[ic]->iat, 1), idlls[ic]->lookupt) != 0) { @@ -1311,13 +1312,13 @@ void PeFile::processExports(Export *xport, unsigned newoffs) // pass2 // of course it was impossible to debug this ;-) template <> -struct PeFile::tls_traits { +struct PeFile::tls_traits final { struct alignas(1) tls { LE32 datastart; // VA tls init data start LE32 dataend; // VA tls init data end LE32 tlsindex; // VA tls index LE32 callbacks; // VA tls callbacks - char _[8]; // zero init, characteristics + byte _[8]; // zero init, characteristics }; static const unsigned sotls = 24; @@ -1328,13 +1329,13 @@ struct PeFile::tls_traits { }; template <> -struct PeFile::tls_traits { +struct PeFile::tls_traits final { struct alignas(1) tls { LE64 datastart; // VA tls init data start LE64 dataend; // VA tls init data end LE64 tlsindex; // VA tls index LE64 callbacks; // VA tls callbacks - char _[8]; // zero init, characteristics + byte _[8]; // zero init, characteristics }; static const unsigned sotls = 40; @@ -1545,7 +1546,7 @@ struct alignas(1) PeFile::Resource::res_dir_entry { }; struct alignas(1) PeFile::Resource::res_dir { - char _[12]; // flags, timedate, version + byte _[12]; // flags, timedate, version LE16 namedentr; LE16 identr; @@ -1558,7 +1559,7 @@ struct alignas(1) PeFile::Resource::res_dir { struct alignas(1) PeFile::Resource::res_data { LE32 offset; LE32 size; - char _[8]; // codepage, reserved + byte _[8]; // codepage, reserved }; struct PeFile::Resource::upx_rnode { @@ -1856,11 +1857,10 @@ static bool match(unsigned itype, const byte *ntype, unsigned iname, const byte // typex and namex can be string or number // hopefully resource names do not have '/' or ',' characters inside - struct helper { + struct Helper final { static bool match(unsigned num, const byte *unistr, const char *mkeep) { if (!unistr) return (unsigned) atoi(mkeep) == num; - unsigned ic; for (ic = 0; ic < get_le16(unistr); ic++) if (unistr[2 + ic * 2] != (byte) mkeep[ic]) @@ -1871,17 +1871,16 @@ static bool match(unsigned itype, const byte *ntype, unsigned iname, const byte // FIXME this comparison is not too exact for (;;) { - char const *delim1 = strchr(keep, '/'); - char const *delim2 = strchr(keep, ','); - if (helper::match(itype, ntype, keep)) { + const char *delim1 = strchr(keep, '/'); + const char *delim2 = strchr(keep, ','); + if (Helper::match(itype, ntype, keep)) { if (!delim1) return true; if (delim2 && delim2 < delim1) return true; - if (helper::match(iname, nname, delim1 + 1)) + if (Helper::match(iname, nname, delim1 + 1)) return true; } - if (delim2 == nullptr) break; keep = delim2 + 1; @@ -1967,13 +1966,15 @@ void PeFile::processResources(Resource *res) { else if (rtype > 0 && rtype < RT_LAST) do_compress = opt->win32_pe.compress_rt[rtype] ? true : false; - if (keep_icons) + if (do_compress && keep_icons) do_compress &= !match(res->itype(), res->ntype(), res->iname(), res->nname(), keep_icons); - do_compress &= - !match(res->itype(), res->ntype(), res->iname(), res->nname(), "TYPELIB,REGISTRY,16"); - do_compress &= !match(res->itype(), res->ntype(), res->iname(), res->nname(), - opt->win32_pe.keep_resource); + if (do_compress) + do_compress &= !match(res->itype(), res->ntype(), res->iname(), res->nname(), + "TYPELIB,REGISTRY,16"); + if (do_compress) + do_compress &= !match(res->itype(), res->ntype(), res->iname(), res->nname(), + opt->win32_pe.keep_resource); if (do_compress) { csize += res->size(); @@ -2042,22 +2043,22 @@ unsigned PeFile::stripDebug(unsigned overlaystart) { if (IDADDR(PEDIR_DEBUG) == 0) return overlaystart; - struct alignas(1) debug_dir_t { - char _[16]; // flags, time/date, version, type + struct alignas(1) DebugDir final { + byte _[16]; // flags, time/date, version, type LE32 size; - char __[4]; // rva + byte __[4]; // rva LE32 fpos; }; - COMPILE_TIME_ASSERT(sizeof(debug_dir_t) == 28) - COMPILE_TIME_ASSERT_ALIGNED1(debug_dir_t) - COMPILE_TIME_ASSERT(sizeof(((debug_dir_t *) nullptr)->_) == 16) - COMPILE_TIME_ASSERT(sizeof(((debug_dir_t *) nullptr)->__) == 4) + COMPILE_TIME_ASSERT(sizeof(DebugDir) == 28) + COMPILE_TIME_ASSERT_ALIGNED1(DebugDir) + COMPILE_TIME_ASSERT(sizeof(((DebugDir *) nullptr)->_) == 16) + COMPILE_TIME_ASSERT(sizeof(((DebugDir *) nullptr)->__) == 4) const unsigned skip = IDADDR(PEDIR_DEBUG); const unsigned take = IDSIZE(PEDIR_DEBUG); - const debug_dir_t *dd = (const debug_dir_t *) ibuf.subref("bad debug %#x", skip, take); - for (unsigned ic = 0; ic < IDSIZE(PEDIR_DEBUG) / sizeof(debug_dir_t); ic++, dd++) + const DebugDir *dd = (const DebugDir *) ibuf.subref("bad debug %#x", skip, take); + for (unsigned ic = 0; ic < IDSIZE(PEDIR_DEBUG) / sizeof(DebugDir); ic++, dd++) if (overlaystart == dd->fpos) overlaystart += dd->size; ibuf.fill(IDADDR(PEDIR_DEBUG), IDSIZE(PEDIR_DEBUG), FILLVAL); @@ -2770,7 +2771,7 @@ struct VPtr final { // "virtual pointer" pointing before a buffer SPAN_S(T) base; size_t x; // return base + (n - x) - auto operator+(size_t n) const { return base + mem_size_get_n(sizeof(T), n - x); } + SPAN_S(T) operator+(size_t n) const { return base + mem_size_get_n(sizeof(T), n - x); } }; } // namespace diff --git a/src/util/windows_lean.h b/src/util/windows_lean.h index 93d885e8..ffc4600e 100644 --- a/src/util/windows_lean.h +++ b/src/util/windows_lean.h @@ -26,9 +26,13 @@ #pragma once +// UPX only uses the very basic Windows API #if !defined(WIN32_LEAN_AND_MEAN) #define WIN32_LEAN_AND_MEAN 1 #endif +#if !defined(_WIN32_WINNT) +#define _WIN32_WINNT 0x0400 // _WIN32_WINNT_NT4 aka Windows NT 4 +#endif #if (defined(_MSC_VER) && (_MSC_VER >= 1000 && _MSC_VER < 1200)) && !defined(__clang__) /* avoid -W4 warnings in */