1
0
mirror of https://github.com/upx/upx synced 2025-09-28 19:06:07 +08:00

CI updates and cleanups

This commit is contained in:
Markus F.X.J. Oberhumer 2023-10-29 15:12:33 +01:00
parent 6dac3dd248
commit 4a24fe8c53
12 changed files with 183 additions and 143 deletions

View File

@ -13,8 +13,8 @@ env:
CTEST_OUTPUT_ON_FAILURE: "ON" CTEST_OUTPUT_ON_FAILURE: "ON"
DEBIAN_FRONTEND: noninteractive DEBIAN_FRONTEND: noninteractive
UPX_CMAKE_BUILD_FLAGS: --verbose UPX_CMAKE_BUILD_FLAGS: --verbose
# 2023-10-27 # 2023-10-29
ZIG_DIST_VERSION: 0.12.0-dev.1297+a9e66ed73 ZIG_DIST_VERSION: 0.12.0-dev.1327+256ab68a9
jobs: jobs:
job-rebuild-and-verify-stubs: job-rebuild-and-verify-stubs:
@ -317,7 +317,8 @@ jobs:
where cl & where link where cl & where link
set RUN_CL=cl ${{ matrix.cl_machine_flags }} -MT set RUN_CL=cl ${{ matrix.cl_machine_flags }} -MT
set RUN_LIB=link -lib ${{ matrix.link_machine_flags }} 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% set BDIR=%H%\build\%C%\%B%
git rev-parse --short=12 HEAD > %BDIR%\upx\.GITREV.txt git rev-parse --short=12 HEAD > %BDIR%\upx\.GITREV.txt
@REM ===== build bzip2 ===== @REM ===== build bzip2 =====

View File

@ -27,9 +27,9 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
with: { submodules: true } with: { submodules: true }
- { name: 'Config cmake Xcode', run: 'cmake -S . -B build/xcode -G Xcode' } - { 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 default', run: 'cmake --build build/xcode --parallel --verbose' }
- { name: 'Build cmake Xcode Debug', run: 'cmake --build build/xcode --config Debug' } - { name: 'Build cmake Xcode Debug', run: 'cmake --build build/xcode --parallel --verbose --config Debug' }
- { name: 'Build cmake Xcode Release', run: 'cmake --build build/xcode --config Release' } - { name: 'Build cmake Xcode Release', run: 'cmake --build build/xcode --parallel --verbose --config Release' }
- name: 'Make artifact' - name: 'Make artifact'
run: | run: |
N=$(echo "upx-${GITHUB_REF_NAME}-${GITHUB_SHA:0:7}-weekly-ci-xcode-${{ matrix.os }}" | sed 's/[^0-9a-zA-Z_.-]/-/g') N=$(echo "upx-${GITHUB_REF_NAME}-${GITHUB_SHA:0:7}-weekly-ci-xcode-${{ matrix.os }}" | sed 's/[^0-9a-zA-Z_.-]/-/g')

View File

@ -11,6 +11,7 @@ env:
CMAKE_REQUIRED_QUIET: "OFF" CMAKE_REQUIRED_QUIET: "OFF"
CTEST_OUTPUT_ON_FAILURE: "ON" CTEST_OUTPUT_ON_FAILURE: "ON"
DEBIAN_FRONTEND: noninteractive DEBIAN_FRONTEND: noninteractive
UPX_CMAKE_CONFIG_FLAGS: -DCMAKE_VERBOSE_MAKEFILE=ON
jobs: jobs:
job-llvm-mingw: # uses cmake + make job-llvm-mingw: # uses cmake + make
@ -51,18 +52,22 @@ jobs:
- name: 'Build clang aarch64' - name: 'Build clang aarch64'
run: | run: |
export CC="aarch64-w64-mingw32-clang -static" CXX="aarch64-w64-mingw32-clang++ -static" 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 make UPX_XTARGET=aarch64-w64-mingw32-clang xtarget/debug xtarget/release
- name: 'Build clang armv7' - name: 'Build clang armv7'
run: | run: |
export CC="armv7-w64-mingw32-clang -static" CXX="armv7-w64-mingw32-clang++ -static" 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 make UPX_XTARGET=armv7-w64-mingw32-clang xtarget/debug xtarget/release
- name: 'Build clang i686' - name: 'Build clang i686'
run: | run: |
export CC="i686-w64-mingw32-clang -static" CXX="i686-w64-mingw32-clang++ -static" 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 make UPX_XTARGET=i686-w64-mingw32-clang xtarget/debug xtarget/release
- name: 'Build clang x86_64' - name: 'Build clang x86_64'
run: | run: |
export CC="x86_64-w64-mingw32-clang -static" CXX="x86_64-w64-mingw32-clang++ -static" 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 make UPX_XTARGET=x86_64-w64-mingw32-clang xtarget/debug xtarget/release
- name: 'Make artifact' - name: 'Make artifact'
run: | run: |

View File

@ -11,8 +11,8 @@ env:
CMAKE_REQUIRED_QUIET: "OFF" CMAKE_REQUIRED_QUIET: "OFF"
CTEST_OUTPUT_ON_FAILURE: "ON" CTEST_OUTPUT_ON_FAILURE: "ON"
DEBIAN_FRONTEND: noninteractive DEBIAN_FRONTEND: noninteractive
# 2023-10-27 # 2023-10-29
ZIG_DIST_VERSION: 0.12.0-dev.1297+a9e66ed73 ZIG_DIST_VERSION: 0.12.0-dev.1327+256ab68a9
jobs: jobs:
job-linux-zigcc: # uses cmake + make job-linux-zigcc: # uses cmake + make

View File

@ -11,9 +11,23 @@
if(DEFINED UPX_CONFIG_CMAKE_MINIMUM_REQUIRED_VERSION) if(DEFINED UPX_CONFIG_CMAKE_MINIMUM_REQUIRED_VERSION)
cmake_minimum_required(VERSION "${UPX_CONFIG_CMAKE_MINIMUM_REQUIRED_VERSION}" FATAL_ERROR) cmake_minimum_required(VERSION "${UPX_CONFIG_CMAKE_MINIMUM_REQUIRED_VERSION}" FATAL_ERROR)
else() 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() endif()
# Sections of this CMakeLists.txt:
# - options
# - init
# - common compilation flags
# - targets
# - target compilation flags
# - test
# - install
# - print summary
#***********************************************************************
# options
#***********************************************************************
# compilation config options # compilation config options
if(NOT IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/.git") if(NOT IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/.git")
# permissive config defaults when building from source code tarball # permissive config defaults when building from source code tarball
@ -281,12 +295,23 @@ if(NOT CMAKE_C_COMPILER_ID MATCHES "^MSVC")
endif() endif()
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 $<$<CONFIG:Debug>:-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) function(upx_compile_source_debug_with_O2)
set(flags "$<$<CONFIG:Debug>:-O2>") set(flags "$<$<CONFIG:Debug>:-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 # 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() return()
endif() endif()
set(flags "-O2") set(flags "-O2")
@ -309,17 +334,6 @@ function(upx_compile_source_debug_with_O2)
endforeach() endforeach()
endfunction() 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 $<$<CONFIG:Debug>:-O2>)
endif()
endforeach()
endfunction()
# sanitize a target # sanitize a target
function(upx_sanitize_target) function(upx_sanitize_target)
foreach(t ${ARGV}) foreach(t ${ARGV})
@ -484,7 +498,7 @@ if(HAVE_UTIMENSAT)
target_compile_definitions(${t} PRIVATE HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC=1) target_compile_definitions(${t} PRIVATE HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC=1)
endif() endif()
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/compress/compress_lzma.cpp)
upx_compile_source_debug_with_O2(src/filter/filter_impl.cpp) upx_compile_source_debug_with_O2(src/filter/filter_impl.cpp)
#upx_compile_target_debug_with_O2(${t}) #upx_compile_target_debug_with_O2(${t})
@ -496,9 +510,10 @@ else()
endif() endif()
#*********************************************************************** #***********************************************************************
# ctest # test
# make test # ctest
# ninja test # make test
# ninja test
#*********************************************************************** #***********************************************************************
if(NOT UPX_CONFIG_CMAKE_DISABLE_TEST) if(NOT UPX_CONFIG_CMAKE_DISABLE_TEST)
@ -536,9 +551,10 @@ endif()
endif() # UPX_CONFIG_CMAKE_DISABLE_TEST endif() # UPX_CONFIG_CMAKE_DISABLE_TEST
#*********************************************************************** #***********************************************************************
# cmake --install . # install
# make install # cmake --install .
# ninja install # make install
# ninja install
#*********************************************************************** #***********************************************************************
if(NOT UPX_CONFIG_CMAKE_DISABLE_INSTALL) if(NOT UPX_CONFIG_CMAKE_DISABLE_INSTALL)
@ -563,13 +579,16 @@ endif()
endif() # UPX_CONFIG_CMAKE_DISABLE_INSTALL 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) function(print_var)
foreach(var ${ARGV}) foreach(var ${ARGV})
if(${var}) if(DEFINED ${var} AND NOT ",${${var}}," STREQUAL ",,")
message(STATUS "${var} = ${${var}}") if(${var})
message(STATUS "${var} = ${${var}}")
endif()
endif() endif()
endforeach() endforeach()
endfunction() endfunction()

View File

@ -56,7 +56,11 @@ build/%/all: $$(dir $$@)debug $$(dir $$@)release ;
# #
# extra pre-defined build configurations and some utility; optional # 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 include ./misc/make/Makefile-extra.mk
endif
# developer convenience # developer convenience
ifneq ($(wildcard /usr/bin/env),) # needs bash, perl, xargs, etc. ifneq ($(wildcard /usr/bin/env),) # needs bash, perl, xargs, etc.

View File

@ -3,6 +3,9 @@
# Copyright (C) Markus Franz Xaver Johannes Oberhumer # 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 # 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) # 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/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/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 CC = i686-w64-mingw32-gcc -static -D_WIN32_WINNT=0x0400
build/extra/cross-windows-mingw32/%: export CXX = i686-w64-mingw32-g++ -static build/extra/cross-windows-mingw32/%: export CXX = i686-w64-mingw32-g++ -static -D_WIN32_WINNT=0x0400
# cross compiler: Windows x64 win64 MinGW (amd64) # 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/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/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 CC = x86_64-w64-mingw32-gcc -static -D_WIN32_WINNT=0x0400
build/extra/cross-windows-mingw64/%: export CXX = x86_64-w64-mingw32-g++ -static build/extra/cross-windows-mingw64/%: export CXX = x86_64-w64-mingw32-g++ -static -D_WIN32_WINNT=0x0400
# cross compiler: macOS arm64 (aarch64) # cross compiler: macOS arm64 (aarch64)
build/extra/cross-darwin-arm64/debug: PHONY; $(call run_config_and_build,$@,Debug) 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,\ dummy := $(foreach m,$(SUBMODULES),$(if $(wildcard vendor/$m/[CL]*),$m,\
$(error ERROR: missing git submodule '$m'; run 'git submodule update --init'))) $(error ERROR: missing git submodule '$m'; run 'git submodule update --init')))
endif # UPX_MAKEFILE_EXTRA_MK_INCLUDED

View File

@ -106,7 +106,8 @@ TEST_CASE("noncopyable") {
} }
/************************************************************************* /*************************************************************************
// TriBool // TriBool checks
// (modern compilers will optimize away most of this code)
**************************************************************************/ **************************************************************************/
namespace { namespace {

View File

@ -855,8 +855,8 @@ int main_get_options(int argc, char **argv) {
{"fake-stub-version", 0x31, N, 542}, // for internal debugging {"fake-stub-version", 0x31, N, 542}, // for internal debugging
{"fake-stub-year", 0x31, N, 543}, // for internal debugging {"fake-stub-year", 0x31, N, 543}, // for internal debugging
{"disable-random-id", 0x90, N, 545}, // 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-method", 0x90, N, 546}, // for internal debugging / fuzz testing
{"debug-use-random-filter", 0x90, N, 547}, // for internal debugging {"debug-use-random-filter", 0x90, N, 547}, // for internal debugging / fuzz testing
// backup options // backup options
{"backup", 0x10, N, 'k'}, {"backup", 0x10, N, 'k'},

View File

@ -54,10 +54,12 @@ enum {
}; };
struct Options final { struct Options final {
int cmd; void reset() noexcept;
int cmd; // CMD_xxx
// compression options // compression options
int method; int method; // M_xxx
bool method_lzma_seen; bool method_lzma_seen;
bool method_nrv2b_seen; bool method_nrv2b_seen;
bool method_nrv2d_seen; bool method_nrv2d_seen;
@ -90,6 +92,20 @@ struct Options final {
int verbose; int verbose;
bool to_stdout; 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 // debug options
struct { struct {
int debug_level; int debug_level;
@ -98,14 +114,10 @@ struct Options final {
char fake_stub_version[4 + 1]; // for internal debugging char fake_stub_version[4 + 1]; // for internal debugging
char fake_stub_year[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 getopt_throw_instead_of_exit; // for internal doctest checks
bool use_random_method; // for internal debugging bool use_random_method; // for internal debugging / fuzz testing
bool use_random_filter; // for internal debugging bool use_random_filter; // for internal debugging / fuzz testing
} debug; } debug;
// overlay handling
enum { SKIP_OVERLAY = 0, COPY_OVERLAY = 1, STRIP_OVERLAY = 2 };
int overlay;
// CRP - Compression Runtime Parameters (undocumented and subject to change) // CRP - Compression Runtime Parameters (undocumented and subject to change)
struct { struct {
lzma_compress_config_t crp_lzma; lzma_compress_config_t crp_lzma;
@ -120,16 +132,6 @@ struct Options final {
} }
} crp; } 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 // options for various executable formats
struct { struct {
bool split_segments; bool split_segments;
@ -173,8 +175,6 @@ struct Options final {
int strip_relocs; int strip_relocs;
const char *keep_resource; const char *keep_resource;
} win32_pe; } win32_pe;
void reset() noexcept;
}; };
/* vim:set ts=4 sw=4 et: */ /* vim:set ts=4 sw=4 et: */

View File

@ -49,8 +49,8 @@ static void xcheck(const void *p) {
throwCantUnpack("xcheck unexpected nullptr pointer; take care!"); throwCantUnpack("xcheck unexpected nullptr pointer; take care!");
} }
static void xcheck(const void *p, size_t plen, const void *b, size_t blen) { static void xcheck(const void *p, size_t plen, const void *b, size_t blen) {
const char *pp = (const char *) p; const charptr pp = (const charptr) p;
const char *bb = (const char *) b; const charptr bb = (const charptr) b;
if very_unlikely (pp < bb || pp > bb + blen || pp + plen > bb + blen) if very_unlikely (pp < bb || pp > bb + blen || pp + plen > bb + blen)
throwCantUnpack("xcheck pointer out of range; take care!"); throwCantUnpack("xcheck pointer out of range; take care!");
} }
@ -154,7 +154,7 @@ bool PeFile::testUnpackVersion(int version) const {
} }
int PeFile::readFileHeader() { int PeFile::readFileHeader() {
struct alignas(1) exe_header_t { struct alignas(1) ExeHeader final {
LE16 mz; LE16 mz;
LE16 m512; LE16 m512;
LE16 p512; LE16 p512;
@ -164,12 +164,12 @@ int PeFile::readFileHeader() {
LE32 nexepos; LE32 nexepos;
}; };
COMPILE_TIME_ASSERT(sizeof(exe_header_t) == 64) COMPILE_TIME_ASSERT(sizeof(ExeHeader) == 64)
COMPILE_TIME_ASSERT_ALIGNED1(exe_header_t) COMPILE_TIME_ASSERT_ALIGNED1(ExeHeader)
COMPILE_TIME_ASSERT(sizeof(((exe_header_t *) nullptr)->_) == 18) COMPILE_TIME_ASSERT(sizeof(((ExeHeader *) nullptr)->_) == 18)
COMPILE_TIME_ASSERT(sizeof(((exe_header_t *) nullptr)->__) == 34) COMPILE_TIME_ASSERT(sizeof(((ExeHeader *) nullptr)->__) == 34)
exe_header_t h; ExeHeader h;
int ic; int ic;
pe_offset = 0; pe_offset = 0;
@ -179,11 +179,11 @@ int PeFile::readFileHeader() {
if (h.mz == 'M' + 'Z' * 256) // dos exe 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. // Overlapping MZ and PE headers by 'leanify', etc.
char buf[64]; char buf[64];
snprintf(buf, sizeof(buf), "PE and MZ header overlap: %#x < %#x", 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); throwCantPack(buf);
} }
const unsigned delta = (h.relocoffs >= 0x40) const unsigned delta = (h.relocoffs >= 0x40)
@ -283,11 +283,11 @@ void PeFile::Interval::dump() const {
**************************************************************************/ **************************************************************************/
namespace { namespace {
struct FixDeleter { // don't leak memory on exceptions struct FixDeleter final { // helper so we don't leak memory on exceptions
LE32 **fix; LE32 **fix;
size_t n; size_t count;
~FixDeleter() noexcept { ~FixDeleter() noexcept {
for (size_t i = 0; i < n; i++) { for (size_t i = 0; i < count; i++) {
delete[] fix[i]; delete[] fix[i];
fix[i] = nullptr; fix[i] = nullptr;
} }
@ -492,7 +492,7 @@ void PeFile32::processRelocs() // pass1
FixDeleter fixdel{fix, 0}; // don't leak memory FixDeleter fixdel{fix, 0}; // don't leak memory
for (ic = 0; ic < 4; ic++) { for (ic = 0; ic < 4; ic++) {
fix[ic] = New(LE32, counts[ic]); fix[ic] = New(LE32, counts[ic]);
fixdel.n += 1; fixdel.count += 1;
} }
unsigned xcounts[4]; unsigned xcounts[4];
@ -594,7 +594,7 @@ void PeFile64::processRelocs() // pass1
FixDeleter fixdel{fix, 0}; // don't leak memory FixDeleter fixdel{fix, 0}; // don't leak memory
for (ic = 0; ic < 16; ic++) { for (ic = 0; ic < 16; ic++) {
fix[ic] = New(LE32, counts[ic]); fix[ic] = New(LE32, counts[ic]);
fixdel.n += 1; fixdel.count += 1;
} }
unsigned xcounts[16]; unsigned xcounts[16];
@ -686,16 +686,18 @@ const LE32 &PeFile::IDADDR(unsigned x) const { return iddirs[x].vaddr; }
*/ */
class PeFile::ImportLinker final : public ElfLinkerAMD64 { class PeFile::ImportLinker final : public ElfLinkerAMD64 {
struct tstr : private ::noncopyable { struct TStr final : private ::noncopyable { // temporary string owner, deletes on destruction
char *s = nullptr; explicit TStr(char *str) noexcept : s(str) {}
explicit tstr(char *str) : s(str) {} ~TStr() noexcept { delete[] s; } // delete!
~tstr() noexcept { delete[] s; } operator char *() noexcept { return s; }
operator char *() const { return s; } operator const char *() const noexcept { return s; }
private:
char *s;
}; };
// encoding of dll and proc names are required, so that our special // encoding of dll and proc names are required, so that our special
// control characters in the name of sections can work as intended // 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) { while (*name) {
*buf++ = 'a' + ((*name >> 4) & 0xf); *buf++ = 'a' + ((*name >> 4) & 0xf);
*buf++ = 'a' + (*name & 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) { static char *name_for_dll(const char *dll, char first_char) {
assert(dll); assert(dll != nullptr);
unsigned l = strlen(dll); const unsigned l = strlen(dll);
assert(l > 0); assert(l > 0);
const unsigned new_size = 1 + 3 * l + 1;
char *name = New(char, 3 * l + 2); char *const new_name = New(char, new_size);
SPAN_S_VAR(char, const name, new_name, new_size);
name[0] = first_char; name[0] = first_char;
char *n = name + 1 + 2 * l; SPAN_S_VAR(char, n, name + (1 + 2 * l));
do { do {
*n++ = tolower((uchar) *dll); *n++ = tolower((uchar) *dll);
} while (*dll++); } while (*dll++);
encode_name(name + 1 + 2 * l, name + 1); encode_name(new_name + (1 + 2 * l), name + 1);
return name; return new_name;
} }
static char *name_for_proc(const char *dll, const char *proc, char first_char, char separator) { 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; const unsigned new_size = 1 + 2 * strlen(dll) + 1 + 2 * strlen(proc) + 1 + 1;
tstr dlln(name_for_dll(dll, first_char)); TStr dll_name(name_for_dll(dll, first_char));
char *procn = New(char, len); char *const new_name = New(char, new_size);
upx_safe_snprintf(procn, len, "%s%c", (const char *) dlln, separator); SPAN_S_VAR(char, const name, new_name, new_size);
encode_name(proc, procn + strlen(procn)); upx_safe_snprintf(new_name, new_size, "%s%c", (const char *) dll_name, separator);
return procn; encode_name(proc, name + strlen(name));
return new_name;
} }
static const char zeros[sizeof(import_desc)]; static const char zeros[sizeof(import_desc)];
@ -747,8 +751,8 @@ class PeFile::ImportLinker final : public ElfLinkerAMD64 {
unsigned thunk_size; // 4 or 8 bytes unsigned thunk_size; // 4 or 8 bytes
void add(const char *dll, const char *proc, unsigned ordinal) { void add(const char *dll, const char *proc, unsigned ordinal) {
tstr sdll(name_for_dll(dll, dll_name_id)); TStr sdll(name_for_dll(dll, dll_name_id));
tstr desc_name(name_for_dll(dll, descriptor_id)); TStr desc_name(name_for_dll(dll, descriptor_id));
char tsep = thunk_separator; char tsep = thunk_separator;
if (findSection(sdll, false) == nullptr) { if (findSection(sdll, false) == nullptr) {
@ -759,7 +763,7 @@ class PeFile::ImportLinker final : public ElfLinkerAMD64 {
addSection(desc_name, zeros, sizeof(zeros), 0); // descriptor addSection(desc_name, zeros, sizeof(zeros), 0); // descriptor
addRelocation(desc_name, offsetof(import_desc, dllname), "R_X86_64_32", sdll, 0); 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)); : name_for_proc(dll, proc, thunk_id, tsep));
if (findSection(thunk, false) != nullptr) if (findSection(thunk, false) != nullptr)
@ -769,7 +773,7 @@ class PeFile::ImportLinker final : public ElfLinkerAMD64 {
if (tsep == thunk_separator_first) { if (tsep == thunk_separator_first) {
addRelocation(desc_name, offsetof(import_desc, iat), "R_X86_64_32", thunk, 0); 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); addSection(last_thunk, zeros, thunk_size, 0);
} }
@ -777,7 +781,7 @@ class PeFile::ImportLinker final : public ElfLinkerAMD64 {
if (ordinal != 0u) { if (ordinal != 0u) {
addRelocation(thunk, 0, reltype, "*UND*", ordinal | (1ull << (thunk_size * 8 - 1))); addRelocation(thunk, 0, reltype, "*UND*", ordinal | (1ull << (thunk_size * 8 - 1)));
} else if (proc != nullptr) { } 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" addSection(proc_name, zeros, 2, 1); // 2 bytes of word aligned "hint"
addSymbol(proc_name, proc_name, 0); addSymbol(proc_name, proc_name, 0);
addRelocation(thunk, 0, reltype, 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 { const Section *getThunk(const char *dll, const char *proc, char tsep) const {
assert(dll); assert(dll);
assert(proc); 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); return findSection(thunk, false);
} }
@ -887,7 +891,6 @@ public:
assert(ordinal > 0 && ordinal < 0x10000); assert(ordinal > 0 && ordinal < 0x10000);
char ord[1 + 5 + 1]; char ord[1 + 5 + 1];
upx_safe_snprintf(ord, sizeof(ord), "%c%05u", ordinal_id, ordinal); upx_safe_snprintf(ord, sizeof(ord), "%c%05u", ordinal_id, ordinal);
const Section *s = getThunk((const char *) dll, ord, thunk_separator_first); const Section *s = getThunk((const char *) dll, ord, thunk_separator_first);
if (s == nullptr && (s = getThunk((const char *) dll, ord, thunk_separator)) == nullptr) if (s == nullptr && (s = getThunk((const char *) dll, ord, thunk_separator)) == nullptr)
throwInternalError("entry not found"); throwInternalError("entry not found");
@ -897,18 +900,18 @@ public:
template <typename C> template <typename C>
upx_uint64_t getAddress(const C *dll) const { upx_uint64_t getAddress(const C *dll) const {
ACC_COMPILE_TIME_ASSERT(sizeof(C) == 1) // "char" or "byte" 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; return findSection(sdll, true)->offset;
} }
template <typename C> template <typename C>
upx_uint64_t hasDll(const C *dll) const { upx_uint64_t hasDll(const C *dll) const {
ACC_COMPILE_TIME_ASSERT(sizeof(C) == 1) // "char" or "byte" 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; 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); } 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 void PeFile::processImports2(unsigned myimport, unsigned) // pass 2
{ {
COMPILE_TIME_ASSERT(sizeof(import_desc) == 20) COMPILE_TIME_ASSERT(sizeof(import_desc) == 20)
if (ilinker == nullptr)
if (!ilinker)
return; return;
ilinker->relocate_import(myimport); ilinker->relocate_import(myimport);
int len; int len;
oimpdlls = ilinker->getLoader(&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 if (dllnum > 4096) // just some arbitrary limit/sanity check
throwCantPack("too many DLL imports %u", dllnum); throwCantPack("too many DLL imports %u", dllnum);
struct udll { struct UDll final {
const byte *name; const byte *name;
const byte *shname; const byte *shname;
unsigned ordinal; unsigned ordinal;
@ -968,8 +969,8 @@ unsigned PeFile::processImports0(ord_mask_t ord_mask) // pass 1
bool isk32; bool isk32;
static int __acc_cdecl_qsort compare(const void *aa, const void *bb) { static int __acc_cdecl_qsort compare(const void *aa, const void *bb) {
const udll *a = *(const udll *const *) aa; const UDll *a = *(const UDll *const *) aa;
const udll *b = *(const udll *const *) bb; const UDll *b = *(const UDll *const *) bb;
if (a->original_position == b->original_position) // identical object, poor qsort() if (a->original_position == b->original_position) // identical object, poor qsort()
return 0; return 0;
if (a->isk32 != b->isk32) if (a->isk32 != b->isk32)
@ -997,8 +998,8 @@ unsigned PeFile::processImports0(ord_mask_t ord_mask) // pass 1
}; };
// +1 for dllnum=0 // +1 for dllnum=0
Array(struct udll, dlls, dllnum + 1); Array(UDll, dlls, dllnum + 1);
Array(struct udll *, idlls, dllnum + 1); Array(UDll *, idlls, dllnum + 1);
soimport = 1024; // safety soimport = 1024; // safety
@ -1036,7 +1037,7 @@ unsigned PeFile::processImports0(ord_mask_t ord_mask) // pass 1
mb_oimport.clear(); mb_oimport.clear();
oimport = mb_oimport; 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); info("Processing imports: %d DLLs", dllnum);
for (unsigned ic = 0; ic < dllnum; ic++) { for (unsigned ic = 0; ic < dllnum; ic++) {
@ -1081,7 +1082,7 @@ unsigned PeFile::processImports0(ord_mask_t ord_mask) // pass 1
ppi += 8; ppi += 8;
for (; *tarr; tarr++) for (; *tarr; tarr++)
if (*tarr & ord_mask) { if (*tarr & ord_mask) {
unsigned ord = *tarr & 0xffff; const unsigned ord = *tarr & 0xffff;
if (idlls[ic]->isk32 && kernel32ordinal) { if (idlls[ic]->isk32 && kernel32ordinal) {
*ppi++ = 0xfe; // signed + odd parity *ppi++ = 0xfe; // signed + odd parity
set_le32(ppi, ilinker->getAddress(idlls[ic]->name, ord)); set_le32(ppi, ilinker->getAddress(idlls[ic]->name, ord));
@ -1101,7 +1102,7 @@ unsigned PeFile::processImports0(ord_mask_t ord_mask) // pass 1
} }
ppi++; 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); lookups.add(idlls[ic]->lookupt, esize);
if (ptr_diff_bytes(ibuf.subref("bad import name %#x", idlls[ic]->iat, 1), if (ptr_diff_bytes(ibuf.subref("bad import name %#x", idlls[ic]->iat, 1),
idlls[ic]->lookupt) != 0) { idlls[ic]->lookupt) != 0) {
@ -1311,13 +1312,13 @@ void PeFile::processExports(Export *xport, unsigned newoffs) // pass2
// of course it was impossible to debug this ;-) // of course it was impossible to debug this ;-)
template <> template <>
struct PeFile::tls_traits<LE32> { struct PeFile::tls_traits<LE32> final {
struct alignas(1) tls { struct alignas(1) tls {
LE32 datastart; // VA tls init data start LE32 datastart; // VA tls init data start
LE32 dataend; // VA tls init data end LE32 dataend; // VA tls init data end
LE32 tlsindex; // VA tls index LE32 tlsindex; // VA tls index
LE32 callbacks; // VA tls callbacks LE32 callbacks; // VA tls callbacks
char _[8]; // zero init, characteristics byte _[8]; // zero init, characteristics
}; };
static const unsigned sotls = 24; static const unsigned sotls = 24;
@ -1328,13 +1329,13 @@ struct PeFile::tls_traits<LE32> {
}; };
template <> template <>
struct PeFile::tls_traits<LE64> { struct PeFile::tls_traits<LE64> final {
struct alignas(1) tls { struct alignas(1) tls {
LE64 datastart; // VA tls init data start LE64 datastart; // VA tls init data start
LE64 dataend; // VA tls init data end LE64 dataend; // VA tls init data end
LE64 tlsindex; // VA tls index LE64 tlsindex; // VA tls index
LE64 callbacks; // VA tls callbacks LE64 callbacks; // VA tls callbacks
char _[8]; // zero init, characteristics byte _[8]; // zero init, characteristics
}; };
static const unsigned sotls = 40; static const unsigned sotls = 40;
@ -1545,7 +1546,7 @@ struct alignas(1) PeFile::Resource::res_dir_entry {
}; };
struct alignas(1) PeFile::Resource::res_dir { struct alignas(1) PeFile::Resource::res_dir {
char _[12]; // flags, timedate, version byte _[12]; // flags, timedate, version
LE16 namedentr; LE16 namedentr;
LE16 identr; LE16 identr;
@ -1558,7 +1559,7 @@ struct alignas(1) PeFile::Resource::res_dir {
struct alignas(1) PeFile::Resource::res_data { struct alignas(1) PeFile::Resource::res_data {
LE32 offset; LE32 offset;
LE32 size; LE32 size;
char _[8]; // codepage, reserved byte _[8]; // codepage, reserved
}; };
struct PeFile::Resource::upx_rnode { 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 // typex and namex can be string or number
// hopefully resource names do not have '/' or ',' characters inside // 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) { static bool match(unsigned num, const byte *unistr, const char *mkeep) {
if (!unistr) if (!unistr)
return (unsigned) atoi(mkeep) == num; return (unsigned) atoi(mkeep) == num;
unsigned ic; unsigned ic;
for (ic = 0; ic < get_le16(unistr); ic++) for (ic = 0; ic < get_le16(unistr); ic++)
if (unistr[2 + ic * 2] != (byte) mkeep[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 // FIXME this comparison is not too exact
for (;;) { for (;;) {
char const *delim1 = strchr(keep, '/'); const char *delim1 = strchr(keep, '/');
char const *delim2 = strchr(keep, ','); const char *delim2 = strchr(keep, ',');
if (helper::match(itype, ntype, keep)) { if (Helper::match(itype, ntype, keep)) {
if (!delim1) if (!delim1)
return true; return true;
if (delim2 && delim2 < delim1) if (delim2 && delim2 < delim1)
return true; return true;
if (helper::match(iname, nname, delim1 + 1)) if (Helper::match(iname, nname, delim1 + 1))
return true; return true;
} }
if (delim2 == nullptr) if (delim2 == nullptr)
break; break;
keep = delim2 + 1; keep = delim2 + 1;
@ -1967,13 +1966,15 @@ void PeFile::processResources(Resource *res) {
else if (rtype > 0 && rtype < RT_LAST) else if (rtype > 0 && rtype < RT_LAST)
do_compress = opt->win32_pe.compress_rt[rtype] ? true : false; do_compress = opt->win32_pe.compress_rt[rtype] ? true : false;
if (keep_icons) if (do_compress && keep_icons)
do_compress &= do_compress &=
!match(res->itype(), res->ntype(), res->iname(), res->nname(), keep_icons); !match(res->itype(), res->ntype(), res->iname(), res->nname(), keep_icons);
do_compress &= if (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(),
do_compress &= !match(res->itype(), res->ntype(), res->iname(), res->nname(), "TYPELIB,REGISTRY,16");
opt->win32_pe.keep_resource); if (do_compress)
do_compress &= !match(res->itype(), res->ntype(), res->iname(), res->nname(),
opt->win32_pe.keep_resource);
if (do_compress) { if (do_compress) {
csize += res->size(); csize += res->size();
@ -2042,22 +2043,22 @@ unsigned PeFile::stripDebug(unsigned overlaystart) {
if (IDADDR(PEDIR_DEBUG) == 0) if (IDADDR(PEDIR_DEBUG) == 0)
return overlaystart; return overlaystart;
struct alignas(1) debug_dir_t { struct alignas(1) DebugDir final {
char _[16]; // flags, time/date, version, type byte _[16]; // flags, time/date, version, type
LE32 size; LE32 size;
char __[4]; // rva byte __[4]; // rva
LE32 fpos; LE32 fpos;
}; };
COMPILE_TIME_ASSERT(sizeof(debug_dir_t) == 28) COMPILE_TIME_ASSERT(sizeof(DebugDir) == 28)
COMPILE_TIME_ASSERT_ALIGNED1(debug_dir_t) COMPILE_TIME_ASSERT_ALIGNED1(DebugDir)
COMPILE_TIME_ASSERT(sizeof(((debug_dir_t *) nullptr)->_) == 16) COMPILE_TIME_ASSERT(sizeof(((DebugDir *) nullptr)->_) == 16)
COMPILE_TIME_ASSERT(sizeof(((debug_dir_t *) nullptr)->__) == 4) COMPILE_TIME_ASSERT(sizeof(((DebugDir *) nullptr)->__) == 4)
const unsigned skip = IDADDR(PEDIR_DEBUG); const unsigned skip = IDADDR(PEDIR_DEBUG);
const unsigned take = IDSIZE(PEDIR_DEBUG); const unsigned take = IDSIZE(PEDIR_DEBUG);
const debug_dir_t *dd = (const debug_dir_t *) ibuf.subref("bad debug %#x", skip, take); const DebugDir *dd = (const DebugDir *) ibuf.subref("bad debug %#x", skip, take);
for (unsigned ic = 0; ic < IDSIZE(PEDIR_DEBUG) / sizeof(debug_dir_t); ic++, dd++) for (unsigned ic = 0; ic < IDSIZE(PEDIR_DEBUG) / sizeof(DebugDir); ic++, dd++)
if (overlaystart == dd->fpos) if (overlaystart == dd->fpos)
overlaystart += dd->size; overlaystart += dd->size;
ibuf.fill(IDADDR(PEDIR_DEBUG), IDSIZE(PEDIR_DEBUG), FILLVAL); 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; SPAN_S(T) base;
size_t x; size_t x;
// return base + (n - 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 } // namespace

View File

@ -26,9 +26,13 @@
#pragma once #pragma once
// UPX only uses the very basic Windows API
#if !defined(WIN32_LEAN_AND_MEAN) #if !defined(WIN32_LEAN_AND_MEAN)
#define WIN32_LEAN_AND_MEAN 1 #define WIN32_LEAN_AND_MEAN 1
#endif #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__) #if (defined(_MSC_VER) && (_MSC_VER >= 1000 && _MSC_VER < 1200)) && !defined(__clang__)
/* avoid -W4 warnings in <conio.h> */ /* avoid -W4 warnings in <conio.h> */