chiark / gitweb /
vim: Use sensible.vim as system vimrc
[termux-packages] / packages / termux-elf-cleaner / termux-elf-cleaner.cpp
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_VERSYM 0x6ffffff0
18 #define DT_VERNEEDED 0x6ffffffe
19 #define DT_VERNEEDNUM 0x6fffffff
20
21 template<typename ElfHeaderType /*Elf{32,64}_Ehdr*/,
22          typename ElfSectionHeaderType /*Elf{32,64}_Shdr*/,
23          typename ElfDynamicSectionEntryType /* Elf{32,64}_Dyn */>
24 bool process_elf(uint8_t* bytes, size_t elf_file_size, char const* file_name)
25 {
26         if (sizeof(ElfSectionHeaderType) > elf_file_size) {
27                 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);
28                 return false;
29         }
30         ElfHeaderType* elf_hdr = reinterpret_cast<ElfHeaderType*>(bytes);
31
32         size_t last_section_header_byte = elf_hdr->e_shoff + sizeof(ElfSectionHeaderType) * elf_hdr->e_shnum;
33         if (last_section_header_byte > elf_file_size) {
34                 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);
35                 return false;
36         }
37         ElfSectionHeaderType* section_header_table = reinterpret_cast<ElfSectionHeaderType*>(bytes + elf_hdr->e_shoff);
38
39         for (unsigned int i = 1; i < elf_hdr->e_shnum; i++) {
40                 ElfSectionHeaderType* section_header_entry = section_header_table + i;
41                 if (section_header_entry->sh_type == SHT_DYNAMIC) {
42                         size_t const last_dynamic_section_byte = section_header_entry->sh_offset + section_header_entry->sh_size;
43                         if (last_dynamic_section_byte > elf_file_size) {
44                                 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);
45                                 return false;
46                         }
47
48                         size_t const dynamic_section_entries = section_header_entry->sh_size / sizeof(ElfDynamicSectionEntryType);
49                         ElfDynamicSectionEntryType* const dynamic_section =
50                                 reinterpret_cast<ElfDynamicSectionEntryType*>(bytes + section_header_entry->sh_offset);
51
52                         unsigned int last_nonnull_entry_idx = 0;
53                         for (unsigned int j = dynamic_section_entries - 1; j > 0; j--) {
54                                 ElfDynamicSectionEntryType* dynamic_section_entry = dynamic_section + j;
55                                 if (dynamic_section_entry->d_tag != DT_NULL) {
56                                         last_nonnull_entry_idx = j;
57                                         break;
58                                 }
59                         }
60
61                         for (unsigned int j = 0; j < dynamic_section_entries; j++) {
62                                 ElfDynamicSectionEntryType* dynamic_section_entry = dynamic_section + j;
63                                 char const* removed_name = nullptr;
64                                 switch (dynamic_section_entry->d_tag) {
65                                         case DT_VERSYM: removed_name = "DT_VERSYM"; break;
66                                         case DT_VERNEEDED: removed_name = "DT_VERNEEDED"; break;
67                                         case DT_VERNEEDNUM: removed_name = "DT_VERNEEDNUM"; break;
68                                         case DT_VERDEF: removed_name = "DT_VERDEF"; break;
69                                         case DT_VERDEFNUM: removed_name = "DT_VERDEFNUM"; break;
70                                         case DT_RPATH: removed_name = "DT_RPATH"; break;
71                                         case DT_RUNPATH: removed_name = "DT_RUNPATH"; break;
72                                 }
73                                 if (removed_name != nullptr) {
74                                         printf("termux-elf-cleaner: Removing the %s dynamic section entry from '%s'\n", removed_name, file_name);
75                                         // Tag the entry with DT_NULL and put it last:
76                                         dynamic_section_entry->d_tag = DT_NULL;
77                                         // Decrease j to process new entry index:
78                                         std::swap(dynamic_section[j--], dynamic_section[last_nonnull_entry_idx--]);
79                                 }
80                         }
81                 } else if (section_header_entry->sh_type == SHT_GNU_verdef ||
82                            section_header_entry->sh_type == SHT_GNU_verneed ||
83                            section_header_entry->sh_type == SHT_GNU_versym) {
84                         printf("termux-elf-cleaner: Removing version section from '%s'\n", file_name);
85                         section_header_entry->sh_type = SHT_NULL;
86                 }
87         }
88         return true;
89 }
90
91
92 int main(int argc, char const** argv)
93 {
94         if (argc < 2 || (argc == 2 && strcmp(argv[1], "-h")==0)) {
95                 fprintf(stderr, "usage: %s <filename>\n", argv[0]);
96                 fprintf(stderr, "       Processes ELF files to remove DT_VERNEEDED, DT_VERNEEDNUM, DT_RPATH\n"
97                                 "       and DT_RUNPATH entries (which the Android linker warns about)\n");
98                 return 1;
99         }
100
101         for (int i = 1; i < argc; i++) {
102                 char const* file_name = argv[i];
103                 int fd = open(file_name, O_RDWR);
104                 if (fd < 0) {
105                         char* error_message;
106                         if (asprintf(&error_message, "open(\"%s\")", file_name) == -1) error_message = (char*) "open()";
107                         perror(error_message);
108                         return 1;
109                 }
110
111                 struct stat st;
112                 if (fstat(fd, &st) < 0) { perror("fstat()"); return 1; }
113
114                 if (st.st_size < (long long) sizeof(Elf32_Ehdr)) {
115                         close(fd);
116                         continue;
117                 }
118
119                 void* mem = mmap(0, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
120                 if (mem == MAP_FAILED) { perror("mmap()"); return 1; }
121
122                 uint8_t* bytes = reinterpret_cast<uint8_t*>(mem);
123                 if (!(bytes[0] == 0x7F && bytes[1] == 'E' && bytes[2] == 'L' && bytes[3] == 'F')) {
124                         // Not the ELF magic number.
125                         munmap(mem, st.st_size);
126                         close(fd);
127                         continue;
128                 }
129
130                 if (bytes[/*EI_DATA*/5] != 1) {
131                         fprintf(stderr, "termux-elf-cleaner: Not little endianness in '%s'\n", file_name);
132                         munmap(mem, st.st_size);
133                         close(fd);
134                         continue;
135                 }
136
137                 uint8_t const bit_value = bytes[/*EI_CLASS*/4];
138                 if (bit_value == 1) {
139                         if (!process_elf<Elf32_Ehdr, Elf32_Shdr, Elf32_Dyn>(bytes, st.st_size, file_name)) return 1;
140                 } else if (bit_value == 2) {
141                         if (!process_elf<Elf64_Ehdr, Elf64_Shdr, Elf64_Dyn>(bytes, st.st_size, file_name)) return 1;
142                 } else {
143                         printf("termux-elf-cleaner: Incorrect bit value %d in '%s'\n", bit_value, file_name);
144                         return 1;
145                 }
146
147                 if (msync(mem, st.st_size, MS_SYNC) < 0) { perror("msync()"); return 1; }
148
149                 munmap(mem, st.st_size);
150                 close(fd);
151         }
152         return 0;
153 }
154