blob: 7ee8d3cae1b3d7e139e8549a37b11a7b3bebe6e5 [file] [log] [blame]
Brian Carlstrom6a47b9d2013-05-17 10:58:25 -07001/*
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 <vector>
20
21#include <llvm/Support/ELF.h>
22
23#include "UniquePtr.h"
24#include "base/logging.h"
25#include "elf_file.h"
26#include "utils.h"
27
28namespace art {
29
Ian Rogers8d31bbd2013-10-13 10:44:14 -070030bool ElfStripper::Strip(File* file, std::string* error_msg) {
31 UniquePtr<ElfFile> elf_file(ElfFile::Open(file, true, false, error_msg));
32 if (elf_file.get() == nullptr) {
33 return false;
34 }
Brian Carlstrom6a47b9d2013-05-17 10:58:25 -070035
36 // ELF files produced by MCLinker look roughly like this
37 //
38 // +------------+
39 // | Elf32_Ehdr | contains number of Elf32_Shdr and offset to first
40 // +------------+
41 // | Elf32_Phdr | program headers
42 // | Elf32_Phdr |
43 // | ... |
44 // | Elf32_Phdr |
45 // +------------+
46 // | section | mixture of needed and unneeded sections
47 // +------------+
48 // | section |
49 // +------------+
50 // | ... |
51 // +------------+
52 // | section |
53 // +------------+
54 // | Elf32_Shdr | section headers
55 // | Elf32_Shdr |
56 // | ... | contains offset to section start
57 // | Elf32_Shdr |
58 // +------------+
59 //
60 // To strip:
61 // - leave the Elf32_Ehdr and Elf32_Phdr values in place.
62 // - walk the sections making a new set of Elf32_Shdr section headers for what we want to keep
63 // - move the sections are keeping up to fill in gaps of sections we want to strip
64 // - write new Elf32_Shdr section headers to end of file, updating Elf32_Ehdr
65 // - truncate rest of file
66 //
67
68 std::vector<llvm::ELF::Elf32_Shdr> section_headers;
69 std::vector<llvm::ELF::Elf32_Word> section_headers_original_indexes;
70 section_headers.reserve(elf_file->GetSectionHeaderNum());
71
72
73 llvm::ELF::Elf32_Shdr& string_section = elf_file->GetSectionNameStringSection();
74 for (llvm::ELF::Elf32_Word i = 0; i < elf_file->GetSectionHeaderNum(); i++) {
75 llvm::ELF::Elf32_Shdr& sh = elf_file->GetSectionHeader(i);
76 const char* name = elf_file->GetString(string_section, sh.sh_name);
77 if (name == NULL) {
78 CHECK_EQ(0U, i);
79 section_headers.push_back(sh);
80 section_headers_original_indexes.push_back(0);
81 continue;
82 }
83 if (StartsWith(name, ".debug")
84 || (strcmp(name, ".strtab") == 0)
85 || (strcmp(name, ".symtab") == 0)) {
86 continue;
87 }
88 section_headers.push_back(sh);
89 section_headers_original_indexes.push_back(i);
90 }
91 CHECK_NE(0U, section_headers.size());
92 CHECK_EQ(section_headers.size(), section_headers_original_indexes.size());
93
94 // section 0 is the NULL section, sections start at offset of first section
95 llvm::ELF::Elf32_Off offset = elf_file->GetSectionHeader(1).sh_offset;
96 for (size_t i = 1; i < section_headers.size(); i++) {
97 llvm::ELF::Elf32_Shdr& new_sh = section_headers[i];
98 llvm::ELF::Elf32_Shdr& old_sh = elf_file->GetSectionHeader(section_headers_original_indexes[i]);
99 CHECK_EQ(new_sh.sh_name, old_sh.sh_name);
100 if (old_sh.sh_addralign > 1) {
101 offset = RoundUp(offset, old_sh.sh_addralign);
102 }
103 if (old_sh.sh_offset == offset) {
104 // already in place
105 offset += old_sh.sh_size;
106 continue;
107 }
108 // shift section earlier
109 memmove(elf_file->Begin() + offset,
110 elf_file->Begin() + old_sh.sh_offset,
111 old_sh.sh_size);
112 new_sh.sh_offset = offset;
113 offset += old_sh.sh_size;
114 }
115
116 llvm::ELF::Elf32_Off shoff = offset;
117 size_t section_headers_size_in_bytes = section_headers.size() * sizeof(llvm::ELF::Elf32_Shdr);
118 memcpy(elf_file->Begin() + offset, &section_headers[0], section_headers_size_in_bytes);
119 offset += section_headers_size_in_bytes;
120
121 elf_file->GetHeader().e_shnum = section_headers.size();
122 elf_file->GetHeader().e_shoff = shoff;
123 int result = ftruncate(file->Fd(), offset);
124 if (result != 0) {
Ian Rogers8d31bbd2013-10-13 10:44:14 -0700125 *error_msg = StringPrintf("Failed to truncate while stripping ELF file: '%s': %s",
126 file->GetPath().c_str(), strerror(errno));
Brian Carlstrom6a47b9d2013-05-17 10:58:25 -0700127 return false;
128 }
129 return true;
130}
131
132} // namespace art