17 #define DT_VERNEEDED 0x6ffffffe
18 #define DT_VERNEEDNUM 0x6fffffff
20 template<typename ElfHeaderType
/*Elf{32,64}_Ehdr*/,
21 typename ElfSectionHeaderType
/*Elf{32,64}_Shdr*/,
22 typename ElfDynamicSectionEntryType
/* Elf{32,64}_Dyn */>
23 bool process_elf(uint8_t* bytes
, size_t elf_file_size
, char const* file_name
)
25 if (sizeof(ElfSectionHeaderType
) > elf_file_size
) {
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
);
29 ElfHeaderType
* elf_hdr
= reinterpret_cast<ElfHeaderType
*>(bytes
);
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
) {
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
);
36 ElfSectionHeaderType
* section_header_table
= reinterpret_cast<ElfSectionHeaderType
*>(bytes
+ elf_hdr
->e_shoff
);
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
) {
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
);
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
);
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
;
60 for (unsigned int j
= 0; j
< dynamic_section_entries
; j
++) {
61 ElfDynamicSectionEntryType
* dynamic_section_entry
= dynamic_section
+ j
;
62 char const* removed_name
= nullptr;
63 switch (dynamic_section_entry
->d_tag
) {
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;
69 if (removed_name
!= nullptr) {
70 printf("termux-elf-cleaner: Removing the %s dynamic section entry from '%s'\n", removed_name
, file_name
);
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
--]);
83 int main(int argc
, char const** argv
)
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");
92 for (int i
= 1; i
< argc
; i
++) {
93 char const* file_name
= argv
[i
];
94 int fd
= open(file_name
, O_RDWR
);
97 if (asprintf(&error_message
, "open(\"%s\")", file_name
) == -1) error_message
= (char*) "open()";
98 perror(error_message
);
103 if (fstat(fd
, &st
) < 0) { perror("fstat()"); return 1; }
105 if (st
.st_size
< (long long) sizeof(Elf32_Ehdr
)) {
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; }
113 uint8_t* bytes
= reinterpret_cast<uint8_t*>(mem
);
114 if (!(bytes
[0] == 0x7F && bytes
[1] == 'E' && bytes
[2] == 'L' && bytes
[3] == 'F')) {
115 // Not the ELF magic number.
116 munmap(mem
, st
.st_size
);
121 if (bytes
[/*EI_DATA*/5] != 1) {
122 fprintf(stderr
, "termux-elf-cleaner: Not little endianness in '%s'\n", file_name
);
123 munmap(mem
, st
.st_size
);
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;
134 printf("termux-elf-cleaner: Incorrect bit value %d in '%s'\n", bit_value
, file_name
);
138 if (msync(mem
, st
.st_size
, MS_SYNC
) < 0) { perror("msync()"); return 1; }
140 munmap(mem
, st
.st_size
);