diff --git a/src/Makefile b/src/Makefile index 20b8fb58..55fb3b6a 100644 --- a/src/Makefile +++ b/src/Makefile @@ -207,7 +207,7 @@ endif # automatically format some C++ source code files ifeq ($(shell uname),Linux) CLANG_FORMAT_FILES += bele.h bele_policy.h -CLANG_FORMAT_FILES += except.cpp except.h +CLANG_FORMAT_FILES += dt_check.cpp dt_impl.cpp except.cpp except.h CLANG_FORMAT_FILES += linker.cpp linker.h packhead.cpp packmast.cpp packmast.h CLANG_FORMAT_FILES += main.cpp options.h packer.cpp packer.h CLANG_FORMAT_FILES += p_tos.cpp p_tos.h diff --git a/src/bele_policy.h b/src/bele_policy.h index d972d39b..1b7eb5c5 100644 --- a/src/bele_policy.h +++ b/src/bele_policy.h @@ -79,6 +79,11 @@ struct AbstractPolicy { S u32_compare_signed(const void *a, const void *b) C = 0; S u64_compare_signed(const void *a, const void *b) C = 0; +private: + // disable copy, assignment and move assignment + AbstractPolicy(const AbstractPolicy &) = delete; + AbstractPolicy &operator=(const AbstractPolicy &) = delete; + AbstractPolicy &operator=(AbstractPolicy &&) = delete; // disable dynamic allocation ACC_CXX_DISABLE_NEW_DELETE }; @@ -141,6 +146,11 @@ struct BEPolicy COMPILE_TIME_ASSERT_ALIGNED1(U64) } +private: + // disable copy, assignment and move assignment + BEPolicy(const BEPolicy &) = delete; + BEPolicy &operator=(const BEPolicy &) = delete; + BEPolicy &operator=(BEPolicy &&) = delete; // disable dynamic allocation ACC_CXX_DISABLE_NEW_DELETE }; @@ -197,6 +207,11 @@ struct LEPolicy COMPILE_TIME_ASSERT_ALIGNED1(U64) } +private: + // disable copy, assignment and move assignment + LEPolicy(const LEPolicy &) = delete; + LEPolicy &operator=(const LEPolicy &) = delete; + LEPolicy &operator=(LEPolicy &&) = delete; // disable dynamic allocation ACC_CXX_DISABLE_NEW_DELETE }; diff --git a/src/compress_ucl.cpp b/src/compress_ucl.cpp index b54bbe10..56e38c66 100644 --- a/src/compress_ucl.cpp +++ b/src/compress_ucl.cpp @@ -262,10 +262,8 @@ int upx_ucl_init(void) { if (ucl_init() != UCL_E_OK) return -1; -#if defined(UPX_OFFICIAL_BUILD) - if (UCL_VERSION != ucl_version()) + if (UCL_VERSION != ucl_version() || strcmp(UCL_VERSION_STRING, ucl_version_string()) != 0) return -2; -#endif ucl_set_malloc_hooks(my_malloc, my_free); return 0; } @@ -287,4 +285,56 @@ unsigned upx_ucl_crc32(const void *buf, unsigned len, unsigned crc) } #endif +/************************************************************************* +// Debug checks +**************************************************************************/ + +#if DEBUG && 1 + +#include "mem.h" + +static bool check_ucl(const int method, const unsigned expected_c_len) { + const unsigned u_len = 16384; + const unsigned c_extra = 4096; + MemBuffer u_buf, c_buf, d_buf; + unsigned c_len, d_len; + upx_compress_result_t cresult; + int r; + const int level = 3; // don't waste time + + u_buf.alloc(u_len); + memset(u_buf, 0, u_len); + c_buf.allocForCompression(u_len, c_extra); + d_buf.allocForUncompression(u_len); + + c_len = c_buf.getSize() - c_extra; + r = upx_ucl_compress(u_buf, u_len, c_buf + c_extra, &c_len, nullptr, method, level, NULL_cconf, &cresult); + if (r != 0 || c_len != expected_c_len) return false; + + d_len = d_buf.getSize(); + r = upx_ucl_decompress(c_buf + c_extra, c_len, d_buf, &d_len, method, nullptr); + if (r != 0 || d_len != u_len) return false; + if (memcmp(u_buf, d_buf, u_len) != 0) return false; + + // TODO: rewrite Packer::findOverlapOverhead() so that we can test it here + //unsigned x_len = d_len; + //r = upx_ucl_test_overlap(c_buf, u_buf, c_extra, c_len, &x_len, method, nullptr); + return true; +} + +TEST_CASE("compress_ucl") { + CHECK(check_ucl(M_NRV2B_8, 34)); + CHECK(check_ucl(M_NRV2B_LE16, 34)); + CHECK(check_ucl(M_NRV2B_LE32, 34)); + CHECK(check_ucl(M_NRV2D_8, 32)); + CHECK(check_ucl(M_NRV2D_LE16, 32)); + CHECK(check_ucl(M_NRV2D_LE32, 34)); + CHECK(check_ucl(M_NRV2E_8, 32)); + CHECK(check_ucl(M_NRV2E_LE16, 32)); + CHECK(check_ucl(M_NRV2E_LE32, 34)); +} + +#endif // DEBUG + + /* vim:set ts=4 sw=4 et: */ diff --git a/src/compress_zlib.cpp b/src/compress_zlib.cpp index 11e07481..6b438ce0 100644 --- a/src/compress_zlib.cpp +++ b/src/compress_zlib.cpp @@ -217,10 +217,8 @@ int upx_zlib_test_overlap ( const upx_bytep buf, int upx_zlib_init(void) { -#if defined(UPX_OFFICIAL_BUILD) if (strcmp(ZLIB_VERSION, zlibVersion()) != 0) return -2; -#endif return 0; } diff --git a/src/conf.h b/src/conf.h index 98c49441..32ec3d60 100644 --- a/src/conf.h +++ b/src/conf.h @@ -376,6 +376,27 @@ private: }; +namespace compile_time { +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)); +} +constexpr bool string_ne(const char *a, const char *b) { + return !string_eq(a, b); +} +constexpr bool string_gt(const char *a, const char *b) { + return string_lt(b, a); +} +constexpr bool string_le(const char *a, const char *b) { + return !string_lt(b, a); +} +constexpr bool string_ge(const char *a, const char *b) { + return !string_lt(a, b); +} +} + /************************************************************************* // constants **************************************************************************/ @@ -697,12 +718,6 @@ struct upx_compress_result_t #include #include #include -ACC_COMPILE_TIME_ASSERT_HEADER((std::is_same::value)) -ACC_COMPILE_TIME_ASSERT_HEADER((std::is_same::value)) -ACC_COMPILE_TIME_ASSERT_HEADER((std::is_same::value)) -ACC_COMPILE_TIME_ASSERT_HEADER((std::is_same::value)) -ACC_COMPILE_TIME_ASSERT_HEADER((std::is_same::value)) -ACC_COMPILE_TIME_ASSERT_HEADER((std::is_same::value)) #include "options.h" #include "except.h" @@ -710,19 +725,22 @@ ACC_COMPILE_TIME_ASSERT_HEADER((std::is_same:: #include "util.h" #include "console.h" +//#define DOCTEST_CONFIG_DISABLE +#include // classes class ElfLinker; typedef ElfLinker Linker; +// dt_check.cpp +void upx_compiler_sanity_check(void); +bool upx_doctest_check(void); // main.cpp extern const char *progname; bool set_exit_code(int ec); -void upx_compiler_sanity_check(void); int upx_main(int argc, char *argv[]); - // msg.cpp void printSetNl(int need_nl); void printClearLine(FILE *f = nullptr); @@ -737,12 +755,10 @@ void info(const char *format, ...) attribute_format(1, 2); void infoHeader(void); void infoWriting(const char *what, long size); - // work.cpp void do_one_file(const char *iname, char *oname); int do_files(int i, int argc, char *argv[]); - // help.cpp extern const char gitrev[]; void show_head(void); @@ -751,7 +767,6 @@ void show_license(void); void show_usage(void); void show_version(int); - // compress.cpp unsigned upx_adler32(const void *buf, unsigned len, unsigned adler=1); unsigned upx_crc32(const void *buf, unsigned len, unsigned crc=0); diff --git a/src/dt_check.cpp b/src/dt_check.cpp new file mode 100644 index 00000000..abeb81ee --- /dev/null +++ b/src/dt_check.cpp @@ -0,0 +1,400 @@ +/* dt_check.cpp -- doctest check + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2022 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2022 Laszlo Molnar + 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 Laszlo Molnar + + */ + +#include "conf.h" + +/************************************************************************* +// upx_doctest_check() +**************************************************************************/ + +bool upx_doctest_check(void) { + bool minimal = true; // only show failing tests + bool duration = false; // show timings + const char *e = getenv("UPX_DEBUG_DOCTEST_VERBOSE"); + if (e && e[0] && strcmp(e, "0") != 0) { + minimal = false; + if (strcmp(e, "2") == 0) + duration = true; + } +#if DEBUG + minimal = false; + // duration = true; +#endif + doctest::Context context; + if (minimal) + context.setOption("dt-minimal", true); + if (duration) + context.setOption("dt-duration", true); + int r = context.run(); + if (context.shouldExit() || r != 0) + return false; + return true; +} + +/************************************************************************* +// compile-time checks +**************************************************************************/ + +ACC_COMPILE_TIME_ASSERT_HEADER((std::is_same::value)) +ACC_COMPILE_TIME_ASSERT_HEADER((std::is_same::value)) +ACC_COMPILE_TIME_ASSERT_HEADER((std::is_same::value)) +ACC_COMPILE_TIME_ASSERT_HEADER((std::is_same::value)) +ACC_COMPILE_TIME_ASSERT_HEADER((std::is_same::value)) +ACC_COMPILE_TIME_ASSERT_HEADER((std::is_same::value)) + +ACC_COMPILE_TIME_ASSERT_HEADER(no_bswap16(0x04030201) == 0x0201) +ACC_COMPILE_TIME_ASSERT_HEADER(no_bswap32(0x04030201) == 0x04030201) +ACC_COMPILE_TIME_ASSERT_HEADER(no_bswap64(0x0807060504030201ull) == 0x0807060504030201ull) +#if !(ACC_CC_MSC) +ACC_COMPILE_TIME_ASSERT_HEADER(bswap16(0x04030201) == 0x0102) +ACC_COMPILE_TIME_ASSERT_HEADER(bswap32(0x04030201) == 0x01020304) +ACC_COMPILE_TIME_ASSERT_HEADER(bswap64(0x0807060504030201ull) == 0x0102030405060708ull) +#endif + +ACC_COMPILE_TIME_ASSERT_HEADER(compile_time::string_eq("", "")) +ACC_COMPILE_TIME_ASSERT_HEADER(!compile_time::string_eq("a", "")) +ACC_COMPILE_TIME_ASSERT_HEADER(!compile_time::string_eq("", "a")) +ACC_COMPILE_TIME_ASSERT_HEADER(compile_time::string_eq("abc", "abc")) +ACC_COMPILE_TIME_ASSERT_HEADER(!compile_time::string_eq("ab", "abc")) +ACC_COMPILE_TIME_ASSERT_HEADER(!compile_time::string_eq("abc", "ab")) + +ACC_COMPILE_TIME_ASSERT_HEADER(!compile_time::string_lt("", "")) +ACC_COMPILE_TIME_ASSERT_HEADER(!compile_time::string_lt("a", "")) +ACC_COMPILE_TIME_ASSERT_HEADER(compile_time::string_lt("", "a")) +ACC_COMPILE_TIME_ASSERT_HEADER(!compile_time::string_lt("abc", "abc")) +ACC_COMPILE_TIME_ASSERT_HEADER(compile_time::string_lt("ab", "abc")) +ACC_COMPILE_TIME_ASSERT_HEADER(!compile_time::string_lt("abc", "ab")) +ACC_COMPILE_TIME_ASSERT_HEADER(!compile_time::string_lt("abc", "aba")) +ACC_COMPILE_TIME_ASSERT_HEADER(compile_time::string_lt("abc", "abz")) + +ACC_COMPILE_TIME_ASSERT_HEADER(compile_time::string_ne("abc", "abz")) +ACC_COMPILE_TIME_ASSERT_HEADER(!compile_time::string_gt("abc", "abz")) +ACC_COMPILE_TIME_ASSERT_HEADER(!compile_time::string_ge("abc", "abz")) +ACC_COMPILE_TIME_ASSERT_HEADER(compile_time::string_le("abc", "abz")) + +/************************************************************************* +// upx_compiler_sanity_check() +// assert a sane architecture and compiler +**************************************************************************/ + +namespace { + +template +struct TestBELE { + __acc_static_noinline bool test(void) { + COMPILE_TIME_ASSERT_ALIGNED1(T) + struct alignas(1) test1_t { + char a; + T b; + }; + struct alignas(1) test2_t { + char a; + T b[3]; + }; + test1_t t1[7]; + UNUSED(t1); + test2_t t2[7]; + UNUSED(t2); + COMPILE_TIME_ASSERT(sizeof(test1_t) == 1 + sizeof(T)) + COMPILE_TIME_ASSERT_ALIGNED1(test1_t) + COMPILE_TIME_ASSERT(sizeof(t1) == 7 + 7 * sizeof(T)) + COMPILE_TIME_ASSERT(sizeof(test2_t) == 1 + 3 * sizeof(T)) + COMPILE_TIME_ASSERT_ALIGNED1(test2_t) + COMPILE_TIME_ASSERT(sizeof(t2) == 7 + 21 * sizeof(T)) +#if defined(__acc_alignof) + COMPILE_TIME_ASSERT(__acc_alignof(t1) == 1) + COMPILE_TIME_ASSERT(__acc_alignof(t2) == 1) +#endif +#if 1 + T allbits; + allbits = 0; + allbits += 1; + allbits -= 2; + T v1; + v1 = 1; + v1 *= 2; + v1 -= 1; + T v2; + v2 = 1; + assert((v1 == v2)); + assert(!(v1 != v2)); + assert((v1 <= v2)); + assert((v1 >= v2)); + assert(!(v1 < v2)); + assert(!(v1 > v2)); + v2 ^= allbits; + assert(!(v1 == v2)); + assert((v1 != v2)); + assert((v1 <= v2)); + assert(!(v1 >= v2)); + assert((v1 < v2)); + assert(!(v1 > v2)); + v2 += 2; + assert(v1 == 1); + assert(v2 == 0); + v1 <<= 1; + v1 |= v2; + v1 >>= 1; + v2 &= v1; + v2 /= v1; + v2 *= v1; + assert(v1 == 1); + assert(v2 == 0); + if ((v1 ^ v2) != 1) + return false; +#endif + return true; + } +}; + +template +struct TestNoAliasingStruct { + __acc_static_noinline bool test(A *a, B *b) { + *a = 0; + *b = 0; + *b -= 3; + return *a != 0; + } +}; +template +__acc_static_forceinline bool testNoAliasing(A *a, B *b) { + return TestNoAliasingStruct::test(a, b); +} +template +struct TestIntegerWrap { + static inline bool inc(T x) { return x + 1 > x; } + static inline bool dec(T x) { return x - 1 < x; } +}; + +} // namespace + +#define ACC_WANT_ACC_CHK_CH 1 +#undef ACCCHK_ASSERT +#include "miniacc.h" + +void upx_compiler_sanity_check(void) { +#define ACC_WANT_ACC_CHK_CH 1 +#undef ACCCHK_ASSERT +#define ACCCHK_ASSERT(expr) ACC_COMPILE_TIME_ASSERT(expr) +#include "miniacc.h" +#undef ACCCHK_ASSERT + + COMPILE_TIME_ASSERT(sizeof(char) == 1) + COMPILE_TIME_ASSERT(sizeof(short) == 2) + COMPILE_TIME_ASSERT(sizeof(int) == 4) + COMPILE_TIME_ASSERT(sizeof(long) >= 4) + COMPILE_TIME_ASSERT(sizeof(void *) >= 4) + + COMPILE_TIME_ASSERT(sizeof(BE16) == 2) + COMPILE_TIME_ASSERT(sizeof(BE32) == 4) + COMPILE_TIME_ASSERT(sizeof(BE64) == 8) + COMPILE_TIME_ASSERT(sizeof(LE16) == 2) + COMPILE_TIME_ASSERT(sizeof(LE32) == 4) + COMPILE_TIME_ASSERT(sizeof(LE64) == 8) + + COMPILE_TIME_ASSERT_ALIGNED1(BE16) + COMPILE_TIME_ASSERT_ALIGNED1(BE32) + COMPILE_TIME_ASSERT_ALIGNED1(BE64) + COMPILE_TIME_ASSERT_ALIGNED1(LE16) + COMPILE_TIME_ASSERT_ALIGNED1(LE32) + COMPILE_TIME_ASSERT_ALIGNED1(LE64) + + COMPILE_TIME_ASSERT(sizeof(UPX_VERSION_STRING4) == 4 + 1) + assert(strlen(UPX_VERSION_STRING4) == 4); + COMPILE_TIME_ASSERT(sizeof(UPX_VERSION_YEAR) == 4 + 1) + assert(strlen(UPX_VERSION_YEAR) == 4); + assert(memcmp(UPX_VERSION_DATE_ISO, UPX_VERSION_YEAR, 4) == 0); + assert(memcmp(&UPX_VERSION_DATE[sizeof(UPX_VERSION_DATE) - 1 - 4], UPX_VERSION_YEAR, 4) == 0); + if (gitrev[0]) { + size_t revlen = strlen(gitrev); + if (strncmp(gitrev, "ERROR", 5) == 0) { + assert(revlen == 5 || revlen == 6); + } else { + assert(revlen == 12 || revlen == 13); + } + if (revlen == 6 || revlen == 13) { + assert(gitrev[revlen - 1] == '+'); + } + } + assert(UPX_RSIZE_MAX_MEM == 805306368); + +#if 1 + assert(TestBELE::test()); + assert(TestBELE::test()); + assert(TestBELE::test()); + assert(TestBELE::test()); + assert(TestBELE::test()); + assert(TestBELE::test()); + { + alignas(16) static const unsigned char dd[32] = { + 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}; + const unsigned char *d; + const N_BELE_RTP::AbstractPolicy *bele; + d = dd + 7; + assert(upx_adler32(d, 4) == 0x09f003f7); + assert(upx_adler32(d, 4, 0) == 0x09ec03f6); + assert(upx_adler32(d, 4, 1) == 0x09f003f7); + bele = &N_BELE_RTP::be_policy; + assert(get_be16(d) == 0xfffe); + assert(bele->get16(d) == 0xfffe); + assert(get_be16_signed(d) == -2); + assert(get_be24(d) == 0xfffefd); + assert(bele->get24(d) == 0xfffefd); + assert(get_be24_signed(d) == -259); + assert(get_be32(d) == 0xfffefdfc); + assert(bele->get32(d) == 0xfffefdfc); + assert(get_be32_signed(d) == -66052); + bele = &N_BELE_RTP::le_policy; + assert(get_le16(d) == 0xfeff); + assert(bele->get16(d) == 0xfeff); + assert(get_le16_signed(d) == -257); + assert(get_le24(d) == 0xfdfeff); + assert(bele->get24(d) == 0xfdfeff); + assert(get_le24_signed(d) == -131329); + assert(get_le32(d) == 0xfcfdfeff); + assert(bele->get32(d) == 0xfcfdfeff); + assert(get_le32_signed(d) == -50462977); + assert(get_le64_signed(d) == -506097522914230529LL); + assert(find_be16(d, 2, 0xfffe) == 0); + assert(find_le16(d, 2, 0xfeff) == 0); + assert(find_be32(d, 4, 0xfffefdfc) == 0); + assert(find_le32(d, 4, 0xfcfdfeff) == 0); + d += 12; + assert(get_be16_signed(d) == 32638); + assert(get_be24_signed(d) == 8355453); + assert(get_be32_signed(d) == 2138996092); + assert(get_be64_signed(d) == 9186918263483431288LL); + } + { + unsigned dd; + void *const d = ⅆ + dd = ne32_to_le32(0xf7f6f5f4); + assert(get_le26(d) == 0x03f6f5f4); + set_le26(d, 0); + assert(get_le26(d) == 0); + assert(dd == ne32_to_le32(0xf4000000)); + set_le26(d, 0xff020304); + assert(get_le26(d) == 0x03020304); + assert(dd == ne32_to_le32(0xf7020304)); + } +#endif + union { + short v_short; + int v_int; + long v_long; + long long v_llong; + BE16 b16; + BE32 b32; + BE64 b64; + LE16 l16; + LE32 l32; + LE64 l64; + } u; + assert(testNoAliasing(&u.v_short, &u.b32)); + assert(testNoAliasing(&u.v_short, &u.l32)); + assert(testNoAliasing(&u.v_int, &u.b64)); + assert(testNoAliasing(&u.v_int, &u.l64)); + // check working -fno-strict-aliasing + assert(testNoAliasing(&u.v_short, &u.v_int)); + assert(testNoAliasing(&u.v_int, &u.v_long)); + assert(testNoAliasing(&u.v_int, &u.v_llong)); + assert(testNoAliasing(&u.v_long, &u.v_llong)); + + assert(TestIntegerWrap::inc(0)); + assert(!TestIntegerWrap::inc(UINT_MAX)); + assert(TestIntegerWrap::dec(1)); + assert(!TestIntegerWrap::dec(0)); + // check working -fno-strict-overflow + assert(TestIntegerWrap::inc(0)); + assert(!TestIntegerWrap::inc(INT_MAX)); + assert(TestIntegerWrap::dec(0)); + assert(!TestIntegerWrap::dec(INT_MIN)); +} + +/************************************************************************* +// some doctest test cases +**************************************************************************/ + +TEST_CASE("acc_vget") { + CHECK_EQ(acc_vget_int(0, 0), 0); + CHECK_EQ(acc_vget_long(1, -1), 1); + CHECK_EQ(acc_vget_acc_int64l_t(2, 1), 2); + CHECK_EQ(acc_vget_acc_hvoid_p(nullptr, 0), nullptr); +} + +TEST_CASE("working -fno-strict-aliasing") { + bool ok; + long v = 0; + short *ps = ACC_STATIC_CAST(short *, acc_vget_acc_hvoid_p(&v, 0)); + int *pi = ACC_STATIC_CAST(int *, acc_vget_acc_hvoid_p(&v, 0)); + long *pl = ACC_STATIC_CAST(long *, acc_vget_acc_hvoid_p(&v, 0)); + *ps = 0; + *pl = -1; + ok = *ps == -1; + CHECK(ok); + *pi = 0; + *pl = -1; + ok = *pi == -1; + CHECK(ok); + *pl = 0; + *ps = -1; + ok = *pl != 0; + CHECK(ok); + *pl = 0; + *pi = -1; + ok = *pl != 0; + CHECK(ok); +} + +TEST_CASE("working -fno-strict-overflow") { + CHECK_EQ(acc_vget_int(INT_MAX, 0) + 1, INT_MIN); + CHECK_EQ(acc_vget_int(INT_MIN, 0) - 1, INT_MAX); + CHECK_EQ(acc_vget_long(LONG_MAX, 0) + 1, LONG_MIN); + CHECK_EQ(acc_vget_long(LONG_MIN, 0) - 1, LONG_MAX); + bool ok; + int i; + i = INT_MAX; + i += 1; + ok = i == INT_MIN; + CHECK(ok); + i = INT_MIN; + i -= 1; + ok = i == INT_MAX; + CHECK(ok); +} + +TEST_CASE("libc snprintf") { + // runtime check that Win32/MinGW works as expected + long long ll = acc_vget_int(-1, 0); + unsigned long long llu = (unsigned long long) ll; + char buf[64]; + 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); +} + +/* vim:set ts=4 sw=4 et: */ diff --git a/src/dt_impl.cpp b/src/dt_impl.cpp new file mode 100644 index 00000000..219eb5ec --- /dev/null +++ b/src/dt_impl.cpp @@ -0,0 +1,37 @@ +/* dt_impl.cpp -- doctest support code implementation + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2022 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2022 Laszlo Molnar + 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 Laszlo Molnar + + */ + +/************************************************************************* +// doctest support code implementation +**************************************************************************/ + +#define DOCTEST_CONFIG_IMPLEMENT +#define DOCTEST_CONFIG_NO_MULTITHREADING +#define DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS +#include + +/* vim:set ts=4 sw=4 et: */ diff --git a/src/main.cpp b/src/main.cpp index e1365e6f..f1278340 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -926,8 +926,6 @@ static int get_options(int argc, char **argv) { {"crp-zlib-ml", 0x31, N, 821}, {"crp-zlib-wb", 0x31, N, 822}, {"crp-zlib-st", 0x31, N, 823}, - // [deprecated - only for compatibility with UPX 2.0x] - {"crp-ms", 0x31, N, 807}, // atari/tos {"split-segments", 0x10, N, 650}, @@ -1164,242 +1162,6 @@ static void first_options(int argc, char **argv) { do_option(519, argv[i]); } -/************************************************************************* -// assert a sane architecture and compiler -**************************************************************************/ - -template -struct TestBELE { - __acc_static_noinline bool test(void) { - COMPILE_TIME_ASSERT_ALIGNED1(T) - struct alignas(1) test1_t { - char a; - T b; - }; - struct alignas(1) test2_t { - char a; - T b[3]; - }; - test1_t t1[7]; - UNUSED(t1); - test2_t t2[7]; - UNUSED(t2); - COMPILE_TIME_ASSERT(sizeof(test1_t) == 1 + sizeof(T)) - COMPILE_TIME_ASSERT_ALIGNED1(test1_t) - COMPILE_TIME_ASSERT(sizeof(t1) == 7 + 7 * sizeof(T)) - COMPILE_TIME_ASSERT(sizeof(test2_t) == 1 + 3 * sizeof(T)) - COMPILE_TIME_ASSERT_ALIGNED1(test2_t) - COMPILE_TIME_ASSERT(sizeof(t2) == 7 + 21 * sizeof(T)) -#if defined(__acc_alignof) - COMPILE_TIME_ASSERT(__acc_alignof(t1) == 1) - COMPILE_TIME_ASSERT(__acc_alignof(t2) == 1) -#endif -#if 1 - T allbits; - allbits = 0; - allbits += 1; - allbits -= 2; - T v1; - v1 = 1; - v1 *= 2; - v1 -= 1; - T v2; - v2 = 1; - assert((v1 == v2)); - assert(!(v1 != v2)); - assert((v1 <= v2)); - assert((v1 >= v2)); - assert(!(v1 < v2)); - assert(!(v1 > v2)); - v2 ^= allbits; - assert(!(v1 == v2)); - assert((v1 != v2)); - assert((v1 <= v2)); - assert(!(v1 >= v2)); - assert((v1 < v2)); - assert(!(v1 > v2)); - v2 += 2; - assert(v1 == 1); - assert(v2 == 0); - v1 <<= 1; - v1 |= v2; - v1 >>= 1; - v2 &= v1; - v2 /= v1; - v2 *= v1; - assert(v1 == 1); - assert(v2 == 0); - if ((v1 ^ v2) != 1) - return false; -#endif - return true; - } -}; - -template -struct TestNoAliasingStruct { - __acc_static_noinline bool test(A *a, B *b) { - *a = 0; - *b = 0; - *b -= 3; - return *a != 0; - } -}; -template -__acc_static_forceinline bool testNoAliasing(A *a, B *b) { - return TestNoAliasingStruct::test(a, b); -} -template -struct TestIntegerWrap { - static inline bool inc(T x) { return x + 1 > x; } - static inline bool dec(T x) { return x - 1 < x; } -}; - -#define ACC_WANT_ACC_CHK_CH 1 -#undef ACCCHK_ASSERT -#include "miniacc.h" - -void upx_compiler_sanity_check(void) { -#define ACC_WANT_ACC_CHK_CH 1 -#undef ACCCHK_ASSERT -#define ACCCHK_ASSERT(expr) ACC_COMPILE_TIME_ASSERT(expr) -#include "miniacc.h" -#undef ACCCHK_ASSERT - - COMPILE_TIME_ASSERT(sizeof(char) == 1) - COMPILE_TIME_ASSERT(sizeof(short) == 2) - COMPILE_TIME_ASSERT(sizeof(int) == 4) - COMPILE_TIME_ASSERT(sizeof(long) >= 4) - COMPILE_TIME_ASSERT(sizeof(void *) >= 4) - - COMPILE_TIME_ASSERT(sizeof(BE16) == 2) - COMPILE_TIME_ASSERT(sizeof(BE32) == 4) - COMPILE_TIME_ASSERT(sizeof(BE64) == 8) - COMPILE_TIME_ASSERT(sizeof(LE16) == 2) - COMPILE_TIME_ASSERT(sizeof(LE32) == 4) - COMPILE_TIME_ASSERT(sizeof(LE64) == 8) - - COMPILE_TIME_ASSERT_ALIGNED1(BE16) - COMPILE_TIME_ASSERT_ALIGNED1(BE32) - COMPILE_TIME_ASSERT_ALIGNED1(BE64) - COMPILE_TIME_ASSERT_ALIGNED1(LE16) - COMPILE_TIME_ASSERT_ALIGNED1(LE32) - COMPILE_TIME_ASSERT_ALIGNED1(LE64) - - COMPILE_TIME_ASSERT(sizeof(UPX_VERSION_STRING4) == 4 + 1) - assert(strlen(UPX_VERSION_STRING4) == 4); - COMPILE_TIME_ASSERT(sizeof(UPX_VERSION_YEAR) == 4 + 1) - assert(strlen(UPX_VERSION_YEAR) == 4); - assert(memcmp(UPX_VERSION_DATE_ISO, UPX_VERSION_YEAR, 4) == 0); - assert(memcmp(&UPX_VERSION_DATE[sizeof(UPX_VERSION_DATE) - 1 - 4], UPX_VERSION_YEAR, 4) == 0); - if (gitrev[0]) { - size_t revlen = strlen(gitrev); - if (strncmp(gitrev, "ERROR", 5) == 0) { - assert(revlen == 5 || revlen == 6); - } else { - assert(revlen == 12 || revlen == 13); - } - if (revlen == 6 || revlen == 13) { - assert(gitrev[revlen - 1] == '+'); - } - } - assert(UPX_RSIZE_MAX_MEM == 805306368); - -#if 1 - assert(TestBELE::test()); - assert(TestBELE::test()); - assert(TestBELE::test()); - assert(TestBELE::test()); - assert(TestBELE::test()); - assert(TestBELE::test()); - { - alignas(16) static const unsigned char dd[32] = { - 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}; - const unsigned char *d; - const N_BELE_RTP::AbstractPolicy *bele; - d = dd + 7; - assert(upx_adler32(d, 4) == 0x09f003f7); - assert(upx_adler32(d, 4, 0) == 0x09ec03f6); - assert(upx_adler32(d, 4, 1) == 0x09f003f7); - bele = &N_BELE_RTP::be_policy; - assert(get_be16(d) == 0xfffe); - assert(bele->get16(d) == 0xfffe); - assert(get_be16_signed(d) == -2); - assert(get_be24(d) == 0xfffefd); - assert(bele->get24(d) == 0xfffefd); - assert(get_be24_signed(d) == -259); - assert(get_be32(d) == 0xfffefdfc); - assert(bele->get32(d) == 0xfffefdfc); - assert(get_be32_signed(d) == -66052); - bele = &N_BELE_RTP::le_policy; - assert(get_le16(d) == 0xfeff); - assert(bele->get16(d) == 0xfeff); - assert(get_le16_signed(d) == -257); - assert(get_le24(d) == 0xfdfeff); - assert(bele->get24(d) == 0xfdfeff); - assert(get_le24_signed(d) == -131329); - assert(get_le32(d) == 0xfcfdfeff); - assert(bele->get32(d) == 0xfcfdfeff); - assert(get_le32_signed(d) == -50462977); - assert(get_le64_signed(d) == -506097522914230529LL); - assert(find_be16(d, 2, 0xfffe) == 0); - assert(find_le16(d, 2, 0xfeff) == 0); - assert(find_be32(d, 4, 0xfffefdfc) == 0); - assert(find_le32(d, 4, 0xfcfdfeff) == 0); - d += 12; - assert(get_be16_signed(d) == 32638); - assert(get_be24_signed(d) == 8355453); - assert(get_be32_signed(d) == 2138996092); - assert(get_be64_signed(d) == 9186918263483431288LL); - } - { - unsigned dd; - void *const d = ⅆ - dd = ne32_to_le32(0xf7f6f5f4); - assert(get_le26(d) == 0x03f6f5f4); - set_le26(d, 0); - assert(get_le26(d) == 0); - assert(dd == ne32_to_le32(0xf4000000)); - set_le26(d, 0xff020304); - assert(get_le26(d) == 0x03020304); - assert(dd == ne32_to_le32(0xf7020304)); - } -#endif - union { - short v_short; - int v_int; - long v_long; - long long v_llong; - BE16 b16; - BE32 b32; - BE64 b64; - LE16 l16; - LE32 l32; - LE64 l64; - } u; - assert(testNoAliasing(&u.v_short, &u.b32)); - assert(testNoAliasing(&u.v_short, &u.l32)); - assert(testNoAliasing(&u.v_int, &u.b64)); - assert(testNoAliasing(&u.v_int, &u.l64)); -#if 1 - // check working -fno-strict-aliasing - assert(testNoAliasing(&u.v_short, &u.v_int)); - assert(testNoAliasing(&u.v_int, &u.v_long)); - assert(testNoAliasing(&u.v_int, &u.v_llong)); - assert(testNoAliasing(&u.v_long, &u.v_llong)); -#endif - - assert(TestIntegerWrap::inc(0)); - assert(!TestIntegerWrap::inc(INT_MAX)); - assert(TestIntegerWrap::dec(0)); - assert(!TestIntegerWrap::dec(INT_MIN)); - assert(TestIntegerWrap::inc(0)); - assert(!TestIntegerWrap::inc(UINT_MAX)); - assert(TestIntegerWrap::dec(1)); - assert(!TestIntegerWrap::dec(0)); -} - /************************************************************************* // main entry point **************************************************************************/ @@ -1407,15 +1169,21 @@ void upx_compiler_sanity_check(void) { int upx_main(int argc, char *argv[]) { int i; static char default_argv0[] = "upx"; + assert(argc >= 1); // sanity check + if (!argv[0] || !argv[0][0]) + argv[0] = default_argv0; + argv0 = argv[0]; upx_compiler_sanity_check(); + if (!upx_doctest_check()) { + fprintf(stderr, "%s: internal error: doctest failed\n", argv0); + e_exit(EXIT_INIT); + } + // Allow serial re-use of upx_main() as a subroutine done_output_name = 0; opt->reset(); - if (!argv[0] || !argv[0][0]) - argv[0] = default_argv0; - argv0 = argv[0]; #if (ACC_OS_CYGWIN || ACC_OS_DOS16 || ACC_OS_DOS32 || ACC_OS_EMX || ACC_OS_TOS || ACC_OS_WIN16 || \ ACC_OS_WIN32 || ACC_OS_WIN64) { @@ -1442,15 +1210,8 @@ int upx_main(int argc, char *argv[]) { set_term(stderr); - if (upx_ucl_init() != 0) { - show_head(); - fprintf(stderr, "ucl_init() failed - check your UCL installation !\n"); - if (UCL_VERSION != ucl_version()) - fprintf(stderr, "library version conflict (%lx, %lx) - check your UCL installation !\n", - (long) UCL_VERSION, (long) ucl_version()); - e_exit(EXIT_INIT); - } assert(upx_lzma_init() == 0); + assert(upx_ucl_init() == 0); assert(upx_zlib_init() == 0); #if (WITH_NRV) assert(upx_nrv_init() == 0); @@ -1570,17 +1331,6 @@ int __acc_cdecl_main main(int argc, char *argv[]) { // srand((int) time(nullptr)); srand((int) clock()); -#if defined(_WIN32) || 1 - // runtime check that Win32/MinGW works as expected - { - long long ll = argc < 0 ? 0 : -1; - unsigned long long llu = (unsigned long long) ll; - char buf[256]; - snprintf(buf, sizeof(buf), ".%d.%ld.%lld.%u.%lu.%llu", -3, -2L, ll, 3U, 2LU, llu); - assert(strcmp(buf, ".-3.-2.-1.3.2.18446744073709551615") == 0); - } -#endif - int r = upx_main(argc, argv); #if 0 && defined(__GLIBC__) diff --git a/src/mem.cpp b/src/mem.cpp index 786abedc..ae453771 100644 --- a/src/mem.cpp +++ b/src/mem.cpp @@ -53,7 +53,7 @@ __acc_static_forceinline bool use_simple_mcheck() return (bool) use_simple_mcheck_flag; } #else -__acc_static_forceinline bool use_simple_mcheck() { return true; } +__acc_static_forceinline constexpr bool use_simple_mcheck() { return true; } #endif diff --git a/src/mem.h b/src/mem.h index 072c8381..6307cb57 100644 --- a/src/mem.h +++ b/src/mem.h @@ -75,10 +75,10 @@ private: static unsigned global_alloc_counter; - // disable copy and assignment + // disable copy, assignment and move assignment MemBuffer(const MemBuffer &) = delete; MemBuffer& operator= (const MemBuffer &) = delete; - + MemBuffer& operator= (MemBuffer &&) = delete; // disable dynamic allocation ACC_CXX_DISABLE_NEW_DELETE }; diff --git a/src/miniacc.h b/src/miniacc.h index 7c337487..178a1a52 100644 --- a/src/miniacc.h +++ b/src/miniacc.h @@ -1674,7 +1674,11 @@ extern "C" { # define __acc_forceinline __acc_inline #endif #if !defined(__acc_noinline) -#if 1 && (ACC_ARCH_I386) && (ACC_CC_GNUC >= 0x040000ul) && (ACC_CC_GNUC < 0x040003ul) +#if (ACC_CC_GNUC >= 0x080000ul) +# define __acc_noinline __attribute__((__noinline__,__noipa__)) +#elif (ACC_CC_GNUC >= 0x050000ul) +# define __acc_noinline __attribute__((__noinline__,__noclone__,__no_icf__)) +#elif 1 && (ACC_ARCH_I386) && (ACC_CC_GNUC >= 0x040000ul) && (ACC_CC_GNUC < 0x040003ul) # define __acc_noinline __attribute__((__noinline__,__used__)) #elif (ACC_CC_GNUC >= 0x030200ul) # define __acc_noinline __attribute__((__noinline__)) @@ -6316,13 +6320,13 @@ ACCLIB_PUBLIC(void, acc_ua_set_le64) (acc_hvoid_p p, acc_uint64l_t v) #endif extern void* volatile acc_vget_ptr__; #if (ACC_CC_CLANG || (ACC_CC_GNUC >= 0x030400ul) || ACC_CC_LLVM) -void* volatile __attribute__((__used__)) acc_vget_ptr__ = ACC_STATIC_CAST(void *, 0); +void* volatile __attribute__((__used__)) acc_vget_ptr__ = ACC_STATIC_CAST(void *, nullptr); #else -void* volatile acc_vget_ptr__ = ACC_STATIC_CAST(void *, 0); +void* volatile acc_vget_ptr__ = ACC_STATIC_CAST(void *, nullptr); #endif #ifndef __ACCLIB_VGET_BODY #define __ACCLIB_VGET_BODY(T) \ - if __acc_unlikely(acc_vget_ptr__) { \ + if __acc_very_unlikely(acc_vget_ptr__) { \ typedef T __acc_may_alias TT; \ unsigned char e; expr &= 255; e = ACC_STATIC_CAST(unsigned char, expr); \ * ACC_STATIC_CAST(TT *, acc_vget_ptr__) = v; \ diff --git a/src/packer.cpp b/src/packer.cpp index 8e63c232..b552d999 100644 --- a/src/packer.cpp +++ b/src/packer.cpp @@ -1434,8 +1434,9 @@ void Packer::compressWithFilters(upx_bytep i_ptr, void Packer::compressWithFilters(Filter *ft, const unsigned overlap_range, const upx_compress_config_t *cconf, int filter_strategy, bool inhibit_compression_check) { - compressWithFilters( // call the subroutine immediately below - ft, overlap_range, cconf, filter_strategy, 0, 0, 0, nullptr, 0, inhibit_compression_check); + // call the subroutine immediately below + compressWithFilters(ft, overlap_range, cconf, filter_strategy, 0, 0, 0, nullptr, 0, + inhibit_compression_check); } void Packer::compressWithFilters(Filter *ft, const unsigned overlap_range, @@ -1454,9 +1455,9 @@ void Packer::compressWithFilters(Filter *ft, const unsigned overlap_range, assert(f_ptr + f_len <= i_ptr + i_len); - compressWithFilters( // call the first one in this file - i_ptr, i_len, o_ptr, f_ptr, f_len, hdr_ptr, hdr_len, ft, overlap_range, cconf, - filter_strategy, inhibit_compression_check); + // call the first one in this file + compressWithFilters(i_ptr, i_len, o_ptr, f_ptr, f_len, hdr_ptr, hdr_len, ft, overlap_range, + cconf, filter_strategy, inhibit_compression_check); ibuf.checkState(); obuf.checkState(); diff --git a/src/util.cpp b/src/util.cpp index 68bfefc6..bc414adc 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -33,6 +33,7 @@ #define ACC_WANT_ACCLIB_GETOPT 1 #define ACC_WANT_ACCLIB_HSREAD 1 #define ACC_WANT_ACCLIB_MISC 1 +#define ACC_WANT_ACCLIB_VGET 1 #define ACC_WANT_ACCLIB_WILDARGV 1 #undef HAVE_MKDIR #include "miniacc.h" @@ -92,6 +93,15 @@ bool mem_size_valid_bytes(upx_uint64_t bytes) { return true; } +TEST_CASE("mem_size") { + CHECK(mem_size_valid(1, 0)); + CHECK(mem_size_valid(1, 0x30000000)); + CHECK(!mem_size_valid(1, 0x30000000 + 1)); + CHECK(!mem_size_valid(1, 0x30000000, 1)); + CHECK(!mem_size_valid(1, 0x30000000, 0, 1)); + CHECK(!mem_size_valid(1, 0x30000000, 0x30000000, 0x30000000)); +} + int ptr_diff(const void *p1, const void *p2) { assert(p1 != nullptr); assert(p2 != nullptr); @@ -284,6 +294,24 @@ int find_le64(const void *b, int blen, upx_uint64_t what) { return find(b, blen, w, 8); } +TEST_CASE("find") { + static const unsigned char b[16] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + CHECK(find(b, 16, b, 0) == -1); + for (int i = 0; i < 16; i++) { + CHECK(find(b, 16, b + i, 1) == i); + } + for (int i = 1; i <= 16; i++) { + CHECK(find(b, 16, b, i) == 0); + } + CHECK(find(b, 16, b, 17) == -1); + CHECK(find_be16(b, 16, 0x0203) == 2); + CHECK(find_le16(b, 16, 0x0302) == 2); + CHECK(find_be32(b, 16, 0x04050607) == 4); + CHECK(find_le32(b, 16, 0x07060504) == 4); + CHECK(find_be64(b, 16, 0x08090a0b0c0d0e0fULL) == 8); + CHECK(find_le64(b, 16, 0x0f0e0d0c0b0a0908ULL) == 8); +} + int mem_replace(void *bb, int blen, const void *what, int wlen, const void *r) { unsigned char *b = (unsigned char *) bb; int boff = 0; @@ -301,6 +329,16 @@ int mem_replace(void *bb, int blen, const void *what, int wlen, const void *r) { return n; } +TEST_CASE("mem_replace") { + char b[16 + 1] = "aaaaaaaaaaaaaaaa"; + CHECK(mem_replace(b, 16, "a", 0, "x") == 0); + CHECK(mem_replace(b, 16, "a", 1, "b") == 16); + CHECK(mem_replace(b, 8, "bb", 2, "cd") == 4); + CHECK(mem_replace(b + 8, 8, "bbb", 3, "efg") == 2); + CHECK(mem_replace(b, 16, "b", 1, "h") == 2); + CHECK(strcmp(b, "cdcdcdcdefgefghh") == 0); +} + /************************************************************************* // fn - FileName util **************************************************************************/ @@ -512,4 +550,16 @@ unsigned get_ratio(upx_uint64_t u_len, upx_uint64_t c_len) { return ACC_ICONV(unsigned, x); } +TEST_CASE("get_ratio") { + CHECK(get_ratio(0, 0) == 0); + CHECK(get_ratio(0, 1) == 1000000); + CHECK(get_ratio(1, 0) == 50); + CHECK(get_ratio(1, 1) == 1000050); + CHECK(get_ratio(1, 9) == 9000050); + CHECK(get_ratio(1, 10) == 9999999); + CHECK(get_ratio(1, 11) == 9999999); + CHECK(get_ratio(100000, 100000) == 1000050); + CHECK(get_ratio(100000, 200000) == 2000050); +} + /* vim:set ts=4 sw=4 et: */