diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4a768e2e..601d521e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -377,8 +377,8 @@ jobs: - { zig_target: x86_64-macos.13-none } - { zig_target: x86_64-windows-gnu } env: - # 2023-02-19 - ZIG_DIST_VERSION: 0.11.0-dev.1650+5e7b09ce9 + # 2023-03-14 + ZIG_DIST_VERSION: 0.11.0-dev.1970+962299157 # for zig-cc wrapper scripts (see below): ZIG_CPPFLAGS: -DUPX_DOCTEST_CONFIG_MULTITHREADING ZIG_FLAGS: ${{ matrix.zig_flags }} diff --git a/.github/workflows/close-stale-issues.yml b/.github/workflows/close-stale-issues.yml index 639bacf2..4bcf158d 100644 --- a/.github/workflows/close-stale-issues.yml +++ b/.github/workflows/close-stale-issues.yml @@ -27,7 +27,7 @@ jobs: with: operations-per-run: 300 exempt-all-milestones: true - exempt-issue-labels: 'blocker,enhancement,regression' + exempt-issue-labels: 'blocker,enhancement,help wanted,regression' days-before-stale: 60 days-before-close: 30 stale-issue-message: 'This issue is stale because it has been open 60 days with no activity. Please remove the stale label or add a comment or this issue will be closed in 30 days.' diff --git a/CMakeLists.txt b/CMakeLists.txt index 3102402b..6b808241 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.8.0 FATAL_ERROR) +cmake_minimum_required(VERSION 3.8.0 FATAL_ERROR) # CMake >= 3.20.0 is recommended # compilation config options if(NOT IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/.git") @@ -101,9 +101,13 @@ endif() # targets #*********************************************************************** -#find_package(Threads) # multithreading is currently not used; maybe in UPX version 5 +set(UPX_CONFIG_DISABLE_THREADS ON) # multithreading is currently not used; maybe in UPX version 5 set(UPX_CONFIG_DISABLE_ZSTD ON) # zstd is currently not used; maybe in UPX version 5 +if(NOT UPX_CONFIG_DISABLE_THREADS) + find_package(Threads) +endif() + file(GLOB ucl_SOURCES "vendor/ucl/src/*.c") list(SORT ucl_SOURCES) add_library(upx_vendor_ucl STATIC ${ucl_SOURCES}) @@ -129,6 +133,9 @@ target_link_libraries(upx upx_vendor_ucl upx_vendor_zlib) if(NOT UPX_CONFIG_DISABLE_ZSTD) target_link_libraries(upx upx_vendor_zstd) endif() +if(Threads_FOUND) + target_link_libraries(upx Threads::Threads) +endif() #*********************************************************************** # compilation flags @@ -215,10 +222,11 @@ endif() set(t upx_vendor_zlib) upx_compile_target_debug_with_O2(${t}) upx_sanitize_target(${t}) +target_compile_definitions(${t} PRIVATE HAVE_STDARG_H=1 HAVE_VSNPRINTF=1) if(MSVC) - target_compile_options(${t} PRIVATE -DHAVE_STDARG_H -DHAVE_VSNPRINTF -J -W3 ${warn_WX}) + target_compile_options(${t} PRIVATE -J -W3 ${warn_WX}) else() - target_compile_options(${t} PRIVATE -DHAVE_STDARG_H -DHAVE_UNISTD_H -DHAVE_VSNPRINTF) + target_compile_definitions(${t} PRIVATE HAVE_UNISTD_H=1) # clang-15: -Wno-strict-prototypes is needed to silence the new -Wdeprecated-non-prototype warning target_compile_options(${t} PRIVATE -Wall -Wno-strict-prototypes ${warn_Werror}) ##target_compile_options(${t} PRIVATE ${warn_Wall} -Wno-cast-align -Wno-cast-qual -Wno-strict-prototypes ${warn_Werror}) @@ -228,7 +236,7 @@ if(NOT UPX_CONFIG_DISABLE_ZSTD) set(t upx_vendor_zstd) upx_compile_target_debug_with_O2(${t}) upx_sanitize_target(${t}) -target_compile_options(${t} PRIVATE -DDYNAMIC_BMI2=0 -DZSTD_DISABLE_ASM) +target_compile_definitions(${t} PRIVATE DYNAMIC_BMI2=0 ZSTD_DISABLE_ASM=1) if(MSVC) target_compile_options(${t} PRIVATE -J ${warn_WN} ${warn_WX}) else() @@ -245,6 +253,9 @@ if(GITREV_SHORT) target_compile_definitions(${t} PRIVATE UPX_VERSION_GIT_DESCRIBE="${GIT_DESCRIBE}") endif() endif() +if(Threads_FOUND) + target_compile_definitions(${t} PRIVATE WITH_THREADS=1) +endif() if(UPX_CONFIG_DISABLE_WSTRICT) target_compile_definitions(${t} PRIVATE UPX_CONFIG_DISABLE_WSTRICT=1) endif() @@ -263,9 +274,9 @@ else() endif() #*********************************************************************** -# "ctest" -# "make test" -# "ninja test" +# ctest +# make test +# ninja test #*********************************************************************** if(NOT UPX_CONFIG_CMAKE_DISABLE_TEST) @@ -302,9 +313,9 @@ endif() endif() # UPX_CONFIG_CMAKE_DISABLE_TEST #*********************************************************************** -# "cmake --install ." -# "make install" -# "ninja install" +# cmake --install . +# make install +# ninja install #*********************************************************************** if(NOT UPX_CONFIG_CMAKE_DISABLE_INSTALL) @@ -350,12 +361,12 @@ print_var(CMAKE_C_COMPILER_ID CMAKE_C_COMPILER_VERSION CMAKE_C_COMPILER_ARCHITEC print_var(CMAKE_CXX_COMPILER_ID CMAKE_CXX_COMPILER_VERSION CMAKE_CXX_COMPILER_ARCHITECTURE_ID CMAKE_CXX_PLATFORM_ID CMAKE_CXX_COMPILER_ABI) endif() # UPX_CONFIG_CMAKE_DISABLE_PRINT_INFO print_var(CMAKE_INSTALL_PREFIX CMAKE_CONFIGURATION_TYPES CMAKE_BUILD_TYPE) -if(CMAKE_BUILD_TYPE AND NOT CMAKE_BUILD_TYPE MATCHES "^(Debug|Release)$") +if(CMAKE_BUILD_TYPE AND NOT CMAKE_BUILD_TYPE MATCHES "^(Debug|None|Release)$") message(WARNING "WARNING: unsupported CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}; please use \"Debug\" or \"Release\"") endif() -if(NOT UPX_CONFIG_CMAKE_DISABLE_PLATFORM_CHECK) # extra sanity checks to detect incompatible C vs CXX settings +if(NOT UPX_CONFIG_CMAKE_DISABLE_PLATFORM_CHECK) if(NOT ",${CMAKE_C_PLATFORM_ID}," STREQUAL ",${CMAKE_CXX_PLATFORM_ID},") message(FATAL_ERROR "ERROR: CMAKE_C_PLATFORM_ID CMAKE_CXX_PLATFORM_ID mismatch") endif() diff --git a/Makefile b/Makefile index b1e0f866..b1eb7d8f 100644 --- a/Makefile +++ b/Makefile @@ -44,8 +44,9 @@ all: build/debug build/release debug: build/debug release: build/release -.PHONY: PHONY .NOTPARALLEL: # because the actual builds use "cmake --parallel" +.PHONY: PHONY +.SECONDEXPANSION: .SUFFIXES: # END of Makefile; extra stuff follows @@ -114,7 +115,7 @@ build/extra/scan-build/%: CMAKE := scan-build $(CMAKE) build/extra/scan-build/%: export CCC_CC ?= clang build/extra/scan-build/%: export CCC_CXX ?= clang++ -# cross compiler: Linux glibc aarch64-linux-gnu +# cross compiler: Linux glibc aarch64-linux-gnu (arm64) build/extra/cross-linux-aarch64/debug: PHONY; $(call run_config_and_build,$@,Debug) build/extra/cross-linux-aarch64/release: PHONY; $(call run_config_and_build,$@,Release) build/extra/cross-linux-aarch64/%: export CC = aarch64-linux-gnu-gcc @@ -126,13 +127,13 @@ build/extra/cross-linux-arm/release: PHONY; $(call run_config_and_build,$@,Relea build/extra/cross-linux-arm/%: export CC = arm-linux-gnueabihf-gcc build/extra/cross-linux-arm/%: export CXX = arm-linux-gnueabihf-g++ -Wno-psabi -# cross compiler: Windows x86 win32 MinGW +# 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 -# cross compiler: Windows x64 win64 MinGW +# 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 @@ -144,7 +145,7 @@ build/extra/cross-darwin-arm64/release: PHONY; $(call run_config_and_build,$@,Re build/extra/cross-darwin-arm64/%: export CC = clang -target arm64-apple-darwin build/extra/cross-darwin-arm64/%: export CXX = clang++ -target arm64-apple-darwin -# cross compiler: macOS x86_64 +# cross compiler: macOS x86_64 (amd64) build/extra/cross-darwin-x86_64/debug: PHONY; $(call run_config_and_build,$@,Debug) build/extra/cross-darwin-x86_64/release: PHONY; $(call run_config_and_build,$@,Release) build/extra/cross-darwin-x86_64/%: export CC = clang -target x86_64-apple-darwin diff --git a/THANKS b/THANKS index cee1388b..986715ee 100644 --- a/THANKS +++ b/THANKS @@ -41,12 +41,14 @@ Joergen Ibsen and d'b for the relocation & address optimization ideas John S. Fine for the new version of the dos/exe decompressor +Kornel Pal + for the EFI support Lukundoo for beta testing Michael Devore for initial dos/exe device driver support Oleg V. Volkov - for various FreeBSD specific informations + for various FreeBSD specific information The Owl & G-RoM for the --compress-icons fix Ralph Roth diff --git a/doc/upx-doc.html b/doc/upx-doc.html index b817ab99..b29aaff5 100644 --- a/doc/upx-doc.html +++ b/doc/upx-doc.html @@ -160,7 +160,7 @@ Copyright (c) 1996-2023 Markus Oberhumer, Laszlo Molnar & John Reiser

Info: An "overlay" means auxiliary data attached after the logical end of an executable, and it often contains application specific data (this is a common practice to avoid an extra data file, though it would be better to use resource sections).

-

UPX handles overlays like many other executable packers do: it simply copies the overlay after the compressed image. This works with some files, but doesn't work with others, depending on how an application actually accesses this overlayed data.

+

UPX handles overlays like many other executable packers do: it simply copies the overlay after the compressed image. This works with some files, but doesn't work with others, depending on how an application actually accesses this overlaid data.

--overlay=copy    Copy any extra data attached to the file. [DEFAULT]
 
diff --git a/doc/upx-doc.txt b/doc/upx-doc.txt
index d474ef30..12917c30 100644
--- a/doc/upx-doc.txt
+++ b/doc/upx-doc.txt
@@ -153,7 +153,7 @@ OVERLAY HANDLING OPTIONS
     UPX handles overlays like many other executable packers do: it simply
     copies the overlay after the compressed image. This works with some
     files, but doesn't work with others, depending on how an application
-    actually accesses this overlayed data.
+    actually accesses this overlaid data.
 
       --overlay=copy    Copy any extra data attached to the file. [DEFAULT]
 
diff --git a/doc/upx.1 b/doc/upx.1
index a3a5daed..84b4b67a 100644
--- a/doc/upx.1
+++ b/doc/upx.1
@@ -296,7 +296,7 @@ it would be better to use resource sections).
 \&\fB\s-1UPX\s0\fR handles overlays like many other executable packers do: it simply
 copies the overlay after the compressed image. This works with some
 files, but doesn't work with others, depending on how an application
-actually accesses this overlayed data.
+actually accesses this overlaid data.
 .PP
 .Vb 1
 \&  \-\-overlay=copy    Copy any extra data attached to the file. [DEFAULT]
diff --git a/doc/upx.pod b/doc/upx.pod
index ba32f496..48f47baf 100644
--- a/doc/upx.pod
+++ b/doc/upx.pod
@@ -208,7 +208,7 @@ it would be better to use resource sections).
 B handles overlays like many other executable packers do: it simply
 copies the overlay after the compressed image. This works with some
 files, but doesn't work with others, depending on how an application
-actually accesses this overlayed data.
+actually accesses this overlaid data.
 
   --overlay=copy    Copy any extra data attached to the file. [DEFAULT]
 
diff --git a/src/Makefile b/src/Makefile
index b6d4ec10..d83a2ba8 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,9 +1,10 @@
 #
-# UPX Makefile - needs GNU make
+# UPX src Makefile - needs GNU make and CMake >= 3.13
 #
 
-# IMPORTANT NOTE: this Makefile is deprecated - please
-#   directly use the CMake build instead!
+# NOTE: this Makefile is deprecated - please directly use the CMake build
+# instead. And see the top-level Makefile for some pre-defined CMake
+# build configurations.
 
 MAKEFLAGS += -r
 .SUFFIXES:
@@ -21,6 +22,7 @@ endif
 # redirect to top-level CMake build
 #
 
+# note that top-level Makefile .DEFAULT_GOAL is build/release
 .DEFAULT_GOAL = build/debug
 
 build/debug:   $(top_srcdir)/build/debug/upx
@@ -35,8 +37,10 @@ $(top_srcdir)/build/debug/upx: PHONY
 $(top_srcdir)/build/release/upx: PHONY
 	$(MAKE) -C $(top_srcdir) build/release
 
+.NOTPARALLEL: # because the actual builds use "cmake --parallel"
 .PHONY: PHONY
-.NOTPARALLEL:
+.SECONDEXPANSION:
+.SUFFIXES:
 
 #
 # "make run-testsuite"
@@ -79,9 +83,8 @@ endif
 # automatically format some C++ source code files
 ifeq ($(shell uname),Linux)
 # Markus loves clang-format, but John hates it; find a compromise
-CLANG_FORMAT_EXCLUDE_FILES += conf.h miniacc.h version.h help.cpp
+CLANG_FORMAT_EXCLUDE_FILES += conf.h miniacc.h version.h packer_c.cpp
 CLANG_FORMAT_EXCLUDE_FILES += p_elf.h p_elf_enum.h p_lx_% p_mach% p_unix% p_vmlin%
-CLANG_FORMAT_EXCLUDE_FILES += packer_c.cpp
 CLANG_FORMAT_EXCLUDE_FILES += compress/compress.h filter/filter_impl.cpp
 CLANG_FORMAT_FILES := $(sort $(wildcard *.[ch]* ../maint/src/*.[ch]* [cu]*/*.[ch]*))
 CLANG_FORMAT_FILES := $(filter-out $(CLANG_FORMAT_EXCLUDE_FILES),$(CLANG_FORMAT_FILES))
diff --git a/src/bele.h b/src/bele.h
index 91078257..28c0e533 100644
--- a/src/bele.h
+++ b/src/bele.h
@@ -38,35 +38,35 @@
 // core - NE
 **************************************************************************/
 
-static forceinline unsigned get_ne16(const void *p) {
+static forceinline unsigned get_ne16(const void *p) noexcept {
     upx_uint16_t v = 0;
     upx_memcpy_inline(&v, p, sizeof(v));
     return v;
 }
 
-static forceinline unsigned get_ne32(const void *p) {
+static forceinline unsigned get_ne32(const void *p) noexcept {
     upx_uint32_t v = 0;
     upx_memcpy_inline(&v, p, sizeof(v));
     return v;
 }
 
-static forceinline upx_uint64_t get_ne64(const void *p) {
+static forceinline upx_uint64_t get_ne64(const void *p) noexcept {
     upx_uint64_t v = 0;
     upx_memcpy_inline(&v, p, sizeof(v));
     return v;
 }
 
-static forceinline void set_ne16(void *p, unsigned vv) {
+static forceinline void set_ne16(void *p, unsigned vv) noexcept {
     upx_uint16_t v = (upx_uint16_t) (vv & 0xffff);
     upx_memcpy_inline(p, &v, sizeof(v));
 }
 
-static forceinline void set_ne32(void *p, unsigned vv) {
+static forceinline void set_ne32(void *p, unsigned vv) noexcept {
     upx_uint32_t v = vv;
     upx_memcpy_inline(p, &v, sizeof(v));
 }
 
-static forceinline void set_ne64(void *p, upx_uint64_t vv) {
+static forceinline void set_ne64(void *p, upx_uint64_t vv) noexcept {
     upx_uint64_t v = vv;
     upx_memcpy_inline(p, &v, sizeof(v));
 }
@@ -79,31 +79,35 @@ static forceinline void set_ne64(void *p, upx_uint64_t vv) {
 
 ACC_COMPILE_TIME_ASSERT_HEADER(sizeof(long) == 4)
 
-// unfortunately *not* constexpr with MSVC
-static forceinline unsigned bswap16(unsigned v) { return (unsigned) _byteswap_ulong(v << 16); }
-static forceinline unsigned bswap32(unsigned v) { return (unsigned) _byteswap_ulong(v); }
-static forceinline upx_uint64_t bswap64(upx_uint64_t v) { return _byteswap_uint64(v); }
+// unfortunately *not* constexpr with current MSVC
+static forceinline unsigned bswap16(unsigned v) noexcept {
+    return (unsigned) _byteswap_ulong(v << 16);
+}
+static forceinline unsigned bswap32(unsigned v) noexcept { return (unsigned) _byteswap_ulong(v); }
+static forceinline upx_uint64_t bswap64(upx_uint64_t v) noexcept { return _byteswap_uint64(v); }
 
 #else
 
-static forceinline constexpr unsigned bswap16(unsigned v) {
+static forceinline constexpr unsigned bswap16(unsigned v) noexcept {
     // return __builtin_bswap16((upx_uint16_t) (v & 0xffff));
     // return (unsigned) __builtin_bswap64((upx_uint64_t) v << 48);
     return __builtin_bswap32(v << 16);
 }
-static forceinline constexpr unsigned bswap32(unsigned v) {
+static forceinline constexpr unsigned bswap32(unsigned v) noexcept {
     // return (unsigned) __builtin_bswap64((upx_uint64_t) v << 32);
     return __builtin_bswap32(v);
 }
-static forceinline constexpr upx_uint64_t bswap64(upx_uint64_t v) { return __builtin_bswap64(v); }
+static forceinline constexpr upx_uint64_t bswap64(upx_uint64_t v) noexcept {
+    return __builtin_bswap64(v);
+}
 
 #endif
 
-static forceinline constexpr unsigned no_bswap16(unsigned v) {
+static forceinline constexpr unsigned no_bswap16(unsigned v) noexcept {
     return v & 0xffff; // needed so that this is equivalent to bswap16() above
 }
-static forceinline constexpr unsigned no_bswap32(unsigned v) { return v; }
-static forceinline constexpr upx_uint64_t no_bswap64(upx_uint64_t v) { return v; }
+static forceinline constexpr unsigned no_bswap32(unsigned v) noexcept { return v; }
+static forceinline constexpr upx_uint64_t no_bswap64(upx_uint64_t v) noexcept { return v; }
 
 #if (ACC_ABI_BIG_ENDIAN)
 #define ne16_to_be16(v) no_bswap16(v)
@@ -170,30 +174,27 @@ inline unsigned get_le26(const void *p) { return get_le32(p) & 0x03ffffff; }
 
 inline void set_le26(void *p, unsigned v) {
     // preserve the top 6 bits
-#if 0
-    set_le32(p, (get_le32(p) & 0xfc000000) | (v & 0x03ffffff));
-#else
+    //   set_le32(p, (get_le32(p) & 0xfc000000) | (v & 0x03ffffff));
     // optimized version, saving a runtime bswap32
     set_ne32(p, (get_ne32(p) & ne32_to_le32(0xfc000000)) |
                     (ne32_to_le32(v) & ne32_to_le32(0x03ffffff)));
-#endif
 }
 
 /*************************************************************************
 // get signed values
 **************************************************************************/
 
-static forceinline int sign_extend(unsigned v, unsigned bits) {
+static forceinline int sign_extend(unsigned v, unsigned bits) noexcept {
     const unsigned sign_bit = 1u << (bits - 1);
     v &= sign_bit | (sign_bit - 1);
-    v |= 0 - (v & sign_bit);
+    v |= 0u - (v & sign_bit);
     return ACC_ICAST(int, v);
 }
 
-static forceinline upx_int64_t sign_extend(upx_uint64_t v, unsigned bits) {
+static forceinline upx_int64_t sign_extend(upx_uint64_t v, unsigned bits) noexcept {
     const upx_uint64_t sign_bit = 1ull << (bits - 1);
     v &= sign_bit | (sign_bit - 1);
-    v |= 0 - (v & sign_bit);
+    v |= 0ull - (v & sign_bit);
     return ACC_ICAST(upx_int64_t, v);
 }
 
@@ -252,359 +253,348 @@ struct alignas(1) BE16 {
     typedef unsigned integral_conversion_type; // automatic conversion to unsigned
     byte d[2];
 
-    BE16 &operator=(unsigned v) {
+    BE16 &operator=(unsigned v) noexcept {
         set_be16(d, v);
         return *this;
     }
-    BE16 &operator+=(unsigned v) {
+    BE16 &operator+=(unsigned v) noexcept {
         set_be16(d, get_be16(d) + v);
         return *this;
     }
-    BE16 &operator-=(unsigned v) {
+    BE16 &operator-=(unsigned v) noexcept {
         set_be16(d, get_be16(d) - v);
         return *this;
     }
-    BE16 &operator*=(unsigned v) {
+    BE16 &operator*=(unsigned v) noexcept {
         set_be16(d, get_be16(d) * v);
         return *this;
     }
-    BE16 &operator/=(unsigned v) {
+    BE16 &operator/=(unsigned v) noexcept {
         set_be16(d, get_be16(d) / v);
         return *this;
     }
-    BE16 &operator&=(unsigned v) {
+    BE16 &operator&=(unsigned v) noexcept {
         set_be16(d, get_be16(d) & v);
         return *this;
     }
-    BE16 &operator|=(unsigned v) {
+    BE16 &operator|=(unsigned v) noexcept {
         set_be16(d, get_be16(d) | v);
         return *this;
     }
-    BE16 &operator^=(unsigned v) {
+    BE16 &operator^=(unsigned v) noexcept {
         set_be16(d, get_be16(d) ^ v);
         return *this;
     }
-    BE16 &operator<<=(unsigned v) {
+    BE16 &operator<<=(unsigned v) noexcept {
         set_be16(d, get_be16(d) << v);
         return *this;
     }
-    BE16 &operator>>=(unsigned v) {
+    BE16 &operator>>=(unsigned v) noexcept {
         set_be16(d, get_be16(d) >> v);
         return *this;
     }
 
-    operator unsigned() const { return get_be16(d); }
+    operator unsigned() const noexcept { return get_be16(d); }
 
-    bool operator<(const BE16 &v) const { return unsigned(*this) < unsigned(v); }
+    bool operator<(const BE16 &v) const noexcept { return unsigned(*this) < unsigned(v); }
 };
 
 struct alignas(1) BE32 {
     typedef unsigned integral_conversion_type; // automatic conversion to unsigned
     byte d[4];
 
-    BE32 &operator=(unsigned v) {
+    BE32 &operator=(unsigned v) noexcept {
         set_be32(d, v);
         return *this;
     }
-    BE32 &operator+=(unsigned v) {
+    BE32 &operator+=(unsigned v) noexcept {
         set_be32(d, get_be32(d) + v);
         return *this;
     }
-    BE32 &operator-=(unsigned v) {
+    BE32 &operator-=(unsigned v) noexcept {
         set_be32(d, get_be32(d) - v);
         return *this;
     }
-    BE32 &operator*=(unsigned v) {
+    BE32 &operator*=(unsigned v) noexcept {
         set_be32(d, get_be32(d) * v);
         return *this;
     }
-    BE32 &operator/=(unsigned v) {
+    BE32 &operator/=(unsigned v) noexcept {
         set_be32(d, get_be32(d) / v);
         return *this;
     }
-    BE32 &operator&=(unsigned v) {
+    BE32 &operator&=(unsigned v) noexcept {
         set_be32(d, get_be32(d) & v);
         return *this;
     }
-    BE32 &operator|=(unsigned v) {
+    BE32 &operator|=(unsigned v) noexcept {
         set_be32(d, get_be32(d) | v);
         return *this;
     }
-    BE32 &operator^=(unsigned v) {
+    BE32 &operator^=(unsigned v) noexcept {
         set_be32(d, get_be32(d) ^ v);
         return *this;
     }
-    BE32 &operator<<=(unsigned v) {
+    BE32 &operator<<=(unsigned v) noexcept {
         set_be32(d, get_be32(d) << v);
         return *this;
     }
-    BE32 &operator>>=(unsigned v) {
+    BE32 &operator>>=(unsigned v) noexcept {
         set_be32(d, get_be32(d) >> v);
         return *this;
     }
 
-    operator unsigned() const { return get_be32(d); }
+    operator unsigned() const noexcept { return get_be32(d); }
 
-    bool operator<(const BE32 &v) const { return unsigned(*this) < unsigned(v); }
+    bool operator<(const BE32 &v) const noexcept { return unsigned(*this) < unsigned(v); }
 };
 
 struct alignas(1) BE64 {
     typedef upx_uint64_t integral_conversion_type; // automatic conversion to upx_uint64_t
     byte d[8];
 
-    BE64 &operator=(upx_uint64_t v) {
+    BE64 &operator=(upx_uint64_t v) noexcept {
         set_be64(d, v);
         return *this;
     }
-    BE64 &operator+=(upx_uint64_t v) {
+    BE64 &operator+=(upx_uint64_t v) noexcept {
         set_be64(d, get_be64(d) + v);
         return *this;
     }
-    BE64 &operator-=(upx_uint64_t v) {
+    BE64 &operator-=(upx_uint64_t v) noexcept {
         set_be64(d, get_be64(d) - v);
         return *this;
     }
-    BE64 &operator*=(upx_uint64_t v) {
+    BE64 &operator*=(upx_uint64_t v) noexcept {
         set_be64(d, get_be64(d) * v);
         return *this;
     }
-    BE64 &operator/=(upx_uint64_t v) {
+    BE64 &operator/=(upx_uint64_t v) noexcept {
         set_be64(d, get_be64(d) / v);
         return *this;
     }
-    BE64 &operator&=(upx_uint64_t v) {
+    BE64 &operator&=(upx_uint64_t v) noexcept {
         set_be64(d, get_be64(d) & v);
         return *this;
     }
-    BE64 &operator|=(upx_uint64_t v) {
+    BE64 &operator|=(upx_uint64_t v) noexcept {
         set_be64(d, get_be64(d) | v);
         return *this;
     }
-    BE64 &operator^=(upx_uint64_t v) {
+    BE64 &operator^=(upx_uint64_t v) noexcept {
         set_be64(d, get_be64(d) ^ v);
         return *this;
     }
-    BE64 &operator<<=(unsigned v) {
+    BE64 &operator<<=(unsigned v) noexcept {
         set_be64(d, get_be64(d) << v);
         return *this;
     }
-    BE64 &operator>>=(unsigned v) {
+    BE64 &operator>>=(unsigned v) noexcept {
         set_be64(d, get_be64(d) >> v);
         return *this;
     }
 
-    operator upx_uint64_t() const { return get_be64(d); }
+    operator upx_uint64_t() const noexcept { return get_be64(d); }
 
-    bool operator<(const BE64 &v) const { return upx_uint64_t(*this) < upx_uint64_t(v); }
+    bool operator<(const BE64 &v) const noexcept { return upx_uint64_t(*this) < upx_uint64_t(v); }
 };
 
 struct alignas(1) LE16 {
     typedef unsigned integral_conversion_type; // automatic conversion to unsigned
     byte d[2];
 
-    LE16 &operator=(unsigned v) {
+    LE16 &operator=(unsigned v) noexcept {
         set_le16(d, v);
         return *this;
     }
-    LE16 &operator+=(unsigned v) {
+    LE16 &operator+=(unsigned v) noexcept {
         set_le16(d, get_le16(d) + v);
         return *this;
     }
-    LE16 &operator-=(unsigned v) {
+    LE16 &operator-=(unsigned v) noexcept {
         set_le16(d, get_le16(d) - v);
         return *this;
     }
-    LE16 &operator*=(unsigned v) {
+    LE16 &operator*=(unsigned v) noexcept {
         set_le16(d, get_le16(d) * v);
         return *this;
     }
-    LE16 &operator/=(unsigned v) {
+    LE16 &operator/=(unsigned v) noexcept {
         set_le16(d, get_le16(d) / v);
         return *this;
     }
-    LE16 &operator&=(unsigned v) {
+    LE16 &operator&=(unsigned v) noexcept {
         set_le16(d, get_le16(d) & v);
         return *this;
     }
-    LE16 &operator|=(unsigned v) {
+    LE16 &operator|=(unsigned v) noexcept {
         set_le16(d, get_le16(d) | v);
         return *this;
     }
-    LE16 &operator^=(unsigned v) {
+    LE16 &operator^=(unsigned v) noexcept {
         set_le16(d, get_le16(d) ^ v);
         return *this;
     }
-    LE16 &operator<<=(unsigned v) {
+    LE16 &operator<<=(unsigned v) noexcept {
         set_le16(d, get_le16(d) << v);
         return *this;
     }
-    LE16 &operator>>=(unsigned v) {
+    LE16 &operator>>=(unsigned v) noexcept {
         set_le16(d, get_le16(d) >> v);
         return *this;
     }
 
-    operator unsigned() const { return get_le16(d); }
+    operator unsigned() const noexcept { return get_le16(d); }
 
-    bool operator<(const LE16 &v) const { return unsigned(*this) < unsigned(v); }
+    bool operator<(const LE16 &v) const noexcept { return unsigned(*this) < unsigned(v); }
 };
 
 struct alignas(1) LE32 {
     typedef unsigned integral_conversion_type; // automatic conversion to unsigned
     byte d[4];
 
-    LE32 &operator=(unsigned v) {
+    LE32 &operator=(unsigned v) noexcept {
         set_le32(d, v);
         return *this;
     }
-    LE32 &operator+=(unsigned v) {
+    LE32 &operator+=(unsigned v) noexcept {
         set_le32(d, get_le32(d) + v);
         return *this;
     }
-    LE32 &operator-=(unsigned v) {
+    LE32 &operator-=(unsigned v) noexcept {
         set_le32(d, get_le32(d) - v);
         return *this;
     }
-    LE32 &operator*=(unsigned v) {
+    LE32 &operator*=(unsigned v) noexcept {
         set_le32(d, get_le32(d) * v);
         return *this;
     }
-    LE32 &operator/=(unsigned v) {
+    LE32 &operator/=(unsigned v) noexcept {
         set_le32(d, get_le32(d) / v);
         return *this;
     }
-    LE32 &operator&=(unsigned v) {
+    LE32 &operator&=(unsigned v) noexcept {
         set_le32(d, get_le32(d) & v);
         return *this;
     }
-    LE32 &operator|=(unsigned v) {
+    LE32 &operator|=(unsigned v) noexcept {
         set_le32(d, get_le32(d) | v);
         return *this;
     }
-    LE32 &operator^=(unsigned v) {
+    LE32 &operator^=(unsigned v) noexcept {
         set_le32(d, get_le32(d) ^ v);
         return *this;
     }
-    LE32 &operator<<=(unsigned v) {
+    LE32 &operator<<=(unsigned v) noexcept {
         set_le32(d, get_le32(d) << v);
         return *this;
     }
-    LE32 &operator>>=(unsigned v) {
+    LE32 &operator>>=(unsigned v) noexcept {
         set_le32(d, get_le32(d) >> v);
         return *this;
     }
 
-    operator unsigned() const { return get_le32(d); }
+    operator unsigned() const noexcept { return get_le32(d); }
 
-    bool operator<(const LE32 &v) const { return unsigned(*this) < unsigned(v); }
+    bool operator<(const LE32 &v) const noexcept { return unsigned(*this) < unsigned(v); }
 };
 
 struct alignas(1) LE64 {
     typedef upx_uint64_t integral_conversion_type; // automatic conversion to upx_uint64_t
     byte d[8];
 
-    LE64 &operator=(upx_uint64_t v) {
+    LE64 &operator=(upx_uint64_t v) noexcept {
         set_le64(d, v);
         return *this;
     }
-    LE64 &operator+=(upx_uint64_t v) {
+    LE64 &operator+=(upx_uint64_t v) noexcept {
         set_le64(d, get_le64(d) + v);
         return *this;
     }
-    LE64 &operator-=(upx_uint64_t v) {
+    LE64 &operator-=(upx_uint64_t v) noexcept {
         set_le64(d, get_le64(d) - v);
         return *this;
     }
-    LE64 &operator*=(upx_uint64_t v) {
+    LE64 &operator*=(upx_uint64_t v) noexcept {
         set_le64(d, get_le64(d) * v);
         return *this;
     }
-    LE64 &operator/=(upx_uint64_t v) {
+    LE64 &operator/=(upx_uint64_t v) noexcept {
         set_le64(d, get_le64(d) / v);
         return *this;
     }
-    LE64 &operator&=(upx_uint64_t v) {
+    LE64 &operator&=(upx_uint64_t v) noexcept {
         set_le64(d, get_le64(d) & v);
         return *this;
     }
-    LE64 &operator|=(upx_uint64_t v) {
+    LE64 &operator|=(upx_uint64_t v) noexcept {
         set_le64(d, get_le64(d) | v);
         return *this;
     }
-    LE64 &operator^=(upx_uint64_t v) {
+    LE64 &operator^=(upx_uint64_t v) noexcept {
         set_le64(d, get_le64(d) ^ v);
         return *this;
     }
-    LE64 &operator<<=(unsigned v) {
+    LE64 &operator<<=(unsigned v) noexcept {
         set_le64(d, get_le64(d) << v);
         return *this;
     }
-    LE64 &operator>>=(unsigned v) {
+    LE64 &operator>>=(unsigned v) noexcept {
         set_le64(d, get_le64(d) >> v);
         return *this;
     }
 
-    operator upx_uint64_t() const { return get_le64(d); }
+    operator upx_uint64_t() const noexcept { return get_le64(d); }
 
-    bool operator<(const LE64 &v) const { return upx_uint64_t(*this) < upx_uint64_t(v); }
+    bool operator<(const LE64 &v) const noexcept { return upx_uint64_t(*this) < upx_uint64_t(v); }
 };
 
-// native types
-#if (ACC_ABI_BIG_ENDIAN)
-typedef BE16 NE16;
-typedef BE32 NE32;
-typedef BE64 NE64;
-#else
-typedef LE16 NE16;
-typedef LE32 NE32;
-typedef LE64 NE64;
-#endif
-
 /*************************************************************************
 // global operators (pointer addition/subtraction)
 **************************************************************************/
 
 template 
-inline T *operator+(T *ptr, const BE16 &v) {
+inline T *operator+(T *ptr, const BE16 &v) noexcept {
     return ptr + unsigned(v);
 }
 template 
-inline T *operator-(T *ptr, const BE16 &v) {
+inline T *operator-(T *ptr, const BE16 &v) noexcept {
     return ptr - unsigned(v);
 }
 template 
-inline T *operator+(T *ptr, const BE32 &v) {
+inline T *operator+(T *ptr, const BE32 &v) noexcept {
     return ptr + unsigned(v);
 }
 template 
-inline T *operator-(T *ptr, const BE32 &v) {
+inline T *operator-(T *ptr, const BE32 &v) noexcept {
     return ptr - unsigned(v);
 }
 template 
-inline T *operator+(T *ptr, const LE16 &v) {
+inline T *operator+(T *ptr, const LE16 &v) noexcept {
     return ptr + unsigned(v);
 }
 template 
-inline T *operator-(T *ptr, const LE16 &v) {
+inline T *operator-(T *ptr, const LE16 &v) noexcept {
     return ptr - unsigned(v);
 }
 template 
-inline T *operator+(T *ptr, const LE32 &v) {
+inline T *operator+(T *ptr, const LE32 &v) noexcept {
     return ptr + unsigned(v);
 }
 template 
-inline T *operator-(T *ptr, const LE32 &v) {
+inline T *operator-(T *ptr, const LE32 &v) noexcept {
     return ptr - unsigned(v);
 }
 
 // these are not implemented on purpose and will cause errors
 template 
-T *operator+(T *ptr, const BE64 &v) DELETED_FUNCTION;
+T *operator+(T *ptr, const BE64 &v) noexcept DELETED_FUNCTION;
 template 
-T *operator-(T *ptr, const BE64 &v) DELETED_FUNCTION;
+T *operator-(T *ptr, const BE64 &v) noexcept DELETED_FUNCTION;
 template 
-T *operator+(T *ptr, const LE64 &v) DELETED_FUNCTION;
+T *operator+(T *ptr, const LE64 &v) noexcept DELETED_FUNCTION;
 template 
-T *operator-(T *ptr, const LE64 &v) DELETED_FUNCTION;
+T *operator-(T *ptr, const LE64 &v) noexcept DELETED_FUNCTION;
 
 /*************************************************************************
 // global overloads
@@ -644,6 +634,19 @@ inline unsigned UPX_MIN(const LE32 &a, unsigned b) { return UPX_MIN(unsigned(a),
 // misc support
 **************************************************************************/
 
+template <>
+struct upx_is_integral : public std::true_type {};
+template <>
+struct upx_is_integral : public std::true_type {};
+template <>
+struct upx_is_integral : public std::true_type {};
+template <>
+struct upx_is_integral : public std::true_type {};
+template <>
+struct upx_is_integral : public std::true_type {};
+template <>
+struct upx_is_integral : public std::true_type {};
+
 // for use with qsort()
 extern "C" {
 int __acc_cdecl_qsort be16_compare(const void *, const void *);
@@ -664,6 +667,23 @@ int __acc_cdecl_qsort le32_compare_signed(const void *, const void *);
 int __acc_cdecl_qsort le64_compare_signed(const void *, const void *);
 } // extern "C"
 
+// native types
+#if (ACC_ABI_BIG_ENDIAN)
+typedef BE16 NE16;
+typedef BE32 NE32;
+typedef BE64 NE64;
+#define ne16_compare be16_compare
+#define ne32_compare be32_compare
+#define ne64_compare be64_compare
+#else
+typedef LE16 NE16;
+typedef LE32 NE32;
+typedef LE64 NE64;
+#define ne16_compare le16_compare
+#define ne32_compare le32_compare
+#define ne64_compare le64_compare
+#endif
+
 /*************************************************************************
 // Provide namespaces and classes to abstract endianness policies.
 //
diff --git a/src/bele_policy.h b/src/bele_policy.h
index 1b97f21b..0370b3a7 100644
--- a/src/bele_policy.h
+++ b/src/bele_policy.h
@@ -37,20 +37,20 @@
 // CTP - Compile-Time Polymorphism (templates)
 #define V static inline
 #define S static int __acc_cdecl_qsort
-#define C /*empty*/
+#define C noexcept
 #elif defined(BELE_RTP)
 // RTP - Run-Time Polymorphism (virtual functions)
 #define V virtual
 #define S virtual int
-#define C const
+#define C const noexcept
 #else
 #error
 #endif
 
 #if defined(BELE_RTP)
 struct AbstractPolicy {
-    inline AbstractPolicy() {}
-    virtual inline ~AbstractPolicy() {}
+    inline AbstractPolicy() noexcept {}
+    virtual inline ~AbstractPolicy() noexcept {}
     V bool isBE() C = 0;
     V bool isLE() C = 0;
 
@@ -91,7 +91,7 @@ private:
 
 #if defined(BELE_RTP)
 #undef C
-#define C const override
+#define C const noexcept override
 #endif
 
 struct BEPolicy
@@ -99,7 +99,7 @@ struct BEPolicy
     final : public AbstractPolicy
 #endif
 {
-    inline BEPolicy() {}
+    inline BEPolicy() noexcept {}
 #if defined(BELE_CTP)
     typedef N_BELE_RTP::BEPolicy RTP_Policy;
 #elif defined(BELE_RTP)
@@ -160,7 +160,7 @@ struct LEPolicy
     final : public AbstractPolicy
 #endif
 {
-    inline LEPolicy() {}
+    inline LEPolicy() noexcept {}
 #if defined(BELE_CTP)
     typedef N_BELE_RTP::LEPolicy RTP_Policy;
 #elif defined(BELE_RTP)
@@ -225,24 +225,6 @@ typedef LEPolicy HostPolicy;
 #error "ACC_ABI_ENDIAN"
 #endif
 
-#if 0 /* UNUSED */
-struct HostAlignedPolicy {
-#if defined(BELE_CTP)
-    enum { isBE = HostPolicy::isBE, isLE = HostPolicy::isLE };
-#endif
-
-    typedef upx_uint16_t U16;
-    typedef upx_uint32_t U32;
-    typedef upx_uint64_t U64;
-
-    static void compileTimeAssertions() {
-        COMPILE_TIME_ASSERT(sizeof(U16) == 2)
-        COMPILE_TIME_ASSERT(sizeof(U32) == 4)
-        COMPILE_TIME_ASSERT(sizeof(U64) == 8)
-    }
-};
-#endif
-
 #undef V
 #undef S
 #undef C
diff --git a/src/check/dt_check.cpp b/src/check/dt_check.cpp
index 04b7751f..e3718751 100644
--- a/src/check/dt_check.cpp
+++ b/src/check/dt_check.cpp
@@ -39,7 +39,7 @@ int upx_doctest_check(int argc, char **argv) {
     const char *e = getenv("UPX_DEBUG_DOCTEST_DISABLE");
     if (e && e[0] && strcmp(e, "0") != 0)
         return 0;
-    bool minimal = true;   // only show failing tests
+    bool minimal = true;   // don't show summary
     bool duration = false; // show timings
     bool success = false;  // show all tests
 #if DEBUG
@@ -47,12 +47,13 @@ int upx_doctest_check(int argc, char **argv) {
 #endif
     e = getenv("UPX_DEBUG_DOCTEST_VERBOSE");
     if (e && e[0]) {
-        minimal = false;
         if (strcmp(e, "0") == 0) {
             minimal = true;
         } else if (strcmp(e, "2") == 0) {
+            minimal = false;
             duration = true;
         } else if (strcmp(e, "3") == 0) {
+            minimal = false;
             duration = true;
             success = true;
         }
@@ -100,6 +101,7 @@ ACC_COMPILE_TIME_ASSERT_HEADER(bswap64(0x0807060504030201ull) == 0x0102030405060
 
 ACC_COMPILE_TIME_ASSERT_HEADER(compile_time::string_len("") == 0)
 ACC_COMPILE_TIME_ASSERT_HEADER(compile_time::string_len("a") == 1)
+ACC_COMPILE_TIME_ASSERT_HEADER(compile_time::string_len("ab") == 2)
 
 ACC_COMPILE_TIME_ASSERT_HEADER(compile_time::string_eq("", ""))
 ACC_COMPILE_TIME_ASSERT_HEADER(!compile_time::string_eq("a", ""))
@@ -145,15 +147,18 @@ namespace {
 
 template 
 struct TestBELE {
-    __acc_static_noinline bool test(void) {
+    static noinline bool test(void) {
         // POD checks
         {
             COMPILE_TIME_ASSERT(std::is_standard_layout::value)
             COMPILE_TIME_ASSERT(std::is_trivial::value)
-            // extra checks, these are probably implied by std::is_trivial:
+            // extra checks, these are probably implied by std::is_trivial
             COMPILE_TIME_ASSERT(std::is_nothrow_default_constructible::value)
             COMPILE_TIME_ASSERT(std::is_trivially_copyable::value)
             COMPILE_TIME_ASSERT(std::is_trivially_default_constructible::value)
+            // UPX
+            COMPILE_TIME_ASSERT(upx_is_integral::value)
+            COMPILE_TIME_ASSERT(upx_is_integral_v)
         }
         // alignment checks
         {
@@ -177,8 +182,7 @@ struct TestBELE {
             UNUSED(t1);
             UNUSED(t2);
         }
-#if 1
-        // arithmetic checks
+        // arithmetic checks (modern compilers will optimize this away)
         {
             T allbits;
             allbits = 0;
@@ -187,6 +191,7 @@ struct TestBELE {
             T v1;
             v1 = 1;
             v1 *= 2;
+            v1 /= 1;
             v1 -= 1;
             T v2;
             v2 = 1;
@@ -217,14 +222,13 @@ struct TestBELE {
             if ((v1 ^ v2) != 1)
                 return false;
         }
-#endif
         return true;
     }
 };
 
 template 
 struct TestNoAliasingStruct {
-    __acc_static_noinline bool test(A *a, B *b) {
+    static noinline bool test(A *a, B *b) {
         *a = 0;
         *b = 0;
         *b -= 3;
@@ -232,7 +236,7 @@ struct TestNoAliasingStruct {
     }
 };
 template 
-__acc_static_forceinline bool testNoAliasing(A *a, B *b) {
+static forceinline bool testNoAliasing(A *a, B *b) {
     return TestNoAliasingStruct::test(a, b);
 }
 template 
@@ -274,6 +278,10 @@ void upx_compiler_sanity_check(void) {
     COMPILE_TIME_ASSERT_ALIGNED1(LE32)
     COMPILE_TIME_ASSERT_ALIGNED1(LE64)
 
+    COMPILE_TIME_ASSERT(sizeof(upx_charptr_unit_type) == 1)
+    COMPILE_TIME_ASSERT_ALIGNED1(upx_charptr_unit_type)
+    COMPILE_TIME_ASSERT(sizeof(*((charptr) nullptr)) == 1)
+
     COMPILE_TIME_ASSERT(sizeof(UPX_VERSION_STRING4) == 4 + 1)
     assert(strlen(UPX_VERSION_STRING4) == 4);
     COMPILE_TIME_ASSERT(sizeof(UPX_VERSION_YEAR) == 4 + 1)
@@ -293,7 +301,7 @@ void upx_compiler_sanity_check(void) {
     }
     assert(UPX_RSIZE_MAX_MEM == 805306368);
 
-#if 1
+#if DEBUG || 1
     assert(TestBELE::test());
     assert(TestBELE::test());
     assert(TestBELE::test());
@@ -354,9 +362,9 @@ void upx_compiler_sanity_check(void) {
         assert(dd == ne32_to_le32(0xf7020304));
     }
     {
-        upx_uint16_t a;
-        upx_uint32_t b;
-        upx_uint64_t c;
+        upx_uint16_t a = 0;
+        upx_uint32_t b = 0;
+        upx_uint64_t c = 0;
         set_ne16(&a, 0x04030201); // ignore upper bits
         set_ne32(&b, 0x04030201);
         set_ne64(&c, 0x0807060504030201ull);
@@ -454,7 +462,7 @@ TEST_CASE("working -fno-strict-overflow") {
 }
 
 TEST_CASE("libc snprintf") {
-    // runtime check that Win32/MinGW  works as expected
+    // runtime check that Windows/MinGW  works as expected
     char buf[64];
     long long ll = acc_vget_int(-1, 0);
     unsigned long long llu = (unsigned long long) ll;
diff --git a/src/check/dt_impl.cpp b/src/check/dt_impl.cpp
index a684d6f7..b0a349a7 100644
--- a/src/check/dt_impl.cpp
+++ b/src/check/dt_impl.cpp
@@ -29,23 +29,30 @@
 **************************************************************************/
 
 #define DOCTEST_CONFIG_IMPLEMENT
-#if !defined(UPX_DOCTEST_CONFIG_MULTITHREADING)
-#define DOCTEST_CONFIG_NO_MULTITHREADING
-#endif
+#define DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS
+
+#if !defined(DOCTEST_CONFIG_DISABLE)
+
 #if defined(__i386__) && defined(__MSDOS__) && defined(__DJGPP__) && defined(__GNUC__)
+#define DOCTEST_CONFIG_NO_MULTITHREADING
 #define DOCTEST_CONFIG_NO_POSIX_SIGNALS
 #elif defined(__m68k__) && defined(__atarist__) && defined(__GNUC__)
 #define DOCTEST_CONFIG_COLORS_NONE
+#define DOCTEST_CONFIG_NO_MULTITHREADING
 #define DOCTEST_CONFIG_NO_POSIX_SIGNALS
 #pragma GCC diagnostic ignored "-Wshadow"
 #endif
-#define DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS
-#if !defined(DOCTEST_CONFIG_DISABLE)
+
+#if !defined(UPX_DOCTEST_CONFIG_MULTITHREADING)
+#define DOCTEST_CONFIG_NO_MULTITHREADING
+#endif
+
 #if defined(__clang__) && defined(__FAST_MATH__) && defined(__INTEL_LLVM_COMPILER)
 // warning: comparison with NaN always evaluates to false in fast floating point modes
 #pragma clang diagnostic ignored "-Wtautological-constant-compare"
 #endif
+
 #include 
-#endif
+#endif // DOCTEST_CONFIG_DISABLE
 
 /* vim:set ts=4 sw=4 et: */
diff --git a/src/check/dt_xspan.cpp b/src/check/dt_xspan.cpp
index 12ab7251..d0ff9ba8 100644
--- a/src/check/dt_xspan.cpp
+++ b/src/check/dt_xspan.cpp
@@ -91,6 +91,17 @@ TEST_CASE("basic xspan usage") {
         CHECK_NOTHROW(raw_bytes(a0, 0));
         CHECK_THROWS(raw_bytes(a0, 1));
         CHECK_THROWS(raw_index_bytes(a0, 0, 0));
+
+        CHECK(raw_bytes(b0, 0) == buf);
+        CHECK(raw_bytes(bp, 0) == buf);
+        // info: these will fail if we ever add an overload for bounded-arrays
+#if WITH_XSPAN >= 2
+        CHECK(b0.raw_size_in_bytes() == 0u);
+        CHECK(bp.raw_size_in_bytes() == 0u);
+#endif
+        CHECK(raw_bytes(b0, 999999) == buf);
+        CHECK(raw_bytes(bp, 999999) == buf);
+
         CHECK(raw_bytes(c0, 4) == buf);
         CHECK(raw_index_bytes(c0, 1, 3) == buf + 1);
         CHECK(raw_bytes(cp, 4) == buf);
@@ -136,6 +147,17 @@ TEST_CASE("basic xspan usage") {
         CHECK_NOTHROW(raw_bytes(a0, 0));
         CHECK_THROWS(raw_bytes(a0, 1));
         CHECK_THROWS(raw_index_bytes(a0, 0, 0));
+
+        CHECK(raw_bytes(b0, 0) == buf);
+        CHECK(raw_bytes(bp, 0) == buf);
+        // info: these will fail if we ever add an overload for bounded-arrays
+#if WITH_XSPAN >= 2
+        CHECK(b0.raw_size_in_bytes() == 0u);
+        CHECK(bp.raw_size_in_bytes() == 0u);
+#endif
+        CHECK(raw_bytes(b0, 999999) == buf);
+        CHECK(raw_bytes(bp, 999999) == buf);
+
         CHECK(raw_bytes(c0, 4) == buf);
         CHECK(raw_index_bytes(c0, 1, 3) == buf + 1);
         CHECK(raw_bytes(cp, 4) == buf);
diff --git a/src/compress/compress.cpp b/src/compress/compress.cpp
index e64b12d2..d190cff7 100644
--- a/src/compress/compress.cpp
+++ b/src/compress/compress.cpp
@@ -78,7 +78,7 @@ int upx_compress(const upx_bytep src, unsigned src_len, upx_bytep dst, unsigned
     // force users to provide *dst_len
     assert(*dst_len != 0);
 #endif
-    // for UPX, we always require a reasonably sized outbut buffer
+    // for UPX, we always require a reasonably sized output buffer
     assert(*dst_len >= MemBuffer::getSizeForCompression(src_len));
 
     if (!cresult)
diff --git a/src/compress/compress_lzma.cpp b/src/compress/compress_lzma.cpp
index 9d8f4de5..33e76072 100644
--- a/src/compress/compress_lzma.cpp
+++ b/src/compress/compress_lzma.cpp
@@ -116,7 +116,7 @@ static bool prepare_result(lzma_compress_result_t *res, unsigned src_len, int me
         res->lit_context_bits = (method >> 8) & 15;
     }
 #if 0
-    // DEBUG - set sizes so that we use a maxmimum amount of stack.
+    // DEBUG - set sizes so that we use a maximum amount of stack.
     //  These settings cause res->num_probs == 3147574, i.e. we will
     //  need about 6 MiB of stack during runtime decompression.
     res->lit_pos_bits     = 4;
@@ -524,13 +524,12 @@ const char *upx_lzma_version_string(void) { return "4.43"; }
 **************************************************************************/
 
 TEST_CASE("upx_lzma_decompress") {
-    typedef const upx_byte C;
-    C *c_data;
-    upx_byte d_buf[16];
+    const byte *c_data;
+    byte d_buf[16];
     unsigned d_len;
     int r;
 
-    c_data = (C *) "\x1a\x03\x00\x7f\xed\x3c\x00\x00\x00";
+    c_data = (const byte *) "\x1a\x03\x00\x7f\xed\x3c\x00\x00\x00";
     d_len = 16;
     r = upx_lzma_decompress(c_data, 9, d_buf, &d_len, M_LZMA, nullptr);
     CHECK((r == 0 && d_len == 16));
diff --git a/src/compress/compress_ucl.cpp b/src/compress/compress_ucl.cpp
index 4bf942ba..d074850f 100644
--- a/src/compress/compress_ucl.cpp
+++ b/src/compress/compress_ucl.cpp
@@ -113,14 +113,14 @@ int upx_ucl_compress(const upx_bytep src, unsigned src_len, upx_bytep dst, unsig
     cconf.bb_endian = 0;
     cconf.bb_size = 0;
     if (method >= M_NRV2B_LE32 && method <= M_NRV2E_LE16) {
-        static const unsigned char sizes[3] = {32, 8, 16};
+        static const upx_uint8_t sizes[3] = {32, 8, 16};
         cconf.bb_size = sizes[(method - M_NRV2B_LE32) % 3];
     } else {
         throwInternalError("unknown compression method");
         return UPX_E_ERROR;
     }
 
-    // optimize compression parms
+    // optimize compression params
     if (level <= 3 && cconf.max_offset == UCL_UINT_MAX)
         cconf.max_offset = 8 * 1024 - 1;
     else if (level == 4 && cconf.max_offset == UCL_UINT_MAX)
@@ -241,9 +241,9 @@ int upx_ucl_test_overlap(const upx_bytep buf, const upx_bytep tbuf, unsigned src
 **************************************************************************/
 
 extern "C" {
-static ucl_voidp __UCL_CDECL my_malloc(ucl_uint n) { return calloc(1, n); }
+static ucl_voidp __UCL_CDECL my_malloc(ucl_uint n) { return upx_calloc(n, 1); }
 static void __UCL_CDECL my_free(ucl_voidp p) { free(p); }
-}
+} // extern "C"
 
 int upx_ucl_init(void) {
     if (ucl_init() != UCL_E_OK)
@@ -327,13 +327,12 @@ TEST_CASE("compress_ucl") {
 #endif // DEBUG
 
 TEST_CASE("upx_ucl_decompress") {
-    typedef const upx_byte C;
-    C *c_data;
-    upx_byte d_buf[16];
+    const byte *c_data;
+    byte d_buf[16];
     unsigned d_len;
     int r;
 
-    c_data = (C *) "\x92\xff\x10\x00\x00\x00\x00\x00\x48\xff";
+    c_data = (const byte *) "\x92\xff\x10\x00\x00\x00\x00\x00\x48\xff";
     d_len = 16;
     r = upx_ucl_decompress(c_data, 10, d_buf, &d_len, M_NRV2B_8, nullptr);
     CHECK((r == 0 && d_len == 16));
@@ -343,7 +342,7 @@ TEST_CASE("upx_ucl_decompress") {
     r = upx_ucl_decompress(c_data, 10, d_buf, &d_len, M_NRV2B_8, nullptr);
     CHECK(r == UPX_E_OUTPUT_OVERRUN);
 
-    c_data = (C *) "\x92\xff\x10\x92\x49\x24\x92\xa0\xff";
+    c_data = (const byte *) "\x92\xff\x10\x92\x49\x24\x92\xa0\xff";
     d_len = 16;
     r = upx_ucl_decompress(c_data, 9, d_buf, &d_len, M_NRV2D_8, nullptr);
     CHECK((r == 0 && d_len == 16));
@@ -353,7 +352,7 @@ TEST_CASE("upx_ucl_decompress") {
     r = upx_ucl_decompress(c_data, 9, d_buf, &d_len, M_NRV2D_8, nullptr);
     CHECK(r == UPX_E_OUTPUT_OVERRUN);
 
-    c_data = (C *) "\x90\xff\xb0\x92\x49\x24\x92\xa0\xff";
+    c_data = (const byte *) "\x90\xff\xb0\x92\x49\x24\x92\xa0\xff";
     d_len = 16;
     r = upx_ucl_decompress(c_data, 9, d_buf, &d_len, M_NRV2E_8, nullptr);
     CHECK((r == 0 && d_len == 16));
diff --git a/src/compress/compress_zlib.cpp b/src/compress/compress_zlib.cpp
index 4ab44649..0e3828be 100644
--- a/src/compress/compress_zlib.cpp
+++ b/src/compress/compress_zlib.cpp
@@ -287,13 +287,12 @@ TEST_CASE("compress_zlib") {
 #endif // DEBUG
 
 TEST_CASE("upx_zlib_decompress") {
-    typedef const upx_byte C;
-    C *c_data;
-    upx_byte d_buf[16];
+    const byte *c_data;
+    byte d_buf[16];
     unsigned d_len;
     int r;
 
-    c_data = (C *) "\xfb\xff\x1f\x15\x00\x00";
+    c_data = (const byte *) "\xfb\xff\x1f\x15\x00\x00";
     d_len = 16;
     r = upx_zlib_decompress(c_data, 6, d_buf, &d_len, M_DEFLATE, nullptr);
     CHECK((r == 0 && d_len == 16));
diff --git a/src/compress/compress_zstd.cpp b/src/compress/compress_zstd.cpp
index 249fe0a7..fed3dc9f 100644
--- a/src/compress/compress_zstd.cpp
+++ b/src/compress/compress_zstd.cpp
@@ -207,13 +207,12 @@ TEST_CASE("compress_zstd") {
 #endif // DEBUG
 
 TEST_CASE("upx_zstd_decompress") {
-    typedef const upx_byte C;
-    C *c_data;
-    upx_byte d_buf[32];
+    const byte *c_data;
+    byte d_buf[32];
     unsigned d_len;
     int r;
 
-    c_data = (C *) "\x28\xb5\x2f\xfd\x20\x20\x3d\x00\x00\x08\xff\x01\x00\x34\x4e\x08";
+    c_data = (const byte *) "\x28\xb5\x2f\xfd\x20\x20\x3d\x00\x00\x08\xff\x01\x00\x34\x4e\x08";
     d_len = 32;
     r = upx_zstd_decompress(c_data, 16, d_buf, &d_len, M_ZSTD, nullptr);
     CHECK((r == 0 && d_len == 32));
diff --git a/src/conf.h b/src/conf.h
index f3ed0cda..1cb81126 100644
--- a/src/conf.h
+++ b/src/conf.h
@@ -100,6 +100,7 @@ ACC_COMPILE_TIME_ASSERT_HEADER((char)(-1) == 255) // -funsigned-char
 #  pragma warning(error: 4805)
 #endif
 #endif // !UPX_CONFIG_DISABLE_WSTRICT && !UPX_CONFIG_DISABLE_WERROR
+
 // disable some warnings
 #if (ACC_CC_MSC)
 #  pragma warning(disable: 4244) // -Wconversion
@@ -124,22 +125,30 @@ ACC_COMPILE_TIME_ASSERT_HEADER((char)(-1) == 255) // -funsigned-char
 #include 
 #include 
 #include 
-#if __STDC_NO_ATOMICS__ || 1
-// UPX currently does not use multithreading
-#define upx_std_atomic(Type)    Type
-//#define upx_std_atomic(Type)    typename std::add_volatile::type
-#define upx_std_once_flag       upx_std_atomic(size_t)
-template 
-inline void upx_std_call_once(upx_std_once_flag &flag, NoexceptCallable &&f) {
-    if (__acc_unlikely(!flag)) { flag = 1; f(); }
-}
-#else
+
+// multithreading (UPX currently does not use multithreading)
+#ifndef WITH_THREADS
+#  define WITH_THREADS 0
+#endif
+#if __STDC_NO_ATOMICS__
+#  undef WITH_THREADS
+#endif
+#if (WITH_THREADS)
+#define upx_thread_local        thread_local
 #include 
 #define upx_std_atomic(Type)    std::atomic
 #include 
 #define upx_std_once_flag       std::once_flag
 #define upx_std_call_once       std::call_once
-#endif
+#else
+#define upx_thread_local        /*empty*/
+#define upx_std_atomic(Type)    Type
+#define upx_std_once_flag       upx_std_atomic(size_t)
+template 
+inline void upx_std_call_once(upx_std_once_flag &flag, NoexceptCallable &&f) {
+    if (__acc_unlikely(!flag)) { flag = 1; f(); }
+}
+#endif // WITH_THREADS
 
 // C++ submodule headers
 #include 
@@ -166,16 +175,39 @@ inline void upx_std_call_once(upx_std_once_flag &flag, NoexceptCallable &&f) {
 
 //  C++20 std::is_bounded_array
 template 
-struct std_is_bounded_array : public std::false_type {};
+struct upx_std_is_bounded_array : public std::false_type {};
 template 
-struct std_is_bounded_array : public std::true_type {};
+struct upx_std_is_bounded_array : public std::true_type {};
+template 
+inline constexpr bool upx_std_is_bounded_array_v = upx_std_is_bounded_array::value;
+
+// see bele.h
+template 
+struct upx_is_integral : public std::is_integral {};
+template 
+inline constexpr bool upx_is_integral_v = upx_is_integral::value;
+
+#if (ACC_ARCH_M68K && ACC_OS_TOS && ACC_CC_GNUC) && defined(__MINT__)
+// horrible hack for broken compiler
+#define upx_fake_alignas_1      __attribute__((__aligned__(1),__packed__))
+#define upx_fake_alignas_16     __attribute__((__aligned__(2))) // object file maximum 2 ???
+#define upx_fake_alignas__(a)   upx_fake_alignas_ ## a
+#define alignas(x)              upx_fake_alignas__(x)
+#endif
 
 
 /*************************************************************************
 // core
 **************************************************************************/
 
-// intergral types
+// protect against integer overflows and malicious header fields
+// see C 11 standard, Annex K
+typedef size_t upx_rsize_t;
+#define UPX_RSIZE_MAX       UPX_RSIZE_MAX_MEM
+#define UPX_RSIZE_MAX_MEM   (768 * 1024 * 1024)   // DO NOT CHANGE !!!
+#define UPX_RSIZE_MAX_STR   (256 * 1024)
+
+// integral types
 typedef acc_int8_t      upx_int8_t;
 typedef acc_uint8_t     upx_uint8_t;
 typedef acc_int16_t     upx_int16_t;
@@ -186,16 +218,17 @@ typedef acc_int64_t     upx_int64_t;
 typedef acc_uint64_t    upx_uint64_t;
 typedef acc_uintptr_t   upx_uintptr_t;
 
+// convention: use "byte" when dealing with data; use "char/uchar" when dealing
+// with strings; use "upx_uint8_t" when dealing with small integers
 typedef unsigned char   byte;
 #define upx_byte        byte
 #define upx_bytep       byte *
-
-// protect against integer overflows and malicious header fields
-// see C 11 standard, Annex K
-typedef size_t upx_rsize_t;
-#define UPX_RSIZE_MAX       UPX_RSIZE_MAX_MEM
-#define UPX_RSIZE_MAX_MEM   (768 * 1024 * 1024)   // DO NOT CHANGE !!!
-#define UPX_RSIZE_MAX_STR   (1024 * 1024)
+typedef unsigned char   uchar;
+// use "charptr" when dealing with pointer arithmetics
+#define charptr         upx_charptr_unit_type *
+// upx_charptr_unit_type is some opaque type with sizeof(type) == 1
+struct alignas(1) upx_charptr_unit_type { char dummy; };
+ACC_COMPILE_TIME_ASSERT_HEADER(sizeof(upx_charptr_unit_type) == 1)
 
 // using the system off_t was a bad idea even back in 199x...
 typedef upx_int64_t upx_off_t;
@@ -244,8 +277,8 @@ typedef upx_int64_t upx_off_t;
 #endif
 #if (ACC_OS_DOS32) && defined(__DJGPP__)
 #  undef sopen
-#  undef __unix__
 #  undef __unix
+#  undef __unix__
 #endif
 
 #ifndef STDIN_FILENO
@@ -304,7 +337,7 @@ typedef upx_int64_t upx_off_t;
 #  endif
 #endif
 
-// avoid warnings about shadowing global functions
+// avoid warnings about shadowing global symbols
 #undef _base
 #undef basename
 #undef index
@@ -391,14 +424,6 @@ inline void NO_fprintf(FILE *, const char *, ...) {}
 #define __packed_struct(s)      struct alignas(1) s {
 #define __packed_struct_end()   };
 
-#if (ACC_ARCH_M68K && ACC_OS_TOS && ACC_CC_GNUC) && defined(__MINT__)
-// horrible hack for broken compiler
-#define upx_fake_alignas_1      __attribute__((__aligned__(1),__packed__))
-#define upx_fake_alignas_16     __attribute__((__aligned__(2))) // object file maximum 2 ???
-#define upx_fake_alignas__(a)   upx_fake_alignas_ ## a
-#define alignas(x)              upx_fake_alignas__(x)
-#endif
-
 #define COMPILE_TIME_ASSERT_ALIGNOF_USING_SIZEOF__(a,b) { \
      typedef a acc_tmp_a_t; typedef b acc_tmp_b_t; \
      struct alignas(1) acc_tmp_t { acc_tmp_b_t x; acc_tmp_a_t y; acc_tmp_b_t z; }; \
@@ -426,15 +451,13 @@ inline const T& UPX_MAX(const T& a, const T& b) { if (a < b) return b; return a;
 template 
 inline const T& UPX_MIN(const T& a, const T& b) { if (a < b) return a; return b; }
 
-template 
-struct USizeOfTypeImpl {
-    static forceinline constexpr unsigned value() {
-        COMPILE_TIME_ASSERT(TypeSize >= 1 && TypeSize <= 64 * 1024) // arbitrary limit
-        return ACC_ICONV(unsigned, TypeSize);
-    }
+template 
+struct UnsignedSizeOf {
+    static_assert(Size >= 1 && Size <= UPX_RSIZE_MAX_MEM);
+    static constexpr unsigned value = unsigned(Size);
 };
-#define usizeof(type)   (USizeOfTypeImpl::value())
-ACC_COMPILE_TIME_ASSERT_HEADER(usizeof(int) == 4)
+#define usizeof(expr)   (UnsignedSizeOf::value)
+ACC_COMPILE_TIME_ASSERT_HEADER(usizeof(int) == sizeof(int))
 
 // An Array allocates memory on the heap, and automatically
 // gets destructed when leaving scope or on exceptions.
@@ -442,16 +465,16 @@ ACC_COMPILE_TIME_ASSERT_HEADER(usizeof(int) == 4)
     MemBuffer var ## _membuf(mem_size(sizeof(type), size)); \
     type * const var = ACC_STATIC_CAST(type *, var ## _membuf.getVoidPtr())
 
-#define ByteArray(var, size)    Array(unsigned char, var, size)
+#define ByteArray(var, size)    Array(byte, var, size)
 
 
 class noncopyable
 {
 protected:
-    inline noncopyable() {}
-    inline ~noncopyable() {}
+    inline noncopyable() noexcept {}
+    inline ~noncopyable() noexcept {}
 private:
-    noncopyable(const noncopyable &) DELETED_FUNCTION; // copy constuctor
+    noncopyable(const noncopyable &) DELETED_FUNCTION; // copy constructor
     noncopyable& operator=(const noncopyable &) DELETED_FUNCTION; // copy assignment
     noncopyable(noncopyable &&) DELETED_FUNCTION; // move constructor
     noncopyable& operator=(noncopyable &&) DELETED_FUNCTION; // move assignment
@@ -466,7 +489,7 @@ constexpr bool string_eq(const char *a, const char *b) {
     return *a == *b && (*a == '\0' || string_eq(a + 1, b + 1));
 }
 constexpr bool string_lt(const char *a, const char *b) {
-    return (unsigned char)*a < (unsigned char)*b || (*a != '\0' && *a == *b && string_lt(a + 1, b + 1));
+    return (uchar)*a < (uchar)*b || (*a != '\0' && *a == *b && string_lt(a + 1, b + 1));
 }
 constexpr bool string_ne(const char *a, const char *b) {
     return !string_eq(a, b);
@@ -644,7 +667,7 @@ struct upx_callback_t
     upx_progress_func_t nprogress;
     void *user;
 
-    void reset() { memset(this, 0, sizeof(*this)); }
+    void reset() noexcept { memset(this, 0, sizeof(*this)); }
 };
 
 
@@ -670,7 +693,7 @@ struct OptVar
         assertValue(v);
     }
 
-    OptVar() : v(default_value), is_set(false) { }
+    OptVar() noexcept : v(default_value), is_set(false) { }
     OptVar& operator= (const T &other) {
         assertValue(other);
         v = other;
@@ -678,8 +701,8 @@ struct OptVar
         return *this;
     }
 
-    void reset() { v = default_value; is_set = false; }
-    operator T () const { return v; }
+    void reset() noexcept { v = default_value; is_set = false; }
+    operator T () const noexcept { return v; }
 
     T v;
     bool is_set;
@@ -818,26 +841,16 @@ struct upx_compress_result_t
 // globals
 **************************************************************************/
 
-#include "util/snprintf.h"   // must get included first!
-#include "options.h"
-#include "except.h"
-#include "bele.h"
-#include "console/console.h"
-#include "util/util.h"
-
 // classes
 class ElfLinker;
 typedef ElfLinker Linker;
+class Throwable;
 
 // util/membuffer.h
 class MemBuffer;
 void *membuffer_get_void_ptr(MemBuffer &mb);
 unsigned membuffer_get_size(MemBuffer &mb);
 
-// xspan
-#include "util/raw_bytes.h"
-#include "util/xspan.h"
-
 // util/dt_check.cpp
 void upx_compiler_sanity_check();
 int upx_doctest_check();
@@ -870,7 +883,7 @@ int do_files(int i, int argc, char *argv[]);
 
 // help.cpp
 extern const char gitrev[];
-void show_head();
+void show_header();
 void show_help(int verbose=0);
 void show_license();
 void show_usage();
@@ -898,6 +911,17 @@ int upx_test_overlap       ( const upx_bytep buf,
                              const upx_compress_result_t *cresult );
 
 
+#include "util/snprintf.h"   // must get included first!
+#include "options.h"
+#include "except.h"
+#include "bele.h"
+#include "console/console.h"
+#include "util/util.h"
+// xspan
+#include "util/raw_bytes.h"
+#include "util/xspan.h"
+
+
 #if (ACC_OS_CYGWIN || ACC_OS_DOS16 || ACC_OS_DOS32 || ACC_OS_EMX || ACC_OS_OS2 || ACC_OS_OS216 || ACC_OS_WIN16 || ACC_OS_WIN32 || ACC_OS_WIN64)
 #  if defined(INVALID_HANDLE_VALUE) || defined(MAKEWORD) || defined(RT_CURSOR)
 #    error "something pulled in "
diff --git a/src/file.cpp b/src/file.cpp
index efa1e313..cee8dd77 100644
--- a/src/file.cpp
+++ b/src/file.cpp
@@ -269,6 +269,17 @@ void OutputFile::write(SPAN_0(const void) buf, int len) {
     if (l != len)
         throwIOException("write error", errno);
     bytes_written += len;
+#if TESTING && 0
+    static upx_std_atomic(bool) dumping;
+    if (!dumping) {
+        dumping = true;
+        char fn[64];
+        static int part = 0;
+        snprintf(fn, sizeof(fn), "upx-dump-%04d.data", part++);
+        OutputFile::dump(fn, buf, len);
+        dumping = false;
+    }
+#endif
 }
 
 upx_off_t OutputFile::st_size() const {
diff --git a/src/help.cpp b/src/help.cpp
index c6139fa3..3be518a6 100644
--- a/src/help.cpp
+++ b/src/help.cpp
@@ -25,36 +25,33 @@
                   
  */
 
-
 #include "conf.h"
 #include "packmast.h"
 #include "packer.h"
 #include "compress/compress.h" // upx_ucl_version_string()
 
-
 /*************************************************************************
-//
+// header
 **************************************************************************/
 
-static bool head_done = 0;
-
 // also see UPX_CONFIG_DISABLE_GITREV in CMakeLists.txt
 #if defined(UPX_VERSION_GITREV)
 const char gitrev[] = UPX_VERSION_GITREV;
 #else
-const char gitrev[1] = { 0 };
+const char gitrev[1] = {0};
 #endif
 
-void show_head(void)
-{
+void show_header(void) {
     FILE *f = con_term;
     int fg;
 
-    if (head_done)
+    static bool header_done;
+    if (header_done)
         return;
-    head_done = 1;
+    header_done = true;
 
-    fg = con_fg(f,FG_GREEN);
+    fg = con_fg(f, FG_GREEN);
+    // clang-format off
     con_fprintf(f,
                 "                       Ultimate Packer for eXecutables\n"
                 "                          Copyright (C) 1996 - " UPX_VERSION_YEAR "\n"
@@ -71,22 +68,19 @@ void show_head(void)
                 UPX_VERSION_STRING,
 #endif
                 UPX_VERSION_DATE);
-    fg = con_fg(f,fg);
-#undef V
-
+    // clang-format on
+    fg = con_fg(f, fg);
     UNUSED(fg);
 }
 
-
 /*************************************************************************
-//
+// usage
 **************************************************************************/
 
-void show_usage(void)
-{
+void show_usage(void) {
     FILE *f = con_term;
 
-    con_fprintf(f,"Usage: %s [-123456789dlthVL] [-qvfk] [-o file] %sfile..\n", progname,
+    con_fprintf(f, "Usage: %s [-123456789dlthVL] [-qvfk] [-o file] %sfile..\n", progname,
 #if (ACC_OS_DOS32) && defined(__DJGPP__)
                 "[@]");
 #else
@@ -94,34 +88,30 @@ void show_usage(void)
 #endif
 }
 
-
 /*************************************************************************
-//
+// util
 **************************************************************************/
 
-struct PackerNames
-{
+namespace {
+struct PackerNames {
     struct Entry {
-        const char* fname;
-        const char* sname;
+        const char *fname;
+        const char *sname;
     };
     Entry names[64];
     size_t names_count;
-    const options_t *o;
-    PackerNames() : names_count(0), o(nullptr) { }
-    void add(const Packer *p)
-    {
-        p->assertPacker();
+    const Options *o;
+    PackerNames() : names_count(0), o(nullptr) {}
+    void add(const Packer *p) {
         assert(names_count < 64);
         names[names_count].fname = p->getFullName(o);
         names[names_count].sname = p->getName();
         names_count++;
     }
-    static Packer* visit(Packer *p, void *user)
-    {
+    static Packer *visit(Packer *p, void *user) {
         PackerNames *self = (PackerNames *) user;
         self->add(p);
-        delete p; p = nullptr;
+        delete p;
         return nullptr;
     }
     static int __acc_cdecl_qsort cmp_fname(const void *a, const void *b) {
@@ -132,23 +122,20 @@ struct PackerNames
     }
 };
 
-static void show_all_packers(FILE *f, int verbose)
-{
-    options_t o; o.reset();
-    PackerNames pn; pn.o = &o;
+static void show_all_packers(FILE *f, int verbose) {
+    Options o;
+    o.reset();
+    PackerNames pn;
+    pn.o = &o;
     PackMaster::visitAllPackers(PackerNames::visit, nullptr, &o, &pn);
     qsort(pn.names, pn.names_count, sizeof(PackerNames::Entry), PackerNames::cmp_fname);
     size_t pos = 0;
-    for (size_t i = 0; i < pn.names_count; ++i)
-    {
+    for (size_t i = 0; i < pn.names_count; ++i) {
         const char *fn = pn.names[i].fname;
         const char *sn = pn.names[i].sname;
-        if (verbose > 0)
-        {
+        if (verbose > 0) {
             con_fprintf(f, "    %-36s %s\n", fn, sn);
-        }
-        else
-        {
+        } else {
             size_t fl = strlen(fn);
             if (pos == 0) {
                 con_fprintf(f, "  %s", fn);
@@ -165,23 +152,23 @@ static void show_all_packers(FILE *f, int verbose)
     if (verbose <= 0 && pn.names_count)
         con_fprintf(f, "\n");
 }
-
+} // namespace
 
 /*************************************************************************
-//
+// help
 **************************************************************************/
 
-void show_help(int verbose)
-{
+void show_help(int verbose) {
     FILE *f = con_term;
     int fg;
 
-    show_head();
+    show_header();
     show_usage();
 
-    fg = con_fg(f,FG_YELLOW);
-    con_fprintf(f,"\nCommands:\n");
-    fg = con_fg(f,fg);
+    // clang-format off
+    fg = con_fg(f, FG_YELLOW);
+    con_fprintf(f, "\nCommands:\n");
+    fg = con_fg(f, fg);
     con_fprintf(f,
                 "  -1     compress faster                   -9    compress better\n"
                 "%s"
@@ -191,9 +178,9 @@ void show_help(int verbose)
                 verbose == 0 ? "" : "  --best compress best (can be slow for big files)\n",
                 verbose == 0 ? "more" : "this", verbose == 0 ? "" : "\n");
 
-    fg = con_fg(f,FG_YELLOW);
-    con_fprintf(f,"Options:\n");
-    fg = con_fg(f,fg);
+    fg = con_fg(f, FG_YELLOW);
+    con_fprintf(f, "Options:\n");
+    fg = con_fg(f, fg);
 
     con_fprintf(f,
                 "  -q     be quiet                          -v    be verbose\n"
@@ -211,72 +198,72 @@ void show_help(int verbose)
 
     if (verbose > 0)
     {
-        fg = con_fg(f,FG_YELLOW);
-        con_fprintf(f,"\nCompression tuning options:\n");
-        fg = con_fg(f,fg);
+        fg = con_fg(f, FG_YELLOW);
+        con_fprintf(f, "\nCompression tuning options:\n");
+        fg = con_fg(f, fg);
         con_fprintf(f,
                     "  --lzma              try LZMA [slower but tighter than NRV]\n"
                     "  --brute             try all available compression methods & filters [slow]\n"
                     "  --ultra-brute       try even more compression variants [very slow]\n"
                     "\n");
-        fg = con_fg(f,FG_YELLOW);
-        con_fprintf(f,"Backup options:\n");
-        fg = con_fg(f,fg);
+        fg = con_fg(f, FG_YELLOW);
+        con_fprintf(f, "Backup options:\n");
+        fg = con_fg(f, fg);
         con_fprintf(f,
                     "  -k, --backup        keep backup files\n"
                     "  --no-backup         no backup files [default]\n"
                     "\n");
-        fg = con_fg(f,FG_YELLOW);
-        con_fprintf(f,"Overlay options:\n");
-        fg = con_fg(f,fg);
+        fg = con_fg(f, FG_YELLOW);
+        con_fprintf(f, "Overlay options:\n");
+        fg = con_fg(f, fg);
         con_fprintf(f,
                     "  --overlay=copy      copy any extra data attached to the file [default]\n"
                     "  --overlay=strip     strip any extra data attached to the file [DANGEROUS]\n"
                     "  --overlay=skip      don't compress a file with an overlay\n"
                     "\n");
-        fg = con_fg(f,FG_YELLOW);
-        con_fprintf(f,"Options for djgpp2/coff:\n");
-        fg = con_fg(f,fg);
+        fg = con_fg(f, FG_YELLOW);
+        con_fprintf(f, "Options for djgpp2/coff:\n");
+        fg = con_fg(f, fg);
         con_fprintf(f,
                     "  --coff              produce COFF output [default: EXE]\n"
                     "\n");
-        fg = con_fg(f,FG_YELLOW);
-        con_fprintf(f,"Options for dos/com:\n");
-        fg = con_fg(f,fg);
+        fg = con_fg(f, FG_YELLOW);
+        con_fprintf(f, "Options for dos/com:\n");
+        fg = con_fg(f, fg);
         con_fprintf(f,
                     "  --8086              make compressed com work on any 8086\n"
                     "\n");
-        fg = con_fg(f,FG_YELLOW);
-        con_fprintf(f,"Options for dos/exe:\n");
-        fg = con_fg(f,fg);
+        fg = con_fg(f, FG_YELLOW);
+        con_fprintf(f, "Options for dos/exe:\n");
+        fg = con_fg(f, fg);
         con_fprintf(f,
                     "  --8086              make compressed exe work on any 8086\n"
                     "  --no-reloc          put no relocations in to the exe header\n"
                     "\n");
-        fg = con_fg(f,FG_YELLOW);
-        con_fprintf(f,"Options for dos/sys:\n");
-        fg = con_fg(f,fg);
+        fg = con_fg(f, FG_YELLOW);
+        con_fprintf(f, "Options for dos/sys:\n");
+        fg = con_fg(f, fg);
         con_fprintf(f,
                     "  --8086              make compressed sys work on any 8086\n"
                     "\n");
-        fg = con_fg(f,FG_YELLOW);
-        con_fprintf(f,"Options for ps1/exe:\n");
-        fg = con_fg(f,fg);
+        fg = con_fg(f, FG_YELLOW);
+        con_fprintf(f, "Options for ps1/exe:\n");
+        fg = con_fg(f, fg);
         con_fprintf(f,
                     "  --8-bit             uses 8 bit size compression [default: 32 bit]\n"
                     "  --8mib-ram          8 megabyte memory limit [default: 2 MiB]\n"
                     "  --boot-only         disables client/host transfer compatibility\n"
                     "  --no-align          don't align to 2048 bytes [enables: --console-run]\n"
                     "\n");
-        fg = con_fg(f,FG_YELLOW);
-        con_fprintf(f,"Options for watcom/le:\n");
-        fg = con_fg(f,fg);
+        fg = con_fg(f, FG_YELLOW);
+        con_fprintf(f, "Options for watcom/le:\n");
+        fg = con_fg(f, fg);
         con_fprintf(f,
                     "  --le                produce LE output [default: EXE]\n"
                     "\n");
-        fg = con_fg(f,FG_YELLOW);
-        con_fprintf(f,"Options for win32/pe, win64/pe & rtm32/pe:\n");
-        fg = con_fg(f,fg);
+        fg = con_fg(f, FG_YELLOW);
+        con_fprintf(f, "Options for win32/pe, win64/pe & rtm32/pe:\n");
+        fg = con_fg(f, fg);
         con_fprintf(f,
                     "  --compress-exports=0    do not compress the export section\n"
                     "  --compress-exports=1    compress the export section [default]\n"
@@ -289,59 +276,55 @@ void show_help(int verbose)
                     "  --strip-relocs=0        do not strip relocations\n"
                     "  --strip-relocs=1        strip relocations [default]\n"
                     "\n");
-        fg = con_fg(f,FG_YELLOW);
-        con_fprintf(f,"Options for linux/elf:\n");
-        fg = con_fg(f,fg);
+        fg = con_fg(f, FG_YELLOW);
+        con_fprintf(f, "Options for linux/elf:\n");
+        fg = con_fg(f, fg);
         con_fprintf(f,
                     "  --preserve-build-id     copy .gnu.note.build-id to compressed output\n"
                     "\n");
     }
+    // clang-format on
 
     con_fprintf(f, "file..   executables to (de)compress\n");
 
-    if (verbose > 0)
-    {
-        fg = con_fg(f,FG_YELLOW);
-        con_fprintf(f,"\nThis version supports:\n");
-        fg = con_fg(f,fg);
+    if (verbose > 0) {
+        fg = con_fg(f, FG_YELLOW);
+        con_fprintf(f, "\nThis version supports:\n");
+        fg = con_fg(f, fg);
         show_all_packers(f, verbose);
-    }
-    else
-    {
-        con_fprintf(f,"\nType '%s --help' for more detailed help.\n", progname);
+    } else {
+        con_fprintf(f, "\nType '%s --help' for more detailed help.\n", progname);
     }
 
-    con_fprintf(f,"\nUPX comes with ABSOLUTELY NO WARRANTY; for details visit https://upx.github.io\n"
-//                "\nUPX comes with ABSOLUTELY NO WARRANTY; for details type 'upx -L'.\n"
-                "");
+    con_fprintf(f, "\nUPX comes with ABSOLUTELY NO WARRANTY; "
+                   "for details visit https://upx.github.io\n");
 
 #if DEBUG || TESTING
-    fg = con_fg(f,FG_RED);
-    con_fprintf(f,"\nWARNING: this version is compiled with"
+    fg = con_fg(f, FG_RED);
+    con_fprintf(f, "\nWARNING: this version is compiled with"
 #if DEBUG
-                " -DDEBUG"
+                   " -DDEBUG"
 #endif
 #if TESTING
-                " -DTESTING"
+                   " -DTESTING"
 #endif
-                "\n");
-    fg = con_fg(f,fg);
+                   "\n");
+    fg = con_fg(f, fg);
 #endif
 
     UNUSED(fg);
 }
 
-
 /*************************************************************************
-//
+// license
 **************************************************************************/
 
-void show_license(void)
-{
+void show_license(void) {
     FILE *f = con_term;
 
-    show_head();
+    show_header();
 
+    // clang-format off
     con_fprintf(f,
         "   This program may be used freely, and you are welcome to\n"
         "   redistribute it under certain conditions.\n"
@@ -356,29 +339,27 @@ void show_license(void)
         "   If not, visit one of the following pages:\n"
         "\n"
     );
-    int fg = con_fg(f,FG_CYAN);
+    int fg = con_fg(f, FG_CYAN);
     con_fprintf(f,
         "        https://upx.github.io\n"
         "        https://www.oberhumer.com/opensource/upx/\n"
     );
-    (void)con_fg(f,FG_ORANGE);
+    (void) con_fg(f, FG_ORANGE);
     con_fprintf(f,
         "\n"
         "   Markus F.X.J. Oberhumer              Laszlo Molnar\n"
         "                  \n"
     );
-    fg = con_fg(f,fg);
-
+    // clang-format on
+    fg = con_fg(f, fg);
     UNUSED(fg);
 }
 
-
 /*************************************************************************
-//
+// version
 **************************************************************************/
 
-void show_version(bool one_line)
-{
+void show_version(bool one_line) {
     FILE *fp = stdout;
     const char *v;
 
@@ -391,6 +372,7 @@ void show_version(bool one_line)
 #endif
     if (one_line)
         return;
+
 #if (WITH_NRV)
     v = upx_nrv_version_string();
     if (v != nullptr && v[0])
@@ -419,6 +401,7 @@ void show_version(bool one_line)
 #if !defined(DOCTEST_CONFIG_DISABLE)
     fprintf(fp, "doctest C++ testing framework version %s\n", DOCTEST_VERSION_STR);
 #endif
+    // clang-format off
     fprintf(fp, "Copyright (C) 1996-2023 Markus Franz Xaver Johannes Oberhumer\n");
     fprintf(fp, "Copyright (C) 1996-2023 Laszlo Molnar\n");
     fprintf(fp, "Copyright (C) 2000-2023 John F. Reiser\n");
@@ -437,6 +420,7 @@ void show_version(bool one_line)
     fprintf(fp, "Copyright (C) 2016" "-2021 Viktor Kirilov\n");
 #endif
     fprintf(fp, "UPX comes with ABSOLUTELY NO WARRANTY; for details type '%s -L'.\n", progname);
+    // clang-format on
 }
 
 /* vim:set ts=4 sw=4 et: */
diff --git a/src/linker.cpp b/src/linker.cpp
index 28bf3d21..4bae3d06 100644
--- a/src/linker.cpp
+++ b/src/linker.cpp
@@ -28,7 +28,7 @@
 #include "conf.h"
 #include "linker.h"
 
-static unsigned hex(unsigned char c) { return (c & 0xf) + (c > '9' ? 9 : 0); }
+static unsigned hex(uchar c) { return (c & 0xf) + (c > '9' ? 9 : 0); }
 
 static bool update_capacity(unsigned size, unsigned *capacity) {
     if (size < *capacity)
@@ -67,7 +67,7 @@ ElfLinker::Section::Section(const char *n, const void *i, unsigned s, unsigned a
     ((char *) input)[s] = 0;
 }
 
-ElfLinker::Section::~Section() {
+ElfLinker::Section::~Section() noexcept {
     free(name);
     free(input);
 }
@@ -83,7 +83,7 @@ ElfLinker::Symbol::Symbol(const char *n, Section *s, upx_uint64_t o)
     assert(section != nullptr);
 }
 
-ElfLinker::Symbol::~Symbol() { free(name); }
+ElfLinker::Symbol::~Symbol() noexcept { free(name); }
 
 /*************************************************************************
 // Relocation
@@ -105,7 +105,7 @@ ElfLinker::ElfLinker()
       nsections_capacity(0), nsymbols(0), nsymbols_capacity(0), nrelocations(0),
       nrelocations_capacity(0), reloc_done(false) {}
 
-ElfLinker::~ElfLinker() {
+ElfLinker::~ElfLinker() noexcept {
     delete[] input;
     delete[] output;
 
@@ -548,7 +548,7 @@ upx_uint64_t ElfLinker::getSymbolOffset(const char *name) const {
     return symbol->section->offset + symbol->offset;
 }
 
-void ElfLinker::alignWithByte(unsigned len, unsigned char b) {
+void ElfLinker::alignWithByte(unsigned len, byte b) {
     memset(output + outputlen, b, len);
     outputlen += len;
 }
@@ -559,7 +559,7 @@ void ElfLinker::relocate1(const Relocation *rel, byte *, upx_uint64_t, const cha
 
 /*************************************************************************
 // ElfLinker arch subclasses
-// FIXME: add more displacment overflow checks
+// FIXME: add more displacement overflow checks
 // FIXME: add support for our special "ignore_reloc_overflow" section
 **************************************************************************/
 
@@ -802,12 +802,12 @@ void ElfLinkerPpc32::relocate1(const Relocation *rel, byte *location, upx_uint64
     if (strcmp(type, "24") == 0) {
         if (3 & value)
             internal_error("unaligned word diplacement");
-        // FIXME: displacment overflow?
+        // FIXME: displacement overflow?
         set_be32(location, (0xfc000003 & get_be32(location)) + (0x03fffffc & value));
     } else if (strcmp(type, "14") == 0) {
         if (3 & value)
             internal_error("unaligned word diplacement");
-        // FIXME: displacment overflow?
+        // FIXME: displacement overflow?
         set_be32(location, (0xffff0003 & get_be32(location)) + (0x0000fffc & value));
     } else
         super::relocate1(rel, location, value, type);
diff --git a/src/linker.h b/src/linker.h
index 70981213..95d51620 100644
--- a/src/linker.h
+++ b/src/linker.h
@@ -77,7 +77,7 @@ protected:
 
 public:
     ElfLinker();
-    virtual ~ElfLinker();
+    virtual ~ElfLinker() noexcept;
 
     void init(const void *pdata, int plen, unsigned pxtra = 0);
     // virtual void setLoaderAlignOffset(int phase);
@@ -98,7 +98,7 @@ public:
     void dumpSymbol(const Symbol *, unsigned flags, FILE *fp) const;
     void dumpSymbols(unsigned flags = 0, FILE *fp = nullptr) const;
 
-    void alignWithByte(unsigned len, unsigned char b);
+    void alignWithByte(unsigned len, byte b);
     virtual void alignCode(unsigned len) { alignWithByte(len, 0); }
     virtual void alignData(unsigned len) { alignWithByte(len, 0); }
 
@@ -133,7 +133,7 @@ struct ElfLinker::Section : private noncopyable {
     Section *next = nullptr;
 
     Section(const char *n, const void *i, unsigned s, unsigned a = 0);
-    ~Section();
+    ~Section() noexcept;
 };
 
 struct ElfLinker::Symbol : private noncopyable {
@@ -142,7 +142,7 @@ struct ElfLinker::Symbol : private noncopyable {
     upx_uint64_t offset = 0;
 
     Symbol(const char *n, Section *s, upx_uint64_t o);
-    ~Symbol();
+    ~Symbol() noexcept;
 };
 
 struct ElfLinker::Relocation : private noncopyable {
@@ -153,6 +153,7 @@ struct ElfLinker::Relocation : private noncopyable {
     upx_uint64_t add; // used in .rela relocations
 
     Relocation(const Section *s, unsigned o, const char *t, const Symbol *v, upx_uint64_t a);
+    ~Relocation() noexcept {}
 };
 
 /*************************************************************************
diff --git a/src/main.cpp b/src/main.cpp
index 319652a3..f3613a3e 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -626,7 +626,7 @@ static int do_option(int optc, const char *arg) {
         opt->backup = 1;
         break;
     case 541:
-        if (opt->backup != 1) // do not overide '--backup'
+        if (opt->backup != 1) // do not override '--backup'
             opt->backup = 0;
         break;
     // overlay
@@ -974,11 +974,10 @@ int main_get_options(int argc, char **argv) {
     int optc, longind;
     char shortopts[256];
 
-    prepare_shortopts(shortopts, "123456789hH?V", longopts),
-        acc_getopt_init(&mfx_getopt, 1, argc, argv);
+    prepare_shortopts(shortopts, "123456789hH?V", longopts);
+    acc_getopt_init(&mfx_getopt, 1, argc, argv);
     mfx_getopt.progname = progname;
     mfx_getopt.opterr = handle_opterr;
-    opt->o_unix.osabi0 = Elf32_Ehdr::ELFOSABI_LINUX;
     while ((optc = acc_getopt(&mfx_getopt, shortopts, longopts, &longind)) >= 0) {
         if (do_option(optc, argv[mfx_optind - 1]) != 0)
             e_usage();
@@ -1088,7 +1087,7 @@ void main_get_envoptions() {
 
     /* alloc temp argv */
     if (targc > 1)
-        targv = (const char **) calloc(targc + 1, sizeof(char *));
+        targv = (const char **) upx_calloc(targc + 1, sizeof(char *));
     if (targv == nullptr) {
         free(env);
         return;
@@ -1188,7 +1187,7 @@ int upx_main(int argc, char *argv[]) {
         char *p;
         bool allupper = true;
         for (p = prog; *p; p++)
-            if (islower((unsigned char) *p))
+            if (islower((uchar) *p))
                 allupper = false;
         if (allupper)
             fn_strlwr(prog);
diff --git a/src/options.cpp b/src/options.cpp
index 2607f84f..cc8b64a9 100644
--- a/src/options.cpp
+++ b/src/options.cpp
@@ -27,15 +27,19 @@
 
 #include "conf.h"
 
-static options_t global_options;
-options_t *opt = &global_options; // also see class PackMaster
+static Options global_options;
+Options *opt = &global_options; // also see class PackMaster
+
+#if WITH_THREADS
+std::mutex opt_lock_mutex;
+#endif
 
 /*************************************************************************
 // reset
 **************************************************************************/
 
-void options_t::reset() {
-    options_t *o = this;
+void Options::reset() {
+    Options *const o = this;
     mem_clear(o, sizeof(*o));
     o->crp.reset();
 
@@ -65,7 +69,7 @@ void options_t::reset() {
     o->win32_pe.compress_exports = 1;
     o->win32_pe.compress_icons = 2;
     o->win32_pe.compress_resources = -1;
-    for (unsigned i = 0; i < TABLESIZE(o->win32_pe.compress_rt); i++)
+    for (size_t i = 0; i < TABLESIZE(o->win32_pe.compress_rt); i++)
         o->win32_pe.compress_rt[i] = -1;
     o->win32_pe.compress_rt[24] = false; // 24 == RT_MANIFEST
     o->win32_pe.strip_relocs = -1;
@@ -76,9 +80,13 @@ void options_t::reset() {
 // doctest checks
 **************************************************************************/
 
-TEST_CASE("options_t::reset") {
-    options_t local_options;
-    options_t *o = &local_options;
+TEST_CASE("Options::reset") {
+    COMPILE_TIME_ASSERT(std::is_standard_layout::value)
+    COMPILE_TIME_ASSERT(std::is_nothrow_default_constructible::value)
+    COMPILE_TIME_ASSERT(std::is_trivially_copyable::value)
+
+    Options local_options;
+    Options *o = &local_options;
     o->reset();
     CHECK(o->o_unix.osabi0 == 3);
 }
@@ -89,8 +97,11 @@ static inline void test_options(const char *(&a)[N]) {
 }
 
 TEST_CASE("getopt") {
-    options_t *const saved_opt = opt;
-    options_t local_options;
+#if WITH_THREADS
+    std::lock_guard lock(opt_lock_mutex);
+#endif
+    Options *const saved_opt = opt;
+    Options local_options;
     opt = &local_options;
     opt->reset();
     opt->debug.getopt_throw_instead_of_exit = true;
diff --git a/src/options.h b/src/options.h
index f5654482..aae90bea 100644
--- a/src/options.h
+++ b/src/options.h
@@ -29,6 +29,14 @@
 #ifndef UPX_OPTIONS_H__
 #define UPX_OPTIONS_H__ 1
 
+struct Options;
+extern Options *opt;
+#define options_t Options // old name
+
+#if WITH_THREADS
+extern std::mutex opt_lock_mutex;
+#endif
+
 /*************************************************************************
 // globals
 **************************************************************************/
@@ -46,7 +54,7 @@ enum {
     CMD_VERSION,
 };
 
-struct options_t final {
+struct Options final {
     int cmd;
 
     // compression options
@@ -164,8 +172,6 @@ struct options_t final {
     void reset();
 };
 
-extern struct options_t *opt;
-
 #endif /* already included */
 
 /* vim:set ts=4 sw=4 et: */
diff --git a/src/p_com.h b/src/p_com.h
index 03f265be..2d394b79 100644
--- a/src/p_com.h
+++ b/src/p_com.h
@@ -41,7 +41,7 @@ public:
     virtual int getVersion() const override { return 13; }
     virtual int getFormat() const override { return UPX_F_DOS_COM; }
     virtual const char *getName() const override { return "dos/com"; }
-    virtual const char *getFullName(const options_t *) const override { return "i086-dos16.com"; }
+    virtual const char *getFullName(const Options *) const override { return "i086-dos16.com"; }
     virtual const int *getCompressionMethods(int method, int level) const override;
     virtual const int *getFilters() const override;
 
diff --git a/src/p_djgpp2.h b/src/p_djgpp2.h
index 30f850c1..1e46b5d0 100644
--- a/src/p_djgpp2.h
+++ b/src/p_djgpp2.h
@@ -41,7 +41,7 @@ public:
     virtual int getVersion() const override { return 14; }
     virtual int getFormat() const override { return UPX_F_DJGPP2_COFF; }
     virtual const char *getName() const override { return "djgpp2/coff"; }
-    virtual const char *getFullName(const options_t *) const override {
+    virtual const char *getFullName(const Options *) const override {
         return "i386-dos32.djgpp2.coff";
     }
     virtual const int *getCompressionMethods(int method, int level) const override;
diff --git a/src/p_exe.cpp b/src/p_exe.cpp
index 36bab65e..bf15a16e 100644
--- a/src/p_exe.cpp
+++ b/src/p_exe.cpp
@@ -35,9 +35,8 @@
 static const CLANG_FORMAT_DUMMY_STATEMENT
 #include "stub/i086-dos16.exe.h"
 
-#define RSFCRI 4096 // Reserved Space For Compressed Relocation Info
 #define MAXMATCH 0x2000
-#define MAXRELOCS (0x8000 - MAXMATCH)
+#define MAXRELOCSIZE (0x8000 - MAXMATCH)
 
 #define DI_LIMIT 0xff00 // see the assembly why
 
@@ -49,9 +48,6 @@ PackExe::PackExe(InputFile *f) : super(f) {
     bele = &N_BELE_RTP::le_policy;
     COMPILE_TIME_ASSERT(sizeof(exe_header_t) == 32)
     COMPILE_TIME_ASSERT_ALIGNED1(exe_header_t)
-    ih_exesize = ih_imagesize = ih_overlay = 0;
-    stack_for_lzma = 0;
-    use_clear_dirty_stack = false;
 }
 
 Linker *PackExe::newLinker() const { return new ElfLinkerX86(); }
@@ -59,8 +55,7 @@ Linker *PackExe::newLinker() const { return new ElfLinkerX86(); }
 const int *PackExe::getCompressionMethods(int method, int level) const {
     bool small = ih_imagesize <= 256 * 1024;
     // disable lzma for "--brute" unless explicitly given "--lzma"
-    // WARNING: this side effect may persists for later files;
-    //   but note that class PackMaster creates per-file local options
+    // (note that class PackMaster creates per-file local options)
     if (opt->all_methods_use_lzma == 1 && !opt->method_lzma_seen)
         opt->all_methods_use_lzma = 0;
     return Packer::getDefaultCompressionMethods_8(method, level, small);
@@ -125,7 +120,7 @@ void PackExe::addLoaderEpilogue(int flag) {
     linker->defineSymbol("original_ss", ih.ss);
     linker->defineSymbol(
         "reloc_size",
-        (ph.u_len <= DI_LIMIT || (ph.u_len & 0x7fff) >= relocsize ? 0 : MAXRELOCS) - relocsize);
+        (ph.u_len <= DI_LIMIT || (ph.u_len & 0x7fff) >= relocsize ? 0 : MAXRELOCSIZE) - relocsize);
 }
 
 void PackExe::buildLoader(const Filter *) {
@@ -229,18 +224,18 @@ int PackExe::readFileHeader() {
     ih_overlay = file_size - ih_exesize;
     if (file_size_u < sizeof(ih) || ((ih.m512 | ih.p512) && ih.m512 + ih.p512 * 512u < sizeof(ih)))
         throwCantPack("illegal exe header");
-    if (file_size_u < ih_exesize || ih_imagesize <= 0 || ih_imagesize > ih_exesize)
+    if (ih_exesize > file_size_u || ih_imagesize <= 0 || ih_imagesize > ih_exesize)
         throwCantPack("exe header corrupted");
     NO_printf("dos/exe header: %d %d %d\n", ih_exesize, ih_imagesize, ih_overlay);
     return UPX_F_DOS_EXE;
 }
 
 bool PackExe::canPack() {
-    if (fn_has_ext(fi->getName(), "sys"))
+    if (fn_has_ext(fi->getName(), "sys")) // dos/sys
         return false;
     if (!readFileHeader())
         return false;
-    if (file_size < 1024)
+    if (file_size < 1024 || ih_imagesize < 512)
         throwCantPack("file is too small for dos/exe");
     fi->seek(0x3c, SEEK_SET);
     LE32 offs;
@@ -258,13 +253,17 @@ bool PackExe::canPack() {
 //
 **************************************************************************/
 
-static unsigned optimize_relocs(byte *b, const unsigned size, const byte *relocs,
-                                const unsigned nrelocs, byte *crel, bool *has_9a) {
+static unsigned optimize_relocs(SPAN_S(byte) image, const unsigned image_size,
+                                SPAN_S(const byte) relocs, const unsigned relocnum,
+                                SPAN_S(byte) crel, bool *has_9a) {
+#if WITH_XSPAN >= 2
+    ptr_check_no_overlap(image.data(image_size), image_size, relocs.data(), relocs.size_bytes(),
+                         crel.data(), crel.size_bytes());
+#endif
     if (opt->exact)
         throwCantPackExact();
 
-    byte *const crel_save = crel;
-    unsigned i;
+    SPAN_S_VAR(byte, const crel_start, crel);
     unsigned seg_high = 0;
 #if 0
     unsigned seg_low = 0xffffffff;
@@ -274,19 +273,19 @@ static unsigned optimize_relocs(byte *b, const unsigned size, const byte *relocs
     unsigned linear_high = 0;
 #endif
 
-    // pass 1 - find 0x9a bounds
-    for (i = 0; i < nrelocs; i++) {
+    // pass 1 - find 0x9a bounds in image
+    for (unsigned i = 0; i < relocnum; i++) {
         unsigned addr = get_le32(relocs + 4 * i);
-        if (addr >= size - 1)
+        if (addr >= image_size - 1)
             throwCantPack("unexpected relocation 1");
-        if (addr >= 3 && b[addr - 3] == 0x9a) {
-            unsigned seg = get_le16(b + addr);
+        if (addr >= 3 && image[addr - 3] == 0x9a) {
+            unsigned seg = get_le16(image + addr);
             if (seg > seg_high)
                 seg_high = seg;
 #if 0
             if (seg < seg_low)
                 seg_low = seg;
-            unsigned off = get_le16(b+addr-2);
+            unsigned off = get_le16(image + addr - 2);
             if (off < off_low)
                 off_low = off;
             if (off > off_high)
@@ -308,19 +307,20 @@ static unsigned optimize_relocs(byte *b, const unsigned size, const byte *relocs
     crel += 4; // to be filled in later
 
     unsigned ones = 0;
-    unsigned es = 0, di, t;
-    i = 0;
-    do {
+    unsigned es = 0;
+    for (unsigned i = 0; i < relocnum;) {
         unsigned addr = get_le32(relocs + 4 * i);
-        set_le16(crel, di = addr & 0x0f);
+        unsigned di = addr & 0x0f;
+        set_le16(crel + 0, di);
         set_le16(crel + 2, (addr >> 4) - es);
-        es = addr >> 4;
         crel += 4;
+        es = addr >> 4;
 
-        for (++i; i < nrelocs; i++) {
+        for (++i; i < relocnum; i++) {
+            unsigned t;
             addr = get_le32(relocs + 4 * i);
-            // printf ("%x\n",es*16+di);
-            if ((addr - es * 16 > 0xfffe) || (i == nrelocs - 1 && addr - es * 16 > 0xff00)) {
+            NO_printf("%x\n", es * 16 + di);
+            if ((addr - es * 16 > 0xfffe) || (i == relocnum - 1 && addr - es * 16 > 0xff00)) {
                 // segment change
                 t = 1 + (0xffff - di) / 254;
                 memset(crel, 1, t);
@@ -329,9 +329,9 @@ static unsigned optimize_relocs(byte *b, const unsigned size, const byte *relocs
                 break;
             }
             unsigned offs = addr - es * 16;
-            if (offs >= 3 && b[es * 16 + offs - 3] == 0x9a && offs > di + 3) {
+            if (offs >= 3 && image[es * 16 + offs - 3] == 0x9a && offs > di + 3) {
                 for (t = di; t < offs - 3; t++)
-                    if (b[es * 16 + t] == 0x9a && get_le16(b + es * 16 + t + 3) <= seg_high)
+                    if (image[es * 16 + t] == 0x9a && get_le16(image + es * 16 + t + 3) <= seg_high)
                         break;
                 if (t == offs - 3) {
                     // code 0: search for 0x9a
@@ -342,9 +342,8 @@ static unsigned optimize_relocs(byte *b, const unsigned size, const byte *relocs
                 }
             }
             t = offs - di;
-            if (t < 2)
+            if ((int) t < 2)
                 throwCantPack("unexpected relocation 2");
-
             while (t >= 256) {
                 // code 1: add 254, don't reloc
                 *crel++ = 1;
@@ -354,14 +353,14 @@ static unsigned optimize_relocs(byte *b, const unsigned size, const byte *relocs
             *crel++ = (byte) t;
             di = offs;
         }
-    } while (i < nrelocs);
+    }
     *crel++ = 1;
     ones++;
-    set_le16(crel_save, ones);
-    set_le16(crel_save + 2, seg_high);
+    set_le16(crel_start, ones);
+    set_le16(crel_start + 2, seg_high);
 
-    // OutputFile::dump("x.rel", crel_save, crel - crel_save);
-    return (unsigned) (crel - crel_save);
+    // OutputFile::dump("x.rel", crel_start, ptr_udiff_bytes(crel, crel_start));
+    return ptr_udiff_bytes(crel, crel_start);
 }
 
 /*************************************************************************
@@ -371,16 +370,14 @@ static unsigned optimize_relocs(byte *b, const unsigned size, const byte *relocs
 void PackExe::pack(OutputFile *fo) {
     unsigned ic;
 
-    if (ih.relocs > MAXRELOCS)
+    const unsigned relocnum = ih.relocs;
+    if (relocnum > MAXRELOCSIZE) // early check
         throwCantPack("too many relocations");
     checkOverlay(ih_overlay);
 
-    // alloc buffers
-    relocsize = RSFCRI + 4 * ih.relocs;
-    ibuf.alloc(ih_imagesize + 16 + relocsize + 2);
-    obuf.allocForCompression(ih_imagesize + 16 + relocsize + 2);
-
     // read image
+    // image + space for optimized relocs + safety/alignments
+    ibuf.alloc(ih_imagesize + 4 * relocnum + 1024);
     fi->seek(ih.headsize16 * 16, SEEK_SET);
     fi->readx(ibuf, ih_imagesize);
 
@@ -389,37 +386,41 @@ void PackExe::pack(OutputFile *fo) {
     device_driver = get_le32(ibuf) == 0xffffffffu;
 
     // relocations
+    relocsize = 0;
     has_9a = false;
-    byte *w = ibuf + ih_imagesize;
-    if (ih.relocs) {
-        byte *wr = w + RSFCRI;
-
+    if (relocnum) {
+        MemBuffer mb_relocs(4 * relocnum);
+        SPAN_S_VAR(byte, relocs, mb_relocs);
         fi->seek(ih.relocoffs, SEEK_SET);
-        fi->readx(wr, 4 * ih.relocs);
+        fi->readx(relocs, 4 * relocnum);
 
-        for (ic = 0; ic < ih.relocs; ic++) {
-            unsigned jc = get_le32(wr + 4 * ic);
-            set_le32(wr + 4 * ic, ((jc >> 16) * 16 + (jc & 0xffff)) & 0xfffff);
+        // dos/exe runs in real-mode, so convert to linear addresses
+        for (ic = 0; ic < relocnum; ic++) {
+            unsigned jc = get_le32(relocs + 4 * ic);
+            set_le32(relocs + 4 * ic, ((jc >> 16) * 16 + (jc & 0xffff)) & 0xfffff);
         }
-        qsort(wr, ih.relocs, 4, le32_compare);
-        relocsize = optimize_relocs(ibuf, ih_imagesize, wr, ih.relocs, w, &has_9a);
-        set_le16(w + relocsize, relocsize + 2);
+        qsort(raw_bytes(relocs, 4 * relocnum), relocnum, 4, le32_compare);
+
+        SPAN_S_VAR(byte, image, ibuf + 0, ih_imagesize);
+        SPAN_S_VAR(byte, crel, ibuf + ih_imagesize, ibuf);
+        relocsize = optimize_relocs(image, ih_imagesize, relocs, relocnum, crel, &has_9a);
+        set_le16(crel + relocsize, relocsize + 2);
         relocsize += 2;
-        if (relocsize > MAXRELOCS)
+        assert(relocsize >= 11);
+        if (relocsize > MAXRELOCSIZE) // optimize_relocs did not help
             throwCantPack("too many relocations");
-#if 0
-        byte out[9*relocsize/8+1024];
-        unsigned in_len = relocsize;
-        unsigned out_len = 0;
-        ucl_nrv2b_99_compress(w, in_len, out, &out_len, nullptr, 9, nullptr, nullptr);
-        printf("reloc compress: %d -> %d\n", in_len, out_len);
+#if TESTING && 0
+        unsigned rout_len = MemBuffer::getSizeForCompression(relocsize);
+        MemBuffer rout(rout_len);
+        ucl_nrv2b_99_compress(raw_bytes(crel, relocsize), relocsize, rout, &rout_len, nullptr, 9,
+                              nullptr, nullptr);
+        printf("dos/exe reloc compress: %d -> %d\n", relocsize, rout_len);
 #endif
-    } else {
-        relocsize = 0;
     }
 
     // prepare packheader
     ph.u_len = ih_imagesize + relocsize;
+    obuf.allocForCompression(ph.u_len);
     // prepare filter
     Filter ft(ph.level);
     // compress (max_match = 8192)
@@ -511,9 +512,7 @@ void PackExe::pack(OutputFile *fo) {
         oh.firstreloc = ih.cs * 0x10000 + ih.ip;
     }
 
-    // g++ 3.1 does not like the following line...
     oh.relocoffs = offsetof(exe_header_t, firstreloc);
-    // oh.relocoffs = ptr_udiff_bytes(&oh.firstreloc, &oh);
 
     linker->defineSymbol("destination_segment", oh.ss - ph.c_len / 16 - e_len / 16);
     linker->defineSymbol("source_segment", e_len / 16 + (copysize - firstcopy) / 16);
@@ -526,7 +525,7 @@ void PackExe::pack(OutputFile *fo) {
     linker->defineSymbol("attribute", get_le16(ibuf + 4));
     linker->defineSymbol("orig_strategy", get_le16(ibuf + 6));
 
-    const unsigned outputlen = sizeof(oh) + lsize + packedsize + eisize;
+    const unsigned outputlen = sizeof(oh) + e_len + packedsize + d_len + eisize;
     oh.m512 = outputlen & 511;
     oh.p512 = (outputlen + 511) >> 9;
 
@@ -538,20 +537,19 @@ void PackExe::pack(OutputFile *fo) {
     memcpy(loader, getLoader(), lsize);
     patchPackHeader(loader, e_len);
 
-    NO_fprintf(stderr, "\ne_len=%x d_len=%x c_len=%x oo=%x ulen=%x destp=%x copys=%x images=%x",
-               e_len, d_len, packedsize, ph.overlap_overhead, ph.u_len, /*destpara*/ 0, copysize,
-               ih_imagesize);
+    NO_fprintf(stderr, "\ne_len=%x d_len=%x c_len=%x oo=%x ulen=%x copysize=%x imagesize=%x", e_len,
+               d_len, packedsize, ph.overlap_overhead, ph.u_len, copysize, ih_imagesize);
 
     // write header + write loader + compressed file
 #if TESTING
     if (opt->debug.debug_level)
         printf("\n%d %d %d %d\n", (int) sizeof(oh), e_len, packedsize, d_len);
 #endif
-    fo->write(&oh, sizeof(oh));
-    fo->write(loader, e_len); // entry
-    fo->write(obuf, packedsize);
-    fo->write(loader + e_len, d_len); // decompressor
-    fo->write(extra_info, eisize);
+    fo->write(&oh, sizeof(oh));       // program header
+    fo->write(loader, e_len);         // entry code
+    fo->write(obuf, packedsize);      // compressed data
+    fo->write(loader + e_len, d_len); // decompressor code
+    fo->write(extra_info, eisize);    // extra info for unpacking
     assert(eisize <= 9);
     NO_printf("%-13s: program hdr  : %8u bytes\n", getName(), usizeof(oh));
     NO_printf("%-13s: entry        : %8u bytes\n", getName(), e_len);
@@ -564,7 +562,7 @@ void PackExe::pack(OutputFile *fo) {
 
     // copy the overlay
     copyOverlay(fo, ih_overlay, obuf);
-    // fprintf (stderr,"%x %x\n",relocsize,ph.u_len);
+    NO_fprintf(stderr, "dos/exe %x %x\n", relocsize, ph.u_len);
 
     // finally check the compression ratio
     if (!checkFinalCompressionRatio(fo))
@@ -607,41 +605,46 @@ void PackExe::unpack(OutputFile *fo) {
     decompress(ibuf + e_len, obuf);
 
     unsigned imagesize = ih_imagesize;
-    imagesize--;
+    imagesize -= 1;
     const byte flag = ibuf[imagesize];
 
-    unsigned relocn = 0;
-    SPAN_S_VAR(byte, relocs, obuf + ph.u_len, obuf);
-
-    MemBuffer mb_wrkmem;
-    SPAN_0_VAR(byte, wrkmem, nullptr);
+    // relocations
+    unsigned relocnum = 0;
+    SPAN_S_VAR(const byte, relocstart, obuf + ph.u_len, obuf);
+    MemBuffer mb_relocs;
+    SPAN_0_VAR(byte, relocs, nullptr);
     if (!(flag & NORELOC)) {
-        relocs -= get_le16(obuf + (ph.u_len - 2));
+        mb_relocs.alloc(4 * MAXRELOCSIZE);
+        relocs = mb_relocs; // => now a SPAN_S
+
+        relocsize = get_le16(obuf + ph.u_len - 2);
         ph.u_len -= 2;
+        if (relocsize < 11 || relocsize > MAXRELOCSIZE || relocsize >= imagesize)
+            throwCantUnpack("bad relocations");
+        relocstart -= relocsize;
 
-        mb_wrkmem.alloc(4 * MAXRELOCS);
-        wrkmem = mb_wrkmem; // => now a SPAN_S
-        unsigned es = 0, ones = get_le16(relocs);
-        const unsigned seghi = get_le16(relocs + 2);
-        SPAN_S_VAR(const byte, p, relocs + 4);
-
+        // unoptimize_relocs
+        unsigned ones = get_le16(relocstart);
+        const unsigned seg_high = get_le16(relocstart + 2);
+        SPAN_S_VAR(const byte, p, relocstart + 4);
+        unsigned es = 0;
         while (ones) {
             unsigned di = get_le16(p);
             es += get_le16(p + 2);
             bool dorel = true;
             for (p += 4; ones && di < 0x10000; p++) {
                 if (dorel) {
-                    set_le16(wrkmem + (4 * relocn), di);
-                    set_le16(wrkmem + (2 + 4 * relocn++), es);
-                    NO_printf("%x\n", es * 16 + di);
+                    set_le16(relocs + (4 * relocnum + 0), di);
+                    set_le16(relocs + (4 * relocnum + 2), es);
+                    NO_printf("dos/exe unreloc %4d %6x\n", relocnum, es * 16 + di);
+                    relocnum++;
                 }
                 dorel = true;
                 if (*p == 0) {
-                    SPAN_S_VAR(const byte, q, obuf);
-                    for (q = obuf + (es * 16 + di); !(*q == 0x9a && get_le16(q + 3) <= seghi);
-                         q++) {
-                    }
-                    di = ptr_diff_bytes(q, obuf + (es * 16)) + 3;
+                    SPAN_S_VAR(const byte, q, obuf + (es * 16 + di), obuf);
+                    while (!(*q == 0x9a && get_le16(q + 3) <= seg_high))
+                        q++;
+                    di = ptr_udiff_bytes(q, obuf + (es * 16)) + 3;
                 } else if (*p == 1) {
                     di += 254;
                     if (di < 0x10000)
@@ -657,16 +660,16 @@ void PackExe::unpack(OutputFile *fo) {
     memset(&oh, 0, sizeof(oh));
     oh.ident = 'M' + 'Z' * 256;
 
-    if (relocn) {
-        oh.relocs = relocn;
-        while (relocn & 3)
-            set_le32(wrkmem + (4 * relocn++), 0);
+    if (relocnum) {
+        oh.relocs = relocnum;
+        while (relocnum & 3) // paragraph align
+            set_le32(relocs + (4 * relocnum++), 0);
     }
 
-    unsigned outputlen = ptr_udiff_bytes(relocs, obuf) + sizeof(oh) + relocn * 4;
+    unsigned outputlen = sizeof(oh) + 4 * relocnum + ptr_udiff_bytes(relocstart, obuf);
     oh.m512 = outputlen & 511;
     oh.p512 = (outputlen + 511) >> 9;
-    oh.headsize16 = 2 + relocn / 4;
+    oh.headsize16 = 2 + relocnum / 4;
 
     oh.max = ih.max;
     oh.min = ih.min;
@@ -701,9 +704,9 @@ void PackExe::unpack(OutputFile *fo) {
 
     // write header + relocations + uncompressed file
     fo->write(&oh, sizeof(oh));
-    if (relocn)
-        fo->write(wrkmem, relocn * 4);
-    fo->write(obuf, ptr_udiff_bytes(relocs, obuf));
+    if (relocnum)
+        fo->write(relocs, 4 * relocnum);
+    fo->write(obuf, ptr_udiff_bytes(relocstart, obuf));
 
     // copy the overlay
     copyOverlay(fo, ih_overlay, obuf);
diff --git a/src/p_exe.h b/src/p_exe.h
index 1bcbb370..958262b1 100644
--- a/src/p_exe.h
+++ b/src/p_exe.h
@@ -41,7 +41,7 @@ public:
     virtual int getVersion() const override { return 13; }
     virtual int getFormat() const override { return UPX_F_DOS_EXE; }
     virtual const char *getName() const override { return "dos/exe"; }
-    virtual const char *getFullName(const options_t *) const override { return "i086-dos16.exe"; }
+    virtual const char *getFullName(const Options *) const override { return "i086-dos16.exe"; }
     virtual const int *getCompressionMethods(int method, int level) const override;
     virtual const int *getFilters() const override;
 
@@ -90,18 +90,18 @@ protected:
 
     exe_header_t ih, oh;
 
-    unsigned ih_exesize;
-    unsigned ih_imagesize;
-    unsigned ih_overlay;
-    unsigned relocsize;
+    unsigned ih_exesize = 0;
+    unsigned ih_imagesize = 0;
+    unsigned ih_overlay = 0;
+    unsigned relocsize = 0;
 
-    bool has_9a;
-    bool device_driver;
+    bool has_9a = false;
+    bool device_driver = false;
 
     enum { NORELOC = 1, USEJUMP = 2, SS = 4, SP = 8, MINMEM = 16, MAXMEM = 32 };
 
-    unsigned stack_for_lzma; // stack size required for lzma
-    bool use_clear_dirty_stack;
+    unsigned stack_for_lzma = 0; // stack size required for lzma
+    bool use_clear_dirty_stack = false;
 };
 
 #endif /* already included */
diff --git a/src/p_ps1.h b/src/p_ps1.h
index d002fcc2..6a9b6127 100644
--- a/src/p_ps1.h
+++ b/src/p_ps1.h
@@ -45,7 +45,7 @@ public:
     virtual int getVersion() const override { return 13; }
     virtual int getFormat() const override { return UPX_F_PS1_EXE; }
     virtual const char *getName() const override { return "ps1/exe"; }
-    virtual const char *getFullName(const options_t *) const override { return "mipsel.r3000-ps1"; }
+    virtual const char *getFullName(const Options *) const override { return "mipsel.r3000-ps1"; }
     virtual const int *getCompressionMethods(int method, int level) const override;
     virtual const int *getFilters() const override;
 
diff --git a/src/p_sys.h b/src/p_sys.h
index 95e7a586..6bbc486f 100644
--- a/src/p_sys.h
+++ b/src/p_sys.h
@@ -41,7 +41,7 @@ public:
     virtual int getVersion() const override { return 13; }
     virtual int getFormat() const override { return UPX_F_DOS_SYS; }
     virtual const char *getName() const override { return "dos/sys"; }
-    virtual const char *getFullName(const options_t *) const override { return "i086-dos16.sys"; }
+    virtual const char *getFullName(const Options *) const override { return "i086-dos16.sys"; }
 
     virtual bool canPack() override;
 
diff --git a/src/p_tmt.cpp b/src/p_tmt.cpp
index 28834643..75a27438 100644
--- a/src/p_tmt.cpp
+++ b/src/p_tmt.cpp
@@ -188,15 +188,18 @@ void PackTmt::pack(OutputFile *fo) {
     checkOverlay(overlay);
 
     for (unsigned ic = 0; ic < relocnum; ic++)
-        set_le32(relocs + ic * 4, get_le32(relocs + ic * 4) - 4);
+        set_le32(relocs + 4 * ic, get_le32(relocs + 4 * ic) - 4);
 
     MemBuffer mb_orelocs(4 * relocnum + 8192); // relocations + extra_info
     SPAN_S_VAR(byte, orelocs, mb_orelocs);
     unsigned orelocsize =
         optimizeReloc(relocnum, relocs, orelocs, ibuf, usize, 32, true, &big_relocs);
     mb_relocs.dealloc(); // done
+#if 1
+    // write duplicate end marker; why is this here - historical oversight ???
+    orelocs[orelocsize++] = 0;
+#endif
     // extra_info
-    orelocs[orelocsize++] = 0;                // why is this needed - historical oversight ???
     set_le32(orelocs + orelocsize, ih.entry); // save original entry point
     orelocsize += 4;
     set_le32(orelocs + orelocsize, orelocsize + 4); // save orelocsize
@@ -303,7 +306,7 @@ void PackTmt::unpack(OutputFile *fo) {
     const unsigned relocnum = unoptimizeReloc(orelocs, mb_relocs, reloc_image, osize, 32, true);
     SPAN_S_VAR(byte, relocs, mb_relocs);
     for (unsigned ic = 0; ic < relocnum; ic++)
-        set_le32(relocs + ic * 4, get_le32(relocs + ic * 4) + 4);
+        set_le32(relocs + 4 * ic, get_le32(relocs + 4 * ic) + 4);
 
     memcpy(&oh, &ih, sizeof(oh));
     oh.imagesize = osize;
diff --git a/src/p_tmt.h b/src/p_tmt.h
index e38118a7..2ffe0671 100644
--- a/src/p_tmt.h
+++ b/src/p_tmt.h
@@ -41,7 +41,7 @@ public:
     virtual int getVersion() const override { return 13; }
     virtual int getFormat() const override { return UPX_F_TMT_ADAM; }
     virtual const char *getName() const override { return "tmt/adam"; }
-    virtual const char *getFullName(const options_t *) const override {
+    virtual const char *getFullName(const Options *) const override {
         return "i386-dos32.tmt.adam";
     }
     virtual const int *getCompressionMethods(int method, int level) const override;
diff --git a/src/p_tos.cpp b/src/p_tos.cpp
index 77a3d8c2..a94af9ab 100644
--- a/src/p_tos.cpp
+++ b/src/p_tos.cpp
@@ -223,11 +223,9 @@ void PackTos::buildLoader(const Filter *ft) {
 #define F_FASTLOAD 0x01 // don't zero heap
 #define F_ALTLOAD 0x02  // OK to load in alternate ram
 #define F_ALTALLOC 0x04 // OK to malloc from alt. ram
-#define F_SMALLTPA                                                                                 \
-    0x08                // used in MagiC: TPA can be allocated
-                        // as specified in the program header
-                        // rather than the biggest free memory
-                        // block
+#define F_SMALLTPA 0x08
+// used in MagiC: TPA can be allocated as specified in the program header
+// rather than the biggest free memory block
 #define F_MEMFLAGS 0xf0 // reserved for future use
 #define F_SHTEXT 0x800  // program's text may be shared
 
@@ -297,26 +295,26 @@ bool PackTos::checkFileHeader() {
 // relocs
 **************************************************************************/
 
-// Check relocation for errors to make sure our loader can handle it.
-static int check_relocs(const byte *relocs, unsigned rsize, unsigned isize, unsigned *nrelocs,
+// Check relocation for errors to make sure our loader can handle them
+static int check_relocs(const byte *relocs, unsigned rsize, unsigned image_size, unsigned *relocnum,
                         unsigned *relocsize, unsigned *overlay) {
+    assert(rsize >= 4);
+    assert(image_size >= 4);
     unsigned fixup = get_be32(relocs);
+    assert(fixup > 0);
     unsigned last_fixup = fixup;
     unsigned i = 4;
 
-    assert(isize >= 4);
-    assert(fixup > 0);
-
-    *nrelocs = 1;
+    *relocnum = 1;
     for (;;) {
         if (fixup & 1) // must be word-aligned
             return -1;
-        if (fixup + 4 > isize) // too far
+        if (fixup + 4 > image_size) // out of bounds
             return -1;
         if (i >= rsize) // premature EOF in relocs
             return -1;
         unsigned c = relocs[i++];
-        if (c == 0) // end marker
+        if (c == 0) // EOF end marker
             break;
         else if (c == 1) // increase fixup, no reloc
             fixup += 254;
@@ -328,7 +326,7 @@ static int check_relocs(const byte *relocs, unsigned rsize, unsigned isize, unsi
             if (fixup - last_fixup < 4) // overlapping relocation
                 return -1;
             last_fixup = fixup;
-            *nrelocs += 1;
+            *relocnum += 1;
         }
     }
 
@@ -352,7 +350,7 @@ bool PackTos::canPack() {
     if (!checkFileHeader())
         throwCantPack("unsupported header flags");
     if (file_size < 1024)
-        throwCantPack("program is too small");
+        throwCantPack("program is too small for atari/tos");
 
     return true;
 }
@@ -370,7 +368,7 @@ void PackTos::fileInfo() {
 
 void PackTos::pack(OutputFile *fo) {
     unsigned t;
-    unsigned nrelocs = 0;
+    unsigned relocnum = 0;
     unsigned relocsize = 0;
     unsigned overlay = 0;
 
@@ -426,14 +424,14 @@ void PackTos::pack(OutputFile *fo) {
     } else if (ih.fh_reloc != 0)
         relocsize = 0;
     else {
-        int r = check_relocs(ibuf + t, overlay, t, &nrelocs, &relocsize, &overlay);
+        int r = check_relocs(ibuf + t, overlay, t, &relocnum, &relocsize, &overlay);
         if (r != 0)
             throwCantPack("bad relocation table");
         symbols.need_reloc = true;
     }
 
 #if TESTING
-    printf("xx2: %d relocs: %d, overlay: %d, t: %d\n", nrelocs, relocsize, overlay, t);
+    printf("xx2: %d relocs: %d, overlay: %d, t: %d\n", relocnum, relocsize, overlay, t);
 #endif
 
     checkOverlay(overlay);
@@ -451,7 +449,7 @@ void PackTos::pack(OutputFile *fo) {
     // Now the data in ibuf[0..t] looks like this:
     //   text + data + relocs + original file header
     // After compression this will become the first part of the
-    // data segement. The second part will be the decompressor.
+    // data segment. The second part will be the decompressor.
 
     // alloc buffer (4096 is for decompressor and the various alignments)
     obuf.allocForCompression(t, 4096);
diff --git a/src/p_tos.h b/src/p_tos.h
index 3f998828..68f7310d 100644
--- a/src/p_tos.h
+++ b/src/p_tos.h
@@ -41,7 +41,7 @@ public:
     virtual int getVersion() const override { return 13; }
     virtual int getFormat() const override { return UPX_F_ATARI_TOS; }
     virtual const char *getName() const override { return "atari/tos"; }
-    virtual const char *getFullName(const options_t *) const override { return "m68k-atari.tos"; }
+    virtual const char *getFullName(const Options *) const override { return "m68k-atari.tos"; }
     virtual const int *getCompressionMethods(int method, int level) const override;
     virtual const int *getFilters() const override;
 
diff --git a/src/p_w32pe_i386.cpp b/src/p_w32pe_i386.cpp
index 89abf6f0..6f7e33fb 100644
--- a/src/p_w32pe_i386.cpp
+++ b/src/p_w32pe_i386.cpp
@@ -62,8 +62,8 @@ Linker *PackW32PeI386::newLinker() const { return new ElfLinkerX86; }
 **************************************************************************/
 
 int PackW32PeI386::readFileHeader() {
-    if (fi->st_size() >= 0x206) {
-        char buf[6];
+    if (file_size >= 0x206) {
+        byte buf[6];
         fi->seek(0x200, SEEK_SET);
         fi->readx(buf, 6);
         isrtm = memcmp(buf, "32STUB", 6) == 0;
diff --git a/src/p_w32pe_i386.h b/src/p_w32pe_i386.h
index bd253ecb..45f659de 100644
--- a/src/p_w32pe_i386.h
+++ b/src/p_w32pe_i386.h
@@ -39,7 +39,7 @@ public:
     virtual ~PackW32PeI386();
     virtual int getFormat() const override { return UPX_F_W32PE_I386; }
     virtual const char *getName() const override { return isrtm ? "rtm32/pe" : "win32/pe"; }
-    virtual const char *getFullName(const options_t *) const override { return "i386-win32.pe"; }
+    virtual const char *getFullName(const Options *) const override { return "i386-win32.pe"; }
     virtual const int *getCompressionMethods(int method, int level) const override;
     virtual const int *getFilters() const override;
 
diff --git a/src/p_w64pe_amd64.h b/src/p_w64pe_amd64.h
index 2df8ea10..de74afa7 100644
--- a/src/p_w64pe_amd64.h
+++ b/src/p_w64pe_amd64.h
@@ -39,7 +39,7 @@ public:
     virtual ~PackW64PeAmd64();
     virtual int getFormat() const override { return UPX_F_W64PE_AMD64; }
     virtual const char *getName() const override { return "win64/pe"; }
-    virtual const char *getFullName(const options_t *) const override { return "amd64-win64.pe"; }
+    virtual const char *getFullName(const Options *) const override { return "amd64-win64.pe"; }
     virtual const int *getCompressionMethods(int method, int level) const override;
     virtual const int *getFilters() const override;
 
diff --git a/src/p_w64pe_arm64.h b/src/p_w64pe_arm64.h
index 3ebd7127..c0610859 100644
--- a/src/p_w64pe_arm64.h
+++ b/src/p_w64pe_arm64.h
@@ -39,7 +39,7 @@ public:
     virtual ~PackW64PeArm64() {}
     virtual int getFormat() const override { return UPX_F_W64PE_ARM64; }
     virtual const char *getName() const override { return "win64/arm64"; }
-    virtual const char *getFullName(const options_t *) const override { return "arm64-win64.pe"; }
+    virtual const char *getFullName(const Options *) const override { return "arm64-win64.pe"; }
     virtual const int *getCompressionMethods(int method, int level) const override;
     virtual const int *getFilters() const override;
 
@@ -70,7 +70,7 @@ public:
     PackW64PeArm64EC(InputFile *f) : super(f) {}
     virtual int getFormat() const override { return UPX_F_W64PE_ARM64EC; }
     virtual const char *getName() const override { return "win64/arm64ec"; }
-    virtual const char *getFullName(const options_t *) const override { return "arm64ec-win64.pe"; }
+    virtual const char *getFullName(const Options *) const override { return "arm64ec-win64.pe"; }
 
     virtual bool canPack() override;
 };
diff --git a/src/p_wcle.h b/src/p_wcle.h
index b7626f7a..3e918a59 100644
--- a/src/p_wcle.h
+++ b/src/p_wcle.h
@@ -39,7 +39,7 @@ public:
     virtual int getVersion() const override { return 13; }
     virtual int getFormat() const override { return UPX_F_WATCOM_LE; }
     virtual const char *getName() const override { return "watcom/le"; }
-    virtual const char *getFullName(const options_t *) const override {
+    virtual const char *getFullName(const Options *) const override {
         return "i386-dos32.watcom.le";
     }
     virtual const int *getCompressionMethods(int method, int level) const override;
diff --git a/src/p_wince_arm.h b/src/p_wince_arm.h
index 94de79ec..eb68f6c2 100644
--- a/src/p_wince_arm.h
+++ b/src/p_wince_arm.h
@@ -39,7 +39,7 @@ public:
     virtual ~PackWinCeArm();
     virtual int getFormat() const override { return UPX_F_WINCE_ARM; }
     virtual const char *getName() const override { return "wince/arm"; }
-    virtual const char *getFullName(const options_t *) const override { return "arm-wince.pe"; }
+    virtual const char *getFullName(const Options *) const override { return "arm-wince.pe"; }
     virtual const int *getCompressionMethods(int method, int level) const override;
     virtual const int *getFilters() const override;
     virtual void defineFilterSymbols(const Filter *) override {}
diff --git a/src/packer.cpp b/src/packer.cpp
index f6a8f55e..fab36042 100644
--- a/src/packer.cpp
+++ b/src/packer.cpp
@@ -46,7 +46,7 @@ Packer::Packer(InputFile *f)
     mem_clear(&ph, sizeof(ph));
 }
 
-Packer::~Packer() {
+Packer::~Packer() noexcept {
     delete uip;
     uip = nullptr;
     delete linker;
@@ -834,27 +834,26 @@ static const char *getIdentstr(unsigned *size, int small) {
         "\n";
     static char identtiny[] = UPX_VERSION_STRING4;
 
-    static int done;
-    if (!done && (opt->debug.fake_stub_version[0] || opt->debug.fake_stub_year[0])) {
-        struct strinfo_t {
-            char *s;
-            int size;
-        };
-        static const strinfo_t strlist[] = {{identbig, (int) sizeof(identbig)},
-                                            {identsmall, (int) sizeof(identsmall)},
-                                            {identtiny, (int) sizeof(identtiny)},
-                                            {nullptr, 0}};
-        const strinfo_t *iter;
-
-        for (iter = strlist; iter->s; ++iter) {
-            if (opt->debug.fake_stub_version[0])
-                mem_replace(iter->s, iter->size, UPX_VERSION_STRING4, 4,
-                            opt->debug.fake_stub_version);
-            if (opt->debug.fake_stub_year[0])
-                mem_replace(iter->s, iter->size, UPX_VERSION_YEAR, 4, opt->debug.fake_stub_year);
+    static upx_std_once_flag init_done;
+    upx_std_call_once(init_done, []() noexcept {
+        if (opt->debug.fake_stub_version[0] || opt->debug.fake_stub_year[0]) {
+            struct Ident {
+                char *s;
+                int len;
+            };
+            static const Ident idents[] = {{identbig, (int) sizeof(identbig) - 1},
+                                           {identsmall, (int) sizeof(identsmall) - 1},
+                                           {identtiny, (int) sizeof(identtiny) - 1},
+                                           {nullptr, 0}};
+            for (const Ident *iter = idents; iter->s; ++iter) {
+                if (opt->debug.fake_stub_version[0])
+                    mem_replace(iter->s, iter->len, UPX_VERSION_STRING4, 4,
+                                opt->debug.fake_stub_version);
+                if (opt->debug.fake_stub_year[0])
+                    mem_replace(iter->s, iter->len, UPX_VERSION_YEAR, 4, opt->debug.fake_stub_year);
+            }
         }
-        done = 1;
-    }
+    });
 
     if (small < 0)
         small = opt->small;
diff --git a/src/packer.h b/src/packer.h
index af7e4053..6d148522 100644
--- a/src/packer.h
+++ b/src/packer.h
@@ -112,7 +112,7 @@ protected:
     Packer(InputFile *f);
 
 public:
-    virtual ~Packer();
+    virtual ~Packer() noexcept;
     virtual void assertPacker() const;
 
     // getVersion() enables detecting forward incompatibility of unpack()
@@ -121,7 +121,7 @@ public:
     // A unique integer ID for this executable format. See conf.h.
     virtual int getFormat() const = 0;
     virtual const char *getName() const = 0;
-    virtual const char *getFullName(const options_t *) const = 0;
+    virtual const char *getFullName(const Options *) const = 0;
     virtual const int *getCompressionMethods(int method, int level) const = 0;
     virtual const int *getFilters() const = 0;
 
@@ -187,7 +187,7 @@ protected:
                              unsigned overlap_range, upx_compress_config_t const *cconf,
                              int filter_strategy, bool inhibit_compression_check = false);
 
-    // util for verifying overlapping decompresion
+    // util for verifying overlapping decompression
     //   non-destructive test
     virtual bool testOverlappingDecompression(const byte *buf, const byte *tbuf,
                                               unsigned overlap_overhead) const;
@@ -299,9 +299,9 @@ protected:
     int ph_version;
 
     // compression buffers
-    MemBuffer ibuf;    // input
-    MemBuffer obuf;    // output
-    unsigned ibufgood; // high-water mark in ibuf (pefile.cpp)
+    MemBuffer ibuf;        // input
+    MemBuffer obuf;        // output
+    unsigned ibufgood = 0; // high-water mark in ibuf (pefile.cpp)
 
     // UI handler
     UiPacker *uip = nullptr;
diff --git a/src/packer_r.cpp b/src/packer_r.cpp
index fc608e1a..3d215251 100644
--- a/src/packer_r.cpp
+++ b/src/packer_r.cpp
@@ -30,7 +30,7 @@
 
 /*************************************************************************
 // sort and delta-compress relocations with optional bswap within image
-// returns number of bytes written to |out|
+// returns number of bytes written to 'out'
 **************************************************************************/
 
 unsigned Packer::optimizeReloc(unsigned relocnum, SPAN_P(byte) relocs, SPAN_S(byte) out,
@@ -38,6 +38,10 @@ unsigned Packer::optimizeReloc(unsigned relocnum, SPAN_P(byte) relocs, SPAN_S(by
                                int *big) {
     assert(bits == 32 || bits == 64);
     mem_size_assert(1, image_size);
+#if WITH_XSPAN >= 2
+    ptr_check_no_overlap(relocs.data(), relocs.size_bytes(), image.data(image_size), image_size,
+                         out.data(), out.size_bytes());
+#endif
     SPAN_P_VAR(byte, fix, out);
 
     *big = 0;
@@ -69,7 +73,7 @@ unsigned Packer::optimizeReloc(unsigned relocnum, SPAN_P(byte) relocs, SPAN_S(by
             fix += 4;
         }
         pc += delta;
-        if (pc + 4 >= image_size)
+        if (pc + 4 > image_size)
             throwCantPack("bad reloc[%#x] = %#x", i, pc);
         if (bswap) {
             if (bits == 32)
@@ -84,14 +88,17 @@ unsigned Packer::optimizeReloc(unsigned relocnum, SPAN_P(byte) relocs, SPAN_S(by
 
 /*************************************************************************
 // delta-decompress relocations
-// advances |in|
-// allocates |out| and returns number of relocs written to |out|
+// advances 'in'
+// allocates 'out' and returns number of relocs written to 'out'
 **************************************************************************/
 
 unsigned Packer::unoptimizeReloc(SPAN_S(const byte) & in, MemBuffer &out, SPAN_P(byte) image,
                                  unsigned image_size, int bits, bool bswap) {
     assert(bits == 32 || bits == 64);
     mem_size_assert(1, image_size);
+#if WITH_XSPAN >= 2
+    ptr_check_no_overlap(in.data(), in.size_bytes(), image.data(image_size), image_size);
+#endif
     SPAN_S_VAR(const byte, fix, in);
 
     // count
@@ -125,7 +132,7 @@ unsigned Packer::unoptimizeReloc(SPAN_S(const byte) & in, MemBuffer &out, SPAN_P
         if ((int) delta < 4)
             throwCantUnpack("overlapping fixups");
         pc += delta;
-        if (pc + 4 >= image_size)
+        if (pc + 4 > image_size)
             throwCantUnpack("bad reloc[%#x] = %#x", i, pc);
         *relocs++ = pc;
         if (bswap && image != nullptr) {
diff --git a/src/packmast.cpp b/src/packmast.cpp
index 3e9d75fb..3dcd0f1c 100644
--- a/src/packmast.cpp
+++ b/src/packmast.cpp
@@ -59,34 +59,37 @@
 //
 **************************************************************************/
 
-PackMaster::PackMaster(InputFile *f, options_t *o) : fi(f), p(nullptr) {
+PackMaster::PackMaster(InputFile *f, Options *o) noexcept : fi(f) {
     // replace global options with local options
-    saved_opt = o;
-    if (o) {
+    if (o != nullptr) {
+#if WITH_THREADS
+        std::lock_guard lock(opt_lock_mutex);
+#endif
+        saved_opt = o;
         memcpy(&this->local_options, o, sizeof(*o)); // struct copy
         opt = &this->local_options;
     }
 }
 
-PackMaster::~PackMaster() {
-    fi = nullptr;
-    delete p;
-    p = nullptr;
+PackMaster::~PackMaster() noexcept {
+    delete packer;
+    packer = nullptr;
     // restore global options
-    if (saved_opt)
+    if (saved_opt != nullptr) {
+#if WITH_THREADS
+        std::lock_guard lock(opt_lock_mutex);
+#endif
         opt = saved_opt;
-    saved_opt = nullptr;
+        saved_opt = nullptr;
+    }
 }
 
 /*************************************************************************
 //
 **************************************************************************/
 
-static Packer *try_pack(Packer *p, void *user) {
-    if (p == nullptr)
-        return nullptr;
+static Packer *try_can_pack(Packer *p, void *user) {
     InputFile *f = (InputFile *) user;
-    p->assertPacker();
     try {
         p->initPackHeader();
         f->seek(0, SEEK_SET);
@@ -105,11 +108,8 @@ static Packer *try_pack(Packer *p, void *user) {
     return nullptr;
 }
 
-static Packer *try_unpack(Packer *p, void *user) {
-    if (p == nullptr)
-        return nullptr;
+static Packer *try_can_unpack(Packer *p, void *user) {
     InputFile *f = (InputFile *) user;
-    p->assertPacker();
     try {
         p->initPackHeader();
         f->seek(0, SEEK_SET);
@@ -123,6 +123,7 @@ static Packer *try_unpack(Packer *p, void *user) {
             // see canUnpack() in packer.h
         }
     } catch (const IOException &) {
+        // ignored
     } catch (...) {
         delete p;
         throw;
@@ -135,41 +136,40 @@ static Packer *try_unpack(Packer *p, void *user) {
 //
 **************************************************************************/
 
-Packer *PackMaster::visitAllPackers(visit_func_t func, InputFile *f, const options_t *o,
-                                    void *user) {
-    Packer *p = nullptr;
+/*static*/
+Packer *PackMaster::visitAllPackers(visit_func_t func, InputFile *f, const Options *o, void *user) {
 
 #define D(Klass)                                                                                   \
     ACC_BLOCK_BEGIN                                                                                \
-    Klass *const kp = new Klass(f);                                                                \
+    COMPILE_TIME_ASSERT(std::is_nothrow_destructible_v)                                     \
+    Klass *kp = new Klass(f);                                                                      \
+    kp->assertPacker();                                                                            \
     if (o->debug.debug_level)                                                                      \
         fprintf(stderr, "visitAllPackers: (ver=%d, fmt=%3d) %s\n", kp->getVersion(),               \
                 kp->getFormat(), #Klass);                                                          \
-    if ((p = func(kp, user)) != nullptr)                                                           \
+    Packer *p = func(kp, user);                                                                    \
+    if (p != nullptr)                                                                              \
         return p;                                                                                  \
     ACC_BLOCK_END
 
-    // note: order of tries is important !
+    // NOTE: order of tries is important !!!
 
     //
     // .exe
     //
     if (!o->dos_exe.force_stub) {
+        // dos32
         D(PackDjgpp2);
         D(PackTmt);
         D(PackWcle);
+        // Windows
         // D(PackW64PeArm64EC); // NOT YET IMPLEMENTED
         // D(PackW64PeArm64); // NOT YET IMPLEMENTED
         D(PackW64PeAmd64);
         D(PackW32PeI386);
+        D(PackWinCeArm);
     }
-    D(PackWinCeArm);
-    D(PackExe);
-
-    //
-    // atari
-    //
-    D(PackTos);
+    D(PackExe); // dos/exe
 
     //
     // linux kernel
@@ -210,18 +210,7 @@ Packer *PackMaster::visitAllPackers(visit_func_t func, InputFile *f, const optio
     D(PackMachFat);   // cafebabe conflict
     D(PackLinuxI386); // cafebabe conflict
 
-    //
-    // psone
-    //
-    D(PackPs1);
-
-    //
-    // .sys and .com
-    //
-    D(PackSys);
-    D(PackCom);
-
-    // Mach (macOS)
+    // Mach (Darwin / macOS)
     D(PackDylibAMD64);
     D(PackMachPPC32); // TODO: this works with upx 3.91..3.94 but got broken in 3.95; FIXME
     D(PackMachI386);
@@ -235,24 +224,30 @@ Packer *PackMaster::visitAllPackers(visit_func_t func, InputFile *f, const optio
     //   D(PackDylibI386);
     //   D(PackDylibPPC32);
 
+    //
+    // misc
+    //
+    D(PackTos); // atari/tos
+    D(PackPs1); // ps1/exe
+    D(PackSys); // dos/sys
+    D(PackCom); // dos/com
+
     return nullptr;
 #undef D
 }
 
-Packer *PackMaster::getPacker(InputFile *f) {
-    Packer *pp = visitAllPackers(try_pack, f, opt, f);
-    if (!pp)
+/*static*/ Packer *PackMaster::getPacker(InputFile *f) {
+    Packer *p = visitAllPackers(try_can_pack, f, opt, f);
+    if (!p)
         throwUnknownExecutableFormat();
-    pp->assertPacker();
-    return pp;
+    return p;
 }
 
-Packer *PackMaster::getUnpacker(InputFile *f) {
-    Packer *pp = visitAllPackers(try_unpack, f, opt, f);
-    if (!pp)
+/*static*/ Packer *PackMaster::getUnpacker(InputFile *f) {
+    Packer *p = visitAllPackers(try_can_unpack, f, opt, f);
+    if (!p)
         throwNotPacked();
-    pp->assertPacker();
-    return pp;
+    return p;
 }
 
 /*************************************************************************
@@ -260,39 +255,37 @@ Packer *PackMaster::getUnpacker(InputFile *f) {
 **************************************************************************/
 
 void PackMaster::pack(OutputFile *fo) {
-    p = getPacker(fi);
-    fi = nullptr;
-    p->doPack(fo);
+    assert(packer == nullptr);
+    packer = getPacker(fi);
+    packer->doPack(fo);
 }
 
 void PackMaster::unpack(OutputFile *fo) {
-    p = getUnpacker(fi);
-    p->assertPacker();
-    fi = nullptr;
-    p->doUnpack(fo);
+    assert(packer == nullptr);
+    packer = getUnpacker(fi);
+    packer->doUnpack(fo);
 }
 
 void PackMaster::test() {
-    p = getUnpacker(fi);
-    fi = nullptr;
-    p->doTest();
+    assert(packer == nullptr);
+    packer = getUnpacker(fi);
+    packer->doTest();
 }
 
 void PackMaster::list() {
-    p = getUnpacker(fi);
-    fi = nullptr;
-    p->doList();
+    assert(packer == nullptr);
+    packer = getUnpacker(fi);
+    packer->doList();
 }
 
 void PackMaster::fileInfo() {
-    p = visitAllPackers(try_unpack, fi, opt, fi);
-    if (!p)
-        p = visitAllPackers(try_pack, fi, opt, fi);
-    if (!p)
+    assert(packer == nullptr);
+    packer = visitAllPackers(try_can_unpack, fi, opt, fi);
+    if (!packer)
+        packer = visitAllPackers(try_can_pack, fi, opt, fi);
+    if (!packer)
         throwUnknownExecutableFormat(nullptr, 1); // make a warning here
-    p->assertPacker();
-    fi = nullptr;
-    p->doFileInfo();
+    packer->doFileInfo();
 }
 
 /* vim:set ts=4 sw=4 et: */
diff --git a/src/packmast.h b/src/packmast.h
index a2012359..5419607a 100644
--- a/src/packmast.h
+++ b/src/packmast.h
@@ -32,13 +32,13 @@ class InputFile;
 class OutputFile;
 
 /*************************************************************************
-// interface for work.cpp
+// dispatch to a concrete subclass of class Packer; see work.cpp
 **************************************************************************/
 
 class PackMaster final {
 public:
-    PackMaster(InputFile *f, options_t *o = nullptr);
-    ~PackMaster();
+    PackMaster(InputFile *f, Options *o = nullptr) noexcept;
+    ~PackMaster() noexcept;
 
     void pack(OutputFile *fo);
     void unpack(OutputFile *fo);
@@ -47,18 +47,18 @@ public:
     void fileInfo();
 
     typedef Packer *(*visit_func_t)(Packer *p, void *user);
-    static Packer *visitAllPackers(visit_func_t, InputFile *f, const options_t *, void *user);
+    static Packer *visitAllPackers(visit_func_t, InputFile *f, const Options *, void *user);
 
 private:
-    InputFile *fi = nullptr;
-    Packer *p = nullptr;
+    Packer *packer = nullptr; // owner
+    InputFile *fi = nullptr;  // reference
 
     static Packer *getPacker(InputFile *f);
     static Packer *getUnpacker(InputFile *f);
 
     // setup local options for each file
-    options_t local_options;
-    options_t *saved_opt = nullptr;
+    Options local_options;
+    Options *saved_opt = nullptr;
 };
 
 /* vim:set ts=4 sw=4 et: */
diff --git a/src/pefile.cpp b/src/pefile.cpp
index 16878347..8f3a0b23 100644
--- a/src/pefile.cpp
+++ b/src/pefile.cpp
@@ -140,7 +140,7 @@ bool PeFile::testUnpackVersion(int version) const {
     if (cpu >= IMAGE_FILE_MACHINE_I386 && cpu <= 0x150) // what is this 0x150 ???
         return UPX_F_W32PE_I386;
 
-    // other or unkown (alpha, mips, etc.)
+    // other or unknown (alpha, mips, etc.)
     throwCantPack("pefile: unsupported machine %#x", cpu);
     return 0; // pacify msvc
 }
@@ -274,6 +274,19 @@ void PeFile::Interval::dump() const {
 // relocation handling
 **************************************************************************/
 
+namespace {
+struct FixDeleter { // don't leak memory on exceptions
+    LE32 **fix;
+    size_t n;
+    ~FixDeleter() noexcept {
+        for (size_t i = 0; i < n; i++) {
+            delete[] fix[i];
+            fix[i] = nullptr;
+        }
+    }
+};
+} // namespace
+
 struct alignas(1) PeFile::Reloc::reloc {
     LE32 pagestart;
     LE32 size;
@@ -371,17 +384,21 @@ void PeFile32::processRelocs() // pass1
         }
         mb_orelocs.alloc(1);
         orelocs = mb_orelocs; // => orelocs now is a SPAN_S
+        orelocs[0] = 0;       // clear
         sorelocs = 0;
         return;
     }
 
-    for (ic = 15; ic > 3; ic--)
+    for (ic = 4; ic < 16; ic++)
         if (counts[ic])
             infoWarning("skipping unsupported relocation type %d (%d)", ic, counts[ic]);
 
     LE32 *fix[4];
-    for (; ic; ic--)
+    FixDeleter fixdel{fix, 0}; // don't leak memory
+    for (ic = 0; ic < 4; ic++) {
         fix[ic] = New(LE32, counts[ic]);
+        fixdel.n += 1;
+    }
 
     unsigned xcounts[4];
     memset(xcounts, 0, sizeof(xcounts));
@@ -420,7 +437,6 @@ void PeFile32::processRelocs() // pass1
     orelocs = mb_orelocs;                          // => orelocs now is a SPAN_S
     sorelocs = optimizeReloc(xcounts[3], (byte *) fix[3], orelocs, ibuf + rvamin, ibufgood - rvamin,
                              32, true, &big_relocs);
-    delete[] fix[3];
 
     // Malware that hides behind UPX often has PE header info that is
     // deliberately corrupt.  Sometimes it is even tuned to cause us trouble!
@@ -433,7 +449,6 @@ void PeFile32::processRelocs() // pass1
     for (ic = 2; ic; ic--) {
         memcpy(orelocs + sorelocs, fix[ic], 4 * xcounts[ic]);
         sorelocs += 4 * xcounts[ic];
-        delete[] fix[ic];
 
         set_le32(orelocs + sorelocs, 0);
         if (xcounts[ic]) {
@@ -471,13 +486,16 @@ void PeFile64::processRelocs() // pass1
         return;
     }
 
-    for (ic = 15; ic; ic--)
+    for (ic = 0; ic < 16; ic++)
         if (ic != 10 && counts[ic])
             infoWarning("skipping unsupported relocation type %d (%d)", ic, counts[ic]);
 
     LE32 *fix[16];
-    for (ic = 15; ic; ic--)
+    FixDeleter fixdel{fix, 0}; // don't leak memory
+    for (ic = 0; ic < 16; ic++) {
         fix[ic] = New(LE32, counts[ic]);
+        fixdel.n += 1;
+    }
 
     unsigned xcounts[16];
     memset(xcounts, 0, sizeof(xcounts));
@@ -495,7 +513,7 @@ void PeFile64::processRelocs() // pass1
     }
 
     // remove duplicated records
-    for (ic = 1; ic <= 15; ic++) {
+    for (ic = 1; ic < 16; ic++) {
         qsort(fix[ic], xcounts[ic], 4, le32_compare);
         unsigned prev = ~0u;
         unsigned jc = 0;
@@ -520,9 +538,6 @@ void PeFile64::processRelocs() // pass1
     sorelocs = optimizeReloc(xcounts[10], (byte *) fix[10], orelocs, ibuf + rvamin,
                              ibufgood - rvamin, 64, true, &big_relocs);
 
-    for (ic = 15; ic; ic--)
-        delete[] fix[ic];
-
 #if 0
     // Malware that hides behind UPX often has PE header info that is
     // deliberately corrupt.  Sometimes it is even tuned to cause us trouble!
@@ -536,7 +551,6 @@ void PeFile64::processRelocs() // pass1
     {
         memcpy(orelocs + sorelocs,fix[ic],4 * xcounts[ic]);
         sorelocs += 4 * xcounts[ic];
-        delete [] fix[ic];
 
         set_le32(orelocs + sorelocs,0);
         if (xcounts[ic])
@@ -574,7 +588,7 @@ const LE32 &PeFile::IDADDR(unsigned x) const { return iddirs[x].vaddr; }
  in the sorted order.
  */
 
-class PeFile::ImportLinker : public ElfLinkerAMD64 {
+class PeFile::ImportLinker final : public ElfLinkerAMD64 {
     struct tstr : private ::noncopyable {
         char *s = nullptr;
         explicit tstr(char *str) : s(str) {}
@@ -833,7 +847,7 @@ unsigned PeFile::processImports0(ord_mask_t ord_mask) // pass 1
     import_desc *const im_save = im;
     if (IDADDR(PEDIR_IMPORT)) {
         for (;; ++dllnum, ++im) {
-            unsigned const skip2 = ptr_diff_bytes(im, ibuf);
+            unsigned const skip2 = ptr_udiff_bytes(im, ibuf);
             (void) ibuf.subref("bad import %#x", skip2, sizeof(*im));
             if (!im->dllname)
                 break;
@@ -853,25 +867,17 @@ unsigned PeFile::processImports0(ord_mask_t ord_mask) // pass 1
         static int __acc_cdecl_qsort compare(const void *p1, const void *p2) {
             const udll *u1 = *(const udll *const *) p1;
             const udll *u2 = *(const udll *const *) p2;
-            if (u1->isk32)
-                return -1;
-            if (u2->isk32)
-                return 1;
-            if (!*u1->lookupt)
-                return 1;
-            if (!*u2->lookupt)
-                return -1;
+            if (u1->isk32 != u2->isk32)
+                return u1->isk32 ? -1 : 1;
+            if ((*u1->lookupt != 0) != (*u2->lookupt != 0))
+                return (*u1->lookupt != 0) ? -1 : 1;
             int rc = strcasecmp(u1->name, u2->name);
             if (rc)
                 return rc;
-            if (u1->ordinal)
-                return -1;
-            if (u2->ordinal)
-                return 1;
-            if (!u1->shname)
-                return 1;
-            if (!u2->shname)
-                return -1;
+            if ((u1->ordinal != 0) != (u2->ordinal != 0))
+                return (u1->ordinal != 0) ? -1 : 1;
+            if ((u1->shname != nullptr) != (u2->shname != nullptr))
+                return (u1->shname != nullptr) ? -1 : 1;
             rc = (int) (upx_safe_strlen(u1->shname) - upx_safe_strlen(u2->shname));
             if (rc)
                 return rc;
@@ -904,8 +910,8 @@ unsigned PeFile::processImports0(ord_mask_t ord_mask) // pass 1
                 importbyordinal = true;
                 soimport += 2; // ordinal num: 2 bytes
                 dlls[ic].ordinal = *tarr & 0xffff;
-            } else // it's an import by name
-            {
+            } else {
+                // it's an import by name
                 IPTR_VAR(const byte, const name, ibuf + *tarr + 2);
                 unsigned len = strlen(name);
                 soimport += len + 1;
@@ -919,9 +925,12 @@ unsigned PeFile::processImports0(ord_mask_t ord_mask) // pass 1
     mb_oimport.clear();
     oimport = mb_oimport;
 
-    qsort(idlls, dllnum, sizeof(udll *), udll::compare);
+    qsort(idlls, dllnum, sizeof(*idlls), udll::compare);
 
     info("Processing imports: %d DLLs", dllnum);
+    for (ic = 0; ic < dllnum; ic++) {
+        info("  DLL %3d %s %s", ic, idlls[ic]->name, idlls[ic]->shname);
+    }
 
     ilinker = new ImportLinker(sizeof(LEXX));
     // create the new import table
@@ -981,7 +990,7 @@ unsigned PeFile::processImports0(ord_mask_t ord_mask) // pass 1
             }
         ppi++;
 
-        unsigned esize = ptr_diff_bytes(tarr, idlls[ic]->lookupt);
+        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),
                            (char *) idlls[ic]->lookupt) != 0) {
@@ -1299,7 +1308,7 @@ void PeFile::processTls1(Interval *iv, typename tls_traits::cb_value_t ima
     unsigned const take1 = sizeof(tls);
     unsigned const skip1 = IDADDR(PEDIR_TLS);
     memcpy(otls, ibuf.subref("bad tls %#x", skip1, take1), take1);
-    // WARNING: this can acces data in BSS
+    // WARNING: this can access data in BSS
     unsigned const take3 = sotls - sizeof(tls);
     memcpy(otls + sizeof(tls), ibuf.subref("bad tls %#x", tlsdatastart, take3), take3);
     tlsindex = tlsp->tlsindex - imagebase;
@@ -1887,6 +1896,7 @@ void PeFile::processResources(Resource *res) {
     soresources = ptr_diff_bytes(ores, oresources);
 
     delete[] keep_icons;
+    keep_icons = nullptr;
     if (!res->clear()) {
         // The area occupied by the resource directory is not continuous
         // so to still support uncompression, I can't zero this area.
@@ -2637,6 +2647,16 @@ void PeFile::rebuildTls() {
     // this is an easy one : just do nothing ;-)
 }
 
+namespace {
+template 
+struct VPtr {
+    static_assert(sizeof(T) == 1);
+    SPAN_S(T) ptr;
+    size_t vaddr;
+    auto operator+(size_t n) const { return ptr + mem_size(sizeof(T), n - vaddr); }
+};
+} // namespace
+
 void PeFile::rebuildResources(SPAN_S(byte) & extra_info, unsigned lastvaddr) {
     if (ODSIZE(PEDIR_RESOURCE) == 0 || IDSIZE(PEDIR_RESOURCE) == 0)
         return;
@@ -2646,12 +2666,13 @@ void PeFile::rebuildResources(SPAN_S(byte) & extra_info, unsigned lastvaddr) {
 
     const unsigned vaddr = IDADDR(PEDIR_RESOURCE);
 
-    if (lastvaddr > vaddr || (vaddr - lastvaddr) > ibuf.getSize())
+    if (vaddr < lastvaddr || (vaddr - lastvaddr) > ibuf.getSize())
         throwCantUnpack("corrupted PE header");
 
-    // TODO: introduce WildPtr for "virtual pointer" pointing before a buffer
-    const byte *r = ibuf.raw_bytes(0) - lastvaddr;
-    Resource res(r + vaddr, ibuf, ibuf + ibuf.getSize());
+    // INFO: use VPtr for "virtual pointer" pointing before a buffer
+    //// const byte *const r = ibuf.raw_bytes(0) - lastvaddr;
+    VPtr const r{ibuf, lastvaddr};
+    Resource res(raw_bytes(r + vaddr, 0), ibuf, ibuf + ibuf.getSize());
     while (res.next())
         if (res.offs() > vaddr) {
             ICHECK(r + (res.offs() - 4), 4);
@@ -2702,8 +2723,9 @@ void PeFile::rebuildImports(SPAN_S(byte) & extra_info, ord_mask_t ord_mask, bool
     }
     sdllnames = ALIGN_UP(sdllnames, 2u);
 
-    // TODO: introduce WildPtr for "virtual pointer" pointing before a buffer
-    byte *const Obuf = obuf.raw_bytes(0) - rvamin;
+    // INFO: use VPtr for "virtual pointer" pointing before a buffer
+    //// byte *const Obuf = obuf.raw_bytes(0) - rvamin;
+    VPtr const Obuf{obuf, rvamin};
 #if 0
     import_desc * const im0 = (import_desc*) (Obuf + ODADDR(PEDIR_IMPORT));
     import_desc *im = im0;
@@ -2711,9 +2733,10 @@ void PeFile::rebuildImports(SPAN_S(byte) & extra_info, ord_mask_t ord_mask, bool
     byte *importednames = dllnames + sdllnames;
     byte * const importednames_start = importednames;
 #else
-    SPAN_S_VAR(import_desc, const im0, (import_desc *) (Obuf + ODADDR(PEDIR_IMPORT)), obuf);
+    SPAN_S_VAR(import_desc, const im0, (import_desc *) raw_bytes(Obuf + ODADDR(PEDIR_IMPORT), 0),
+               obuf);
     SPAN_S_VAR(import_desc, im, im0);
-    SPAN_0_VAR(byte, dllnames, inamespos ? Obuf + inamespos : nullptr, obuf);
+    SPAN_0_VAR(byte, dllnames, inamespos ? raw_bytes(Obuf + inamespos, 0) : nullptr, obuf);
     SPAN_0_VAR(byte, importednames, inamespos ? dllnames + sdllnames : nullptr);
     SPAN_0_VAR(byte, const importednames_start, importednames);
 #endif
@@ -2728,7 +2751,7 @@ void PeFile::rebuildImports(SPAN_S(byte) & extra_info, ord_mask_t ord_mask, bool
         if (inamespos) {
             // now I rebuild the dll names
             omemcpy(dllnames, dname, dlen + 1);
-            im->dllname = ptr_udiff_bytes(dllnames, Obuf);
+            im->dllname = ptr_udiff_bytes(dllnames, obuf) + rvamin;
             //;;;printf("\ndll: %s:",dllnames);
             dllnames += dlen + 1;
         } else {
@@ -2738,7 +2761,7 @@ void PeFile::rebuildImports(SPAN_S(byte) & extra_info, ord_mask_t ord_mask, bool
         if (set_oft)
             im->oft = iatoffs;
 
-        OPTR_VAR(LEXX, newiat, (LEXX *) (Obuf + iatoffs));
+        OPTR_VAR(LEXX, newiat, (LEXX *) raw_bytes(Obuf + iatoffs, 0));
 
         // restore the imported names+ordinals
         for (p += 8; *p; ++newiat)
@@ -2749,7 +2772,7 @@ void PeFile::rebuildImports(SPAN_S(byte) & extra_info, ord_mask_t ord_mask, bool
                         importednames -= 1;
                     omemcpy(importednames + 2, p, ilen);
                     //;;;printf(" %s",importednames+2);
-                    *newiat = ptr_udiff_bytes(importednames, Obuf);
+                    *newiat = ptr_udiff_bytes(importednames, obuf) + rvamin;
                     importednames += 2 + ilen;
                 } else {
                     // Beware overlap!
@@ -2986,7 +3009,7 @@ void PeFile32::readPeHeader() {
 void PeFile32::pack0(OutputFile *fo, unsigned subsystem_mask, upx_uint64_t default_imagebase,
                      bool last_section_rsrc_only) {
     super::pack0(fo, ih, oh, subsystem_mask, default_imagebase, last_section_rsrc_only);
-    infoWarning("End of PeFile32::pack0");
+    // infoWarning("End of PeFile32::pack0");
 }
 
 void PeFile32::unpack(OutputFile *fo) {
diff --git a/src/pefile.h b/src/pefile.h
index 383dbaff..1655a500 100644
--- a/src/pefile.h
+++ b/src/pefile.h
@@ -147,7 +147,7 @@ protected:
     unsigned sotls;
     unsigned tlsindex;
     unsigned tlscb_ptr;
-    unsigned tls_handler_offset;
+    unsigned tls_handler_offset = 0;
     bool use_tls_callbacks = false;
 
     void processLoadConf(Reloc *, const Interval *, unsigned);
@@ -288,7 +288,7 @@ protected:
     enum {
         IMAGE_SUBSYSTEM_UNKNOWN = 0,
         IMAGE_SUBSYSTEM_NATIVE = 1,
-        IMAGE_SUBSYSTEM_WINDOWS_GUI = 2, // Grapical
+        IMAGE_SUBSYSTEM_WINDOWS_GUI = 2, // Graphical
         IMAGE_SUBSYSTEM_WINDOWS_CUI = 3, // Character-mode
         IMAGE_SUBSYSTEM_WINDOWS_OS2_CUI = 5,
         IMAGE_SUBSYSTEM_WINDOWS_POSIX_CUI = 7,
diff --git a/src/ui.cpp b/src/ui.cpp
index 7e443b18..096fbb4f 100644
--- a/src/ui.cpp
+++ b/src/ui.cpp
@@ -182,7 +182,7 @@ UiPacker::UiPacker(const Packer *p_) : ui_pass(0), ui_total_passes(0), p(p_), s(
         s->mode = M_CB_SCREEN;
 }
 
-UiPacker::~UiPacker() {
+UiPacker::~UiPacker() noexcept {
     cb.reset();
     delete s;
     s = nullptr;
diff --git a/src/ui.h b/src/ui.h
index 1eaa8e6a..b204bf3e 100644
--- a/src/ui.h
+++ b/src/ui.h
@@ -39,7 +39,7 @@ public:
     UiPacker(const Packer *p_);
 
 public:
-    virtual ~UiPacker();
+    virtual ~UiPacker() noexcept;
 
     static void uiConfirmUpdate();
     static void uiPackTotal();
diff --git a/src/util/membuffer.cpp b/src/util/membuffer.cpp
index 4c9d4dda..c198377e 100644
--- a/src/util/membuffer.cpp
+++ b/src/util/membuffer.cpp
@@ -74,7 +74,7 @@ MemBuffer::MemBuffer(upx_uint64_t bytes) {
     debug_set(debug.last_return_address_alloc, upx_return_address());
 }
 
-MemBuffer::~MemBuffer() { this->dealloc(); }
+MemBuffer::~MemBuffer() noexcept { this->dealloc(); }
 
 // similar to BoundedPtr, except checks only at creation
 // skip == offset, take == size_in_bytes
@@ -215,19 +215,19 @@ void MemBuffer::alloc(upx_uint64_t bytes) {
         set_ne32(p + size_in_bytes + 4, stats.global_alloc_counter);
     }
     ptr = (pointer) (void *) p;
-#if !defined(__SANITIZE_ADDRESS__) && 0
-    fill(0, size_in_bytes, (rand() & 0xff) | 1); // debug
+#if DEBUG
+    memset(ptr, 0xff, size_in_bytes);
     (void) VALGRIND_MAKE_MEM_UNDEFINED(ptr, size_in_bytes);
 #endif
     stats.global_alloc_counter += 1;
     stats.global_total_bytes += size_in_bytes;
     stats.global_total_active_bytes += size_in_bytes;
-#if DEBUG
+#if DEBUG || 1
     checkState();
 #endif
 }
 
-void MemBuffer::dealloc() {
+void MemBuffer::dealloc() noexcept {
     if (ptr != nullptr) {
         debug_set(debug.last_return_address_dealloc, upx_return_address());
         checkState();
diff --git a/src/util/membuffer.h b/src/util/membuffer.h
index e57b0c5f..fd6c48d3 100644
--- a/src/util/membuffer.h
+++ b/src/util/membuffer.h
@@ -46,11 +46,12 @@ protected:
     size_type size_in_bytes;
 
 public:
-    MemBufferBase() : ptr(nullptr), size_in_bytes(0) {}
+    MemBufferBase() noexcept : ptr(nullptr), size_in_bytes(0) {}
+    inline ~MemBufferBase() noexcept {}
 
     // NOTE: implicit conversion to underlying pointer
     // HINT: for fully bound-checked pointer use XSPAN_S from xspan.h
-    operator pointer() const { return ptr; }
+    operator pointer() const noexcept { return ptr; }
 
     // membuffer + n -> pointer
     template 
@@ -66,8 +67,8 @@ private:
         DELETED_FUNCTION;
 
 public: // raw access
-    pointer raw_ptr() const { return ptr; }
-    size_type raw_size_in_bytes() const { return size_in_bytes; }
+    pointer raw_ptr() const noexcept { return ptr; }
+    size_type raw_size_in_bytes() const noexcept { return size_in_bytes; }
 
     pointer raw_bytes(size_t bytes) const {
         if (bytes > 0) {
@@ -84,7 +85,7 @@ class MemBuffer final : public MemBufferBase {
 public:
     MemBuffer() : MemBufferBase() {}
     explicit MemBuffer(upx_uint64_t bytes);
-    ~MemBuffer();
+    ~MemBuffer() noexcept;
 
     static unsigned getSizeForCompression(unsigned uncompressed_size, unsigned extra = 0);
     static unsigned getSizeForDecompression(unsigned uncompressed_size, unsigned extra = 0);
@@ -93,7 +94,7 @@ public:
     void allocForCompression(unsigned uncompressed_size, unsigned extra = 0);
     void allocForDecompression(unsigned uncompressed_size, unsigned extra = 0);
 
-    void dealloc();
+    void dealloc() noexcept;
     void checkState() const;
     unsigned getSize() const { return size_in_bytes; }
 
diff --git a/src/util/raw_bytes.h b/src/util/raw_bytes.h
index f6e7fdde..e0538c0f 100644
--- a/src/util/raw_bytes.h
+++ b/src/util/raw_bytes.h
@@ -35,9 +35,11 @@
 
 // default: for any regular pointer, raw_bytes() is just the pointer itself
 template 
-inline
-    typename std::enable_if::value && !std_is_bounded_array::value, T>::type
-    raw_bytes(T ptr, size_t size_in_bytes) {
+inline typename std::enable_if::value && !upx_std_is_bounded_array::value &&
+                                   (upx_is_integral::type>::value ||
+                                    std::is_void::type>::value),
+                               T>::type
+raw_bytes(T ptr, size_t size_in_bytes) {
     if (size_in_bytes > 0) {
         if very_unlikely (ptr == nullptr)
             throwCantPack("raw_bytes unexpected NULL ptr");
@@ -50,9 +52,10 @@ inline
 // default: for any regular pointer, raw_index_bytes() is just "pointer + index"
 // NOTE: index == number of elements, *NOT* size in bytes!
 template 
-inline
-    typename std::enable_if::value && !std_is_bounded_array::value, T>::type
-    raw_index_bytes(T ptr, size_t index, size_t size_in_bytes) {
+inline typename std::enable_if::value && !upx_std_is_bounded_array::value &&
+                                   upx_is_integral::type>::value,
+                               T>::type
+raw_index_bytes(T ptr, size_t index, size_t size_in_bytes) {
     typedef typename std::remove_pointer::type element_type;
     if very_unlikely (ptr == nullptr)
         throwCantPack("raw_index_bytes unexpected NULL ptr");
diff --git a/src/util/snprintf.h b/src/util/snprintf.h
index a57bf366..eeb69477 100644
--- a/src/util/snprintf.h
+++ b/src/util/snprintf.h
@@ -60,30 +60,30 @@ upx_rsize_t upx_safe_strlen(const char *);
 #define vsnprintf upx_safe_vsnprintf
 
 /*************************************************************************
-// some unsigned char string support functions to avoid casts
+// some uchar string support functions to avoid casts
 **************************************************************************/
 
-inline unsigned char *strcpy(unsigned char *s1, const unsigned char *s2) {
-    return (unsigned char *) strcpy((char *) s1, (const char *) s2);
+forceinline uchar *strcpy(uchar *s1, const uchar *s2) {
+    return (uchar *) strcpy((char *) s1, (const char *) s2);
 }
 
-inline int strcmp(const unsigned char *s1, const char *s2) { return strcmp((const char *) s1, s2); }
-inline int strcmp(const char *s1, const unsigned char *s2) { return strcmp(s1, (const char *) s2); }
-inline int strcmp(const unsigned char *s1, const unsigned char *s2) {
+forceinline int strcmp(const uchar *s1, const char *s2) { return strcmp((const char *) s1, s2); }
+forceinline int strcmp(const char *s1, const uchar *s2) { return strcmp(s1, (const char *) s2); }
+forceinline int strcmp(const uchar *s1, const uchar *s2) {
     return strcmp((const char *) s1, (const char *) s2);
 }
 
-inline int strcasecmp(const unsigned char *s1, const char *s2) {
+forceinline int strcasecmp(const uchar *s1, const char *s2) {
     return strcasecmp((const char *) s1, s2);
 }
-inline int strcasecmp(const char *s1, const unsigned char *s2) {
+forceinline int strcasecmp(const char *s1, const uchar *s2) {
     return strcasecmp(s1, (const char *) s2);
 }
-inline int strcasecmp(const unsigned char *s1, const unsigned char *s2) {
+forceinline int strcasecmp(const uchar *s1, const uchar *s2) {
     return strcasecmp((const char *) s1, (const char *) s2);
 }
 
-inline upx_rsize_t upx_safe_strlen(const unsigned char *s) {
+forceinline upx_rsize_t upx_safe_strlen(const uchar *s) {
     return upx_safe_strlen((const char *) s);
 }
 
diff --git a/src/util/util.cpp b/src/util/util.cpp
index 46fdd91c..9e295e20 100644
--- a/src/util/util.cpp
+++ b/src/util/util.cpp
@@ -101,6 +101,10 @@ TEST_CASE("mem_size") {
     CHECK_THROWS(mem_size(1, 0x30000000, 0x30000000, 0x30000000));
 }
 
+/*************************************************************************
+// ptr util
+**************************************************************************/
+
 int ptr_diff_bytes(const void *a, const void *b) {
     if very_unlikely (a == nullptr) {
         throwCantPack("ptr_diff_bytes null 1; take care");
@@ -108,13 +112,13 @@ int ptr_diff_bytes(const void *a, const void *b) {
     if very_unlikely (b == nullptr) {
         throwCantPack("ptr_diff_bytes null 2; take care");
     }
-    ptrdiff_t d = (const char *) a - (const char *) b;
+    ptrdiff_t d = (const charptr) a - (const charptr) b;
     if (a >= b) {
         if very_unlikely (!mem_size_valid_bytes(d))
-            throwCantPack("ptr_diff_bytes 1; take care");
+            throwCantPack("ptr_diff_bytes-1; take care");
     } else {
         if very_unlikely (!mem_size_valid_bytes(-d))
-            throwCantPack("ptr_diff_bytes 2; take care");
+            throwCantPack("ptr_diff_bytes-2; take care");
     }
     return ACC_ICONV(int, d);
 }
@@ -127,7 +131,7 @@ unsigned ptr_udiff_bytes(const void *a, const void *b) {
 }
 
 TEST_CASE("ptr_diff") {
-    char buf[4] = {0, 1, 2, 3};
+    byte buf[4] = {0, 1, 2, 3};
     CHECK_THROWS(ptr_diff_bytes(nullptr, buf));
     CHECK_THROWS(ptr_diff_bytes(buf, nullptr));
     CHECK(ptr_diff(buf, buf) == 0);
@@ -138,6 +142,108 @@ TEST_CASE("ptr_diff") {
     CHECK_THROWS(ptr_udiff(buf, buf + 1));
 }
 
+// check that 2 buffers do not overlap; will throw on error
+void uintptr_check_no_overlap(upx_uintptr_t a, size_t a_size, upx_uintptr_t b, size_t b_size) {
+    if very_unlikely (a == 0 || b == 0)
+        throwCantPack("ptr_check_no_overlap-nullptr");
+    upx_uintptr_t a_end = a + mem_size(1, a_size);
+    upx_uintptr_t b_end = b + mem_size(1, b_size);
+    if very_unlikely (a_end < a || b_end < b) // wrap-around
+        throwCantPack("ptr_check_no_overlap-overflow");
+    // same as (!(a >= b_end || b >= a_end))
+    if (a < b_end && b < a_end)
+        throwCantPack("ptr_check_no_overlap-ab");
+}
+
+// check that 3 buffers do not overlap; will throw on error
+void uintptr_check_no_overlap(upx_uintptr_t a, size_t a_size, upx_uintptr_t b, size_t b_size,
+                              upx_uintptr_t c, size_t c_size) {
+    if very_unlikely (a == 0 || b == 0 || c == 0)
+        throwCantPack("ptr_check_no_overlap-nullptr");
+    upx_uintptr_t a_end = a + mem_size(1, a_size);
+    upx_uintptr_t b_end = b + mem_size(1, b_size);
+    upx_uintptr_t c_end = c + mem_size(1, c_size);
+    if very_unlikely (a_end < a || b_end < b || c_end < c) // wrap-around
+        throwCantPack("ptr_check_no_overlap-overflow");
+    if (a < b_end && b < a_end)
+        throwCantPack("ptr_check_no_overlap-ab");
+    if (a < c_end && c < a_end)
+        throwCantPack("ptr_check_no_overlap-ac");
+    if (b < c_end && c < b_end)
+        throwCantPack("ptr_check_no_overlap-bc");
+}
+
+TEST_CASE("ptr_check_no_overlap 2") {
+    byte p[4] = {};
+
+    auto check_nothrow = [&p](int a, int as, int b, int bs) {
+        CHECK_NOTHROW(ptr_check_no_overlap(p + a, as, p + b, bs)); // ab
+        CHECK_NOTHROW(ptr_check_no_overlap(p + b, bs, p + a, as)); // ba
+    };
+    auto check_throws_ = [&p](int a, int as, int b, int bs) {
+        CHECK_THROWS(ptr_check_no_overlap(p + a, as, p + b, bs)); // ab
+        CHECK_THROWS(ptr_check_no_overlap(p + b, bs, p + a, as)); // ba
+    };
+
+    check_throws_(0, 1, 0, 1);
+    check_nothrow(0, 1, 1, 1);
+    check_throws_(0, 2, 1, 1);
+    check_nothrow(0, 2, 2, 1);
+    // empty buffers at edge
+    check_nothrow(0, 0, 0, 0);
+    check_nothrow(0, 0, 0, 1);
+    check_nothrow(0, 0, 1, 0);
+    // empty buffer
+    check_nothrow(0, 4, 0, 0);
+    check_throws_(0, 4, 1, 0);
+    check_throws_(0, 4, 2, 0);
+    check_throws_(0, 4, 3, 0);
+    check_nothrow(0, 4, 4, 0);
+}
+
+TEST_CASE("ptr_check_no_overlap 3") {
+    byte p[4] = {};
+
+    auto check_nothrow = [&p](int a, int as, int b, int bs, int c, int cs) {
+        CHECK_NOTHROW(ptr_check_no_overlap(p + a, as, p + b, bs, p + c, cs)); // abc
+        CHECK_NOTHROW(ptr_check_no_overlap(p + a, as, p + c, cs, p + b, bs)); // acb
+        CHECK_NOTHROW(ptr_check_no_overlap(p + b, bs, p + a, as, p + c, cs)); // bac
+        CHECK_NOTHROW(ptr_check_no_overlap(p + b, bs, p + c, cs, p + a, as)); // bca
+        CHECK_NOTHROW(ptr_check_no_overlap(p + c, cs, p + a, as, p + b, bs)); // cab
+        CHECK_NOTHROW(ptr_check_no_overlap(p + c, cs, p + b, bs, p + a, as)); // cba
+    };
+    auto check_throws_ = [&p](int a, int as, int b, int bs, int c, int cs) {
+        CHECK_THROWS(ptr_check_no_overlap(p + a, as, p + b, bs, p + c, cs)); // abc
+        CHECK_THROWS(ptr_check_no_overlap(p + a, as, p + c, cs, p + b, bs)); // acb
+        CHECK_THROWS(ptr_check_no_overlap(p + b, bs, p + a, as, p + c, cs)); // bac
+        CHECK_THROWS(ptr_check_no_overlap(p + b, bs, p + c, cs, p + a, as)); // bca
+        CHECK_THROWS(ptr_check_no_overlap(p + c, cs, p + a, as, p + b, bs)); // cab
+        CHECK_THROWS(ptr_check_no_overlap(p + c, cs, p + b, bs, p + a, as)); // cba
+    };
+
+    check_throws_(0, 1, 0, 1, 1, 1);
+    check_nothrow(0, 1, 1, 1, 2, 1);
+    check_throws_(0, 2, 1, 1, 2, 1);
+    check_nothrow(0, 2, 2, 1, 3, 1);
+    // empty buffers at edge
+    check_nothrow(0, 0, 0, 0, 0, 0);
+    check_nothrow(0, 0, 0, 0, 0, 1);
+    check_nothrow(0, 0, 0, 1, 1, 1);
+    check_nothrow(0, 0, 1, 0, 1, 1);
+    // empty buffer
+    check_nothrow(0, 4, 0, 0, 0, 0);
+    check_throws_(0, 4, 1, 0, 0, 0);
+    check_throws_(0, 4, 2, 0, 0, 0);
+    check_throws_(0, 4, 3, 0, 0, 0);
+    check_nothrow(0, 4, 4, 0, 0, 0);
+    // empty buffer
+    check_throws_(0, 4, 0, 0, 1, 0);
+    check_throws_(0, 4, 1, 0, 1, 0);
+    check_throws_(0, 4, 2, 0, 1, 0);
+    check_throws_(0, 4, 3, 0, 1, 0);
+    check_throws_(0, 4, 4, 0, 1, 0);
+}
+
 /*************************************************************************
 // bele.h
 **************************************************************************/
@@ -152,6 +258,60 @@ const BEPolicy be_policy;
 const LEPolicy le_policy;
 } // namespace N_BELE_RTP
 
+/*************************************************************************
+// stdlib
+**************************************************************************/
+
+void *upx_calloc(size_t n, size_t element_size) {
+    size_t bytes = mem_size(element_size, n); // assert size
+    void *p = malloc(bytes);
+    if (p != nullptr)
+        memset(p, 0, bytes);
+    return p;
+}
+
+// extremely simple stable sort: Gnomesort
+// WARNING: O(n**2) !!!!!
+void upx_stable_sort(void *base, size_t n, size_t element_size,
+                     int(__acc_cdecl_qsort *compare)(const void *, const void *)) {
+    (void) mem_size(element_size, n); // assert size
+    for (size_t i = 1; i < n;) {
+        char *a = (char *) base + element_size * i; // a = &array[i]
+        if (i == 0 || (compare(a - element_size, a) <= 0)) {
+            i += 1;
+        } else {
+            i -= 1;
+            // swap elements a[-1] <=> a[0]
+            //   upx_memswap(a - element_size, a, element_size);
+            size_t j = element_size;
+            do {
+                char tmp = *(a - element_size);
+                *(a - element_size) = *a;
+                *a++ = tmp;
+            } while (--j != 0);
+        }
+    }
+}
+
+TEST_CASE("upx_stable_sort") {
+    // TODO C++20: use std::next_permutation() to test all permutations
+    {
+        unsigned a[] = {0, 1};
+        upx_stable_sort(a, 2, sizeof(*a), ne32_compare);
+        CHECK((a[0] == 0 && a[1] == 1));
+    }
+    {
+        unsigned a[] = {1, 0};
+        upx_stable_sort(a, 2, sizeof(*a), ne32_compare);
+        CHECK((a[0] == 0 && a[1] == 1));
+    }
+    {
+        unsigned a[] = {2, 1, 0};
+        upx_stable_sort(a, 3, sizeof(*a), ne32_compare);
+        CHECK((a[0] == 0 && a[1] == 1 && a[2] == 2));
+    }
+}
+
 /*************************************************************************
 // qsort() util
 **************************************************************************/
@@ -258,7 +418,7 @@ int __acc_cdecl_qsort le64_compare_signed(const void *e1, const void *e2) {
 
 int find(const void *buf, int blen, const void *what, int wlen) {
     // nullptr is explicitly allowed here
-    if (buf == nullptr || blen <= 0 || what == nullptr || wlen <= 0)
+    if (buf == nullptr || blen < wlen || what == nullptr || wlen <= 0)
         return -1;
 
     const byte *b = (const byte *) buf;
@@ -367,7 +527,7 @@ static const char dir_sep[] = "/\\";
 #define fn_is_drive(s) (s[0] && s[1] == ':')
 #define fn_is_sep(c) (strchr(dir_sep, c) != nullptr)
 #define fn_skip_drive(s) (fn_is_drive(s) ? (s) + 2 : (s))
-#define fn_tolower(c) (tolower(((unsigned char) (c))))
+#define fn_tolower(c) (tolower(((uchar) (c))))
 
 #else
 
diff --git a/src/util/util.h b/src/util/util.h
index c3947272..81433f16 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -40,9 +40,6 @@ inline bool mem_size_valid_bytes(upx_uint64_t bytes) noexcept { return bytes <=
 bool mem_size_valid(upx_uint64_t element_size, upx_uint64_t n, upx_uint64_t extra1 = 0,
                     upx_uint64_t extra2 = 0) noexcept;
 
-// "new" with asserted size; will throw on invalid size
-#define New(type, n) new type[mem_size_get_n(sizeof(type), n)]
-
 // will throw on invalid size
 upx_rsize_t mem_size(upx_uint64_t element_size, upx_uint64_t n, upx_uint64_t extra1,
                      upx_uint64_t extra2 = 0);
@@ -77,6 +74,27 @@ inline void mem_clear(void *p, size_t n) {
     memset(p, 0, n);
 }
 
+// "new" with asserted size; will throw on invalid size
+#if DEBUG
+template 
+T *NewArray(upx_uint64_t n) {
+    size_t bytes = mem_size(sizeof(T), n); // assert size
+    T *array = new T[size_t(n)];
+    if (array) {
+        memset(array, 0xff, bytes);
+        (void) VALGRIND_MAKE_MEM_UNDEFINED(array, bytes);
+    }
+    return array;
+}
+#define New(type, n) (NewArray(n))
+#else
+#define New(type, n) new type[mem_size_get_n(sizeof(type), n)]
+#endif
+
+/*************************************************************************
+// ptr util
+**************************************************************************/
+
 // ptrdiff_t with nullptr checks and asserted size; will throw on failure
 // NOTE: returns size_in_bytes, not number of elements!
 int ptr_diff_bytes(const void *a, const void *b);
@@ -94,6 +112,30 @@ ptr_udiff(const T *a, const U *b) {
     return ptr_udiff_bytes(a, b);
 }
 
+// check that buffers do not overlap; will throw on error
+noinline void uintptr_check_no_overlap(upx_uintptr_t a, size_t a_size, upx_uintptr_t b,
+                                       size_t b_size);
+noinline void uintptr_check_no_overlap(upx_uintptr_t a, size_t a_size, upx_uintptr_t b,
+                                       size_t b_size, upx_uintptr_t c, size_t c_size);
+
+forceinline void ptr_check_no_overlap(const void *a, size_t a_size, const void *b, size_t b_size) {
+    uintptr_check_no_overlap((upx_uintptr_t) a, a_size, (upx_uintptr_t) b, b_size);
+}
+forceinline void ptr_check_no_overlap(const void *a, size_t a_size, const void *b, size_t b_size,
+                                      const void *c, size_t c_size) {
+    uintptr_check_no_overlap((upx_uintptr_t) a, a_size, (upx_uintptr_t) b, b_size,
+                             (upx_uintptr_t) c, c_size);
+}
+
+/*************************************************************************
+// stdlib
+**************************************************************************/
+
+void *upx_calloc(size_t n, size_t element_size);
+
+void upx_stable_sort(void *base, size_t n, size_t element_size,
+                     int(__acc_cdecl_qsort *compare)(const void *, const void *));
+
 /*************************************************************************
 // misc. support functions
 **************************************************************************/
diff --git a/src/util/xspan.cpp b/src/util/xspan.cpp
index fb105a9d..4096b615 100644
--- a/src/util/xspan.cpp
+++ b/src/util/xspan.cpp
@@ -44,28 +44,28 @@ struct XSpanStats {
 static XSpanStats xspan_stats;
 
 // HINT: set env-var "UPX_DEBUG_DOCTEST_DISABLE=1" for improved debugging experience
-noinline void xspan_fail_nullptr() {
+void xspan_fail_nullptr() {
     xspan_stats.fail_nullptr += 1;
     throwCantPack("xspan unexpected NULL pointer; take care!");
 }
-noinline void xspan_fail_nullbase() {
+void xspan_fail_nullbase() {
     xspan_stats.fail_nullbase += 1;
     throwCantPack("xspan unexpected NULL base; take care!");
 }
-noinline void xspan_fail_not_same_base() {
+void xspan_fail_not_same_base() {
     xspan_stats.fail_not_same_base += 1;
     throwCantPack("xspan unexpected base pointer; take care!");
 }
 
-noinline void xspan_fail_range_nullptr() {
+void xspan_fail_range_nullptr() {
     xspan_stats.fail_range_nullptr += 1;
     throwCantPack("xspan_check_range: unexpected NULL pointer; take care!");
 }
-noinline void xspan_fail_range_nullbase() {
+void xspan_fail_range_nullbase() {
     xspan_stats.fail_range_nullbase += 1;
     throwCantPack("xspan_check_range: unexpected NULL base; take care!");
 }
-noinline void xspan_fail_range_range() {
+void xspan_fail_range_range() {
     xspan_stats.fail_range_range += 1;
     throwCantPack("xspan_check_range: pointer out of range; take care!");
 }
@@ -75,7 +75,7 @@ void xspan_check_range(const void *p, const void *base, ptrdiff_t size_in_bytes)
         xspan_fail_range_nullptr();
     if very_unlikely (base == nullptr)
         xspan_fail_range_nullbase();
-    ptrdiff_t off = (const char *) p - (const char *) base;
+    ptrdiff_t off = (const charptr) p - (const charptr) base;
     if very_unlikely (off < 0 || off > size_in_bytes)
         xspan_fail_range_range();
     xspan_stats.check_range_counter += 1;
diff --git a/src/util/xspan.h b/src/util/xspan.h
index fff8cb91..850b2071 100644
--- a/src/util/xspan.h
+++ b/src/util/xspan.h
@@ -146,8 +146,11 @@ inline R *xspan_make_helper__(R * /*dummy*/, MemBuffer &first) {
 
 #endif // WITH_XSPAN
 
-#if 1
+/*************************************************************************
 // nicer names
+**************************************************************************/
+
+#if 1
 #define SPAN_0 XSPAN_0
 #define SPAN_P XSPAN_P
 #define SPAN_S XSPAN_S
diff --git a/src/util/xspan_impl.h b/src/util/xspan_impl.h
index 3acb4052..f2566d6c 100644
--- a/src/util/xspan_impl.h
+++ b/src/util/xspan_impl.h
@@ -52,11 +52,11 @@ void xspan_check_range(const void *p, const void *base, ptrdiff_t size_in_bytes)
 
 // help constructor to distinguish between number of elements and bytes
 struct XSpanCount {
-    explicit XSpanCount(size_t n) : count(n) {}
+    explicit XSpanCount(size_t n) noexcept : count(n) {}
     size_t count; // public
 };
 struct XSpanSizeInBytes {
-    explicit XSpanSizeInBytes(size_t bytes) : size_in_bytes(bytes) {}
+    explicit XSpanSizeInBytes(size_t bytes) noexcept : size_in_bytes(bytes) {}
     size_t size_in_bytes; // public
 };
 
diff --git a/src/util/xspan_impl_common.h b/src/util/xspan_impl_common.h
index 10c11ff9..f9733914 100644
--- a/src/util/xspan_impl_common.h
+++ b/src/util/xspan_impl_common.h
@@ -28,165 +28,176 @@
 //
 **************************************************************************/
 
-public:
-typedef T element_type;
-typedef typename std::add_lvalue_reference::type reference;
-typedef typename std::add_pointer::type pointer;
-typedef size_t size_type;
+#if CLANG_FORMAT_DUMMY_CLASS
+class CSelf {
+#endif
 
-// befriend all
-template 
-friend struct PtrOrSpan;
-template 
-friend struct PtrOrSpanOrNull;
-template 
-friend struct Span;
+public:
+    typedef T element_type;
+    typedef typename std::add_lvalue_reference::type reference;
+    typedef typename std::add_pointer::type pointer;
+    typedef size_t size_type;
+
+    // befriend all
+    template 
+    friend struct PtrOrSpan;
+    template 
+    friend struct PtrOrSpanOrNull;
+    template 
+    friend struct Span;
 
 #if XSPAN_CONFIG_ENABLE_IMPLICIT_CONVERSION
-operator pointer() const { return ptr; }
+public:
+    operator pointer() const noexcept { return ptr; }
 #endif
 
 private:
-pointer ptr; // current view into (base, base+size_in_bytes) iff base != nullptr
-pointer base;
-size_type size_in_bytes;
+    pointer ptr; // current view into (base, base+size_in_bytes) iff base != nullptr
+    pointer base;
+    size_type size_in_bytes;
 
 // debug - internal sanity check; also serves as pseudo-documentation
 #if DEBUG
-noinline void assertInvariants() const {
-    if __acc_cte (configRequirePtr)
-        assert(ptr != nullptr);
-    if __acc_cte (configRequireBase)
-        assert(base != nullptr);
-    if __acc_cte ((configRequirePtr || ptr != nullptr) && (configRequireBase || base != nullptr))
-        xspan_check_range(ptr, base, size_in_bytes);
-}
+    noinline void assertInvariants() const {
+        if __acc_cte (configRequirePtr)
+            assert(ptr != nullptr);
+        if __acc_cte (configRequireBase)
+            assert(base != nullptr);
+        if __acc_cte ((configRequirePtr || ptr != nullptr) &&
+                      (configRequireBase || base != nullptr))
+            xspan_check_range(ptr, base, size_in_bytes);
+    }
 #else
-forceinline void assertInvariants() const {}
+inline void assertInvariants() const noexcept {}
 #endif
 
-static forceinline pointer makeNotNull(pointer p) {
-    if very_unlikely (p == nullptr)
-        xspan_fail_nullptr();
-    return p;
-}
-// enforce config invariants at constructor time - static functions
-static forceinline pointer makePtr(pointer p) {
-    if __acc_cte (configRequirePtr && p == nullptr)
-        xspan_fail_nullptr();
-    return p;
-}
-static forceinline pointer makeBase(pointer b) {
-    if __acc_cte (configRequireBase && b == nullptr)
-        xspan_fail_nullbase();
-    return b;
-}
-// inverse logic for ensuring valid pointers from existing objets
-forceinline pointer ensurePtr() const {
-    if __acc_cte (!configRequirePtr && ptr == nullptr)
-        xspan_fail_nullptr();
-    return ptr;
-}
-forceinline pointer ensureBase() const {
-    if __acc_cte (!configRequireBase && base == nullptr)
-        xspan_fail_nullbase();
-    return base;
-}
+    static inline pointer makeNotNull(pointer p) {
+        if very_unlikely (p == nullptr)
+            xspan_fail_nullptr();
+        return p;
+    }
+    // enforce config invariants at constructor time - static functions
+    static inline pointer makePtr(pointer p) {
+        if __acc_cte (configRequirePtr && p == nullptr)
+            xspan_fail_nullptr();
+        return p;
+    }
+    static inline pointer makeBase(pointer b) {
+        if __acc_cte (configRequireBase && b == nullptr)
+            xspan_fail_nullbase();
+        return b;
+    }
+    // inverse logic for ensuring valid pointers from existing objects
+    inline pointer ensurePtr() const {
+        if __acc_cte (!configRequirePtr && ptr == nullptr)
+            xspan_fail_nullptr();
+        return ptr;
+    }
+    inline pointer ensureBase() const {
+        if __acc_cte (!configRequireBase && base == nullptr)
+            xspan_fail_nullbase();
+        return base;
+    }
 
 public:
-inline ~CSelf() {
 #if DEBUG
-    invalidate();
+    inline ~CSelf() { invalidate(); }
+#else
+inline ~CSelf() noexcept {}
 #endif
-}
-noinline void invalidate() {
-    assertInvariants();
-    ptr = (pointer) (acc_uintptr_t) 16; // point to non-null invalid address
-    // ptr = (pointer) (void *) &ptr; // point to self
-    base = ptr;
-    size_in_bytes = 0;
-    assertInvariants();
-}
-// constructors from pointers
-CSelf(pointer first, XSpanCount count)
-    : ptr(makePtr(first)), base(makeBase(first)), size_in_bytes(xspan_mem_size(count.count)) {
-    assertInvariants();
-}
-CSelf(pointer first, XSpanSizeInBytes bytes)
-    : ptr(makePtr(first)), base(makeBase(first)),
-      size_in_bytes(xspan_mem_size(bytes.size_in_bytes)) {
-    assertInvariants();
-}
-// enable this constructor only if the underlying type is char or void
-template 
-CSelf(U *first, size_type count, XSPAN_REQUIRES_SIZE_1_A)
-    : ptr(makePtr(first)), base(makeBase(first)), size_in_bytes(xspan_mem_size(count)) {
-    assertInvariants();
-}
-CSelf(pointer first, XSpanCount count, pointer base_)
-    : ptr(makePtr(first)), base(makeBase(base_)), size_in_bytes(xspan_mem_size(count.count)) {
-    // check invariants
-    if __acc_cte ((configRequirePtr || ptr != nullptr) && (configRequireBase || base != nullptr))
-        xspan_check_range(ptr, base, size_in_bytes);
-    // double sanity check
-    assertInvariants();
-}
-CSelf(pointer first, XSpanSizeInBytes bytes, pointer base_)
-    : ptr(makePtr(first)), base(makeBase(base_)),
-      size_in_bytes(xspan_mem_size(bytes.size_in_bytes)) {
-    // check invariants
-    if __acc_cte ((configRequirePtr || ptr != nullptr) && (configRequireBase || base != nullptr))
-        xspan_check_range(ptr, base, size_in_bytes);
-    // double sanity check
-    assertInvariants();
-}
-// enable this constructor only if the underlying type is char or void
-template 
-CSelf(pointer first, size_type count, U *base_, XSPAN_REQUIRES_SIZE_1_A)
-    : ptr(makePtr(first)), base(makeBase(base_)), size_in_bytes(xspan_mem_size(count)) {
-    // check invariants
-    if __acc_cte ((configRequirePtr || ptr != nullptr) && (configRequireBase || base != nullptr))
-        xspan_check_range(ptr, base, size_in_bytes);
-    // double sanity check
-    assertInvariants();
-}
+    noinline void invalidate() {
+        assertInvariants();
+        ptr = (pointer) (upx_uintptr_t) 16; // point to non-null invalid address
+        // ptr = (pointer) (void *) &ptr; // point to self
+        base = ptr;
+        size_in_bytes = 0;
+        assertInvariants();
+    }
+    // constructors from pointers
+    CSelf(pointer first, XSpanCount count)
+        : ptr(makePtr(first)), base(makeBase(first)),
+          size_in_bytes(xspan_mem_size(count.count)) {
+        assertInvariants();
+    }
+    CSelf(pointer first, XSpanSizeInBytes bytes)
+        : ptr(makePtr(first)), base(makeBase(first)),
+          size_in_bytes(xspan_mem_size(bytes.size_in_bytes)) {
+        assertInvariants();
+    }
+    // enable this constructor only if the underlying type is char or void
+    template 
+    CSelf(U *first, size_type count, XSPAN_REQUIRES_SIZE_1_A)
+        : ptr(makePtr(first)), base(makeBase(first)), size_in_bytes(xspan_mem_size(count)) {
+        assertInvariants();
+    }
+    CSelf(pointer first, XSpanCount count, pointer base_)
+        : ptr(makePtr(first)), base(makeBase(base_)),
+          size_in_bytes(xspan_mem_size(count.count)) {
+        // check invariants
+        if __acc_cte ((configRequirePtr || ptr != nullptr) &&
+                      (configRequireBase || base != nullptr))
+            xspan_check_range(ptr, base, size_in_bytes);
+        // double sanity check
+        assertInvariants();
+    }
+    CSelf(pointer first, XSpanSizeInBytes bytes, pointer base_)
+        : ptr(makePtr(first)), base(makeBase(base_)),
+          size_in_bytes(xspan_mem_size(bytes.size_in_bytes)) {
+        // check invariants
+        if __acc_cte ((configRequirePtr || ptr != nullptr) &&
+                      (configRequireBase || base != nullptr))
+            xspan_check_range(ptr, base, size_in_bytes);
+        // double sanity check
+        assertInvariants();
+    }
+    // enable this constructor only if the underlying type is char or void
+    template 
+    CSelf(pointer first, size_type count, U *base_, XSPAN_REQUIRES_SIZE_1_A)
+        : ptr(makePtr(first)), base(makeBase(base_)), size_in_bytes(xspan_mem_size(count)) {
+        // check invariants
+        if __acc_cte ((configRequirePtr || ptr != nullptr) &&
+                      (configRequireBase || base != nullptr))
+            xspan_check_range(ptr, base, size_in_bytes);
+        // double sanity check
+        assertInvariants();
+    }
 #ifdef UPX_VERSION_HEX
-// constructors from MemBuffer
-CSelf(MemBuffer &mb)
-    : CSelf(makeNotNull((pointer) membuffer_get_void_ptr(mb)),
-            XSpanSizeInBytes(membuffer_get_size(mb))) {}
-CSelf(pointer first, MemBuffer &mb)
-    : CSelf(first, XSpanSizeInBytes(membuffer_get_size(mb)),
-            makeNotNull((pointer) membuffer_get_void_ptr(mb))) {}
-CSelf(std::nullptr_t, MemBuffer &) XSPAN_DELETED_FUNCTION;
+    // constructors from MemBuffer
+    CSelf(MemBuffer &mb)
+        : CSelf(makeNotNull((pointer) membuffer_get_void_ptr(mb)),
+                XSpanSizeInBytes(membuffer_get_size(mb))) {}
+    CSelf(pointer first, MemBuffer &mb)
+        : CSelf(first, XSpanSizeInBytes(membuffer_get_size(mb)),
+                makeNotNull((pointer) membuffer_get_void_ptr(mb))) {}
+    CSelf(std::nullptr_t, MemBuffer &) XSPAN_DELETED_FUNCTION;
 #endif
-// disable constructors from nullptr to catch compile-time misuse
+    // disable constructors from nullptr to catch compile-time misuse
 private:
-CSelf(std::nullptr_t, XSpanCount) XSPAN_DELETED_FUNCTION;
-CSelf(std::nullptr_t, XSpanCount, std::nullptr_t) XSPAN_DELETED_FUNCTION;
-CSelf(const void *, XSpanCount, std::nullptr_t) XSPAN_DELETED_FUNCTION;
-CSelf(std::nullptr_t, XSpanSizeInBytes) XSPAN_DELETED_FUNCTION;
-CSelf(std::nullptr_t, XSpanSizeInBytes, std::nullptr_t) XSPAN_DELETED_FUNCTION;
-CSelf(const void *, XSpanSizeInBytes, std::nullptr_t) XSPAN_DELETED_FUNCTION;
-CSelf(std::nullptr_t, size_type) XSPAN_DELETED_FUNCTION;
-CSelf(std::nullptr_t, size_type, std::nullptr_t) XSPAN_DELETED_FUNCTION;
-CSelf(const void *, size_type, std::nullptr_t) XSPAN_DELETED_FUNCTION;
+    CSelf(std::nullptr_t, XSpanCount) XSPAN_DELETED_FUNCTION;
+    CSelf(std::nullptr_t, XSpanCount, std::nullptr_t) XSPAN_DELETED_FUNCTION;
+    CSelf(const void *, XSpanCount, std::nullptr_t) XSPAN_DELETED_FUNCTION;
+    CSelf(std::nullptr_t, XSpanSizeInBytes) XSPAN_DELETED_FUNCTION;
+    CSelf(std::nullptr_t, XSpanSizeInBytes, std::nullptr_t) XSPAN_DELETED_FUNCTION;
+    CSelf(const void *, XSpanSizeInBytes, std::nullptr_t) XSPAN_DELETED_FUNCTION;
+    CSelf(std::nullptr_t, size_type) XSPAN_DELETED_FUNCTION;
+    CSelf(std::nullptr_t, size_type, std::nullptr_t) XSPAN_DELETED_FUNCTION;
+    CSelf(const void *, size_type, std::nullptr_t) XSPAN_DELETED_FUNCTION;
 
-// unchecked constructor
+    // unchecked constructor
 protected:
-enum ModeUnchecked { Unchecked };
-CSelf(ModeUnchecked, pointer p, size_type bytes, pointer b)
-    : ptr(p), base(b), size_in_bytes(bytes) {
-    assertInvariants();
-}
-// unchecked assignment
-Self &assign(ModeUnchecked, pointer p, size_type bytes, pointer b) {
-    ptr = p;
-    base = b;
-    size_in_bytes = bytes;
-    assertInvariants();
-    return *this;
-}
+    enum ModeUnchecked { Unchecked };
+    CSelf(ModeUnchecked, pointer p, size_type bytes, pointer b)
+        : ptr(p), base(b), size_in_bytes(bytes) {
+        assertInvariants();
+    }
+    // unchecked assignment
+    Self &assign(ModeUnchecked, pointer p, size_type bytes, pointer b) {
+        ptr = p;
+        base = b;
+        size_in_bytes = bytes;
+        assertInvariants();
+        return *this;
+    }
 #if 0
 Self &assign(ModeUnchecked, const Self &other) {
     ptr = other.ptr;
@@ -198,230 +209,250 @@ Self &assign(ModeUnchecked, const Self &other) {
 #endif
 
 public:
-// assignment - here we can rely on invariants enforced at construction time by makePtr/makeBase
-// NOTE: *this remains unmodified in case of failure
-Self &assign(pointer other) {
-    assertInvariants();
-    other = makePtr(other);
-    if __acc_cte ((configRequirePtr || other != nullptr) && (configRequireBase || base != nullptr))
-        xspan_check_range(other, base, size_in_bytes);
-    // ok
-    ptr = other;
-    assertInvariants();
-    return *this;
-}
-Self &assign(const Self &other) {
-    assertInvariants();
-    other.assertInvariants();
-    if __acc_cte (!configRequireBase && base == nullptr) {
-        // magic 1: if base is unset, automatically set base/size_in_bytes from other
-        if __acc_cte ((configRequirePtr || other.ptr != nullptr) &&
-                      (configRequireBase || other.base != nullptr))
-            xspan_check_range(other.ptr, other.base, other.size_in_bytes);
-        // ok
-        ptr = other.ptr;
-        base = other.base;
-        size_in_bytes = other.size_in_bytes;
-    } else {
-        // magic 2: assert same base (but ignore size_in_bytes !)
-        if __acc_cte (configRequireBase || other.base != nullptr)
-            if very_unlikely (base != other.base)
-                xspan_fail_not_same_base();
-        if __acc_cte ((configRequirePtr || other.ptr != nullptr) &&
+    // assignment - here we can rely on invariants enforced at construction time by makePtr/makeBase
+    // NOTE: *this remains unmodified in case of failure
+    Self &assign(pointer other) {
+        assertInvariants();
+        other = makePtr(other);
+        if __acc_cte ((configRequirePtr || other != nullptr) &&
                       (configRequireBase || base != nullptr))
-            xspan_check_range(other.ptr, base, size_in_bytes);
+            xspan_check_range(other, base, size_in_bytes);
         // ok
-        ptr = other.ptr;
+        ptr = other;
+        assertInvariants();
+        return *this;
+    }
+    Self &assign(const Self &other) {
+        assertInvariants();
+        other.assertInvariants();
+        if __acc_cte (!configRequireBase && base == nullptr) {
+            // magic 1: if base is unset, automatically set base/size_in_bytes from other
+            if __acc_cte ((configRequirePtr || other.ptr != nullptr) &&
+                          (configRequireBase || other.base != nullptr))
+                xspan_check_range(other.ptr, other.base, other.size_in_bytes);
+            // ok
+            ptr = other.ptr;
+            base = other.base;
+            size_in_bytes = other.size_in_bytes;
+        } else {
+            // magic 2: assert same base (but ignore size_in_bytes !)
+            if __acc_cte (configRequireBase || other.base != nullptr)
+                if very_unlikely (base != other.base)
+                    xspan_fail_not_same_base();
+            if __acc_cte ((configRequirePtr || other.ptr != nullptr) &&
+                          (configRequireBase || base != nullptr))
+                xspan_check_range(other.ptr, base, size_in_bytes);
+            // ok
+            ptr = other.ptr;
+        }
+        assertInvariants();
+        return *this;
     }
-    assertInvariants();
-    return *this;
-}
 
-Self &operator=(pointer other) { return assign(other); }
+    Self &operator=(pointer other) { return assign(other); }
 
-Self &operator=(const Self &other) { return assign(other); }
+    Self &operator=(const Self &other) { return assign(other); }
 
-// FIXME: this is not called??
-template 
-XSPAN_REQUIRES_CONVERTIBLE_R(Self &)
-operator=(const CSelf &other) {
-    // assert(0);
-    return assign(Self(other));
-}
+    // FIXME: this is not called??
+    template 
+    XSPAN_REQUIRES_CONVERTIBLE_R(Self &)
+    operator=(const CSelf &other) {
+        // assert(0);
+        return assign(Self(other));
+    }
 
 #ifdef UPX_VERSION_HEX
-Self &operator=(MemBuffer &mb) { return assign(Self(mb)); }
+    Self &operator=(MemBuffer &mb) { return assign(Self(mb)); }
 #endif
 
-Self subspan(ptrdiff_t offset, ptrdiff_t count) {
-    pointer begin = check_add(ptr, offset);
-    pointer end = check_add(begin, count);
-    if (begin <= end)
-        return Self(Unchecked, begin, (end - begin) * sizeof(T), begin);
-    else
-        return Self(Unchecked, end, (begin - end) * sizeof(T), end);
-}
+    Self subspan(ptrdiff_t offset, ptrdiff_t count) {
+        pointer begin = check_add(ptr, offset);
+        pointer end = check_add(begin, count);
+        if (begin <= end)
+            return Self(Unchecked, begin, (end - begin) * sizeof(T), begin);
+        else
+            return Self(Unchecked, end, (begin - end) * sizeof(T), end);
+    }
 
-bool operator==(pointer other) const { return ptr == other; }
-template 
-XSPAN_REQUIRES_CONVERTIBLE_R(bool)
-operator==(U *other) const {
-    return ptr == other;
-}
-bool operator!=(pointer other) const { return ptr != other; }
-template 
-XSPAN_REQUIRES_CONVERTIBLE_R(bool)
-operator!=(U *other) const {
-    return ptr != other;
-}
+    bool operator==(pointer other) const { return ptr == other; }
+    template 
+    XSPAN_REQUIRES_CONVERTIBLE_R(bool)
+    operator==(U *other) const {
+        return ptr == other;
+    }
+    bool operator!=(pointer other) const { return ptr != other; }
+    template 
+    XSPAN_REQUIRES_CONVERTIBLE_R(bool)
+    operator!=(U *other) const {
+        return ptr != other;
+    }
 
-template 
-XSPAN_REQUIRES_CONVERTIBLE_R(bool)
-operator==(const PtrOrSpan &other) const {
-    return ptr == other.ptr;
-}
-template 
-XSPAN_REQUIRES_CONVERTIBLE_R(bool)
-operator==(const PtrOrSpanOrNull &other) const {
-    return ptr == other.ptr;
-}
-template 
-XSPAN_REQUIRES_CONVERTIBLE_R(bool)
-operator==(const Span &other) const {
-    return ptr == other.ptr;
-}
+    template 
+    XSPAN_REQUIRES_CONVERTIBLE_R(bool)
+    operator==(const PtrOrSpan &other) const {
+        return ptr == other.ptr;
+    }
+    template 
+    XSPAN_REQUIRES_CONVERTIBLE_R(bool)
+    operator==(const PtrOrSpanOrNull &other) const {
+        return ptr == other.ptr;
+    }
+    template 
+    XSPAN_REQUIRES_CONVERTIBLE_R(bool)
+    operator==(const Span &other) const {
+        return ptr == other.ptr;
+    }
 
-template 
-XSPAN_REQUIRES_CONVERTIBLE_R(bool)
-operator!=(const PtrOrSpan &other) const {
-    return !(*this == other);
-}
-template 
-XSPAN_REQUIRES_CONVERTIBLE_R(bool)
-operator!=(const PtrOrSpanOrNull &other) const {
-    return !(*this == other);
-}
-template 
-XSPAN_REQUIRES_CONVERTIBLE_R(bool)
-operator!=(const Span &other) const {
-    return !(*this == other);
-}
+    template 
+    XSPAN_REQUIRES_CONVERTIBLE_R(bool)
+    operator!=(const PtrOrSpan &other) const {
+        return !(*this == other);
+    }
+    template 
+    XSPAN_REQUIRES_CONVERTIBLE_R(bool)
+    operator!=(const PtrOrSpanOrNull &other) const {
+        return !(*this == other);
+    }
+    template 
+    XSPAN_REQUIRES_CONVERTIBLE_R(bool)
+    operator!=(const Span &other) const {
+        return !(*this == other);
+    }
 
-// check for notNull here
-bool operator<(std::nullptr_t) const XSPAN_DELETED_FUNCTION;
-bool operator<(pointer other) const { return ensurePtr() < makeNotNull(other); }
+    // check for notNull here
+    bool operator<(std::nullptr_t) const XSPAN_DELETED_FUNCTION;
+    bool operator<(pointer other) const { return ensurePtr() < makeNotNull(other); }
 
-template 
-XSPAN_REQUIRES_CONVERTIBLE_R(bool)
-operator<(const PtrOrSpan &other) const {
-    return ensurePtr() < other.ensurePtr();
-}
-template 
-XSPAN_REQUIRES_CONVERTIBLE_R(bool)
-operator<(const PtrOrSpanOrNull &other) const {
-    return ensurePtr() < other.ensurePtr();
-}
-template 
-XSPAN_REQUIRES_CONVERTIBLE_R(bool)
-operator<(const Span &other) const {
-    return ensurePtr() < other.ensurePtr();
-}
+    template 
+    XSPAN_REQUIRES_CONVERTIBLE_R(bool)
+    operator<(const PtrOrSpan &other) const {
+        return ensurePtr() < other.ensurePtr();
+    }
+    template 
+    XSPAN_REQUIRES_CONVERTIBLE_R(bool)
+    operator<(const PtrOrSpanOrNull &other) const {
+        return ensurePtr() < other.ensurePtr();
+    }
+    template 
+    XSPAN_REQUIRES_CONVERTIBLE_R(bool)
+    operator<(const Span &other) const {
+        return ensurePtr() < other.ensurePtr();
+    }
 
-// dereference
-reference operator*() const { return *check_deref(ptr); }
+    // dereference
+    reference operator*() const { return *check_deref(ptr); }
 
-// array access
-reference operator[](ptrdiff_t i) const { return *check_deref(ptr, i); }
+    // array access
+    reference operator[](ptrdiff_t i) const { return *check_deref(ptr, i); }
 
-// arrow operator
-pointer operator->() const { return check_deref(ptr); }
+    // arrow operator
+    pointer operator->() const { return check_deref(ptr); }
 
-Self &operator++() {
-    ptr = check_add(ptr, 1);
-    return *this;
-}
-Self operator++(int) {
-    Self tmp = *this;
-    ++*this;
-    return tmp;
-}
-Self &operator--() {
-    ptr = check_add(ptr, -1);
-    return *this;
-}
-Self operator--(int) {
-    Self tmp = *this;
-    --*this;
-    return tmp;
-}
+    Self &operator++() {
+        ptr = check_add(ptr, 1);
+        return *this;
+    }
+    Self operator++(int) {
+        Self tmp = *this;
+        ++*this;
+        return tmp;
+    }
+    Self &operator--() {
+        ptr = check_add(ptr, -1);
+        return *this;
+    }
+    Self operator--(int) {
+        Self tmp = *this;
+        --*this;
+        return tmp;
+    }
 
-Self &operator+=(ptrdiff_t n) {
-    ptr = check_add(ptr, n);
-    return *this;
-}
-Self &operator-=(ptrdiff_t n) {
-    ptr = check_add(ptr, -n);
-    return *this;
-}
+    Self &operator+=(ptrdiff_t n) {
+        ptr = check_add(ptr, n);
+        return *this;
+    }
+    Self &operator-=(ptrdiff_t n) {
+        ptr = check_add(ptr, -n);
+        return *this;
+    }
 
-Self operator+(ptrdiff_t n) const {
-    pointer first = check_add(ptr, n);
-    return Self(Unchecked, first, size_in_bytes, base);
-}
-Self operator-(ptrdiff_t n) const {
-    pointer first = check_add(ptr, -n);
-    return Self(Unchecked, first, size_in_bytes, base);
-}
+    Self operator+(ptrdiff_t n) const {
+        pointer first = check_add(ptr, n);
+        return Self(Unchecked, first, size_in_bytes, base);
+    }
+    Self operator-(ptrdiff_t n) const {
+        pointer first = check_add(ptr, -n);
+        return Self(Unchecked, first, size_in_bytes, base);
+    }
 
 private:
-pointer check_deref(pointer p) const {
-    if __acc_cte (!configRequirePtr && p == nullptr)
-        xspan_fail_nullptr();
-    if __acc_cte (configRequireBase || base != nullptr)
-        xspan_check_range(p, base, size_in_bytes - sizeof(T));
-    assertInvariants();
-    return p;
-}
-pointer check_deref(pointer p, ptrdiff_t n) const {
-    if __acc_cte (!configRequirePtr && p == nullptr)
-        xspan_fail_nullptr();
-    xspan_mem_size_assert_ptrdiff(n);
-    p += n;
-    if __acc_cte (configRequireBase || base != nullptr)
-        xspan_check_range(p, base, size_in_bytes - sizeof(T));
-    assertInvariants();
-    return p;
-}
-pointer check_add(pointer p, ptrdiff_t n) const {
-    if __acc_cte (!configRequirePtr && p == nullptr)
-        xspan_fail_nullptr();
-    xspan_mem_size_assert_ptrdiff(n);
-    p += n;
-    if __acc_cte (configRequireBase || base != nullptr)
-        xspan_check_range(p, base, size_in_bytes);
-    assertInvariants();
-    return p;
-}
+    pointer check_deref(pointer p) const {
+        if __acc_cte (!configRequirePtr && p == nullptr)
+            xspan_fail_nullptr();
+        if __acc_cte (configRequireBase || base != nullptr)
+            xspan_check_range(p, base, size_in_bytes - sizeof(T));
+        assertInvariants();
+        return p;
+    }
+    pointer check_deref(pointer p, ptrdiff_t n) const {
+        if __acc_cte (!configRequirePtr && p == nullptr)
+            xspan_fail_nullptr();
+        xspan_mem_size_assert_ptrdiff(n);
+        p += n;
+        if __acc_cte (configRequireBase || base != nullptr)
+            xspan_check_range(p, base, size_in_bytes - sizeof(T));
+        assertInvariants();
+        return p;
+    }
+    pointer check_add(pointer p, ptrdiff_t n) const {
+        if __acc_cte (!configRequirePtr && p == nullptr)
+            xspan_fail_nullptr();
+        xspan_mem_size_assert_ptrdiff(n);
+        p += n;
+        if __acc_cte (configRequireBase || base != nullptr)
+            xspan_check_range(p, base, size_in_bytes);
+        assertInvariants();
+        return p;
+    }
 
-// disable taking the address => force passing by reference
-// [I'm not too sure about this design decision, but we can always allow it if needed]
-Self *operator&() const XSPAN_DELETED_FUNCTION;
+    // disable taking the address => force passing by reference
+    // [I'm not too sure about this design decision, but we can always allow it if needed]
+    Self *operator&() const XSPAN_DELETED_FUNCTION;
 
 public: // raw access
-pointer raw_ptr() const { return ptr; }
-pointer raw_base() const { return base; }
-size_type raw_size_in_bytes() const { return size_in_bytes; }
+    pointer raw_ptr() const noexcept { return ptr; }
+    pointer raw_base() const noexcept { return base; }
+    size_type raw_size_in_bytes() const noexcept { return size_in_bytes; }
 
-pointer raw_bytes(size_t bytes) const {
-    assertInvariants();
-    if (bytes > 0) {
-        if __acc_cte (!configRequirePtr && ptr == nullptr)
-            xspan_fail_nullptr();
-        if __acc_cte (configRequireBase || base != nullptr) {
-            xspan_check_range(ptr, base, size_in_bytes - bytes);
+    pointer raw_bytes(size_t bytes) const {
+        assertInvariants();
+        if (bytes > 0) {
+            if __acc_cte (!configRequirePtr && ptr == nullptr)
+                xspan_fail_nullptr();
+            if __acc_cte (configRequireBase || base != nullptr) {
+                xspan_check_range(ptr, base, size_in_bytes - bytes);
+            }
         }
+        return ptr;
     }
-    return ptr;
-}
+
+    // like C++ std::span
+    pointer data() const noexcept { return ptr; }
+    pointer data(size_t bytes) const { return raw_bytes(bytes); } // UPX extra
+    // size_type size() const { return size_bytes() / sizeof(element_type); } // NOT USED
+    size_type size_bytes() const {
+        assertInvariants();
+        if __acc_cte (!configRequirePtr && ptr == nullptr)
+            return 0;
+        if __acc_cte (!configRequireBase && base == nullptr)
+            return 0;
+        const charptr begin = (const charptr) ptr;
+        const charptr end = (const charptr) base + size_in_bytes;
+        return end - begin;
+    }
+
+#if CLANG_FORMAT_DUMMY_CLASS
+}; // class
+#endif
 
 /* vim:set ts=4 sw=4 et: */
diff --git a/src/util/xspan_impl_ptr.h b/src/util/xspan_impl_ptr.h
index c4df6b6b..07eb3d08 100644
--- a/src/util/xspan_impl_ptr.h
+++ b/src/util/xspan_impl_ptr.h
@@ -51,25 +51,25 @@ private:
     pointer ptr;
 
     // enforce config invariants at constructor time - static functions
-    static forceinline pointer makePtr(pointer p) { return p; }
-    // inverse logic for ensuring valid pointers from existing objets
-    forceinline pointer ensurePtr() const { return ptr; }
+    static inline pointer makePtr(pointer p) { return p; }
+    // inverse logic for ensuring valid pointers from existing objects
+    inline pointer ensurePtr() const { return ptr; }
     // debug
-    forceinline void assertInvariants() const {}
+    inline void assertInvariants() const noexcept {}
 
 public:
 #if XSPAN_CONFIG_ENABLE_IMPLICIT_CONVERSION || 1
-    operator pointer() const { return ptr; }
+    operator pointer() const noexcept { return ptr; }
 #endif
 
-    inline ~CSelf() {
 #if DEBUG
-        invalidate();
+    inline ~CSelf() { invalidate(); }
+#else
+    inline ~CSelf() noexcept {}
 #endif
-    }
     noinline void invalidate() {
         assertInvariants();
-        ptr = (pointer) (acc_uintptr_t) 16; // point to non-null invalid address
+        ptr = (pointer) (upx_uintptr_t) 16; // point to non-null invalid address
         // ptr = (pointer) (void *) &ptr; // point to self
         assertInvariants();
     }
@@ -116,7 +116,7 @@ public:
         return assign(Self(other));
     }
 
-    // comparision
+    // comparison
 
     bool operator==(pointer other) const { return ptr == other; }
     template 
@@ -187,7 +187,7 @@ private:
     forceinline pointer check_add(pointer p, ptrdiff_t n) const { return p + n; }
 
 public: // raw access
-    pointer raw_ptr() const { return ptr; }
+    pointer raw_ptr() const noexcept { return ptr; }
 
     pointer raw_bytes(size_t bytes) const {
         assertInvariants();
diff --git a/src/work.cpp b/src/work.cpp
index 88849e7c..0aff1674 100644
--- a/src/work.cpp
+++ b/src/work.cpp
@@ -26,7 +26,7 @@
  */
 
 // This file implements the central loop, and it uses class PackMaster to
-// dispatch. PackMaster by itself will instatiate a concrete subclass
+// dispatch. PackMaster by itself will instantiate a concrete subclass
 // of class Packer which then does the actual work.
 // And see p_com.cpp for a simple executable format.
 
@@ -141,7 +141,7 @@ void do_one_file(const char *iname, char *oname) {
             else
                 flags |= O_EXCL;
             int shmode = SH_DENYWR;
-#if defined(__MINT__)
+#if (ACC_ARCH_M68K && ACC_OS_TOS && ACC_CC_GNUC) && defined(__MINT__)
             flags |= O_TRUNC;
             shmode = O_DENYRW;
 #endif
@@ -266,7 +266,7 @@ static void unlink_ofile(char *oname) {
 int do_files(int i, int argc, char *argv[]) {
     upx_compiler_sanity_check();
     if (opt->verbose >= 1) {
-        show_head();
+        show_header();
         UiPacker::uiHeader();
     }