OSDN Git Service

Simpleperf: check if elf file paths are valid before reading them.
[android-x86/system-extras.git] / simpleperf / read_elf.cpp
1 /*
2  * Copyright (C) 2015 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 "read_elf.h"
18
19 #include <stdio.h>
20 #include <string.h>
21
22 #include <algorithm>
23 #include <limits>
24
25 #include <android-base/file.h>
26 #include <android-base/logging.h>
27
28 #pragma clang diagnostic push
29 #pragma clang diagnostic ignored "-Wunused-parameter"
30
31 #include <llvm/ADT/StringRef.h>
32 #include <llvm/Object/Binary.h>
33 #include <llvm/Object/ELFObjectFile.h>
34 #include <llvm/Object/ObjectFile.h>
35
36 #pragma clang diagnostic pop
37
38 #include "utils.h"
39
40 #define ELF_NOTE_GNU "GNU"
41 #define NT_GNU_BUILD_ID 3
42
43 bool IsValidElfPath(const std::string& filename) {
44   static const char elf_magic[] = {0x7f, 'E', 'L', 'F'};
45
46   if (!IsRegularFile(filename)) {
47     return false;
48   }
49   FILE* fp = fopen(filename.c_str(), "rb");
50   char buf[4];
51   if (fread(buf, 4, 1, fp) != 1) {
52     fclose(fp);
53     return false;
54   }
55   fclose(fp);
56   return memcmp(buf, elf_magic, 4) == 0;
57 }
58
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;
62   while (p < end) {
63     CHECK_LE(p + 12, end);
64     size_t namesz = *reinterpret_cast<const uint32_t*>(p);
65     p += 4;
66     size_t descsz = *reinterpret_cast<const uint32_t*>(p);
67     p += 4;
68     uint32_t type = *reinterpret_cast<const uint32_t*>(p);
69     p += 4;
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);
75       return true;
76     }
77     p += namesz + descsz;
78   }
79   return false;
80 }
81
82 bool GetBuildIdFromNoteFile(const std::string& filename, BuildId* build_id) {
83   std::string content;
84   if (!android::base::ReadFileToString(filename, &content)) {
85     LOG(DEBUG) << "can't read note file " << filename;
86     return false;
87   }
88   if (GetBuildIdFromNoteSection(content.c_str(), content.size(), build_id) == false) {
89     LOG(DEBUG) << "can't read build_id from note file " << filename;
90     return false;
91   }
92   return true;
93 }
94
95 template <class ELFT>
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();
98        ++section_iterator) {
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";
103         continue;
104       }
105       if (GetBuildIdFromNoteSection(reinterpret_cast<const char*>(contents->data()),
106                                     contents->size(), build_id)) {
107         return true;
108       }
109     }
110   }
111   return false;
112 }
113
114 static bool GetBuildIdFromObjectFile(llvm::object::ObjectFile* obj, BuildId* build_id) {
115   bool result = false;
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);
120   } else {
121     LOG(ERROR) << "unknown elf format in file " << obj->getFileName().data();
122     return false;
123   }
124   if (!result) {
125     LOG(DEBUG) << "no build id present in file " << obj->getFileName().data();
126   }
127   return result;
128 }
129
130 bool GetBuildIdFromElfFile(const std::string& filename, BuildId* build_id) {
131   if (!IsValidElfPath(filename)) {
132     return false;
133   }
134   auto owning_binary = llvm::object::createBinary(llvm::StringRef(filename));
135   if (owning_binary.getError()) {
136     PLOG(DEBUG) << "can't open file " << filename;
137     return false;
138   }
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";
143     return false;
144   }
145   return GetBuildIdFromObjectFile(obj, build_id);
146 }
147
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] == '.');
153 }
154
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();
162   if (begin == end) {
163     begin = elf->begin_dynamic_symbols();
164     end = elf->end_dynamic_symbols();
165   }
166   for (; begin != end; ++begin) {
167     auto& elf_symbol = *begin;
168
169     ElfFileSymbol symbol;
170     memset(&symbol, '\0', sizeof(symbol));
171
172     auto shdr = elf->getSection(&elf_symbol);
173     if (shdr == nullptr) {
174       continue;
175     }
176     auto section_name = elf->getSectionName(shdr);
177     if (section_name.getError() || section_name.get().empty()) {
178       continue;
179     }
180     if (section_name.get() == ".text") {
181       symbol.is_in_text_section = true;
182     }
183
184     auto symbol_name = elf->getSymbolName(begin);
185     if (symbol_name.getError()) {
186       continue;
187     }
188     symbol.name = symbol_name.get();
189     if (symbol.name.empty()) {
190       continue;
191     }
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.
195       symbol.vaddr &= ~1;
196     }
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;
204         if (is_arm) {
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;
211           }
212         }
213       }
214     }
215
216     callback(symbol);
217   }
218 }
219
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 << "'";
225     return nullptr;
226   }
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";
231     return nullptr;
232   }
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();
240     if (!result) {
241       return nullptr;
242     }
243   }
244   return obj;
245 }
246
247 bool ParseSymbolsFromElfFile(const std::string& filename, const BuildId& expected_build_id,
248                              std::function<void(const ElfFileSymbol&)> callback) {
249   if (!IsValidElfPath(filename)) {
250     return false;
251   }
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) {
255     return false;
256   }
257
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);
262   } else {
263     LOG(ERROR) << "unknown elf format in file" << filename;
264     return false;
265   }
266   return true;
267 }
268
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;
277         has_vaddr = true;
278       }
279     }
280   }
281   if (has_vaddr) {
282     *p_vaddr = min_addr;
283   }
284   return has_vaddr;
285 }
286
287 bool ReadMinExecutableVirtualAddressFromElfFile(const std::string& filename,
288                                                 const BuildId& expected_build_id,
289                                                 uint64_t* min_vaddr) {
290   if (!IsValidElfPath(filename)) {
291     return false;
292   }
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) {
296     return false;
297   }
298
299   bool result = false;
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);
304   } else {
305     LOG(ERROR) << "unknown elf format in file" << filename;
306     return false;
307   }
308
309   if (!result) {
310     LOG(ERROR) << "no program header in file " << filename;
311   }
312   return result;
313 }