1 /*
2 * Copyright (c) 2021-2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "dfx_elf.h"
17
18 #include <cstdlib>
19 #include <elf.h>
20 #include <fcntl.h>
21 #include <link.h>
22 #include <securec.h>
23 #include <string>
24 #include <sys/mman.h>
25 #include <sys/stat.h>
26 #include <sys/types.h>
27 #include <unistd.h>
28 #include <utility>
29 #include "dfx_define.h"
30 #include "dfx_log.h"
31 #include "dfx_util.h"
32
33 #ifndef PAGE_SIZE
34 #define PAGE_SIZE 4096
35 #endif
36
37 namespace OHOS {
38 namespace HiviewDFX {
39 namespace {
40 #undef LOG_DOMAIN
41 #undef LOG_TAG
42 #define LOG_DOMAIN 0xD002D11
43 #define LOG_TAG "DfxElf"
44 }
45
GetNameFromPath(const std::string & path)46 std::string ElfFileInfo::GetNameFromPath(const std::string &path)
47 {
48 size_t pos = path.find_last_of("/");
49 return path.substr(pos + 1);
50 }
51
IsValidPc(uint64_t pc)52 bool DfxElfImpl::IsValidPc(uint64_t pc)
53 {
54 if (!ptLoads_.empty()) {
55 for (const auto& iter : ptLoads_) {
56 uint64_t start = iter.second.tableVaddr;
57 uint64_t end = start + iter.second.tableSize;
58 if (pc >= start && pc < end) {
59 return true;
60 }
61 }
62 return false;
63 }
64 return false;
65 }
66
GetRealLoadOffset(uint64_t offset) const67 uint64_t DfxElfImpl::GetRealLoadOffset(uint64_t offset) const
68 {
69 if (!ptLoads_.empty()) {
70 for (auto& iter : ptLoads_) {
71 if ((iter.second.offset & -PAGE_SIZE) == offset) {
72 return offset + (iter.second.tableVaddr - iter.second.offset);
73 }
74 }
75 }
76 return offset;
77 }
78
GetMaxSize(uint64_t * size)79 void DfxElfImpl::GetMaxSize(uint64_t* size)
80 {
81 ElfW(Ehdr) ehdr;
82 if (!ReadElfHeaders(ehdr)) {
83 return;
84 }
85
86 if (ehdr.e_shnum == 0) {
87 return;
88 }
89 *size = ehdr.e_shoff + ehdr.e_shentsize * ehdr.e_shnum;
90 }
91
Init()92 bool DfxElfImpl::Init()
93 {
94 return ReadAllHeaders();
95 }
96
ReadAllHeaders()97 bool DfxElfImpl::ReadAllHeaders()
98 {
99 ElfW(Ehdr) ehdr;
100 if (!ReadElfHeaders(ehdr)) {
101 return false;
102 }
103 ReadProgramHeaders(ehdr);
104 ReadSectionHeaders(ehdr);
105 return true;
106 }
107
ReadElfHeaders(ElfW (Ehdr)& ehdr)108 bool DfxElfImpl::ReadElfHeaders(ElfW(Ehdr)& ehdr)
109 {
110 if (!memory_->ReadFully(0, &ehdr, sizeof(ehdr))) {
111 return false;
112 }
113 return true;
114 }
115
ReadProgramHeaders(const ElfW (Ehdr)& ehdr)116 void DfxElfImpl::ReadProgramHeaders(const ElfW(Ehdr)& ehdr)
117 {
118 uint64_t offset = ehdr.e_phoff;
119 bool firstExecLoadHeader = true;
120 for (size_t i = 0; i < ehdr.e_phnum; i++, offset += ehdr.e_phentsize) {
121 ElfW(Phdr) phdr;
122 if (!memory_->ReadFully(offset, &phdr, sizeof(phdr))) {
123 return;
124 }
125
126 switch (phdr.p_type) {
127 case PT_LOAD: {
128 if ((phdr.p_flags & PF_X) == 0) {
129 continue;
130 }
131
132 ElfLoadInfo elfLoadInfo;
133 elfLoadInfo.offset = phdr.p_offset;
134 elfLoadInfo.tableVaddr = phdr.p_vaddr;
135 elfLoadInfo.tableSize = static_cast<size_t>(phdr.p_memsz);
136 ptLoads_[phdr.p_offset] = elfLoadInfo;
137
138 if (firstExecLoadHeader) {
139 loadBias_ = static_cast<int64_t>(phdr.p_vaddr) - static_cast<int64_t>(phdr.p_offset);
140 }
141 firstExecLoadHeader = false;
142 break;
143 }
144 default:
145 DFXLOG_WARN("phdr type(%lu) error", phdr.p_type);
146 break;
147 }
148 }
149 }
150
ReadSectionHeaders(const ElfW (Ehdr)& ehdr)151 void DfxElfImpl::ReadSectionHeaders(const ElfW(Ehdr)& ehdr)
152 {
153 uint64_t offset = ehdr.e_shoff;
154 uint64_t secOffset = 0;
155 uint64_t secSize = 0;
156 ElfW(Shdr) shdr;
157 if (ehdr.e_shstrndx < ehdr.e_shnum) {
158 uint64_t shOffset = offset + ehdr.e_shstrndx * ehdr.e_shentsize;
159 if (memory_->ReadFully(shOffset, &shdr, sizeof(shdr))) {
160 secOffset = shdr.sh_offset;
161 secSize = shdr.sh_size;
162 }
163 }
164
165 // Skip the first header, it's always going to be NULL.
166 offset += ehdr.e_shentsize;
167
168 for (size_t i = 1; i < ehdr.e_shnum; i++, offset += ehdr.e_shentsize) {
169 if (!memory_->ReadFully(offset, &shdr, sizeof(shdr))) {
170 return;
171 }
172
173 if (shdr.sh_type == SHT_SYMTAB || shdr.sh_type == SHT_DYNSYM) {
174 // Need to go get the information about the section that contains the string terminated names.
175 ElfW(Shdr) strShdr;
176 if (shdr.sh_link >= ehdr.e_shnum || shdr.sh_entsize == 0) {
177 continue;
178 }
179 uint64_t strOffset = ehdr.e_shoff + shdr.sh_link * ehdr.e_shentsize;
180 if (!memory_->ReadFully(strOffset, &strShdr, sizeof(strShdr))) {
181 continue;
182 }
183 if (strShdr.sh_type != SHT_STRTAB) {
184 continue;
185 }
186 uint32_t shCount = static_cast<uint32_t>(shdr.sh_size / shdr.sh_entsize);
187 for (uint32_t j = 0; j < shCount; j++) {
188 ElfW(Sym) sym;
189 if (!memory_->ReadFully(shdr.sh_offset + j * shdr.sh_entsize, &sym, sizeof(sym))) {
190 continue;
191 }
192
193 SymbolInfo info;
194 info.start = sym.st_value;
195 info.end = sym.st_value + sym.st_size;
196 info.name = sym.st_name;
197 info.ndx = sym.st_shndx;
198 info.type = sym.st_info;
199 symbols_.push_back(info);
200 }
201 } else if (shdr.sh_type == SHT_NOTE) {
202 if (shdr.sh_name < secSize) {
203 std::string name;
204 if (memory_->ReadString(secOffset + shdr.sh_name, &name, secSize - shdr.sh_name) &&
205 name == ".note.gnu.build-id") {
206 buildIdOffset_ = shdr.sh_offset;
207 buildIdSize_ = shdr.sh_size;
208 }
209 }
210 }
211 }
212 }
213
GetFuncNameAndOffset(uint64_t addr,std::string * funcName,uint64_t * start,uint64_t * end)214 bool DfxElfImpl::GetFuncNameAndOffset(uint64_t addr, std::string* funcName, uint64_t* start, uint64_t* end)
215 {
216 if (symbols_.empty()) {
217 return false;
218 }
219
220 std::vector<SymbolInfo>::iterator it;
221 for (it = symbols_.begin(); it != symbols_.end(); ++it) {
222 SymbolInfo& symbol = *it;
223 if (symbol.ndx == SHN_UNDEF || ELF32_ST_TYPE(symbol.type) != STT_FUNC) {
224 continue;
225 }
226
227 if ((addr >= symbol.start) && (addr < symbol.end)) {
228 *start = symbol.start;
229 *end = symbol.end;
230 uint64_t nameAddr = symbol.start + symbol.name;
231 if (nameAddr < symbol.end) {
232 memory_->ReadString(nameAddr, funcName, static_cast<size_t>(symbol.end - nameAddr));
233 }
234 return true;
235 }
236 }
237 return false;
238 }
239
GetFuncNameAndOffset(uint64_t addr,std::string * funcName,uint64_t * funcOffset)240 bool DfxElfImpl::GetFuncNameAndOffset(uint64_t addr, std::string* funcName, uint64_t* funcOffset)
241 {
242 if (symbols_.empty()) {
243 return false;
244 }
245
246 std::vector<SymbolInfo>::iterator it;
247 for (it = symbols_.begin(); it != symbols_.end(); ++it) {
248 SymbolInfo& symbol = *it;
249 if (symbol.ndx == SHN_UNDEF || ELF32_ST_TYPE(symbol.type) != STT_FUNC) {
250 continue;
251 }
252
253 if ((addr >= symbol.start) && (addr < symbol.end)) {
254 *funcOffset = addr - symbol.start;
255 uint64_t nameAddr = symbol.start + symbol.name;
256 if (nameAddr < symbol.end) {
257 memory_->ReadString(nameAddr, funcName, static_cast<size_t>(symbol.end - nameAddr));
258 }
259 return true;
260 }
261 }
262 return false;
263 }
264
GetGlobalVariableOffset(const std::string & name,uint64_t * offset)265 bool DfxElfImpl::GetGlobalVariableOffset(const std::string& name, uint64_t* offset)
266 {
267 if (symbols_.empty()) {
268 return false;
269 }
270
271 std::vector<SymbolInfo>::iterator it;
272 for (it = symbols_.begin(); it != symbols_.end(); ++it) {
273 SymbolInfo& symbol = *it;
274 if (symbol.ndx == SHN_UNDEF || ELF32_ST_TYPE(symbol.type) != STT_OBJECT ||
275 ELF32_ST_BIND(symbol.type) != STB_GLOBAL) {
276 continue;
277 }
278
279 uint64_t nameAddr = symbol.start + symbol.name;
280 if (nameAddr < symbol.end) {
281 std::string tmpName;
282 if (memory_->ReadString(nameAddr, &tmpName, static_cast<size_t>(symbol.end - nameAddr))
283 && tmpName == name) {
284 *offset = symbol.start;
285 return true;
286 }
287 }
288 }
289 return false;
290 }
291
GetLoadBias()292 int64_t DfxElfImpl::GetLoadBias()
293 {
294 return loadBias_;
295 }
296
GetBuildID()297 std::string DfxElfImpl::GetBuildID()
298 {
299 // Ensure there is no overflow in any of the calculations below.
300 uint64_t tmp;
301 if (__builtin_add_overflow(buildIdOffset_, buildIdSize_, &tmp)) {
302 return "";
303 }
304
305 uint64_t offset = 0;
306 while (offset < buildIdSize_) {
307 ElfW(Nhdr) nhdr;
308 if (buildIdSize_ - offset < sizeof(nhdr)) {
309 return "";
310 }
311
312 if (!memory_->ReadFully(buildIdOffset_ + offset, &nhdr, sizeof(nhdr))) {
313 return "";
314 }
315 offset += sizeof(nhdr);
316
317 if (buildIdSize_ - offset < nhdr.n_namesz) {
318 return "";
319 }
320
321 if (nhdr.n_namesz > 0) {
322 std::string name(nhdr.n_namesz, '\0');
323 if (!memory_->ReadFully(buildIdOffset_ + offset, &(name[0]), nhdr.n_namesz)) {
324 return "";
325 }
326
327 // Trim trailing \0 as GNU is stored as a C string in the ELF file.
328 if (name.back() == '\0') {
329 name.resize(name.size() - 1);
330 }
331
332 offset += ALIGN_VALUE(nhdr.n_namesz, 4); // Align hdr.n_namesz to next power multiple of 4. See man 5 elf.
333 if (name == "GNU" && nhdr.n_type == NT_GNU_BUILD_ID) {
334 if (buildIdSize_ - offset < nhdr.n_descsz || nhdr.n_descsz == 0) {
335 return "";
336 }
337 std::string buildId(nhdr.n_descsz, '\0');
338 if (memory_->ReadFully(buildIdOffset_ + offset, &buildId[0], nhdr.n_descsz)) {
339 return buildId;
340 }
341 return "";
342 }
343 }
344 offset += ALIGN_VALUE(nhdr.n_descsz, 4); // Align hdr.n_descsz to next power multiple of 4. See man 5 elf.
345 }
346 return "";
347 }
348
Init()349 bool DfxElf::Init()
350 {
351 if (memory_ == nullptr) {
352 return false;
353 }
354
355 if (!ReadElfInfo()) {
356 return false;
357 }
358
359 elfImpl_ = std::unique_ptr<DfxElfImpl>(new DfxElfImpl(memory_));
360 if (elfImpl_ == nullptr) {
361 return false;
362 }
363 valid_ = elfImpl_->Init();
364 return valid_;
365 }
366
ReadElfInfo()367 bool DfxElf::ReadElfInfo()
368 {
369 if (memory_ == nullptr) {
370 return false;
371 }
372
373 if (!IsValidElf(memory_)) {
374 return false;
375 }
376
377 uint8_t eiClass;
378 if (!memory_->ReadFully(EI_CLASS, &eiClass, sizeof(eiClass))) {
379 return false;
380 }
381 class_ = eiClass;
382
383 uint16_t machine;
384 if (!memory_->ReadFully(EI_NIDENT + sizeof(uint16_t), &machine, sizeof(machine))) {
385 return false;
386 }
387 machine_ = machine;
388
389 if (class_ == ELFCLASS32) {
390 if (machine == EM_ARM) {
391 arch_ = ARCH_ARM;
392 } else if (machine == EM_386) {
393 arch_ = ARCH_X86;
394 } else {
395 LOGI("32 bit elf that is neither arm nor x86: machine = %d\n", machine);
396 return false;
397 }
398 } else if (class_ == ELFCLASS64) {
399 if (machine == EM_AARCH64) {
400 arch_ = ARCH_ARM64;
401 } else if (machine == EM_X86_64) {
402 arch_ = ARCH_X86_64;
403 } else {
404 LOGI("64 bit elf that is neither aarch64 nor x86_64: machine = %d\n", machine);
405 return false;
406 }
407 }
408 return true;
409 }
410
GetFuncNameAndOffset(uint64_t addr,std::string * funcName,uint64_t * start,uint64_t * end)411 bool DfxElf::GetFuncNameAndOffset(uint64_t addr, std::string* funcName, uint64_t* start, uint64_t* end)
412 {
413 std::lock_guard<std::mutex> guard(lock_);
414 if (!valid_) {
415 return false;
416 }
417 return elfImpl_->GetFuncNameAndOffset(addr, funcName, start, end);
418 }
419
GetFuncNameAndOffset(uint64_t addr,std::string * funcName,uint64_t * funcOffset)420 bool DfxElf::GetFuncNameAndOffset(uint64_t addr, std::string* funcName, uint64_t* funcOffset)
421 {
422 std::lock_guard<std::mutex> guard(lock_);
423 if (!valid_) {
424 return false;
425 }
426 return elfImpl_->GetFuncNameAndOffset(addr, funcName, funcOffset);
427 }
428
GetGlobalVariableOffset(const std::string & name,uint64_t * offset)429 bool DfxElf::GetGlobalVariableOffset(const std::string& name, uint64_t* offset)
430 {
431 std::lock_guard<std::mutex> guard(lock_);
432 if (!valid_) {
433 return false;
434 }
435
436 uint64_t vaddr;
437 if (!elfImpl_->GetGlobalVariableOffset(name, &vaddr)) {
438 return false;
439 }
440 return true;
441 }
442
GetLoadBias()443 int64_t DfxElf::GetLoadBias()
444 {
445 if (!valid_) {
446 return 0;
447 }
448 return elfImpl_->GetLoadBias();
449 }
450
GetBuildID()451 std::string DfxElf::GetBuildID()
452 {
453 if (!valid_) {
454 return "";
455 }
456 return elfImpl_->GetBuildID();
457 }
458
IsValidPc(uint64_t pc)459 bool DfxElf::IsValidPc(uint64_t pc)
460 {
461 if (!valid_ || (GetLoadBias() > 0 && pc < static_cast<uint64_t>(GetLoadBias()))) {
462 return false;
463 }
464
465 if (elfImpl_->IsValidPc(pc)) {
466 return true;
467 }
468 return false;
469 }
470
GetRealLoadOffset(uint64_t offset) const471 uint64_t DfxElf::GetRealLoadOffset(uint64_t offset) const
472 {
473 if (!valid_) {
474 return offset;
475 }
476 return elfImpl_->GetRealLoadOffset(offset);
477 }
478
IsValidElf(std::shared_ptr<DfxMemory> memory)479 bool DfxElf::IsValidElf(std::shared_ptr<DfxMemory> memory)
480 {
481 if (memory == nullptr) {
482 return false;
483 }
484
485 // Verify that this is a valid elf file.
486 uint8_t e_ident[SELFMAG + 1];
487 if (!memory->ReadFully(0, e_ident, SELFMAG)) {
488 return false;
489 }
490
491 if (memcmp(e_ident, ELFMAG, SELFMAG) != 0) {
492 return false;
493 }
494 return true;
495 }
496
GetMaxSize(std::shared_ptr<DfxMemory> memory)497 uint64_t DfxElf::GetMaxSize(std::shared_ptr<DfxMemory> memory)
498 {
499 if (memory == nullptr) {
500 return 0;
501 }
502
503 uint64_t size = 0;
504 auto elfImpl = std::make_shared<DfxElfImpl>(memory);
505 elfImpl->GetMaxSize(&size);
506 return size;
507 }
508
GetReadableBuildID(const std::string & buildIdHex)509 std::string DfxElf::GetReadableBuildID(const std::string &buildIdHex)
510 {
511 if (buildIdHex.empty()) {
512 return "";
513 }
514 static const char HEXTABLE[] = "0123456789ABCDEF";
515 static const int HEXLENGTH = 16;
516 static const int HEX_EXPAND_PARAM = 2;
517 const size_t len = buildIdHex.length();
518 std::string buildId(len * HEX_EXPAND_PARAM, '\0');
519
520 for (size_t i = 0; i < len; i++) {
521 unsigned int n = buildIdHex[i];
522 buildId[i * HEX_EXPAND_PARAM] = HEXTABLE[(n >> 4) % HEXLENGTH]; // 4 : higher 4 bit of uint8
523 buildId[i * HEX_EXPAND_PARAM + 1] = HEXTABLE[n % HEXLENGTH];
524 }
525 return buildId;
526 }
527 } // namespace HiviewDFX
528 } // namespace OHOS
529