detect-hardlinks.sh: Show which files are hard links
[termux-packages] / packages / termux-tools / termux-elf-cleaner.cpp
CommitLineData
59f0d218
FF
1#include <algorithm>
2#include <fcntl.h>
3#include <stdio.h>
4#include <stdlib.h>
5#include <string.h>
6#include <sys/mman.h>
7#include <sys/stat.h>
8#include <sys/types.h>
9#include <unistd.h>
10
11#ifdef __APPLE__
12# include "elf.h"
13#else
14# include <elf.h>
15#endif
16
17#define DT_VERNEEDED 0x6ffffffe
18#define DT_VERNEEDNUM 0x6fffffff
19
20template<typename ElfHeaderType /*Elf{32,64}_Ehdr*/,
21 typename ElfSectionHeaderType /*Elf{32,64}_Shdr*/,
22 typename ElfDynamicSectionEntryType /* Elf{32,64}_Dyn */>
009c8d94 23bool process_elf(uint8_t* bytes, size_t elf_file_size, char const* file_name)
59f0d218
FF
24{
25 if (sizeof(ElfSectionHeaderType) > elf_file_size) {
009c8d94 26 fprintf(stderr, "termux-elf-cleaner: Elf header for '%s' would end at %zu but file size only %zu\n", file_name, sizeof(ElfSectionHeaderType), elf_file_size);
59f0d218
FF
27 return false;
28 }
29 ElfHeaderType* elf_hdr = reinterpret_cast<ElfHeaderType*>(bytes);
30
31 size_t last_section_header_byte = elf_hdr->e_shoff + sizeof(ElfSectionHeaderType) * elf_hdr->e_shnum;
32 if (last_section_header_byte > elf_file_size) {
009c8d94 33 fprintf(stderr, "termux-elf-cleaner: Section header for '%s' would end at %zu but file size only %zu\n", file_name, last_section_header_byte, elf_file_size);
59f0d218
FF
34 return false;
35 }
36 ElfSectionHeaderType* section_header_table = reinterpret_cast<ElfSectionHeaderType*>(bytes + elf_hdr->e_shoff);
37
38 for (unsigned int i = 1; i < elf_hdr->e_shnum; i++) {
39 ElfSectionHeaderType* section_header_entry = section_header_table + i;
40 if (section_header_entry->sh_type == SHT_DYNAMIC) {
41 size_t const last_dynamic_section_byte = section_header_entry->sh_offset + section_header_entry->sh_size;
42 if (last_dynamic_section_byte > elf_file_size) {
009c8d94 43 fprintf(stderr, "termux-elf-cleaner: Dynamic section for '%s' would end at %zu but file size only %zu\n", file_name, last_dynamic_section_byte, elf_file_size);
59f0d218
FF
44 return false;
45 }
46
47 size_t const dynamic_section_entries = section_header_entry->sh_size / sizeof(ElfDynamicSectionEntryType);
48 ElfDynamicSectionEntryType* const dynamic_section =
49 reinterpret_cast<ElfDynamicSectionEntryType*>(bytes + section_header_entry->sh_offset);
50
51 unsigned int last_nonnull_entry_idx = 0;
52 for (unsigned int j = dynamic_section_entries - 1; j > 0; j--) {
53 ElfDynamicSectionEntryType* dynamic_section_entry = dynamic_section + j;
54 if (dynamic_section_entry->d_tag != DT_NULL) {
55 last_nonnull_entry_idx = j;
56 break;
57 }
58 }
59
60 for (unsigned int j = 0; j < dynamic_section_entries; j++) {
61 ElfDynamicSectionEntryType* dynamic_section_entry = dynamic_section + j;
3ca4ba78 62 char const* removed_name = nullptr;
59f0d218 63 switch (dynamic_section_entry->d_tag) {
3ca4ba78
FF
64 case DT_VERNEEDED: removed_name = "DT_VERNEEDED"; break;
65 case DT_VERNEEDNUM: removed_name = "DT_VERNEEDNUM"; break;
66 case DT_RPATH: removed_name = "DT_RPATH"; break;
67 case DT_RUNPATH: removed_name = "DT_RUNPATH"; break;
68 }
69 if (removed_name != nullptr) {
009c8d94 70 printf("termux-elf-cleaner: Removing the %s dynamic section entry from '%s'\n", removed_name, file_name);
3ca4ba78
FF
71 // Tag the entry with DT_NULL and put it last:
72 dynamic_section_entry->d_tag = DT_NULL;
73 // Decrease j to process new entry index:
74 std::swap(dynamic_section[j--], dynamic_section[last_nonnull_entry_idx--]);
59f0d218
FF
75 }
76 }
77 }
78 }
79 return true;
80}
81
82
009c8d94 83int main(int argc, char const** argv)
59f0d218
FF
84{
85 if (argc < 2 || (argc == 2 && strcmp(argv[1], "-h")==0)) {
86 fprintf(stderr, "usage: %s <filename>\n", argv[0]);
87 fprintf(stderr, " Processes ELF files to remove DT_VERNEEDED, DT_VERNEEDNUM, DT_RPATH\n"
88 " and DT_RUNPATH entries (which the Android linker warns about)\n");
89 return 1;
90 }
91
92 for (int i = 1; i < argc; i++) {
009c8d94
FF
93 char const* file_name = argv[i];
94 int fd = open(file_name, O_RDWR);
360e5c42
FF
95 if (fd < 0) {
96 char* error_message;
97 if (asprintf(&error_message, "open(\"%s\")", file_name) == -1) error_message = (char*) "open()";
98 perror(error_message);
99 return 1;
100 }
59f0d218
FF
101
102 struct stat st;
103 if (fstat(fd, &st) < 0) { perror("fstat()"); return 1; }
104
360e5c42
FF
105 if (st.st_size < (long long) sizeof(Elf32_Ehdr)) {
106 close(fd);
107 continue;
108 }
59f0d218
FF
109
110 void* mem = mmap(0, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
111 if (mem == MAP_FAILED) { perror("mmap()"); return 1; }
112
113 uint8_t* bytes = reinterpret_cast<uint8_t*>(mem);
114 if (!(bytes[0] == 0x7F && bytes[1] == 'E' && bytes[2] == 'L' && bytes[3] == 'F')) {
009c8d94 115 // Not the ELF magic number.
59f0d218
FF
116 munmap(mem, st.st_size);
117 close(fd);
118 continue;
119 }
120
009c8d94
FF
121 if (bytes[/*EI_DATA*/5] != 1) {
122 fprintf(stderr, "termux-elf-cleaner: Not little endianness in '%s'\n", file_name);
360e5c42
FF
123 munmap(mem, st.st_size);
124 close(fd);
009c8d94
FF
125 continue;
126 }
127
128 uint8_t const bit_value = bytes[/*EI_CLASS*/4];
129 if (bit_value == 1) {
130 if (!process_elf<Elf32_Ehdr, Elf32_Shdr, Elf32_Dyn>(bytes, st.st_size, file_name)) return 1;
131 } else if (bit_value == 2) {
132 if (!process_elf<Elf64_Ehdr, Elf64_Shdr, Elf64_Dyn>(bytes, st.st_size, file_name)) return 1;
59f0d218 133 } else {
009c8d94
FF
134 printf("termux-elf-cleaner: Incorrect bit value %d in '%s'\n", bit_value, file_name);
135 return 1;
59f0d218
FF
136 }
137
138 if (msync(mem, st.st_size, MS_SYNC) < 0) { perror("msync()"); return 1; }
139
140 munmap(mem, st.st_size);
141 close(fd);
142 }
143 return 0;
144}
145