diff --git a/CMakeLists.txt b/CMakeLists.txt index 4183c4fb..fe7ada3f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -436,9 +436,13 @@ include(CTest) if(NOT CMAKE_CROSSCOMPILING OR CMAKE_CROSSCOMPILING_EMULATOR) add_test(NAME upx-version COMMAND upx --version) add_test(NAME upx-version-short COMMAND upx --version-short) - add_test(NAME upx-help COMMAND upx --help) add_test(NAME upx-license COMMAND upx --license) - add_test(NAME upx-sysinfo COMMAND upx --sysinfo -v) + add_test(NAME upx-help-1 COMMAND upx --help) + add_test(NAME upx-help-2 COMMAND upx --help-short) + add_test(NAME upx-help-3 COMMAND upx --help-verbose) + add_test(NAME upx-sysinfo-1 COMMAND upx --sysinfo) + add_test(NAME upx-sysinfo-2 COMMAND upx --sysinfo -v) + add_test(NAME upx-sysinfo-3 COMMAND upx --sysinfo -vv) if(NOT UPX_CONFIG_DISABLE_SELF_PACK_TEST) # IMPORTANT NOTE: these tests can only work if the host executable format is supported by UPX! set(emu "") diff --git a/misc/testsuite/mimic_ctest.sh b/misc/testsuite/mimic_ctest.sh index 0613e21e..c978dca8 100755 --- a/misc/testsuite/mimic_ctest.sh +++ b/misc/testsuite/mimic_ctest.sh @@ -25,6 +25,7 @@ if [[ -z $upx_exe ]]; then echo "UPX-ERROR: please set \$upx_exe"; exit 1; fi if [[ ! -f $upx_exe ]]; then echo "UPX-ERROR: file '$upx_exe' does not exist"; exit 1; fi upx_exe=$(readlink -fn "$upx_exe") # make absolute [[ -f $upx_exe ]] || exit 1 + # set emu and run_upx emu=() if [[ -n $upx_exe_runner ]]; then @@ -67,9 +68,13 @@ export UPX="--no-color --no-progress" "${run_upx[@]}" --version "${run_upx[@]}" --version-short -"${run_upx[@]}" --help "${run_upx[@]}" --license +"${run_upx[@]}" --help +"${run_upx[@]}" --help-short +"${run_upx[@]}" --help-verbose +"${run_upx[@]}" --sysinfo "${run_upx[@]}" --sysinfo -v +"${run_upx[@]}" --sysinfo -vv if [[ $UPX_CONFIG_DISABLE_SELF_PACK_TEST == ON ]]; then echo "Self-pack test disabled. All done."; exit 0 diff --git a/misc/testsuite/test_symlinks.sh b/misc/testsuite/test_symlinks.sh index b03b42c0..7a318d47 100755 --- a/misc/testsuite/test_symlinks.sh +++ b/misc/testsuite/test_symlinks.sh @@ -40,6 +40,7 @@ if [[ -z $upx_exe ]]; then echo "UPX-ERROR: please set \$upx_exe"; exit 1; fi if [[ ! -f $upx_exe ]]; then echo "UPX-ERROR: file '$upx_exe' does not exist"; exit 1; fi upx_exe=$(readlink -fn "$upx_exe") # make absolute [[ -f $upx_exe ]] || exit 1 + # set emu and run_upx emu=() if [[ -n $upx_exe_runner ]]; then diff --git a/misc/testsuite/upx_testsuite_1.sh b/misc/testsuite/upx_testsuite_1.sh index b91a195d..038d9387 100755 --- a/misc/testsuite/upx_testsuite_1.sh +++ b/misc/testsuite/upx_testsuite_1.sh @@ -29,6 +29,7 @@ if [[ -z $upx_exe ]]; then echo "UPX-ERROR: please set \$upx_exe"; exit 1; fi if [[ ! -f $upx_exe ]]; then echo "UPX-ERROR: file '$upx_exe' does not exist"; exit 1; fi upx_exe=$(readlink -fn "$upx_exe") # make absolute [[ -f $upx_exe ]] || exit 1 + # set emu and run_upx emu=() if [[ -n $upx_exe_runner ]]; then @@ -43,7 +44,7 @@ fi run_upx=( "${emu[@]}" "$upx_exe" ) echo "run_upx='${run_upx[*]}'" -# run_upx sanity check, part1 +# run_upx sanity check if ! "${run_upx[@]}" --version-short >/dev/null; then echo "UPX-ERROR: FATAL: upx --version-short FAILED"; exit 1; fi if ! "${run_upx[@]}" -L >/dev/null 2>&1; then echo "UPX-ERROR: FATAL: upx -L FAILED"; exit 1; fi if ! "${run_upx[@]}" --help >/dev/null; then echo "UPX-ERROR: FATAL: upx --help FAILED"; exit 1; fi @@ -75,19 +76,30 @@ fi mkdir -p "$upx_testsuite_BUILDDIR" || exit 1 upx_testsuite_BUILDDIR=$(readlink -fn "$upx_testsuite_BUILDDIR") # make absolute [[ -d $upx_testsuite_BUILDDIR ]] || exit 1 - cd / && cd "$upx_testsuite_BUILDDIR" || exit 1 : > ./.mfxnobackup -# run_upx sanity check, part2 +# run_upx sanity check after "cd" if ! "${run_upx[@]}" --version-short >/dev/null; then echo "UPX-ERROR: FATAL: upx --version-short FAILED" echo "please make sure that \$upx_exe contains ABSOLUTE file paths and can be run from any directory" echo "INFO: run_upx='${run_upx[*]}'" exit 1 fi -if ! "${run_upx[@]}" -L >/dev/null 2>&1; then echo "UPX-ERROR: FATAL: upx -L FAILED"; exit 1; fi -if ! "${run_upx[@]}" --help >/dev/null; then echo "UPX-ERROR: FATAL: upx --help FAILED"; exit 1; fi + +#*********************************************************************** +# setup +#*********************************************************************** + +#set -x # debug + +exit_code=0 +num_errors=0 +all_errors= + +export UPX="--prefer-ucl --no-color --no-progress" +export UPX_DEBUG_DISABLE_GITREV_WARNING=1 +export UPX_DEBUG_DOCTEST_VERBOSE=0 case $UPX_TESTSUITE_LEVEL in [0-8]) ;; @@ -98,19 +110,6 @@ if [[ $UPX_TESTSUITE_LEVEL == 0 ]]; then exit 0 fi -#*********************************************************************** -# setup -#*********************************************************************** - -#set -x # debug -exit_code=0 -num_errors=0 -all_errors= - -export UPX="--prefer-ucl --no-color --no-progress" -export UPX_DEBUG_DISABLE_GITREV_WARNING=1 -export UPX_DEBUG_DOCTEST_VERBOSE=0 - rm -rf ./testsuite_1 mkdir testsuite_1 || exit 1 cd testsuite_1 || exit 1 diff --git a/src/conf.h b/src/conf.h index 514edd00..5e374439 100644 --- a/src/conf.h +++ b/src/conf.h @@ -822,7 +822,7 @@ int do_files(int i, int argc, char *argv[]) may_throw; // help.cpp extern const char gitrev[]; void show_header(); -void show_help(int verbose = 0); +void show_help(int verbose); void show_license(); void show_sysinfo(const char *options_var); void show_usage(); diff --git a/src/help.cpp b/src/help.cpp index e57e5478..845df8e0 100644 --- a/src/help.cpp +++ b/src/help.cpp @@ -34,6 +34,7 @@ static constexpr long long initial_win32_winnt = 0; #include "conf.h" #include "compress/compress.h" // upx_ucl_version_string() // for list_all_packers(): +#include "filter.h" // Filter::isValidFilter #include "packer.h" #include "packmast.h" // PackMaster::visitAllPackers @@ -101,48 +102,85 @@ void show_usage(void) { namespace { struct PackerNames final { - PackerNames() noexcept = default; + explicit PackerNames() noexcept = default; ~PackerNames() noexcept = default; + + static constexpr size_t MAX_NAMES = 64; + static constexpr size_t MAX_METHODS = 8; + static constexpr size_t MAX_FILTERS = 16; struct Entry { const char *fname; const char *sname; + size_t methods_count; + size_t filters_count; + unsigned methods[MAX_METHODS]; + unsigned filters[MAX_FILTERS]; }; - static constexpr size_t MAX_NAMES = 64; Entry names[MAX_NAMES]; size_t names_count = 0; const Options *o = nullptr; + void add(const PackerBase *pb) { assert_noexcept(names_count < MAX_NAMES); - names[names_count].fname = pb->getFullName(o); - names[names_count].sname = pb->getName(); - names_count++; + Entry &e = names[names_count++]; + e.fname = pb->getFullName(o); + e.sname = pb->getName(); + e.methods_count = e.filters_count = 0; + for (const int *m = pb->getCompressionMethods(M_ALL, 10); *m != M_END; m++) { + if (*m >= 0) { + assert_noexcept(Packer::isValidCompressionMethod(*m)); + assert_noexcept(e.methods_count < MAX_METHODS); + e.methods[e.methods_count++] = *m; + } + } + for (const int *f = pb->getFilters(); f != nullptr && *f != FT_END; f++) { + if (*f >= 0) { + assert_noexcept(Filter::isValidFilter(*f)); + assert_noexcept(e.filters_count < MAX_FILTERS); + e.filters[e.filters_count++] = *f; + } + } + upx_gnomesort(e.methods, e.methods_count, sizeof(e.methods[0]), ne32_compare); + upx_gnomesort(e.filters, e.filters_count, sizeof(e.filters[0]), ne32_compare); } static tribool visit(PackerBase *pb, void *user) { + NO_fprintf(stderr, "visit %s\n", pb->getFullName(nullptr)); PackerNames *self = (PackerNames *) user; self->add(pb); return false; } - static int __acc_cdecl_qsort cmp_fname(const void *a, const void *b) { + 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 cmp_sname(const void *a, const void *b) { + static int __acc_cdecl_qsort compare_sname(const void *a, const void *b) { return strcmp(((const Entry *) a)->sname, ((const Entry *) b)->sname); } }; } // namespace -static void list_all_packers(FILE *f, int verbose) { +static noinline void list_all_packers(FILE *f, int verbose) { Options o; o.reset(); PackerNames pn; pn.o = &o; (void) PackMaster::visitAllPackers(PackerNames::visit, nullptr, &o, &pn); - upx_qsort(pn.names, pn.names_count, sizeof(PackerNames::Entry), PackerNames::cmp_fname); + upx_gnomesort(pn.names, pn.names_count, sizeof(PackerNames::Entry), PackerNames::compare_fname); size_t pos = 0; - 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) { + for (size_t i = 0; i < pn.names_count; i++) { + const PackerNames::Entry &e = pn.names[i]; + const char *const fn = e.fname; + const char *const sn = e.sname; + if (verbose >= 3) { + con_fprintf(f, " %-36s %s\n", fn, sn); + con_fprintf(f, " methods:"); + for (size_t j = 0; j < e.methods_count; j++) + con_fprintf(f, " %#x", e.methods[j]); + con_fprintf(f, "\n"); + con_fprintf(f, " filters:"); + for (size_t j = 0; j < e.filters_count; j++) + con_fprintf(f, " %#x", e.filters[j]); + con_fprintf(f, "\n"); + } else if (verbose >= 2) { con_fprintf(f, " %-36s %s\n", fn, sn); } else { size_t fl = strlen(fn); @@ -158,7 +196,7 @@ static void list_all_packers(FILE *f, int verbose) { } } } - if (verbose <= 0 && pn.names_count) + if (verbose < 2 && pn.names_count) con_fprintf(f, "\n"); } diff --git a/src/main.cpp b/src/main.cpp index ddcb0c7e..4c6af13c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -218,7 +218,7 @@ static void check_and_update_options(int i, int argc) { **************************************************************************/ static void e_help(void) { - show_help(); + show_help(0); e_exit(EXIT_USAGE); } @@ -386,6 +386,8 @@ static int do_option(int optc, const char *arg) { set_cmd(CMD_HELP); break; case 'h' + 256: + case 996: + case 997: #if 1 if (!acc_isatty(STDOUT_FILENO)) { /* according to GNU standards */ @@ -393,7 +395,7 @@ static int do_option(int optc, const char *arg) { opt->console = CON_FILE; } #endif - show_help(1); + show_help(optc == 996 ? 1 : (optc == 997 ? 3 : 2)); e_exit(EXIT_OK); break; case 'i': @@ -1172,9 +1174,14 @@ static void first_options(int argc, char **argv) { if (strcmp(argv[i], "--version-short") == 0) do_option(998, argv[i]); } - for (i = 1; i < n; i++) + for (i = 1; i < n; i++) { if (strcmp(argv[i], "--help") == 0) do_option('h' + 256, argv[i]); + if (strcmp(argv[i], "--help-short") == 0) // undocumented and subject to change + do_option(996, argv[i]); + if (strcmp(argv[i], "--help-verbose") == 0) // undocumented and subject to change + do_option(997, argv[i]); + } for (i = 1; i < n; i++) if (strcmp(argv[i], "--no-env") == 0) do_option(519, argv[i]); @@ -1272,7 +1279,7 @@ int upx_main(int argc, char *argv[]) may_throw { e_exit(EXIT_OK); break; case CMD_HELP: - show_help(1); + show_help(2); e_exit(EXIT_OK); break; case CMD_LICENSE: diff --git a/src/packer.cpp b/src/packer.cpp index 795503ef..5a17f411 100644 --- a/src/packer.cpp +++ b/src/packer.cpp @@ -58,8 +58,10 @@ void Packer::assertPacker() const { assert(getVersion() <= 14); assert(strlen(getName()) <= 15); // info: 36 is the limit for show_all_packers() in help.cpp, but 32 should be enough - assert(strlen(getFullName(opt)) <= 32); assert(strlen(getFullName(nullptr)) <= 32); + assert(strlen(getFullName(opt)) <= 32); + assert(getCompressionMethods(M_ALL, 10) != nullptr); + (void) getFilters(); if (bele == nullptr) fprintf(stderr, "%s\n", getName()); assert(bele != nullptr); diff --git a/src/util/util.cpp b/src/util/util.cpp index 12d208c3..cf25f8b2 100644 --- a/src/util/util.cpp +++ b/src/util/util.cpp @@ -293,13 +293,13 @@ static inline void memswap_no_overlap(byte *a, byte *b, size_t bytes) noexcept { upx_memswap(a, b, bytes); #else // clang bug upx_alignas_max byte tmp_buf[16]; -#define SWAP(x) \ +#define SWAP(n) \ do { \ - upx_memcpy_inline(tmp_buf, a, x); \ - upx_memcpy_inline(a, b, x); \ - upx_memcpy_inline(b, tmp_buf, x); \ - a += x; \ - b += x; \ + upx_memcpy_inline(tmp_buf, a, n); \ + upx_memcpy_inline(a, b, n); \ + upx_memcpy_inline(b, tmp_buf, n); \ + a += n; \ + b += n; \ } while (0) for (; bytes >= 16; bytes -= 16) @@ -382,7 +382,7 @@ void upx_shellsort_memcpy(void *array, size_t n, size_t element_size, upx_compar // wrap std::stable_sort() template void upx_std_stable_sort(void *array, size_t n, upx_compare_func_t compare) { - static_assert(ElementSize > 0 && ElementSize <= UPX_RSIZE_MAX); + static_assert(ElementSize >= 1 && ElementSize <= UPX_RSIZE_MAX); mem_size_assert(ElementSize, n); // check size #if 0 // just for testing @@ -412,7 +412,7 @@ template void upx_std_stable_sort<16>(void *, size_t, upx_compare_func_t); template void upx_std_stable_sort<32>(void *, size_t, upx_compare_func_t); template void upx_std_stable_sort<56>(void *, size_t, upx_compare_func_t); template void upx_std_stable_sort<72>(void *, size_t, upx_compare_func_t); -#endif +#endif // UPX_CONFIG_USE_STABLE_SORT #if !defined(DOCTEST_CONFIG_DISABLE) && DEBUG #if __cplusplus >= 202002L // use C++20 std::next_permutation() to test all permutations