17 #define DT_VERSYM 0x6ffffff0
18 #define DT_VERNEEDED 0x6ffffffe
19 #define DT_VERNEEDNUM 0x6fffffff
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
)
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
);
30 ElfHeaderType
* elf_hdr
= reinterpret_cast<ElfHeaderType
*>(bytes
);
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
);
37 ElfSectionHeaderType
* section_header_table
= reinterpret_cast<ElfSectionHeaderType
*>(bytes
+ elf_hdr
->e_shoff
);
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
);
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
);
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
;
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;
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
--]);
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
;
92 int main(int argc
, char const** argv
)
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");
101 for (int i
= 1; i
< argc
; i
++) {
102 char const* file_name
= argv
[i
];
103 int fd
= open(file_name
, O_RDWR
);
106 if (asprintf(&error_message
, "open(\"%s\")", file_name
) == -1) error_message
= (char*) "open()";
107 perror(error_message
);
112 if (fstat(fd
, &st
) < 0) { perror("fstat()"); return 1; }
114 if (st
.st_size
< (long long) sizeof(Elf32_Ehdr
)) {
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; }
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
);
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
);
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;
143 printf("termux-elf-cleaner: Incorrect bit value %d in '%s'\n", bit_value
, file_name
);
147 if (msync(mem
, st
.st_size
, MS_SYNC
) < 0) { perror("msync()"); return 1; }
149 munmap(mem
, st
.st_size
);