diff --git a/CMakeLists.txt b/CMakeLists.txt index bec1258..0e64815 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,7 +22,6 @@ set(PACKAGE_NAME "termux-elf-cleaner" CACHE STRING "Name of the package") add_executable("${PACKAGE_NAME}" elf-cleaner.cpp - arghandling.c ) target_compile_definitions("${PACKAGE_NAME}" diff --git a/arghandling.c b/arghandling.c deleted file mode 100644 index f4f7f18..0000000 --- a/arghandling.c +++ /dev/null @@ -1,73 +0,0 @@ -/* Based on function in emacs's emacs.c - - Copyright (C) 1985-1987, 1993-1995, 1997-1999, 2001-2022 Free Software - Foundation, Inc. */ - -#include -#include -#include -#include - -/* Test whether the next argument in ARGV matches SSTR or a prefix of - LSTR (at least MINLEN characters). If so, then if VALPTR is non-null - (the argument is supposed to have a value) store in *VALPTR either - the next argument or the portion of this one after the equal sign. - ARGV is read starting at position *SKIPPTR; this index is advanced - by the number of arguments used. - Too bad we can't just use getopt for all of this, but we don't have - enough information to do it right. */ - -bool -argmatch (char **argv, int argc, const char *sstr, const char *lstr, - int minlen, int *valptr, int *skipptr) -{ - char *p = NULL; - ptrdiff_t arglen; - char *arg; - - /* Don't access argv[argc]; give up in advance. */ - if (argc <= *skipptr + 1) - return 0; - - arg = argv[*skipptr+1]; - if (arg == NULL) - return 0; - if (strcmp (arg, sstr) == 0) - { - if (valptr != NULL) - { - *valptr = atoi(argv[*skipptr+2]); - *skipptr += 2; - } - else - *skipptr += 1; - return 1; - } - arglen = (valptr != NULL && (p = strchr (arg, '=')) != NULL - ? p - arg : strlen (arg)); - if (!lstr) - return 0; - if (arglen < minlen || strncmp (arg, lstr, arglen) != 0) - return 0; - else if (valptr == NULL) - { - *skipptr += 1; - return 1; - } - else if (p != NULL) - { - *valptr = atoi(p+1); - *skipptr += 1; - return 1; - } - else if (argv[*skipptr+2] != NULL) - { - *valptr = atoi(argv[*skipptr+2]); - *skipptr += 2; - return 1; - } - else - { - return 0; - } -} diff --git a/arghandling.h b/arghandling.h deleted file mode 100644 index 9d2fd51..0000000 --- a/arghandling.h +++ /dev/null @@ -1,5 +0,0 @@ -/* Taken from emacs */ -#define ARRAYELTS(arr) (sizeof (arr) / sizeof (arr)[0]) - -extern "C" bool -argmatch (char **, int, const char *, const char *, int, int *, int *); diff --git a/elf-cleaner.cpp b/elf-cleaner.cpp index b706639..fa0e3a3 100644 --- a/elf-cleaner.cpp +++ b/elf-cleaner.cpp @@ -24,6 +24,7 @@ along with termux-elf-cleaner. If not, see #endif #include +#include #include #include #include @@ -38,17 +39,18 @@ along with termux-elf-cleaner. If not, see #include #include -#include "arghandling.h" - // Include a local elf.h copy as not all platforms have it. #include "elf.h" +/* Taken from emacs */ +#define ARRAYELTS(arr) (sizeof (arr) / sizeof (arr)[0]) + /* Default to api level 21 unless arg --api-level given */ uint8_t supported_dt_flags_1 = (DF_1_NOW | DF_1_GLOBAL); int api_level = 21; -bool dry_run = false; -bool quiet = false; +int dry_run = 0; +int quiet = 0; static char const *const usage_message[] = { "\ @@ -160,10 +162,11 @@ bool process_elf(uint8_t* bytes, size_t elf_file_size, char const* file_name) printf("%s: Removing the %s dynamic section entry from '%s'\n", PACKAGE_NAME, removed_name, file_name); // Tag the entry with DT_NULL and put it last: - if (!dry_run) + if (!dry_run) { dynamic_section_entry->d_tag = DT_NULL; - // Decrease j to process new entry index: - std::swap(dynamic_section[j--], dynamic_section[last_nonnull_entry_idx--]); + // Decrease j to process new entry index: + std::swap(dynamic_section[j--], dynamic_section[last_nonnull_entry_idx--]); + } } else if (dynamic_section_entry->d_tag == DT_FLAGS_1) { // Remove unsupported DF_1_* flags to avoid linker warnings. decltype(dynamic_section_entry->d_un.d_val) orig_d_val = @@ -182,14 +185,25 @@ bool process_elf(uint8_t* bytes, size_t elf_file_size, char const* file_name) } } } - } - else if (api_level < 23 && + } else if (api_level < 23 && (section_header_entry->sh_type == SHT_GNU_verdef || section_header_entry->sh_type == SHT_GNU_verneed || section_header_entry->sh_type == SHT_GNU_versym)) { if (!quiet) - printf("%s: Removing version section from '%s'\n", - PACKAGE_NAME, file_name); + switch (section_header_entry->sh_type) { + case SHT_GNU_verdef: + printf("%s: Removing VERDEF section from '%s'\n", + PACKAGE_NAME, file_name); + break; + case SHT_GNU_verneed: + printf("%s: Removing VERNEED section from '%s'\n", + PACKAGE_NAME, file_name); + break; + case SHT_GNU_versym: + printf("%s: Removing VERSYM section from '%s'\n", + PACKAGE_NAME, file_name); + break; + } if (!dry_run) section_header_entry->sh_type = SHT_NULL; } @@ -297,49 +311,77 @@ int parse_file(const char *file_name) int main(int argc, char **argv) { - int skip_args = 0; - if (argc == 1 || argmatch(argv, argc, "-help", "--help", 3, NULL, &skip_args)) { + int c; + int options_index = 0; + int threads_count = std::thread::hardware_concurrency(); + + static struct option options[] = { + {"api-level", required_argument, NULL, 'a'}, + {"dry-run", no_argument, &dry_run, 1}, + {"jobs", required_argument, NULL, 'j'}, + {"quiet", no_argument, &quiet, 1}, + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'v'}, + {0, 0, 0, 0} + }; + + while (true) + { + c = getopt_long(argc, argv, "hva:dqj:", + options, &options_index); + + if (c == -1) + break; + + switch (c) { + case 'a': + api_level = atoi(optarg); + if (api_level <= 0) + api_level = 21; + break; + case 'j': + threads_count = atoi(optarg); + if (threads_count < 1) + threads_count = 1; + break; + case 'v': + printf("%s %s\n", PACKAGE_NAME, PACKAGE_VERSION); + printf(("%s\n" + "%s comes with ABSOLUTELY NO WARRANTY.\n" + "You may redistribute copies of %s\n" + "under the terms of the GNU General Public License.\n" + "For more information about these matters, " + "see the file named COPYING.\n"), + COPYRIGHT, PACKAGE_NAME, PACKAGE_NAME); + return 0; + case 'h': + printf("Usage: %s [OPTION-OR-FILENAME]...\n", argv[0]); + for (unsigned int i = 0; i < ARRAYELTS(usage_message); i++) + fputs(usage_message[i], stdout); + return 0; + } + } + + if (optind >= argc) { printf("Usage: %s [OPTION-OR-FILENAME]...\n", argv[0]); for (unsigned int i = 0; i < ARRAYELTS(usage_message); i++) fputs(usage_message[i], stdout); return 0; } - if (argmatch(argv, argc, "-version", "--version", 3, NULL, &skip_args)) { - printf("%s %s\n", PACKAGE_NAME, PACKAGE_VERSION); - printf(("%s\n" - "%s comes with ABSOLUTELY NO WARRANTY.\n" - "You may redistribute copies of %s\n" - "under the terms of the GNU General Public License.\n" - "For more information about these matters, " - "see the file named COPYING.\n"), - COPYRIGHT, PACKAGE_NAME, PACKAGE_NAME); - return 0; - } - - argmatch(argv, argc, "-api-level", "--api-level", 3, &api_level, &skip_args); + int files_count = argc - (optind); + if (argc - (optind) <= threads_count) + threads_count = files_count; if (api_level >= 23) { // The supported DT_FLAGS_1 values as of Android 6.0. supported_dt_flags_1 = (DF_1_NOW | DF_1_GLOBAL | DF_1_NODELETE); } - if (argmatch(argv, argc, "-dry-run", "--dry-run", 3, NULL, &skip_args)) - dry_run = true; - - if (argmatch(argv, argc, "-quiet", "--quiet", 3, NULL, &skip_args)) - quiet = true; - - int threads_count = std::thread::hardware_concurrency(); - int files_count = argc - (skip_args + 1); - argmatch(argv, argc, "-jobs", "--jobs", 1, &threads_count, &skip_args); - if (argc - (skip_args + 1) <= threads_count) threads_count = files_count; - if (threads_count < 1) threads_count = 1; - std::vector> futures; std::counting_semaphore sem(threads_count); - for (int i = skip_args + 1; i < argc; i++) { + for (int i = optind; i < argc; i++) { sem.acquire(); const char* file = argv[i]; futures.push_back(std::async([file, &sem]() { diff --git a/tests/curl-7.83.1-aarch64-api21-cleaned b/tests/curl-7.83.1-aarch64-api21-cleaned old mode 100755 new mode 100644 diff --git a/tests/curl-7.83.1-aarch64-api24-cleaned b/tests/curl-7.83.1-aarch64-api24-cleaned old mode 100755 new mode 100644 diff --git a/tests/curl-7.83.1-aarch64-original b/tests/curl-7.83.1-aarch64-original old mode 100755 new mode 100644 diff --git a/tests/curl-7.83.1-arm-api21-cleaned b/tests/curl-7.83.1-arm-api21-cleaned old mode 100755 new mode 100644 diff --git a/tests/curl-7.83.1-arm-api24-cleaned b/tests/curl-7.83.1-arm-api24-cleaned old mode 100755 new mode 100644 diff --git a/tests/curl-7.83.1-arm-original b/tests/curl-7.83.1-arm-original old mode 100755 new mode 100644 diff --git a/tests/curl-7.83.1-i686-api21-cleaned b/tests/curl-7.83.1-i686-api21-cleaned old mode 100755 new mode 100644 diff --git a/tests/curl-7.83.1-i686-api24-cleaned b/tests/curl-7.83.1-i686-api24-cleaned old mode 100755 new mode 100644 diff --git a/tests/curl-7.83.1-i686-original b/tests/curl-7.83.1-i686-original old mode 100755 new mode 100644 diff --git a/tests/curl-7.83.1-x86_64-api21-cleaned b/tests/curl-7.83.1-x86_64-api21-cleaned old mode 100755 new mode 100644 diff --git a/tests/curl-7.83.1-x86_64-api24-cleaned b/tests/curl-7.83.1-x86_64-api24-cleaned old mode 100755 new mode 100644 diff --git a/tests/curl-7.83.1-x86_64-original b/tests/curl-7.83.1-x86_64-original old mode 100755 new mode 100644 diff --git a/tests/test-dynamic-section.sh b/tests/test-dynamic-section.sh index 44f3a83..5fded2e 100755 --- a/tests/test-dynamic-section.sh +++ b/tests/test-dynamic-section.sh @@ -2,7 +2,7 @@ set -e if [ $# != 5 ]; then - echo "Usage path/to/test-dynamic-section.sh " + echo "Usage path/to/test-dynamic-section.sh " exit 1 fi @@ -11,35 +11,37 @@ source_dir="$2" binary_name="$3" arch="$4" api="$5" +test_dir="$(dirname $1)/tests" progname="$(basename "$elf_cleaner")" basefile="$source_dir/tests/$binary_name-$arch" origfile="$basefile-original" -testfile="$basefile-api$api.test" expectedfile="$basefile-api$api-cleaned" +testfile="$(basename $origfile)" if [ "$api" = "21" ]; then - expected_logs="$progname: Removing version section from '$testfile' -$progname: Removing version section from '$testfile' -$progname: Removing the DT_RUNPATH dynamic section entry from '$testfile' -$progname: Removing the DT_VERNEEDNUM dynamic section entry from '$testfile' -$progname: Removing the DT_VERNEED dynamic section entry from '$testfile' -$progname: Removing the DT_VERSYM dynamic section entry from '$testfile' -$progname: Replacing unsupported DF_1_* flags 134217737 with 1 in '$testfile' -$progname: Removing the DT_GNU_HASH dynamic section entry from '$testfile'" + expected_logs="$progname: Removing VERSYM section from '$test_dir/$testfile' +$progname: Removing VERNEED section from '$test_dir/$testfile' +$progname: Removing the DT_RUNPATH dynamic section entry from '$test_dir/$testfile' +$progname: Removing the DT_VERNEEDNUM dynamic section entry from '$test_dir/$testfile' +$progname: Removing the DT_VERNEED dynamic section entry from '$test_dir/$testfile' +$progname: Removing the DT_VERSYM dynamic section entry from '$test_dir/$testfile' +$progname: Replacing unsupported DF_1_* flags 134217737 with 1 in '$test_dir/$testfile' +$progname: Removing the DT_GNU_HASH dynamic section entry from '$test_dir/$testfile'" elif [ "$api" = "24" ]; then - expected_logs="$progname: Replacing unsupported DF_1_* flags 134217737 with 9 in '$testfile'" + expected_logs="$progname: Replacing unsupported DF_1_* flags 134217737 with 9 in '$test_dir/$testfile'" else echo "Unknown API level $api" exit 1 fi -cp "$origfile" "$testfile" -if [ "$("$elf_cleaner" --api-level "$api" "$testfile")" != "$expected_logs" ]; then +mkdir -p "$test_dir" +cp "$origfile" "$test_dir/" +if [ "$("$elf_cleaner" --api-level "$api" "$test_dir/$testfile")" != "$expected_logs" ]; then echo "Logs do not match for $testfile" exit 1 fi -if not cmp -s "$testfile" "$expectedfile"; then +if not cmp -s "$test_dir/$testfile" "$expectedfile"; then echo "Expected and actual files differ for $testfile" exit 1 fi diff --git a/tests/test-threads.sh b/tests/test-threads.sh index 85722a5..58fee8b 100755 --- a/tests/test-threads.sh +++ b/tests/test-threads.sh @@ -2,30 +2,32 @@ set -e if [ $# != 2 ]; then - echo "Usage path/to/test-threads.sh " + echo "Usage path/to/test-threads.sh " exit 1 fi elf_cleaner="$1" source_dir="$2" +test_dir="$(dirname $1)/tests" +mkdir -p "$test_dir" for i in {1..100}; do for arch in aarch64 arm i686 x86_64; do for api in 21 24; do - cp "$source_dir/tests/curl-7.83.1-$arch-original" "$source_dir/tests/curl-8.83.1-$arch-api$api-threads-$i.test" + cp "$source_dir/tests/curl-7.83.1-$arch-original" "$test_dir/curl-7.83.1-$arch-api$api-threads-$i.test" done done done for api in 21 24; do - "$elf_cleaner" --api-level "$api" --quiet --jobs 4 "$source_dir/tests/curl-8.83.1-"*"-api$api-threads"*".test" + "$elf_cleaner" --api-level "$api" --quiet --jobs 4 "$test_dir/curl-7.83.1-"*"-api$api-threads"*".test" done for i in {1..100}; do for arch in aarch64 arm i686 x86_64; do for api in 21 24; do - if not cmp -s "$source_dir/tests/curl-7.83.1-$arch-api$api-cleaned" "$source_dir/tests/curl-8.83.1-$arch-api$api-threads-$i.test"; then - echo "Expected and actual files differ for $source_dir/tests/curl-8.83.1-$arch-threads-$i.test" + if not cmp -s "$source_dir/tests/curl-7.83.1-$arch-api$api-cleaned" "$test_dir/curl-7.83.1-$arch-api$api-threads-$i.test"; then + echo "Expected and actual files differ for curl-7.83.1-$arch" exit 1 fi done diff --git a/tests/test-tls-alignment.sh b/tests/test-tls-alignment.sh index 5bc74f7..be760c5 100755 --- a/tests/test-tls-alignment.sh +++ b/tests/test-tls-alignment.sh @@ -2,7 +2,7 @@ set -e if [ $# != 4 ]; then - echo "Usage path/to/test-dynamic-section.sh " + echo "Usage path/to/test-dynamic-section.sh " exit 1 fi @@ -14,24 +14,27 @@ arch="$4" progname="$(basename "$elf_cleaner")" basefile="$source_dir/tests/$binary_name-$arch" origfile="$basefile-original" -testfile="$basefile.test" +testfile="$(basename $basefile).test" expectedfile="$basefile-tls-aligned" +test_dir="$(dirname $1)/tests" if [ "$arch" = "aarch64" ] || [ "$arch" = "x86_64" ]; then - expected_logs="$progname: Changing TLS alignment for '$testfile' to 64, instead of 8" + expected_logs="$progname: Changing TLS alignment for '$test_dir/$testfile' to 64, instead of 8" elif [ "$arch" = "arm" ] || [ "$arch" = "i686" ]; then - expected_logs="$progname: Changing TLS alignment for '$testfile' to 32, instead of 8" + expected_logs="$progname: Changing TLS alignment for '$test_dir/$testfile' to 32, instead of 8" else echo "Unknown architecture $arch" exit 1 fi -cp "$origfile" "$testfile" -if [ "$("$elf_cleaner" "$testfile")" != "$expected_logs" ]; then +mkdir -p "$test_dir" +cp "$origfile" "$test_dir/$testfile" +# echo "$elf_cleaner" "$test_dir/$testfile" +if [ "$("$elf_cleaner" "$test_dir/$testfile")" != "$expected_logs" ]; then echo "Logs do not match for $testfile" exit 1 fi -if not cmp -s "$testfile" "$expectedfile"; then +if not cmp -s "$test_dir/$testfile" "$expectedfile"; then echo "Expected and actual files differ for $testfile" exit 1 fi diff --git a/tests/valgrind-3.19.0-aarch64-original b/tests/valgrind-3.19.0-aarch64-original old mode 100755 new mode 100644 diff --git a/tests/valgrind-3.19.0-aarch64-tls-aligned b/tests/valgrind-3.19.0-aarch64-tls-aligned old mode 100755 new mode 100644 diff --git a/tests/valgrind-3.19.0-arm-original b/tests/valgrind-3.19.0-arm-original old mode 100755 new mode 100644 diff --git a/tests/valgrind-3.19.0-arm-tls-aligned b/tests/valgrind-3.19.0-arm-tls-aligned old mode 100755 new mode 100644 diff --git a/tests/valgrind-3.19.0-i686-original b/tests/valgrind-3.19.0-i686-original old mode 100755 new mode 100644 diff --git a/tests/valgrind-3.19.0-i686-tls-aligned b/tests/valgrind-3.19.0-i686-tls-aligned old mode 100755 new mode 100644 diff --git a/tests/valgrind-3.19.0-x86_64-original b/tests/valgrind-3.19.0-x86_64-original old mode 100755 new mode 100644 diff --git a/tests/valgrind-3.19.0-x86_64-tls-aligned b/tests/valgrind-3.19.0-x86_64-tls-aligned old mode 100755 new mode 100644