2 * Copyright (C) 2015 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
25 #include <android-base/file.h>
26 #include <android-base/logging.h>
28 #pragma clang diagnostic push
29 #pragma clang diagnostic ignored "-Wunused-parameter"
31 #include <llvm/ADT/StringRef.h>
32 #include <llvm/Object/Binary.h>
33 #include <llvm/Object/ELFObjectFile.h>
34 #include <llvm/Object/ObjectFile.h>
36 #pragma clang diagnostic pop
40 #define ELF_NOTE_GNU "GNU"
41 #define NT_GNU_BUILD_ID 3
43 bool IsValidElfPath(const std::string& filename) {
44 static const char elf_magic[] = {0x7f, 'E', 'L', 'F'};
46 if (!IsRegularFile(filename)) {
49 FILE* fp = fopen(filename.c_str(), "rb");
51 if (fread(buf, 4, 1, fp) != 1) {
56 return memcmp(buf, elf_magic, 4) == 0;
59 static bool GetBuildIdFromNoteSection(const char* section, size_t section_size, BuildId* build_id) {
60 const char* p = section;
61 const char* end = p + section_size;
63 CHECK_LE(p + 12, end);
64 size_t namesz = *reinterpret_cast<const uint32_t*>(p);
66 size_t descsz = *reinterpret_cast<const uint32_t*>(p);
68 uint32_t type = *reinterpret_cast<const uint32_t*>(p);
70 namesz = ALIGN(namesz, 4);
71 descsz = ALIGN(descsz, 4);
72 CHECK_LE(p + namesz + descsz, end);
73 if ((type == NT_GNU_BUILD_ID) && (strcmp(p, ELF_NOTE_GNU) == 0)) {
74 *build_id = BuildId(p + namesz, descsz);
82 bool GetBuildIdFromNoteFile(const std::string& filename, BuildId* build_id) {
84 if (!android::base::ReadFileToString(filename, &content)) {
85 LOG(DEBUG) << "can't read note file " << filename;
88 if (GetBuildIdFromNoteSection(content.c_str(), content.size(), build_id) == false) {
89 LOG(DEBUG) << "can't read build_id from note file " << filename;
96 bool GetBuildIdFromELFFile(const llvm::object::ELFFile<ELFT>* elf, BuildId* build_id) {
97 for (auto section_iterator = elf->begin_sections(); section_iterator != elf->end_sections();
99 if (section_iterator->sh_type == llvm::ELF::SHT_NOTE) {
100 auto contents = elf->getSectionContents(&*section_iterator);
101 if (contents.getError()) {
102 LOG(DEBUG) << "read note section error";
105 if (GetBuildIdFromNoteSection(reinterpret_cast<const char*>(contents->data()),
106 contents->size(), build_id)) {
114 static bool GetBuildIdFromObjectFile(llvm::object::ObjectFile* obj, BuildId* build_id) {
116 if (auto elf = llvm::dyn_cast<llvm::object::ELF32LEObjectFile>(obj)) {
117 result = GetBuildIdFromELFFile(elf->getELFFile(), build_id);
118 } else if (auto elf = llvm::dyn_cast<llvm::object::ELF64LEObjectFile>(obj)) {
119 result = GetBuildIdFromELFFile(elf->getELFFile(), build_id);
121 LOG(ERROR) << "unknown elf format in file " << obj->getFileName().data();
125 LOG(DEBUG) << "no build id present in file " << obj->getFileName().data();
130 bool GetBuildIdFromElfFile(const std::string& filename, BuildId* build_id) {
131 if (!IsValidElfPath(filename)) {
134 auto owning_binary = llvm::object::createBinary(llvm::StringRef(filename));
135 if (owning_binary.getError()) {
136 PLOG(DEBUG) << "can't open file " << filename;
139 llvm::object::Binary* binary = owning_binary.get().getBinary();
140 auto obj = llvm::dyn_cast<llvm::object::ObjectFile>(binary);
141 if (obj == nullptr) {
142 LOG(DEBUG) << filename << " is not an object file";
145 return GetBuildIdFromObjectFile(obj, build_id);
148 bool IsArmMappingSymbol(const char* name) {
149 // Mapping symbols in arm, which are described in "ELF for ARM Architecture" and
150 // "ELF for ARM 64-bit Architecture". The regular expression to match mapping symbol
151 // is ^\$(a|d|t|x)(\..*)?$
152 return name[0] == '$' && strchr("adtx", name[1]) != nullptr && (name[2] == '\0' || name[2] == '.');
155 template <class ELFT>
156 void ParseSymbolsFromELFFile(const llvm::object::ELFFile<ELFT>* elf,
157 std::function<void(const ElfFileSymbol&)> callback) {
158 bool is_arm = (elf->getHeader()->e_machine == llvm::ELF::EM_ARM ||
159 elf->getHeader()->e_machine == llvm::ELF::EM_AARCH64);
160 auto begin = elf->begin_symbols();
161 auto end = elf->end_symbols();
163 begin = elf->begin_dynamic_symbols();
164 end = elf->end_dynamic_symbols();
166 for (; begin != end; ++begin) {
167 auto& elf_symbol = *begin;
169 ElfFileSymbol symbol;
170 memset(&symbol, '\0', sizeof(symbol));
172 auto shdr = elf->getSection(&elf_symbol);
173 if (shdr == nullptr) {
176 auto section_name = elf->getSectionName(shdr);
177 if (section_name.getError() || section_name.get().empty()) {
180 if (section_name.get() == ".text") {
181 symbol.is_in_text_section = true;
184 auto symbol_name = elf->getSymbolName(begin);
185 if (symbol_name.getError()) {
188 symbol.name = symbol_name.get();
189 if (symbol.name.empty()) {
192 symbol.vaddr = elf_symbol.st_value;
193 if ((symbol.vaddr & 1) != 0 && is_arm) {
194 // Arm sets bit 0 to mark it as thumb code, remove the flag.
197 symbol.len = elf_symbol.st_size;
198 int type = elf_symbol.getType();
199 if (type == llvm::ELF::STT_FUNC) {
200 symbol.is_func = true;
201 } else if (type == llvm::ELF::STT_NOTYPE) {
202 if (symbol.is_in_text_section) {
203 symbol.is_label = true;
205 // Remove mapping symbols in arm.
206 const char* p = (symbol.name.compare(0, linker_prefix.size(), linker_prefix) == 0)
207 ? symbol.name.c_str() + linker_prefix.size()
208 : symbol.name.c_str();
209 if (IsArmMappingSymbol(p)) {
210 symbol.is_label = false;
220 static llvm::object::ObjectFile* GetObjectFile(
221 llvm::ErrorOr<llvm::object::OwningBinary<llvm::object::Binary>>& owning_binary,
222 const std::string& filename, const BuildId& expected_build_id) {
223 if (owning_binary.getError()) {
224 PLOG(DEBUG) << "can't open file '" << filename << "'";
227 llvm::object::Binary* binary = owning_binary.get().getBinary();
228 auto obj = llvm::dyn_cast<llvm::object::ObjectFile>(binary);
229 if (obj == nullptr) {
230 LOG(DEBUG) << filename << " is not an object file";
233 if (!expected_build_id.IsEmpty()) {
234 BuildId real_build_id;
235 GetBuildIdFromObjectFile(obj, &real_build_id);
236 bool result = (expected_build_id == real_build_id);
237 LOG(DEBUG) << "check build id for \"" << filename << "\" (" << (result ? "match" : "mismatch")
238 << "): expected " << expected_build_id.ToString() << ", real "
239 << real_build_id.ToString();
247 bool ParseSymbolsFromElfFile(const std::string& filename, const BuildId& expected_build_id,
248 std::function<void(const ElfFileSymbol&)> callback) {
249 if (!IsValidElfPath(filename)) {
252 auto owning_binary = llvm::object::createBinary(llvm::StringRef(filename));
253 llvm::object::ObjectFile* obj = GetObjectFile(owning_binary, filename, expected_build_id);
254 if (obj == nullptr) {
258 if (auto elf = llvm::dyn_cast<llvm::object::ELF32LEObjectFile>(obj)) {
259 ParseSymbolsFromELFFile(elf->getELFFile(), callback);
260 } else if (auto elf = llvm::dyn_cast<llvm::object::ELF64LEObjectFile>(obj)) {
261 ParseSymbolsFromELFFile(elf->getELFFile(), callback);
263 LOG(ERROR) << "unknown elf format in file" << filename;
269 template <class ELFT>
270 bool ReadMinExecutableVirtualAddress(const llvm::object::ELFFile<ELFT>* elf, uint64_t* p_vaddr) {
271 bool has_vaddr = false;
272 uint64_t min_addr = std::numeric_limits<uint64_t>::max();
273 for (auto it = elf->begin_program_headers(); it != elf->end_program_headers(); ++it) {
274 if ((it->p_type == llvm::ELF::PT_LOAD) && (it->p_flags & llvm::ELF::PF_X)) {
275 if (it->p_vaddr < min_addr) {
276 min_addr = it->p_vaddr;
287 bool ReadMinExecutableVirtualAddressFromElfFile(const std::string& filename,
288 const BuildId& expected_build_id,
289 uint64_t* min_vaddr) {
290 if (!IsValidElfPath(filename)) {
293 auto owning_binary = llvm::object::createBinary(llvm::StringRef(filename));
294 llvm::object::ObjectFile* obj = GetObjectFile(owning_binary, filename, expected_build_id);
295 if (obj == nullptr) {
300 if (auto elf = llvm::dyn_cast<llvm::object::ELF32LEObjectFile>(obj)) {
301 result = ReadMinExecutableVirtualAddress(elf->getELFFile(), min_vaddr);
302 } else if (auto elf = llvm::dyn_cast<llvm::object::ELF64LEObjectFile>(obj)) {
303 result = ReadMinExecutableVirtualAddress(elf->getELFFile(), min_vaddr);
305 LOG(ERROR) << "unknown elf format in file" << filename;
310 LOG(ERROR) << "no program header in file " << filename;