forked from termux/termux-elf-cleaner
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathtermux-elf-cleaner.cpp
188 lines (161 loc) · 7.05 KB
/
termux-elf-cleaner.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
#include <algorithm>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
// Include a local elf.h copy as not all platforms have it.
#include "elf.h"
#define DT_VERSYM 0x6ffffff0
#define DT_VERNEEDED 0x6ffffffe
#define DT_VERNEEDNUM 0x6fffffff
/** As only DF_1_NOW and DF_1_GLOBAL are supported on Android,
* and DF_1_NODELETE is supported after Android 6.
* so we remove it in order to reduce openjdk-9's warnings.
*/
#define DT_FLAGS_1 0x6ffffffb
/** Copy from toolchain's elf.h */
#define DF_1_NOW 0x00000001 /* Set RTLD_NOW for this object. */
#define DF_1_GLOBAL 0x00000002 /* Set RTLD_GLOBAL for this object. */
#define DF_1_NODELETE 0x00000008 /* Set RTLD_NODELETE for this object.*/
/* Copy from android source and modified. */
#define SUPPORTED_DT_FLAGS_1 (DF_1_NOW | DF_1_GLOBAL)
template<typename ElfHeaderType /*Elf{32,64}_Ehdr*/,
typename ElfSectionHeaderType /*Elf{32,64}_Shdr*/,
typename ElfDynamicSectionEntryType /* Elf{32,64}_Dyn */>
bool process_elf(uint8_t* bytes, size_t elf_file_size, char const* file_name)
{
if (sizeof(ElfSectionHeaderType) > elf_file_size) {
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);
return false;
}
ElfHeaderType* elf_hdr = reinterpret_cast<ElfHeaderType*>(bytes);
size_t last_section_header_byte = elf_hdr->e_shoff + sizeof(ElfSectionHeaderType) * elf_hdr->e_shnum;
if (last_section_header_byte > elf_file_size) {
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);
return false;
}
ElfSectionHeaderType* section_header_table = reinterpret_cast<ElfSectionHeaderType*>(bytes + elf_hdr->e_shoff);
for (unsigned int i = 1; i < elf_hdr->e_shnum; i++) {
ElfSectionHeaderType* section_header_entry = section_header_table + i;
if (section_header_entry->sh_type == SHT_DYNAMIC) {
size_t const last_dynamic_section_byte = section_header_entry->sh_offset + section_header_entry->sh_size;
if (last_dynamic_section_byte > elf_file_size) {
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);
return false;
}
size_t const dynamic_section_entries = section_header_entry->sh_size / sizeof(ElfDynamicSectionEntryType);
ElfDynamicSectionEntryType* const dynamic_section =
reinterpret_cast<ElfDynamicSectionEntryType*>(bytes + section_header_entry->sh_offset);
unsigned int last_nonnull_entry_idx = 0;
for (unsigned int j = dynamic_section_entries - 1; j > 0; j--) {
ElfDynamicSectionEntryType* dynamic_section_entry = dynamic_section + j;
if (dynamic_section_entry->d_tag != DT_NULL) {
last_nonnull_entry_idx = j;
break;
}
}
for (unsigned int j = 0; j < dynamic_section_entries; j++) {
ElfDynamicSectionEntryType* dynamic_section_entry = dynamic_section + j;
char const* removed_name = nullptr;
switch (dynamic_section_entry->d_tag) {
case DT_VERSYM: removed_name = "DT_VERSYM"; break;
case DT_VERNEEDED: removed_name = "DT_VERNEEDED"; break;
case DT_VERNEEDNUM: removed_name = "DT_VERNEEDNUM"; break;
case DT_VERDEF: removed_name = "DT_VERDEF"; break;
case DT_VERDEFNUM: removed_name = "DT_VERDEFNUM"; break;
case DT_RPATH: removed_name = "DT_RPATH"; break;
case DT_RUNPATH: removed_name = "DT_RUNPATH"; break;
}
if (removed_name != nullptr) {
printf("termux-elf-cleaner: Removing the %s dynamic section entry from '%s'\n", removed_name, file_name);
// Tag the entry with DT_NULL and put it last:
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--]);
} else if (dynamic_section_entry->d_tag == DT_FLAGS_1) {
/**
* Modified by Kiva.
* Remove unsupported DF_1_FLAGS in DT_FLAGS_1 tag.
*/
if ((dynamic_section_entry->d_un.d_val & ~SUPPORTED_DT_FLAGS_1) != 0) {
decltype(dynamic_section_entry->d_un.d_val) new_d_val = 0;
if ((dynamic_section_entry->d_un.d_val & DF_1_NOW) == DF_1_NOW) {
new_d_val |= DF_1_NOW;
}
if ((dynamic_section_entry->d_un.d_val & DF_1_GLOBAL) == DF_1_GLOBAL) {
new_d_val |= DF_1_GLOBAL;
}
if (new_d_val != dynamic_section_entry->d_un.d_val) {
printf("termux-elf-cleaner: Replacing the unsupported DF_1_FLAGS %p with %p from '%s'\n",
dynamic_section_entry->d_un.d_val, new_d_val, file_name);
dynamic_section_entry->d_un.d_val = new_d_val;
}
}
}
}
} else if (section_header_entry->sh_type == SHT_GNU_verdef ||
section_header_entry->sh_type == SHT_GNU_verneed ||
section_header_entry->sh_type == SHT_GNU_versym) {
printf("termux-elf-cleaner: Removing version section from '%s'\n", file_name);
section_header_entry->sh_type = SHT_NULL;
}
}
return true;
}
int main(int argc, char const** argv)
{
if (argc < 2 || (argc == 2 && strcmp(argv[1], "-h")==0)) {
fprintf(stderr, "usage: %s <filenames>\n", argv[0]);
fprintf(stderr, "\nProcesses ELF files to remove unsupported section types \n"
"and dynamic section entries which the Android linker warns about.\n");
return 1;
}
for (int i = 1; i < argc; i++) {
char const* file_name = argv[i];
int fd = open(file_name, O_RDWR);
if (fd < 0) {
char* error_message;
if (asprintf(&error_message, "open(\"%s\")", file_name) == -1) error_message = (char*) "open()";
perror(error_message);
return 1;
}
struct stat st;
if (fstat(fd, &st) < 0) { perror("fstat()"); return 1; }
if (st.st_size < (long long) sizeof(Elf32_Ehdr)) {
close(fd);
continue;
}
void* mem = mmap(0, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (mem == MAP_FAILED) { perror("mmap()"); return 1; }
uint8_t* bytes = reinterpret_cast<uint8_t*>(mem);
if (!(bytes[0] == 0x7F && bytes[1] == 'E' && bytes[2] == 'L' && bytes[3] == 'F')) {
// Not the ELF magic number.
munmap(mem, st.st_size);
close(fd);
continue;
}
if (bytes[/*EI_DATA*/5] != 1) {
fprintf(stderr, "termux-elf-cleaner: Not little endianness in '%s'\n", file_name);
munmap(mem, st.st_size);
close(fd);
continue;
}
uint8_t const bit_value = bytes[/*EI_CLASS*/4];
if (bit_value == 1) {
if (!process_elf<Elf32_Ehdr, Elf32_Shdr, Elf32_Dyn>(bytes, st.st_size, file_name)) return 1;
} else if (bit_value == 2) {
if (!process_elf<Elf64_Ehdr, Elf64_Shdr, Elf64_Dyn>(bytes, st.st_size, file_name)) return 1;
} else {
printf("termux-elf-cleaner: Incorrect bit value %d in '%s'\n", bit_value, file_name);
return 1;
}
if (msync(mem, st.st_size, MS_SYNC) < 0) { perror("msync()"); return 1; }
munmap(mem, st.st_size);
close(fd);
}
return 0;
}