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