From 16c8f6d160f749c11f8409912b0479f00cc14546 Mon Sep 17 00:00:00 2001 From: "Markus F.X.J. Oberhumer" Date: Sun, 7 Jul 2024 14:50:27 +0200 Subject: [PATCH] src: portability updates --- .github/workflows/ci.yml | 2 +- src/bele.h | 2 +- src/check/dt_check.cpp | 379 ++++++++++++++++++++++++++++++-- src/check/dt_cxxlib.cpp | 9 +- src/conf.h | 23 +- src/file.cpp | 14 +- src/filter/ct.h | 8 +- src/help.cpp | 34 ++- src/p_lx_elf.cpp | 9 +- src/p_mach.cpp | 3 + src/p_vmlinz.cpp | 2 +- src/pefile.cpp | 6 +- src/util/cxxlib.h | 3 +- src/util/membuffer.cpp | 2 + src/util/system_check_predefs.h | 158 +++++++++++++ src/util/system_defs.h | 9 + src/util/system_headers.h | 66 +----- src/util/util.cpp | 54 ++++- src/util/util.h | 29 ++- src/util/windows_lean.h | 15 ++ src/util/xspan_impl_ptr.h | 2 +- 21 files changed, 700 insertions(+), 129 deletions(-) create mode 100644 src/util/system_check_predefs.h diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6377732b..8bc4137b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,7 +64,7 @@ jobs: - run: bash ./misc/scripts/check_whitespace_git.sh - name: Check source code formatting run: | - export UPX_CLANG_FORMAT="$(readlink -en "$PWD/../deps/bin-upx-20221212/clang-format-15.0.6")" + export UPX_CLANG_FORMAT="$(readlink -en ../deps/bin-upx-20221212/clang-format-15.0.6)" make -C src clang-format if ! git diff --quiet; then git diff; exit 1; fi - name: Rebuild docs diff --git a/src/bele.h b/src/bele.h index 27cdba16..155b7834 100644 --- a/src/bele.h +++ b/src/bele.h @@ -422,7 +422,7 @@ forceinline constexpr int sign_extend(unsigned v, unsigned bits) noexcept { forceinline constexpr upx_int64_t sign_extend(upx_uint64_t v, unsigned bits) noexcept { #if (ACC_ARCH_M68K) // no barrel shifter - const upx_uint64_t sign_bit = 1ull << (bits - 1); + const upx_uint64_t sign_bit = upx_uint64_t(1) << (bits - 1); return ACC_ICAST(upx_int64_t, (v & (sign_bit - 1)) - (v & sign_bit)); #else return ACC_ICAST(upx_int64_t, v << (64 - bits)) >> (64 - bits); diff --git a/src/check/dt_check.cpp b/src/check/dt_check.cpp index 6df6f729..32db45d8 100644 --- a/src/check/dt_check.cpp +++ b/src/check/dt_check.cpp @@ -93,20 +93,193 @@ int upx_doctest_check(int argc, char **argv) { int upx_doctest_check() { return upx_doctest_check(0, nullptr); } /************************************************************************* -// compile-time checks +// check fundamental types **************************************************************************/ -static_assert(std::is_same::value); -static_assert(std::is_same::value); -static_assert(std::is_same::value); -static_assert(std::is_same::value); -static_assert(std::is_same::value); -static_assert(std::is_same::value); +static_assert(std::is_integral::value, ""); +static_assert(std::is_integral::value, ""); +static_assert(std::is_integral::value, ""); +static_assert(std::is_integral::value, ""); + +static_assert(std::is_signed::value, ""); +static_assert(!std::is_signed::value, ""); +static_assert(std::is_signed::value, ""); +static_assert(!std::is_signed::value, ""); + +static_assert(!std::is_unsigned::value, ""); +static_assert(std::is_unsigned::value, ""); +static_assert(!std::is_unsigned::value, ""); +static_assert(std::is_unsigned::value, ""); + +#if defined(__SIZEOF_SHORT__) +static_assert(sizeof(short) == __SIZEOF_SHORT__, ""); +#endif +#if defined(__SIZEOF_INT__) +static_assert(sizeof(int) == __SIZEOF_INT__, ""); +#endif +#if defined(__SIZEOF_LONG__) +static_assert(sizeof(long) == __SIZEOF_LONG__, ""); +#endif +#if defined(__SIZEOF_LONG_LONG__) +static_assert(sizeof(long long) == __SIZEOF_LONG_LONG__, ""); +#endif +#if defined(__SIZEOF_INT128__) +static_assert(16 == __SIZEOF_INT128__, ""); +static_assert(sizeof(__int128) == __SIZEOF_INT128__, ""); +static_assert(sizeof(unsigned __int128) == __SIZEOF_INT128__, ""); +static_assert(sizeof(upx_int128_t) == __SIZEOF_INT128__, ""); +static_assert(sizeof(upx_uint128_t) == __SIZEOF_INT128__, ""); +#endif +#if defined(__SIZEOF_PTRDIFF_T__) +static_assert(sizeof(ptrdiff_t) == __SIZEOF_PTRDIFF_T__, ""); +#endif +#if defined(__SIZEOF_SIZE_T__) +static_assert(sizeof(size_t) == __SIZEOF_SIZE_T__, ""); +#endif +#if defined(__SIZEOF_POINTER__) +static_assert(sizeof(void *) == __SIZEOF_POINTER__, ""); +static_assert(sizeof(intptr_t) == __SIZEOF_POINTER__, ""); +static_assert(sizeof(uintptr_t) == __SIZEOF_POINTER__, ""); +#endif +#if defined(__SIZEOF_PTRADDR_T__) +static_assert(sizeof(__PTRADDR_TYPE__) == __SIZEOF_PTRADDR_T__, ""); +static_assert(sizeof(upx_ptraddr_t) == __SIZEOF_PTRADDR_T__, ""); +#endif + +#if defined(__SCHAR_WIDTH__) +static_assert(8 * sizeof(signed char) == __SCHAR_WIDTH__, ""); +#endif +#if defined(__SHRT_WIDTH__) +static_assert(8 * sizeof(short) == __SHRT_WIDTH__, ""); +#endif +#if defined(__INT_WIDTH__) +static_assert(8 * sizeof(int) == __INT_WIDTH__, ""); +#endif +#if defined(__LONG_WIDTH__) +static_assert(8 * sizeof(long) == __LONG_WIDTH__, ""); +#endif +#if defined(__LONG_LONG_WIDTH__) +static_assert(8 * sizeof(long long) == __LONG_LONG_WIDTH__, ""); +#endif +#if defined(__INTMAX_WIDTH__) +static_assert(8 * sizeof(intmax_t) == __INTMAX_WIDTH__, ""); +static_assert(8 * sizeof(uintmax_t) == __INTMAX_WIDTH__, ""); +#endif +#if defined(__PTRDIFF_WIDTH__) +static_assert(8 * sizeof(ptrdiff_t) == __PTRDIFF_WIDTH__, ""); +#endif +#if defined(__SIZE_WIDTH__) +static_assert(8 * sizeof(size_t) == __SIZE_WIDTH__, ""); +#endif +#if defined(__INTPTR_WIDTH__) +static_assert(8 * sizeof(intptr_t) == __INTPTR_WIDTH__, ""); +static_assert(8 * sizeof(uintptr_t) == __INTPTR_WIDTH__, ""); +#endif +#if defined(__UINTPTR_WIDTH__) +static_assert(8 * sizeof(intptr_t) == __UINTPTR_WIDTH__, ""); +static_assert(8 * sizeof(uintptr_t) == __UINTPTR_WIDTH__, ""); +#endif +#if defined(__PTRADDR_WIDTH__) +static_assert(8 * sizeof(__PTRADDR_TYPE__) == __PTRADDR_WIDTH__, ""); +static_assert(8 * sizeof(upx_ptraddr_t) == __PTRADDR_WIDTH__, ""); +#endif + +// true types from compiler +typedef decltype((const char *) nullptr - (const char *) nullptr) true_ptrdiff_t; +typedef decltype(sizeof(0)) true_size_t; + +// expected types from pre-defined macros +#if defined(__PTRDIFF_TYPE__) +typedef __PTRDIFF_TYPE__ expected_ptrdiff_t; +#endif +#if defined(__SIZE_TYPE__) +typedef __SIZE_TYPE__ expected_size_t; +#endif +#if defined(__INTPTR_TYPE__) +typedef __INTPTR_TYPE__ expected_intptr_t; +#endif +#if defined(__UINTPTR_TYPE__) +typedef __UINTPTR_TYPE__ expected_uintptr_t; +#endif +#if defined(__PTRADDR_TYPE__) +typedef __PTRADDR_TYPE__ expected_ptraddr_t; +#endif + +#define ASSERT_COMPATIBLE_TYPE(A, B) \ + static_assert(std::is_integral::value, ""); \ + static_assert(std::is_integral::value, ""); \ + static_assert(std::is_signed::value == std::is_signed::value, ""); \ + static_assert(std::is_unsigned::value == std::is_unsigned::value, ""); \ + static_assert(sizeof(A) == sizeof(B), ""); \ + static_assert(alignof(A) == alignof(B), "") + +#define ASSERT_SAME_TYPE(A, B) \ + ASSERT_COMPATIBLE_TYPE(A, B); \ + static_assert(std::is_same::value, "") + +// C vs C++ headers +ASSERT_SAME_TYPE(ptrdiff_t, std::ptrdiff_t); +ASSERT_SAME_TYPE(size_t, std::size_t); +ASSERT_SAME_TYPE(intptr_t, std::intptr_t); +ASSERT_SAME_TYPE(uintptr_t, std::uintptr_t); + +// true types +ASSERT_SAME_TYPE(ptrdiff_t, true_ptrdiff_t); +ASSERT_SAME_TYPE(size_t, true_size_t); + +// expected types +#if defined(__PTRDIFF_TYPE__) +static_assert(std::is_signed::value, ""); +ASSERT_SAME_TYPE(ptrdiff_t, expected_ptrdiff_t); +#endif +#if defined(__SIZE_TYPE__) +static_assert(std::is_unsigned::value, ""); +ASSERT_SAME_TYPE(size_t, expected_size_t); +#endif +#if defined(__INTPTR_TYPE__) +static_assert(std::is_signed::value, ""); +ASSERT_COMPATIBLE_TYPE(intptr_t, expected_intptr_t); // some toolchains are buggy +#endif +#if defined(__UINTPTR_TYPE__) +static_assert(std::is_unsigned::value, ""); +ASSERT_COMPATIBLE_TYPE(uintptr_t, expected_uintptr_t); // some toolchains are buggy +#endif +#if defined(__PTRADDR_TYPE__) +static_assert(std::is_unsigned::value, ""); +ASSERT_SAME_TYPE(upx_ptraddr_t, expected_ptraddr_t); +#endif + +#if defined(__INTPTR_TYPE__) && defined(__UINTPTR_TYPE__) +#if defined(__m68k__) && defined(__atarist__) && defined(__GNUC__) +// BUG: compiler/mintlib mismatch: int vs long +#elif (defined(__arm__) || defined(__mips__)) && defined(__linux__) && defined(__clang__) && \ + (__SIZEOF_LONG__ == 4) +// BUG: compiler/libc mismatch: int vs long +// TODO later: check zig and clang for possible misconfiguration under 32-bit arm & mips +#else +ASSERT_SAME_TYPE(intptr_t, expected_intptr_t); +ASSERT_SAME_TYPE(uintptr_t, expected_uintptr_t); +#endif // BUG +#endif + +// UPX types +ASSERT_SAME_TYPE(short, upx_int16_t); +ASSERT_SAME_TYPE(unsigned short, upx_uint16_t); +ASSERT_SAME_TYPE(int, upx_int32_t); +ASSERT_SAME_TYPE(unsigned, upx_uint32_t); +#if (__SIZEOF_LONG_LONG__ + 0 < 128) +ASSERT_SAME_TYPE(long long, upx_int64_t); +ASSERT_SAME_TYPE(unsigned long long, upx_uint64_t); +#endif + +/************************************************************************* +// compile-time checks +**************************************************************************/ static_assert(no_bswap16(0x04030201) == 0x0201); static_assert(no_bswap32(0x04030201) == 0x04030201); static_assert(no_bswap64(0x0807060504030201ull) == 0x0807060504030201ull); -#if !(ACC_CC_MSC) // unfortunately *not* constexpr with current MSVC +#if !(ACC_CC_MSC) || defined(upx_is_constant_evaluated) static_assert(bswap16(0x04030201) == 0x0102); static_assert(bswap32(0x04030201) == 0x01020304); static_assert(bswap64(0x0807060504030201ull) == 0x0102030405060708ull); @@ -125,8 +298,8 @@ static_assert(sign_extend(0u + 257, 8) == 1); static_assert(sign_extend(0u + 383, 8) == 127); static_assert(sign_extend(0u + 384, 8) == -128); static_assert(sign_extend(0u + 511, 8) == -1); -static_assert(sign_extend(0ull + 0, 1) == 0); -static_assert(sign_extend(0ull + 1, 1) == -1); +static_assert(sign_extend(upx_uint64_t(0) + 0, 1) == 0); +static_assert(sign_extend(upx_uint64_t(0) + 1, 1) == -1); static_assert(CHAR_BIT == 8); #if 0 // does not work with MSVC @@ -152,6 +325,8 @@ namespace { template struct CheckIntegral { + static_assert(upx_is_integral::value); + static_assert(upx_is_integral_v); struct TestT { T a; T x[2]; @@ -208,7 +383,7 @@ struct CheckIntegral { static_assert(upx_is_integral::value); static_assert(upx_is_integral_v); } - static void check(void) noexcept { + static void check_core(void) noexcept { { TestT t = {}; assert_noexcept(t.a == 0); @@ -238,14 +413,27 @@ struct CheckIntegral { one = 1; three = 3; four = 4; + assert_noexcept(zero == 0); assert_noexcept(one == 1); + assert_noexcept(three == 3); assert_noexcept(four == 4); // min / max assert_noexcept(upx::min(one, four) == 1); assert_noexcept(upx::min(one, four) == one); assert_noexcept(upx::max(one, four) == 4); assert_noexcept(upx::max(one, four) == four); - // align + } + } + static void check(void) noexcept { + check_core(); + { + T zero, one, three, four; + zero = 0; + one = 1; + three = 3; + four = 4; + // align - needs binary expressions which do not work + // on CHERI uintptr_t because of pointer provenance assert_noexcept(upx::align_down(zero, four) == 0); assert_noexcept(upx::align_down(zero, four) == zero); assert_noexcept(upx::align_down(one, four) == 0); @@ -474,11 +662,18 @@ struct TestBELE { template struct CheckSignedness { + static_assert(std::is_integral_v); + static_assert(std::is_signed_v == T_is_signed); + static_assert(std::is_unsigned_v == !T_is_signed); template static inline void checkU(void) noexcept { + static_assert(std::is_integral_v); + static_assert(std::is_signed_v == U_is_signed); + static_assert(std::is_unsigned_v == !U_is_signed); static_assert(sizeof(U) == sizeof(T)); static_assert(alignof(U) == alignof(T)); - constexpr U all_bits = (U) (U(0) - U(1)); + constexpr U all_bits = U(U(0) - U(1)); + static_assert(all_bits == U(~U(0))); static_assert(U_is_signed ? (all_bits < 0) : (all_bits > 0)); } static void check(void) noexcept { @@ -490,6 +685,23 @@ struct CheckSignedness { } }; +template +struct CheckTypePair { + static_assert(std::is_integral_v); + static_assert(std::is_integral_v); + static_assert(std::is_signed_v); + static_assert(!std::is_unsigned_v); + static_assert(std::is_unsigned_v); + static_assert(!std::is_signed_v); + static_assert(std::is_same_v >); + static_assert(std::is_same_v >); + static_assert(std::is_same_v >); + static_assert(std::is_same_v >); + static_assert(sizeof(A) == sizeof(B)); + static_assert(alignof(A) == alignof(B)); + static inline void check(void) noexcept {} +}; + template struct TestNoAliasingStruct { // check working -fno-strict-aliasing static noinline bool test(A *a, B *b) noexcept { @@ -657,7 +869,10 @@ void upx_compiler_sanity_check(void) noexcept { static_assert(sizeof(short) == 2); static_assert(sizeof(int) == 4); static_assert(sizeof(long) >= 4); + static_assert(sizeof(long long) >= 8); static_assert(sizeof(void *) >= 4); + static_assert(sizeof(upx_off_t) >= 8); + static_assert(sizeof(upx_off_t) >= sizeof(long long)); static_assert(sizeof(BE16) == 2); static_assert(sizeof(BE32) == 4); @@ -673,36 +888,153 @@ void upx_compiler_sanity_check(void) noexcept { COMPILE_TIME_ASSERT_ALIGNED1(LE32) COMPILE_TIME_ASSERT_ALIGNED1(LE64) + // check that these types are not some multi-word macro +#define CHECK_TYPE(T) (void) (T()) + CHECK_TYPE(int8_t); + CHECK_TYPE(uint8_t); + CHECK_TYPE(int16_t); + CHECK_TYPE(uint16_t); + CHECK_TYPE(int32_t); + CHECK_TYPE(uint32_t); + CHECK_TYPE(int64_t); + CHECK_TYPE(uint64_t); + CHECK_TYPE(intmax_t); + CHECK_TYPE(uintmax_t); + CHECK_TYPE(ptrdiff_t); + CHECK_TYPE(size_t); + CHECK_TYPE(intptr_t); + CHECK_TYPE(uintptr_t); +#if 0 + CHECK_TYPE(acc_int8_t); + CHECK_TYPE(acc_uint8_t); + CHECK_TYPE(acc_int16_t); + CHECK_TYPE(acc_uint16_t); + CHECK_TYPE(acc_int32_t); + CHECK_TYPE(acc_uint32_t); + CHECK_TYPE(acc_int64_t); + CHECK_TYPE(acc_uint64_t); + CHECK_TYPE(acc_intptr_t); + CHECK_TYPE(acc_uintptr_t); +#endif + CHECK_TYPE(upx_int8_t); + CHECK_TYPE(upx_uint8_t); + CHECK_TYPE(upx_int16_t); + CHECK_TYPE(upx_uint16_t); + CHECK_TYPE(upx_int32_t); + CHECK_TYPE(upx_uint32_t); + CHECK_TYPE(upx_int64_t); + CHECK_TYPE(upx_uint64_t); +#if (__SIZEOF_INT128__ == 16) + CHECK_TYPE(upx_int128_t); + CHECK_TYPE(upx_uint128_t); +#endif + CHECK_TYPE(upx_ptraddr_t); + CHECK_TYPE(upx_uintptr_t); + CHECK_TYPE(upx_uptrdiff_t); + CHECK_TYPE(upx_ssize_t); +#undef CHECK_TYPE + CheckIntegral::check(); CheckIntegral::check(); CheckIntegral::check(); CheckIntegral::check(); + CheckIntegral::check(); CheckIntegral::check(); + CheckIntegral::check(); CheckIntegral::check(); + CheckIntegral::check(); CheckIntegral::check(); + CheckIntegral::check(); + CheckIntegral::check(); + CheckIntegral::check(); + CheckIntegral::check(); + CheckIntegral::check(); + CheckIntegral::check(); + CheckIntegral::check(); + CheckIntegral::check(); + CheckIntegral::check(); + CheckIntegral::check(); + CheckIntegral::check(); + CheckIntegral::check(); CheckIntegral::check(); CheckIntegral::check(); - CheckIntegral::check(); CheckIntegral::check(); - CheckIntegral::check(); +#if defined(__CHERI__) && defined(__CHERI_PURE_CAPABILITY__) + static_assert(sizeof(upx_ptraddr_t) == 8); + static_assert(alignof(upx_ptraddr_t) == 8); + static_assert(sizeof(void *) == 16); + static_assert(alignof(void *) == 16); + static_assert(sizeof(uintptr_t) == 16); + static_assert(alignof(uintptr_t) == 16); + // warning: binary expression on capability types 'unsigned __intcap' and 'unsigned __intcap' + CheckIntegral::check_core(); + CheckIntegral::check_core(); + CheckIntegral::check_core(); +#else + CheckIntegral::check(); + CheckIntegral::check(); CheckIntegral::check(); +#endif +#if (__SIZEOF_INT128__ == 16) + CheckIntegral::check(); + CheckIntegral::check(); +#endif CheckSignedness::check(); // -funsigned-char CheckSignedness::check(); CheckSignedness::check(); CheckSignedness::check(); CheckSignedness::check(); + CheckSignedness::check(); + CheckSignedness::check(); + CheckSignedness::check(); + CheckSignedness::check(); CheckSignedness::check(); - CheckSignedness::check(); - CheckSignedness::check(); CheckSignedness::check(); - CheckSignedness::check(); - CheckSignedness::check(); + CheckSignedness::check(); + CheckSignedness::check(); + CheckSignedness::check(); + CheckSignedness::check(); + CheckSignedness::check(); + CheckSignedness::check(); + CheckSignedness::check(); + CheckSignedness::check(); + CheckSignedness::check(); + CheckSignedness::check(); +#if (__SIZEOF_INT128__ == 16) + CheckSignedness::check(); + CheckSignedness::check(); +#endif CheckSignedness::check(); + CheckSignedness::check(); + CheckSignedness::check(); CheckSignedness::check(); - CheckSignedness::check(); + CheckSignedness::check(); + CheckSignedness::check(); CheckSignedness::check(); +#define CHECK_TYPE_PAIR(A, B) \ + CheckTypePair::check(); \ + static_assert(alignof(A) == alignof(B)) + CHECK_TYPE_PAIR(signed char, unsigned char); + CHECK_TYPE_PAIR(short, unsigned short); + CHECK_TYPE_PAIR(int, unsigned); + CHECK_TYPE_PAIR(long, unsigned long); + CHECK_TYPE_PAIR(long long, unsigned long long); + CHECK_TYPE_PAIR(intmax_t, uintmax_t); + CHECK_TYPE_PAIR(upx_int16_t, upx_uint16_t); + CHECK_TYPE_PAIR(upx_int32_t, upx_uint32_t); + CHECK_TYPE_PAIR(upx_int64_t, upx_uint64_t); +#if (__SIZEOF_INT128__ == 16) + CHECK_TYPE_PAIR(upx_int128_t, upx_uint128_t); +#endif + CHECK_TYPE_PAIR(ptrdiff_t, upx_uptrdiff_t); + CHECK_TYPE_PAIR(upx_ssize_t, size_t); + CHECK_TYPE_PAIR(upx_sptraddr_t, upx_ptraddr_t); + CHECK_TYPE_PAIR(intptr_t, uintptr_t); + CHECK_TYPE_PAIR(acc_intptr_t, acc_uintptr_t); +#undef CHECK_TYPE_PAIR + static_assert(sizeof(upx_charptr_unit_type) == 1); COMPILE_TIME_ASSERT_ALIGNED1(upx_charptr_unit_type) static_assert(sizeof(*((charptr) nullptr)) == 1); @@ -751,6 +1083,7 @@ void upx_compiler_sanity_check(void) noexcept { 0, 0, 0, 0, 0, 0, 0, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0, 0, 0, 0, 0x7f, 0x7e, 0x7d, 0x7c, 0x7b, 0x7a, 0x79, 0x78, 0, 0, 0, 0, 0}; constexpr const byte *d = dd + 7; + assert_noexcept(ptr_is_aligned<16>(dd)); static_assert(upx::compile_time::get_be16(d) == 0xfffe); static_assert(upx::compile_time::get_be24(d) == 0xfffefd); static_assert(upx::compile_time::get_be32(d) == 0xfffefdfc); @@ -874,7 +1207,7 @@ void upx_compiler_sanity_check(void) noexcept { assert_noexcept(sign_extend(u, 7) == ((i & 64) ? -64 + (i & 63) : (i & 63))); assert_noexcept(sign_extend(u, 8) == ((i & 128) ? -128 + (i & 127) : (i & 127))); assert_noexcept(sign_extend(u, 64) == i); - assert_noexcept(sign_extend(0ull - u, 64) == -i); + assert_noexcept(sign_extend(upx_uint64_t(0) - u, 64) == -i); } } } @@ -1041,7 +1374,7 @@ TEST_CASE("libc snprintf") { // 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; + unsigned long long llu = (upx_uint64_t) (upx_int64_t) ll; snprintf(buf, sizeof(buf), "%d.%ld.%lld.%u.%lu.%llu", -3, -2L, ll, 3U, 2LU, llu); CHECK_EQ(strcmp(buf, "-3.-2.-1.3.2.18446744073709551615"), 0); intmax_t im = ll; @@ -1078,7 +1411,7 @@ TEST_CASE("libc qsort") { const Elem *a = (const Elem *) aa; const Elem *b = (const Elem *) bb; assert_noexcept(a->id != b->id); // check not IDENTICAL - return a->value < b->value ? -1 : (a->value == b->value ? 0 : 1); + return a->value == b->value ? 0 : (a->value < b->value ? -1 : 1); } static noinline bool check_sort(upx_sort_func_t sort, Elem *e, size_t n, bool is_stable) { upx_uint32_t x = 5381 + (upx_rand() & 255); diff --git a/src/check/dt_cxxlib.cpp b/src/check/dt_cxxlib.cpp index b13d5282..a52b09ed 100644 --- a/src/check/dt_cxxlib.cpp +++ b/src/check/dt_cxxlib.cpp @@ -204,8 +204,11 @@ static_assert(!upx::is_same_any_v); static_assert(upx::is_same_any_v); static_assert(upx::is_same_any_v); -// TODO later: CHERI +#if defined(__CHERI__) && defined(__CHERI_PURE_CAPABILITY__) +static_assert(!upx::is_same_any_v); +#else static_assert(upx::is_same_any_v); +#endif /************************************************************************* // @@ -241,10 +244,12 @@ static_assert(upx::min(1, 2) == 1); static_assert(upx::min(1, 2) == 1); static_assert(upx::min(1, 2) == 1); static_assert(upx::min(1, 2) == 1); +static_assert(upx::min(1, 2) == 1); static_assert(upx::max(1, 2) == 2); static_assert(upx::max(1, 2) == 2); static_assert(upx::max(1, 2) == 2); static_assert(upx::max(1, 2) == 2); +static_assert(upx::max(1, 2) == 2); static_assert(upx::min(1, 2) == 1); static_assert(upx::min(1l, 2l) == 1); @@ -257,10 +262,12 @@ static_assert(upx::umin(1, 2) == 1); static_assert(upx::umin(1, 2) == 1); static_assert(upx::umin(1, 2) == 1); static_assert(upx::umin(1, 2) == 1); +static_assert(upx::umin(1, 2) == 1); static_assert(upx::umax(1, 2) == 2); static_assert(upx::umax(1, 2) == 2); static_assert(upx::umax(1, 2) == 2); static_assert(upx::umax(1, 2) == 2); +static_assert(upx::umax(1, 2) == 2); static_assert(upx::umin(1u, 2u) == 1); static_assert(upx::umin(1ul, 2ul) == 1); diff --git a/src/conf.h b/src/conf.h index 8b549a52..76a76bdf 100644 --- a/src/conf.h +++ b/src/conf.h @@ -153,10 +153,19 @@ typedef acc_int32_t upx_int32_t; typedef acc_uint32_t upx_uint32_t; typedef acc_int64_t upx_int64_t; typedef acc_uint64_t upx_uint64_t; +#if (__SIZEOF_INT128__ == 16) +typedef __int128 upx_int128_t; +typedef unsigned __int128 upx_uint128_t; +#endif typedef acc_uintptr_t upx_uintptr_t; -// see CHERI ptraddr_t / vaddr_t -typedef acc_uintptr_t upx_ptraddr_t; -typedef acc_intptr_t upx_sptraddr_t; +#if defined(__PTRADDR_TYPE__) // CHERI +typedef __PTRADDR_TYPE__ upx_ptraddr_t; +#else +typedef upx_uintptr_t upx_ptraddr_t; +#endif +typedef std::make_signed_t upx_sptraddr_t; // signed ptraddr_t +typedef std::make_unsigned_t upx_uptrdiff_t; // unsigned ptrdiff_t +typedef std::make_signed_t upx_ssize_t; // signed size_t // UPX convention: use "byte" when dealing with data; use "char/uchar" when dealing // with strings; use "upx_uint8_t" when dealing with small integers @@ -172,7 +181,11 @@ struct alignas(1) upx_charptr_unit_type final { char hidden__; }; static_assert(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; +#if (__SIZEOF_INT128__ == 16) && 0 +typedef upx_int128_t upx_off_t; +#else +typedef long long upx_off_t; +#endif #undef off_t #if 0 // TODO later cleanup: at some future point we can do this: @@ -845,5 +858,7 @@ int upx_test_overlap ( const upx_bytep buf, // xspan #include "util/raw_bytes.h" #include "util/xspan.h" +// +#include "util/system_check_predefs.h" /* vim:set ts=4 sw=4 et: */ diff --git a/src/file.cpp b/src/file.cpp index 401c7f85..b0d82feb 100644 --- a/src/file.cpp +++ b/src/file.cpp @@ -321,23 +321,21 @@ upx_off_t OutputFile::seek(upx_off_t off, int whence) { mem_size_assert(1, off >= 0 ? off : -off); // sanity check assert(!opt->to_stdout); switch (whence) { - case SEEK_SET: { - if (bytes_written < off) { + case SEEK_SET: + if (bytes_written < off) bytes_written = off; - } _length = bytes_written; // cheap, lazy update; needed? - } break; - case SEEK_END: { + break; + case SEEK_END: _length = bytes_written; // necessary - } break; + break; } return super::seek(off, whence); } // WARNING: fsync() does not exist in some Windows environments. // This trick works only on UNIX-like systems. -// int OutputFile::read(void *buf, int len) -//{ +// int OutputFile::read(void *buf, int len) { // fsync(_fd); // InputFile infile; // infile.open(this->getName(), O_RDONLY | O_BINARY); diff --git a/src/filter/ct.h b/src/filter/ct.h index 5e468b14..bd05955c 100644 --- a/src/filter/ct.h +++ b/src/filter/ct.h @@ -355,6 +355,7 @@ static int s_ct24arm_be(Filter *f) { **************************************************************************/ #if 1 //{ old reliable + #define CT26ARM_LE(f, cond, addvalue, get, set) \ byte *b = f->buf; \ byte *b_end = b + f->buf_len - 4; \ @@ -387,10 +388,9 @@ static int s_ct26arm_le(Filter *f) { #else //}{ new enhanced but DIFFERENT; need new filter type! -static int CTarm64(Filter *f, int dir) // dir: 1, 0, -1 -{ - upx_byte *b = f->buf; // will be incremented - upx_byte *const b_end = b + f->buf_len - 4; +static int CTarm64(Filter *f, int dir) { // dir: 1, 0, -1 + byte *b = f->buf; // will be incremented + byte *const b_end = b + f->buf_len - 4; do { unsigned const a = b - f->buf; int const d = dir * (f->addvalue + (a >> 2)); diff --git a/src/help.cpp b/src/help.cpp index 5b184db2..c30159ff 100644 --- a/src/help.cpp +++ b/src/help.cpp @@ -114,13 +114,15 @@ struct PackerNames final { unsigned methods[PackerBase::MAX_METHODS]; unsigned filters[PackerBase::MAX_FILTERS]; }; - Entry names[MAX_NAMES]; + Entry names_array[MAX_NAMES]; + Entry *names[MAX_NAMES]; unsigned names_count = 0; const Options *o = nullptr; void add(const PackerBase *pb) { assert_noexcept(names_count < MAX_NAMES); - Entry &e = names[names_count++]; + Entry &e = names_array[names_count]; + names[names_count++] = &e; e.fname = pb->getFullName(o); e.sname = pb->getName(); e.methods_count = e.filters_count = 0; @@ -147,11 +149,10 @@ struct PackerNames final { self->add(pb); return false; } - static int __acc_cdecl_qsort compare_fname(const void *a, const void *b) { - return strcmp(((const Entry *) a)->fname, ((const Entry *) b)->fname); - } - static int __acc_cdecl_qsort compare_sname(const void *a, const void *b) { - return strcmp(((const Entry *) a)->sname, ((const Entry *) b)->sname); + static int __acc_cdecl_qsort compare_fname(const void *aa, const void *bb) { + const Entry *a = *(const Entry *const *) aa; + const Entry *b = *(const Entry *const *) bb; + return strcmp(a->fname, b->fname); } }; } // namespace @@ -162,10 +163,10 @@ static noinline void list_all_packers(FILE *f, int verbose) { PackerNames pn; pn.o = &o; (void) PackMaster::visitAllPackers(PackerNames::visit, nullptr, &o, &pn); - upx_gnomesort(pn.names, pn.names_count, sizeof(PackerNames::Entry), PackerNames::compare_fname); + upx_gnomesort(pn.names, pn.names_count, sizeof(pn.names[0]), PackerNames::compare_fname); size_t pos = 0; for (size_t i = 0; i < pn.names_count; i++) { - const PackerNames::Entry &e = pn.names[i]; + const PackerNames::Entry &e = *pn.names[i]; const char *const fn = e.fname; const char *const sn = e.sname; if (verbose >= 3) { @@ -570,6 +571,12 @@ void show_sysinfo(const char *options_var) { #endif // architecture +#if defined(__CHERI__) + cf_print("__CHERI__", "%lld", __CHERI__ + 0, 3); +#endif +#if defined(__CHERI_PURE_CAPABILITY__) + cf_print("__CHERI_PURE_CAPABILITY__", "%lld", __CHERI_PURE_CAPABILITY__ + 0, 3); +#endif #if defined(__mips_hard_float) cf_print("__mips_hard_float", "%lld", __mips_hard_float + 0); #endif @@ -628,6 +635,15 @@ void show_sysinfo(const char *options_var) { #elif defined(__pie__) cf_print("__pie__", "%lld", __pie__ + 0, 3); #endif +#if defined(__SIZEOF_INT128__) + cf_print("__SIZEOF_INT128__", "%lld", __SIZEOF_INT128__ + 0, 3); +#endif +#if defined(__SIZEOF_LONG_LONG__) && (__SIZEOF_LONG_LONG__ + 0 > 8) + cf_print("__SIZEOF_LONG_LONG__", "%lld", __SIZEOF_LONG_LONG__ + 0, 3); +#endif +#if defined(__SIZEOF_POINTER__) && (__SIZEOF_POINTER__ + 0 > 8) + cf_print("__SIZEOF_POINTER__", "%lld", __SIZEOF_POINTER__ + 0, 3); +#endif #if defined(UPX_CONFIG_DISABLE_WSTRICT) cf_print("UPX_CONFIG_DISABLE_WSTRICT", "%lld", UPX_CONFIG_DISABLE_WSTRICT + 0, 3); #endif diff --git a/src/p_lx_elf.cpp b/src/p_lx_elf.cpp index 551a2ff1..6064dbd5 100644 --- a/src/p_lx_elf.cpp +++ b/src/p_lx_elf.cpp @@ -43,6 +43,11 @@ #include "p_lx_elf.h" #include "ui.h" +#if defined(__CHERI__) && defined(__CHERI_PURE_CAPABILITY__) +# pragma clang diagnostic ignored "-Wcheri-capability-misuse" // TODO later +# pragma clang diagnostic ignored "-Wcheri-provenance" // TODO later +#endif + using upx::umin; #define PT_LOAD32 Elf32_Phdr::PT_LOAD @@ -5068,7 +5073,7 @@ int PackLinuxElf32::pack2(OutputFile *fo, Filter &ft) asl_pack2_Shdrs(fo, pre_xct_top); } else { // Just copy up to xct_off - x.size = umin(0ull + x.size, 0ull + xct_off - x.offset); + x.size = umin(uintmax_t(x.size), uintmax_t(0ull + xct_off - x.offset)); if (0) { // DEBUG paranoia fi->seek(x.offset, SEEK_SET); fi->readx(ibuf, x.size); @@ -5328,7 +5333,7 @@ int PackLinuxElf64::pack2(OutputFile *fo, Filter &ft) asl_pack2_Shdrs(fo, pre_xct_top); } else { // Just copy up to xct_off - x.size = umin(0ull + x.size, 0ull + xct_off - x.offset); + x.size = umin(uintmax_t(x.size), uintmax_t(0ull + xct_off - x.offset)); if (0) { // DEBUG paranoia fi->seek(x.offset, SEEK_SET); fi->readx(ibuf, x.size); diff --git a/src/p_mach.cpp b/src/p_mach.cpp index 3a66c617..0c1dfa66 100644 --- a/src/p_mach.cpp +++ b/src/p_mach.cpp @@ -44,6 +44,9 @@ #if (ACC_CC_GNUC >= 0x040200) # pragma GCC diagnostic ignored "-Wcast-align" #endif +#if defined(__CHERI__) && defined(__CHERI_PURE_CAPABILITY__) +# pragma clang diagnostic ignored "-Wcheri-capability-misuse" // TODO later +#endif static const CLANG_FORMAT_DUMMY_STATEMENT #include "stub/i386-darwin.macho-entry.h" diff --git a/src/p_vmlinz.cpp b/src/p_vmlinz.cpp index 75742ccb..0eaa3c72 100644 --- a/src/p_vmlinz.cpp +++ b/src/p_vmlinz.cpp @@ -346,7 +346,7 @@ int PackVmlinuzI386::decompressKernel() // some checks if (fd_pos != file_size) { - NO_printf("fd_pos: %lld, file_size: %lld\n", fd_pos, file_size); + NO_printf("fd_pos: %jd, file_size: %lld\n", (intmax_t) fd_pos, file_size); // linux-2.6.21.5/arch/i386/boot/compressed/vmlinux.lds // puts .data.compressed ahead of .text, .rodata, etc; diff --git a/src/pefile.cpp b/src/pefile.cpp index c28b2924..8fd0cc89 100644 --- a/src/pefile.cpp +++ b/src/pefile.cpp @@ -258,7 +258,7 @@ void PeFile::Interval::add_interval(const Interval *other) { void PeFile::Interval::flatten() { if (!ivnum) return; - upx_qsort(ivarr, ivnum, sizeof(interval), Interval::compare); + upx_qsort(ivarr, ivnum, sizeof(ivarr[0]), Interval::compare); for (unsigned ic = 0; ic < ivnum - 1; ic++) { unsigned jc; for (jc = ic + 1; jc < ivnum && ivarr[ic].start + ivarr[ic].len >= ivarr[jc].start; jc++) @@ -878,7 +878,7 @@ public: // sort the sections by name before adding them all // NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion) - upx_qsort(sections, nsections, sizeof(Section *), ImportLinker::compare); + upx_qsort(sections, nsections, sizeof(sections[0]), ImportLinker::compare); for (unsigned ic = 0; ic < nsections; ic++) addLoader(sections[ic]->name); @@ -1063,7 +1063,7 @@ unsigned PeFile::processImports0(ord_mask_t ord_mask) { // pass 1 oimport = mb_oimport; // NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion) - upx_qsort(idlls, dllnum, sizeof(*idlls), UDll::compare); + upx_qsort(idlls, dllnum, sizeof(idlls[0]), UDll::compare); info("Processing imports: %d DLLs", dllnum); for (unsigned ic = 0; ic < dllnum; ic++) { diff --git a/src/util/cxxlib.h b/src/util/cxxlib.h index eea22ec0..ee416054 100644 --- a/src/util/cxxlib.h +++ b/src/util/cxxlib.h @@ -219,8 +219,7 @@ forceinline constexpr T max(const T &a, const T &b) noexcept { } template -inline constexpr bool is_uminmax_type = - is_same_any_v; +inline constexpr bool is_uminmax_type = std::is_integral_v && std::is_unsigned_v; template , T> > forceinline constexpr T umin(const T &a, const T &b) noexcept { diff --git a/src/util/membuffer.cpp b/src/util/membuffer.cpp index 8eaa9315..df5ebd1e 100644 --- a/src/util/membuffer.cpp +++ b/src/util/membuffer.cpp @@ -49,6 +49,8 @@ unsigned membuffer_get_size(MemBuffer &mb) noexcept { return mb.getSize(); } #if defined(__SANITIZE_ADDRESS__) || defined(__SANITIZE_MEMORY__) static forceinline constexpr bool use_simple_mcheck() noexcept { return false; } +#elif defined(__CHERI__) && defined(__CHERI_PURE_CAPABILITY__) +static forceinline constexpr bool use_simple_mcheck() noexcept { return false; } #elif (WITH_VALGRIND) && defined(RUNNING_ON_VALGRIND) static bool use_simple_mcheck_flag; static noinline void init_use_simple_mcheck() noexcept { diff --git a/src/util/system_check_predefs.h b/src/util/system_check_predefs.h new file mode 100644 index 00000000..5281934a --- /dev/null +++ b/src/util/system_check_predefs.h @@ -0,0 +1,158 @@ +/* system_check_predefs.h -- check pre-defined macros + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2024 Markus Franz Xaver Johannes Oberhumer + All Rights Reserved. + + UPX and the UCL library are free software; you can redistribute them + and/or modify them under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer + + */ + +// windows defines +#if defined(__CYGWIN32__) && !defined(__CYGWIN__) +#error "missing __CYGWIN__" +#endif +#if defined(__CYGWIN64__) && !defined(__CYGWIN__) +#error "missing __CYGWIN__" +#endif +#if defined(__CYGWIN__) && defined(_WIN32) +#error "unexpected _WIN32" +#endif +#if defined(__CYGWIN__) && defined(_WIN64) +#error "unexpected _WIN64" +#endif +#if defined(__MINGW64__) && !defined(__MINGW32__) +#error "missing __MINGW32__" +#endif +#if defined(__MINGW32__) && !defined(_WIN32) +#error "missing _WIN32" +#endif +#if defined(__MINGW64__) && !defined(_WIN64) +#error "missing _WIN64" +#endif +#if defined(_WIN64) && !defined(_WIN32) +#error "missing _WIN32" +#endif + +// available since gcc-4.3 (2008), clang-2.7 (2010) and msvc-20XX +#if !defined(__COUNTER__) +#error "missing __COUNTER__" +#endif + +// fundamental type sizes - these are pre-defined since gcc-4.3 (2008) and clang-2.8 (2010) +#if defined(__clang__) || defined(__GNUC__) +#if !defined(__SIZEOF_SHORT__) +#error "missing __SIZEOF_SHORT__" +#endif +#if !defined(__SIZEOF_INT__) +#error "missing __SIZEOF_INT__" +#endif +#if !defined(__SIZEOF_LONG__) +#error "missing __SIZEOF_LONG__" +#endif +#if !defined(__SIZEOF_LONG_LONG__) +#error "missing __SIZEOF_LONG_LONG__" +#endif +#if !defined(__SIZEOF_PTRDIFF_T__) +#error "missing __SIZEOF_PTRDIFF_T__" +#endif +#if !defined(__SIZEOF_SIZE_T__) +#error "missing __SIZEOF_SIZE_T__" +#endif +#if !defined(__SIZEOF_POINTER__) +#error "missing __SIZEOF_POINTER__" +#endif +#endif + +// fundamental types - these are pre-defined since gcc-4.5 (2010) and clang-3.5 (2014) +#if defined(__clang__) || defined(__GNUC__) +#if !defined(__PTRDIFF_TYPE__) +#error "missing __PTRDIFF_TYPE__" +#endif +#if !defined(__SIZE_TYPE__) +#error "missing __SIZE_TYPE__" +#endif +#if !defined(__INTPTR_TYPE__) +#error "missing __INTPTR_TYPE__" +#endif +#if !defined(__UINTPTR_TYPE__) +#error "missing __UINTPTR_TYPE__" +#endif +#endif +#if defined(__PTRADDR_TYPE__) && !defined(__PTRADDR_WIDTH__) +#error "missing __PTRADDR_WIDTH__" +#elif defined(__PTRADDR_WIDTH__) && !defined(__PTRADDR_TYPE__) +#error "missing __PTRADDR_TYPE__" +#endif + +// byte order - these are pre-defined since gcc-4.6 (2011) and clang-3.2 (2012) +#if defined(__clang__) || defined(__GNUC__) +#if !defined(__ORDER_BIG_ENDIAN__) || (__ORDER_BIG_ENDIAN__ + 0 == 0) +#error "missing __ORDER_BIG_ENDIAN__" +#endif +#if !defined(__ORDER_LITTLE_ENDIAN__) || (__ORDER_LITTLE_ENDIAN__ + 0 == 0) +#error "missing __ORDER_LITTLE_ENDIAN__" +#endif +#if !defined(__BYTE_ORDER__) || (__BYTE_ORDER__ + 0 == 0) +#error "missing __BYTE_ORDER__" +#endif +#if !defined(__ORDER_BIG_ENDIAN__) || (__ORDER_BIG_ENDIAN__ + 0 != 4321) +#error "unexpected __ORDER_BIG_ENDIAN__" +#endif +#if !defined(__ORDER_BIG_ENDIAN__) || (__ORDER_LITTLE_ENDIAN__ + 0 != 1234) +#error "unexpected __ORDER_BIG_ENDIAN__" +#endif +#if (__BYTE_ORDER__ != __ORDER_BIG_ENDIAN__) && (__BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__) +#error "unexpected __BYTE_ORDER__" +#endif +#endif + +// pic and pie +#if defined(__PIC__) && defined(__pic__) +#if (__PIC__ + 0) != (__pic__ + 0) +#error "unexpected __PIC__ vs __pic__ mismatch" +#endif +#endif +#if defined(__PIC__) +#if (__PIC__ != 1) && (__PIC__ != 2) +#error "unexpected __PIC__ value" +#endif +#endif +#if defined(__pic__) +#if (__pic__ != 1) && (__pic__ != 2) +#error "unexpected __pic__ value" +#endif +#endif +#if defined(__PIE__) && defined(__pie__) +#if (__PIE__ + 0) != (__pie__ + 0) +#error "unexpected __PIE__ vs __pie__ mismatch" +#endif +#endif +#if defined(__PIE__) +#if (__PIE__ != 1) && (__PIE__ != 2) +#error "unexpected __PIE__ value" +#endif +#endif +#if defined(__pie__) +#if (__pie__ != 1) && (__pie__ != 2) +#error "unexpected __pie__ value" +#endif +#endif + +/* vim:set ts=4 sw=4 et: */ diff --git a/src/util/system_defs.h b/src/util/system_defs.h index 2b9278a7..85ced16b 100644 --- a/src/util/system_defs.h +++ b/src/util/system_defs.h @@ -26,6 +26,8 @@ #pragma once +#include "system_check_predefs.h" + #if !defined(_FILE_OFFSET_BITS) #define _FILE_OFFSET_BITS 64 #endif @@ -41,6 +43,13 @@ #define __STDC_LIMIT_MACROS 1 #endif +#if defined(__PTRADDR_TYPE__) && !defined(__SIZEOF_PTRADDR_T__) +#if !defined(__PTRADDR_WIDTH__) +#error "missing __PTRADDR_WIDTH__" +#endif +#define __SIZEOF_PTRADDR_T__ (__PTRADDR_WIDTH__ / 8) +#endif + #if !defined(__USE_MINGW_ANSI_STDIO) #if defined(_WIN32) && defined(__MINGW32__) && (defined(__clang__) || defined(__GNUC__)) #define __USE_MINGW_ANSI_STDIO 1 diff --git a/src/util/system_headers.h b/src/util/system_headers.h index 5f3aa93d..54730090 100644 --- a/src/util/system_headers.h +++ b/src/util/system_headers.h @@ -33,62 +33,6 @@ #error "FATAL ERROR: C++17 is required" #endif -// check expected defines -#if defined(__CYGWIN32__) && !defined(__CYGWIN__) -#error "missing __CYGWIN__" -#endif -#if defined(__CYGWIN64__) && !defined(__CYGWIN__) -#error "missing __CYGWIN__" -#endif -#if defined(__MINGW64__) && !defined(__MINGW32__) -#error "missing __MINGW32__" -#endif -#if defined(_WIN64) && !defined(_WIN32) -#error "missing _WIN32" -#endif - -// byte order - these are pre-defined since gcc-4.6 (2011) and clang-3.2 (2012) -#if defined(__clang__) || defined(__GNUC__) -#if !defined(__ORDER_BIG_ENDIAN__) || (__ORDER_BIG_ENDIAN__ + 0 == 0) -#error "missing __ORDER_BIG_ENDIAN__" -#endif -#if !defined(__ORDER_LITTLE_ENDIAN__) || (__ORDER_LITTLE_ENDIAN__ + 0 == 0) -#error "missing __ORDER_LITTLE_ENDIAN__" -#endif -#if !defined(__BYTE_ORDER__) || (__BYTE_ORDER__ + 0 == 0) -#error "missing __BYTE_ORDER__" -#endif -#if !defined(__ORDER_BIG_ENDIAN__) || (__ORDER_BIG_ENDIAN__ + 0 != 4321) -#error "unexpected __ORDER_BIG_ENDIAN__" -#endif -#if !defined(__ORDER_BIG_ENDIAN__) || (__ORDER_LITTLE_ENDIAN__ + 0 != 1234) -#error "unexpected __ORDER_BIG_ENDIAN__" -#endif -#if (__BYTE_ORDER__ != __ORDER_BIG_ENDIAN__) && (__BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__) -#error "unexpected __BYTE_ORDER__" -#endif -#endif - -// pic and pie -#if defined(__PIC__) && defined(__pic__) -static_assert((__PIC__) == (__pic__)); -#endif -#if defined(__PIC__) -static_assert(__PIC__ == 1 || __PIC__ == 2); -#endif -#if defined(__pic__) -static_assert(__pic__ == 1 || __pic__ == 2); -#endif -#if defined(__PIE__) && defined(__pie__) -static_assert((__PIE__) == (__pie__)); -#endif -#if defined(__PIE__) -static_assert(__PIE__ == 1 || __PIE__ == 2); -#endif -#if defined(__pie__) -static_assert(__pie__ == 1 || __pie__ == 2); -#endif - // sanity checks #if defined(_ILP32) || defined(__ILP32) || defined(__ILP32__) static_assert(sizeof(int) == 4); @@ -98,8 +42,14 @@ static_assert(sizeof(void *) == 4); #if defined(_LP64) || defined(__LP64) || defined(__LP64__) static_assert(sizeof(int) == 4); static_assert(sizeof(long) == 8); +#if defined(__CHERI__) && defined(__CHERI_PURE_CAPABILITY__) && (__SIZEOF_POINTER__ == 16) +// CHERI BUG/FEATURE: obviously CHERI should *NOT* pre-define __LP64__ on P128, but +// maybe they do this for porting/compatibility reasons... +static_assert(sizeof(void *) == 16); +#else static_assert(sizeof(void *) == 8); #endif +#endif #if defined(_WIN32) static_assert(sizeof(int) == 4); static_assert(sizeof(long) == 4); @@ -151,6 +101,7 @@ static_assert(sizeof(void *) == sizeof(long)); #include #endif #include +#include #include #include #include @@ -193,6 +144,9 @@ static_assert(sizeof(void *) == sizeof(long)); !defined(__GNUC__) #undef WITH_VALGRIND #endif +#if defined(__CHERI__) && defined(__CHERI_PURE_CAPABILITY__) +#undef WITH_VALGRIND +#endif #if WITH_VALGRIND #include #endif diff --git a/src/util/util.cpp b/src/util/util.cpp index 2b709322..5cff417e 100644 --- a/src/util/util.cpp +++ b/src/util/util.cpp @@ -25,6 +25,7 @@ */ +#define WANT_WINDOWS_LEAN_H 1 #include "system_headers.h" #define ACC_WANT_ACC_INCI_H 1 #include "miniacc.h" @@ -291,23 +292,24 @@ void *upx_calloc(size_t n, size_t element_size) may_throw { } // simple unoptimized memswap() -void upx_memswap(void *a, void *b, size_t bytes) noexcept { - if (a != b && bytes != 0) { - byte *x = (byte *) a; - byte *y = (byte *) b; +// TODO later: CHERI clang bug/miscompilation with upx_memswap() ??? +void upx_memswap(void *aa, void *bb, size_t bytes) noexcept { + if (aa != bb && bytes != 0) { + byte *a = (byte *) aa; + byte *b = (byte *) bb; do { // strange clang-analyzer-15 false positive when compiling in Debug mode // clang-analyzer-core.uninitialized.Assign - byte tmp = *x; // NOLINT(*core.uninitialized.Assign) // bogus clang-analyzer warning - *x++ = *y; - *y++ = tmp; + byte tmp = *a; // NOLINT(*core.uninitialized.Assign) // bogus clang-analyzer warning + *a++ = *b; + *b++ = tmp; } while (--bytes != 0); } } // much better memswap(), optimized for our use case in sort functions below static inline void memswap_no_overlap(byte *a, byte *b, size_t bytes) noexcept { -#if defined(__clang__) && __clang_major__ < 15 +#if defined(__clang__) && (__clang_major__ < 15) && !defined(__CHERI__) // work around a clang < 15 ICE (Internal Compiler Error) // @COMPILER_BUG @CLANG_BUG upx_memswap(a, b, bytes); @@ -418,6 +420,42 @@ void upx_std_stable_sort(void *array, size_t n, upx_compare_func_t compare) { #endif } +TEST_CASE("upx_memswap") { + auto check4 = [](int off1, int off2, int len, int a, int b, int c, int d) { + byte p[4] = {0, 1, 2, 3}; + assert_noexcept(a + b + c + d == 0 + 1 + 2 + 3); + upx_memswap(p + off1, p + off2, len); + CHECK((p[0] == a && p[1] == b && p[2] == c && p[3] == d)); + }; + // identical + check4(0, 0, 4, 0, 1, 2, 3); + // non-overlapping + check4(0, 1, 1, 1, 0, 2, 3); + check4(1, 0, 1, 1, 0, 2, 3); + check4(0, 2, 2, 2, 3, 0, 1); + check4(2, 0, 2, 2, 3, 0, 1); + // overlapping + check4(0, 1, 2, 1, 2, 0, 3); + check4(1, 0, 2, 1, 2, 0, 3); + check4(0, 1, 3, 1, 2, 3, 0); + check4(1, 0, 3, 1, 2, 3, 0); + + // pointer array + { + typedef byte element_type; + element_type a = 11, b = 22; + element_type *array[4]; + memset(array, 0xfb, sizeof(array)); + array[1] = &a; + array[3] = &b; + upx_memswap(array, array + 2, 2 * sizeof(array[0])); + assert(array[1] == &b); + assert(array[3] == &a); + assert(*array[1] == 22); + assert(*array[3] == 11); + } +} + #if UPX_CONFIG_USE_STABLE_SORT // instantiate function templates for all element sizes we need; efficient // run-time, but code size bloat (about 4KiB code size for each function diff --git a/src/util/util.h b/src/util/util.h index 4cd026bb..984135e7 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -93,9 +93,23 @@ T *NewArray(upx_uint64_t n) may_throw { // ptr util **************************************************************************/ -// TODO later: CHERI; see cheri_address_get() +#if defined(__CHERI__) && defined(__CHERI_PURE_CAPABILITY__) +forceinline upx_ptraddr_t ptr_get_address(const void *p) noexcept { + return __builtin_cheri_address_get(p); +} +forceinline upx_ptraddr_t ptr_get_address(upx_uintptr_t p) noexcept { + return __builtin_cheri_address_get(p); +} +#else forceinline upx_ptraddr_t ptr_get_address(const void *p) noexcept { return (upx_uintptr_t) p; } forceinline upx_ptraddr_t ptr_get_address(upx_uintptr_t p) noexcept { return p; } +#endif + +template +forceinline bool ptr_is_aligned(const void *p) noexcept { + static_assert(upx::has_single_bit(Alignment)); + return (ptr_get_address(p) & (Alignment - 1)) == 0; +} // ptrdiff_t with nullptr checks and asserted size; will throw on failure // NOTE: returns size_in_bytes, not number of elements! @@ -138,10 +152,15 @@ forceinline void ptr_check_no_overlap(const void *a, size_t a_size, const void * // - this should play nice with static analyzers like clang-tidy etc. // NOTE: this is clearly UB (Undefined Behaviour), and stricter compilers or // architectures may need a more advanced/costly implementation in the future -// TODO later: CHERI template inline void ptr_invalidate_and_poison(T *(&ptr)) noexcept { - ptr = (T *) (void *) 251; // 0x000000fb // NOLINT(performance-no-int-to-ptr) +#if defined(__CHERI__) && defined(__CHERI_PURE_CAPABILITY__) + // TODO later: examine __builtin_cheri_bounds_set() + ptr = upx::ptr_static_cast(&ptr); // FIXME: HACK: point to address of self +#else + // pretend there is some memory-mapped I/O at address 0x00000200 + ptr = (T *) (void *) 512; // NOLINT(performance-no-int-to-ptr) +#endif } /************************************************************************* @@ -152,7 +171,7 @@ noinline void *upx_calloc(size_t n, size_t element_size) may_throw; noinline const char *upx_getenv(const char *envvar) noexcept; -void upx_memswap(void *a, void *b, size_t bytes) noexcept; +noinline void upx_memswap(void *a, void *b, size_t bytes) noexcept; noinline void upx_rand_init(void) noexcept; noinline int upx_rand(void) noexcept; @@ -174,7 +193,7 @@ void upx_std_stable_sort(void *array, size_t n, upx_compare_func_t compare); #define upx_qsort(a, n, element_size, compare) \ upx_std_stable_sort<(element_size)>((a), (n), (compare)) #else -// use libc qsort(); good enough for our use cases + // use libc qsort(); good enough for our use cases #define upx_qsort ::qsort #endif diff --git a/src/util/windows_lean.h b/src/util/windows_lean.h index d69deaa1..bea53692 100644 --- a/src/util/windows_lean.h +++ b/src/util/windows_lean.h @@ -49,4 +49,19 @@ #define timeval win32_timeval /* struct timeval already in */ #endif +#if defined(__CYGWIN__) && defined(_WIN32) +#error "unexpected _WIN32" +#endif +#if defined(__CYGWIN__) && defined(_WIN64) +#error "unexpected _WIN64" +#endif + #include + +#if defined(__CYGWIN__) && defined(_WIN32) +#error "unexpected _WIN32" +#endif +#if defined(__CYGWIN__) && defined(_WIN64) +// #error "unexpected _WIN64" +#undef _WIN64 +#endif diff --git a/src/util/xspan_impl_ptr.h b/src/util/xspan_impl_ptr.h index 4dfa2df8..f6c8d533 100644 --- a/src/util/xspan_impl_ptr.h +++ b/src/util/xspan_impl_ptr.h @@ -207,7 +207,7 @@ public: // raw access pointer raw_bytes(size_t bytes) const { assertInvariants(); if (bytes > 0) { - if __acc_cte (ptr == nullptr) + if very_unlikely (ptr == nullptr) xspan_fail_nullptr(); } return ptr;