• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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