1 /*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "elf_stripper.h"
18
19 #include <unistd.h>
20 #include <sys/types.h>
21 #include <memory>
22 #include <vector>
23
24 #include "base/logging.h"
25 #include "base/stringprintf.h"
26 #include "elf_file.h"
27 #include "elf_utils.h"
28 #include "utils.h"
29
30 namespace art {
31
Strip(File * file,std::string * error_msg)32 bool ElfStripper::Strip(File* file, std::string* error_msg) {
33 std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file, true, false, error_msg));
34 if (elf_file.get() == nullptr) {
35 return false;
36 }
37
38 // ELF files produced by MCLinker look roughly like this
39 //
40 // +------------+
41 // | Elf32_Ehdr | contains number of Elf32_Shdr and offset to first
42 // +------------+
43 // | Elf32_Phdr | program headers
44 // | Elf32_Phdr |
45 // | ... |
46 // | Elf32_Phdr |
47 // +------------+
48 // | section | mixture of needed and unneeded sections
49 // +------------+
50 // | section |
51 // +------------+
52 // | ... |
53 // +------------+
54 // | section |
55 // +------------+
56 // | Elf32_Shdr | section headers
57 // | Elf32_Shdr |
58 // | ... | contains offset to section start
59 // | Elf32_Shdr |
60 // +------------+
61 //
62 // To strip:
63 // - leave the Elf32_Ehdr and Elf32_Phdr values in place.
64 // - walk the sections making a new set of Elf32_Shdr section headers for what we want to keep
65 // - move the sections are keeping up to fill in gaps of sections we want to strip
66 // - write new Elf32_Shdr section headers to end of file, updating Elf32_Ehdr
67 // - truncate rest of file
68 //
69
70 std::vector<Elf32_Shdr> section_headers;
71 std::vector<Elf32_Word> section_headers_original_indexes;
72 section_headers.reserve(elf_file->GetSectionHeaderNum());
73
74
75 Elf32_Shdr* string_section = elf_file->GetSectionNameStringSection();
76 CHECK(string_section != nullptr);
77 for (Elf32_Word i = 0; i < elf_file->GetSectionHeaderNum(); i++) {
78 Elf32_Shdr* sh = elf_file->GetSectionHeader(i);
79 CHECK(sh != nullptr);
80 const char* name = elf_file->GetString(*string_section, sh->sh_name);
81 if (name == nullptr) {
82 CHECK_EQ(0U, i);
83 section_headers.push_back(*sh);
84 section_headers_original_indexes.push_back(0);
85 continue;
86 }
87 if (StartsWith(name, ".debug")
88 || (strcmp(name, ".strtab") == 0)
89 || (strcmp(name, ".symtab") == 0)) {
90 continue;
91 }
92 section_headers.push_back(*sh);
93 section_headers_original_indexes.push_back(i);
94 }
95 CHECK_NE(0U, section_headers.size());
96 CHECK_EQ(section_headers.size(), section_headers_original_indexes.size());
97
98 // section 0 is the NULL section, sections start at offset of first section
99 CHECK(elf_file->GetSectionHeader(1) != nullptr);
100 Elf32_Off offset = elf_file->GetSectionHeader(1)->sh_offset;
101 for (size_t i = 1; i < section_headers.size(); i++) {
102 Elf32_Shdr& new_sh = section_headers[i];
103 Elf32_Shdr* old_sh = elf_file->GetSectionHeader(section_headers_original_indexes[i]);
104 CHECK(old_sh != nullptr);
105 CHECK_EQ(new_sh.sh_name, old_sh->sh_name);
106 if (old_sh->sh_addralign > 1) {
107 offset = RoundUp(offset, old_sh->sh_addralign);
108 }
109 if (old_sh->sh_offset == offset) {
110 // already in place
111 offset += old_sh->sh_size;
112 continue;
113 }
114 // shift section earlier
115 memmove(elf_file->Begin() + offset,
116 elf_file->Begin() + old_sh->sh_offset,
117 old_sh->sh_size);
118 new_sh.sh_offset = offset;
119 offset += old_sh->sh_size;
120 }
121
122 Elf32_Off shoff = offset;
123 size_t section_headers_size_in_bytes = section_headers.size() * sizeof(Elf32_Shdr);
124 memcpy(elf_file->Begin() + offset, §ion_headers[0], section_headers_size_in_bytes);
125 offset += section_headers_size_in_bytes;
126
127 elf_file->GetHeader().e_shnum = section_headers.size();
128 elf_file->GetHeader().e_shoff = shoff;
129 int result = ftruncate(file->Fd(), offset);
130 if (result != 0) {
131 *error_msg = StringPrintf("Failed to truncate while stripping ELF file: '%s': %s",
132 file->GetPath().c_str(), strerror(errno));
133 return false;
134 }
135 return true;
136 }
137
138 } // namespace art
139