• 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         DFXLOG_ERROR("%s : Failed to read elf headers.", __func__);
102         return false;
103     }
104     ReadProgramHeaders(ehdr);
105     ReadSectionHeaders(ehdr);
106     return true;
107 }
108 
ReadElfHeaders(ElfW (Ehdr)& ehdr)109 bool DfxElfImpl::ReadElfHeaders(ElfW(Ehdr)& ehdr)
110 {
111     if (!memory_->ReadFully(0, &ehdr, sizeof(ehdr))) {
112         return false;
113     }
114     return true;
115 }
116 
ReadProgramHeaders(const ElfW (Ehdr)& ehdr)117 void DfxElfImpl::ReadProgramHeaders(const ElfW(Ehdr)& ehdr)
118 {
119     uint64_t offset = ehdr.e_phoff;
120     bool firstExecLoadHeader = true;
121     for (size_t i = 0; i < ehdr.e_phnum; i++, offset += ehdr.e_phentsize) {
122         ElfW(Phdr) phdr;
123         if (!memory_->ReadFully(offset, &phdr, sizeof(phdr))) {
124             DFXLOG_ERROR("%s : Failed to read memory of program headers.", __func__);
125             return;
126         }
127 
128         switch (phdr.p_type) {
129         case PT_LOAD: {
130             if ((phdr.p_flags & PF_X) == 0) {
131                 continue;
132             }
133 
134             ElfLoadInfo elfLoadInfo;
135             elfLoadInfo.offset = phdr.p_offset;
136             elfLoadInfo.tableVaddr = phdr.p_vaddr;
137             elfLoadInfo.tableSize = static_cast<size_t>(phdr.p_memsz);
138             ptLoads_[phdr.p_offset] = elfLoadInfo;
139 
140             if (firstExecLoadHeader) {
141                 loadBias_ = static_cast<int64_t>(phdr.p_vaddr) - static_cast<int64_t>(phdr.p_offset);
142             }
143             firstExecLoadHeader = false;
144             break;
145         }
146         default:
147             DFXLOG_WARN("phdr type(%u) error", phdr.p_type);
148             break;
149         }
150     }
151 }
152 
ReadSectionHeaders(const ElfW (Ehdr)& ehdr)153 void DfxElfImpl::ReadSectionHeaders(const ElfW(Ehdr)& ehdr)
154 {
155     uint64_t offset = ehdr.e_shoff;
156     uint64_t secOffset = 0;
157     uint64_t secSize = 0;
158     ElfW(Shdr) shdr;
159     if (ehdr.e_shstrndx < ehdr.e_shnum) {
160         uint64_t shOffset = offset + ehdr.e_shstrndx * ehdr.e_shentsize;
161         if (memory_->ReadFully(shOffset, &shdr, sizeof(shdr))) {
162             secOffset = shdr.sh_offset;
163             secSize = shdr.sh_size;
164         }
165     }
166 
167     // Skip the first header, it's always going to be NULL.
168     offset += ehdr.e_shentsize;
169 
170     for (size_t i = 1; i < ehdr.e_shnum; i++, offset += ehdr.e_shentsize) {
171         if (!memory_->ReadFully(offset, &shdr, sizeof(shdr))) {
172             DFXLOG_ERROR("%s : Failed to read memory of section headers.", __func__);
173             return;
174         }
175 
176         if (shdr.sh_type == SHT_SYMTAB || shdr.sh_type == SHT_DYNSYM) {
177             // Need to go get the information about the section that contains the string terminated names.
178             ElfW(Shdr) strShdr;
179             if (shdr.sh_link >= ehdr.e_shnum || shdr.sh_entsize == 0) {
180                 continue;
181             }
182             uint64_t strOffset = ehdr.e_shoff + shdr.sh_link * ehdr.e_shentsize;
183             if (!memory_->ReadFully(strOffset, &strShdr, sizeof(strShdr))) {
184                 continue;
185             }
186             if (strShdr.sh_type != SHT_STRTAB) {
187                 continue;
188             }
189             uint32_t shCount = static_cast<uint32_t>(shdr.sh_size / shdr.sh_entsize);
190             for (uint32_t j = 0; j < shCount; j++) {
191                 ElfW(Sym) sym;
192                 if (!memory_->ReadFully(shdr.sh_offset + j * shdr.sh_entsize, &sym, sizeof(sym))) {
193                     continue;
194                 }
195 
196                 SymbolInfo info;
197                 info.start = sym.st_value;
198                 info.end = sym.st_value + sym.st_size;
199                 info.name = sym.st_name;
200                 info.ndx = sym.st_shndx;
201                 info.type = sym.st_info;
202                 symbols_.push_back(info);
203             }
204         } else if (shdr.sh_type == SHT_NOTE) {
205             if (shdr.sh_name < secSize) {
206                 std::string name;
207                 if (memory_->ReadString(secOffset + shdr.sh_name, &name, secSize - shdr.sh_name) &&
208                     name == ".note.gnu.build-id") {
209                     buildIdOffset_ = shdr.sh_offset;
210                     buildIdSize_ = shdr.sh_size;
211                 }
212             }
213         }
214     }
215 }
216 
GetFuncNameAndOffset(uint64_t addr,std::string * funcName,uint64_t * start,uint64_t * end)217 bool DfxElfImpl::GetFuncNameAndOffset(uint64_t addr, std::string* funcName, uint64_t* start, uint64_t* end)
218 {
219     if (symbols_.empty()) {
220         return false;
221     }
222 
223     std::vector<SymbolInfo>::iterator it;
224     for (it = symbols_.begin(); it != symbols_.end(); ++it) {
225         SymbolInfo& symbol = *it;
226         if (symbol.ndx == SHN_UNDEF || ELF32_ST_TYPE(symbol.type) != STT_FUNC) {
227             continue;
228         }
229 
230         if ((addr >= symbol.start) && (addr < symbol.end)) {
231             *start = symbol.start;
232             *end = symbol.end;
233             uint64_t nameAddr = symbol.start + symbol.name;
234             if (nameAddr < symbol.end) {
235                 memory_->ReadString(nameAddr, funcName, static_cast<size_t>(symbol.end - nameAddr));
236             }
237             return true;
238         }
239     }
240     return false;
241 }
242 
GetFuncNameAndOffset(uint64_t addr,std::string * funcName,uint64_t * funcOffset)243 bool DfxElfImpl::GetFuncNameAndOffset(uint64_t addr, std::string* funcName, uint64_t* funcOffset)
244 {
245     if (symbols_.empty()) {
246         return false;
247     }
248 
249     std::vector<SymbolInfo>::iterator it;
250     for (it = symbols_.begin(); it != symbols_.end(); ++it) {
251         SymbolInfo& symbol = *it;
252         if (symbol.ndx == SHN_UNDEF || ELF32_ST_TYPE(symbol.type) != STT_FUNC) {
253             continue;
254         }
255 
256         if ((addr >= symbol.start) && (addr < symbol.end)) {
257             *funcOffset = addr - symbol.start;
258             uint64_t nameAddr = symbol.start + symbol.name;
259             if (nameAddr < symbol.end) {
260                 memory_->ReadString(nameAddr, funcName, static_cast<size_t>(symbol.end - nameAddr));
261             }
262             return true;
263         }
264     }
265     return false;
266 }
267 
GetGlobalVariableOffset(const std::string & name,uint64_t * offset)268 bool DfxElfImpl::GetGlobalVariableOffset(const std::string& name, uint64_t* offset)
269 {
270     if (symbols_.empty()) {
271         return false;
272     }
273 
274     std::vector<SymbolInfo>::iterator it;
275     for (it = symbols_.begin(); it != symbols_.end(); ++it) {
276         SymbolInfo& symbol = *it;
277         if (symbol.ndx == SHN_UNDEF || ELF32_ST_TYPE(symbol.type) != STT_OBJECT ||
278             ELF32_ST_BIND(symbol.type) != STB_GLOBAL) {
279             continue;
280         }
281 
282         uint64_t nameAddr = symbol.start + symbol.name;
283         if (nameAddr < symbol.end) {
284             std::string tmpName;
285             if (memory_->ReadString(nameAddr, &tmpName, static_cast<size_t>(symbol.end - nameAddr))
286                 && tmpName == name) {
287                 *offset = symbol.start;
288                 return true;
289             }
290         }
291     }
292     return false;
293 }
294 
GetLoadBias()295 int64_t DfxElfImpl::GetLoadBias()
296 {
297     return loadBias_;
298 }
299 
GetBuildID()300 std::string DfxElfImpl::GetBuildID()
301 {
302     // Ensure there is no overflow in any of the calculations below.
303     uint64_t tmp;
304     if (__builtin_add_overflow(buildIdOffset_, buildIdSize_, &tmp)) {
305         return "";
306     }
307 
308     uint64_t offset = 0;
309     while (offset < buildIdSize_) {
310         ElfW(Nhdr) nhdr;
311         if (buildIdSize_ - offset < sizeof(nhdr)) {
312             return "";
313         }
314 
315         if (!memory_->ReadFully(buildIdOffset_ + offset, &nhdr, sizeof(nhdr))) {
316             return "";
317         }
318         offset += sizeof(nhdr);
319 
320         if (buildIdSize_ - offset < nhdr.n_namesz) {
321             return "";
322         }
323 
324         if (nhdr.n_namesz > 0) {
325             std::string name(nhdr.n_namesz, '\0');
326             if (!memory_->ReadFully(buildIdOffset_ + offset, &(name[0]), nhdr.n_namesz)) {
327                 return "";
328             }
329 
330             // Trim trailing \0 as GNU is stored as a C string in the ELF file.
331             if (name.back() == '\0') {
332                 name.resize(name.size() - 1);
333             }
334 
335             offset += ALIGN_VALUE(nhdr.n_namesz, 4); // Align hdr.n_namesz to next power multiple of 4. See man 5 elf.
336             if (name == "GNU" && nhdr.n_type == NT_GNU_BUILD_ID) {
337                 if (buildIdSize_ - offset < nhdr.n_descsz || nhdr.n_descsz == 0) {
338                     return "";
339                 }
340                 std::string buildId(nhdr.n_descsz, '\0');
341                 if (memory_->ReadFully(buildIdOffset_ + offset, &buildId[0], nhdr.n_descsz)) {
342                     return buildId;
343                 }
344                 return "";
345             }
346         }
347         offset += ALIGN_VALUE(nhdr.n_descsz, 4); // Align hdr.n_descsz to next power multiple of 4. See man 5 elf.
348     }
349     return "";
350 }
351 
Init()352 bool DfxElf::Init()
353 {
354     if (memory_ == nullptr) {
355         return false;
356     }
357 
358     if (!ReadElfInfo()) {
359         DFXLOG_ERROR("%s : Failed to read elf info.", __func__);
360         return false;
361     }
362 
363     elfImpl_ = std::unique_ptr<DfxElfImpl>(new DfxElfImpl(memory_));
364     if (elfImpl_ == nullptr) {
365         return false;
366     }
367     valid_ = elfImpl_->Init();
368     return valid_;
369 }
370 
ReadElfInfo()371 bool DfxElf::ReadElfInfo()
372 {
373     if (memory_ == nullptr) {
374         return false;
375     }
376 
377     if (!IsValidElf(memory_)) {
378         return false;
379     }
380 
381     uint8_t eiClass;
382     if (!memory_->ReadFully(EI_CLASS, &eiClass, sizeof(eiClass))) {
383         return false;
384     }
385     class_ = eiClass;
386 
387     uint16_t machine;
388     if (!memory_->ReadFully(EI_NIDENT + sizeof(uint16_t), &machine, sizeof(machine))) {
389         return false;
390     }
391     machine_ = machine;
392 
393     if (class_ == ELFCLASS32) {
394         if (machine == EM_ARM) {
395             arch_ = ARCH_ARM;
396         } else if (machine == EM_386) {
397             arch_ = ARCH_X86;
398         } else {
399             LOGI("32 bit elf that is neither arm nor x86: machine = %d\n", machine);
400             return false;
401         }
402     } else if (class_ == ELFCLASS64) {
403         if (machine == EM_AARCH64) {
404             arch_ = ARCH_ARM64;
405         } else if (machine == EM_X86_64) {
406             arch_ = ARCH_X86_64;
407         } else {
408             LOGI("64 bit elf that is neither aarch64 nor x86_64: machine = %d\n", machine);
409             return false;
410         }
411     }
412     return true;
413 }
414 
GetFuncNameAndOffset(uint64_t addr,std::string * funcName,uint64_t * start,uint64_t * end)415 bool DfxElf::GetFuncNameAndOffset(uint64_t addr, std::string* funcName, uint64_t* start, uint64_t* end)
416 {
417     std::lock_guard<std::mutex> guard(lock_);
418     if (!valid_) {
419         return false;
420     }
421     return elfImpl_->GetFuncNameAndOffset(addr, funcName, start, end);
422 }
423 
GetFuncNameAndOffset(uint64_t addr,std::string * funcName,uint64_t * funcOffset)424 bool DfxElf::GetFuncNameAndOffset(uint64_t addr, std::string* funcName, uint64_t* funcOffset)
425 {
426     std::lock_guard<std::mutex> guard(lock_);
427     if (!valid_) {
428         return false;
429     }
430     return elfImpl_->GetFuncNameAndOffset(addr, funcName, funcOffset);
431 }
432 
GetGlobalVariableOffset(const std::string & name,uint64_t * offset)433 bool DfxElf::GetGlobalVariableOffset(const std::string& name, uint64_t* offset)
434 {
435     std::lock_guard<std::mutex> guard(lock_);
436     if (!valid_) {
437         return false;
438     }
439 
440     uint64_t vaddr;
441     if (!elfImpl_->GetGlobalVariableOffset(name, &vaddr)) {
442         return false;
443     }
444     return true;
445 }
446 
GetLoadBias()447 int64_t DfxElf::GetLoadBias()
448 {
449     if (!valid_) {
450         return 0;
451     }
452     return elfImpl_->GetLoadBias();
453 }
454 
GetBuildID()455 std::string DfxElf::GetBuildID()
456 {
457     if (!valid_) {
458         return "";
459     }
460     return elfImpl_->GetBuildID();
461 }
462 
IsValidPc(uint64_t pc)463 bool DfxElf::IsValidPc(uint64_t pc)
464 {
465     if (!valid_ || (GetLoadBias() > 0 && pc < static_cast<uint64_t>(GetLoadBias()))) {
466         return false;
467     }
468 
469     if (elfImpl_->IsValidPc(pc)) {
470         return true;
471     }
472     return false;
473 }
474 
GetRealLoadOffset(uint64_t offset) const475 uint64_t DfxElf::GetRealLoadOffset(uint64_t offset) const
476 {
477     if (!valid_) {
478         return offset;
479     }
480     return elfImpl_->GetRealLoadOffset(offset);
481 }
482 
IsValidElf(std::shared_ptr<DfxMemory> memory)483 bool DfxElf::IsValidElf(std::shared_ptr<DfxMemory> memory)
484 {
485     if (memory == nullptr) {
486         return false;
487     }
488 
489     // Verify that this is a valid elf file.
490     uint8_t e_ident[SELFMAG + 1];
491     if (!memory->ReadFully(0, e_ident, SELFMAG)) {
492         return false;
493     }
494 
495     if (memcmp(e_ident, ELFMAG, SELFMAG) != 0) {
496         return false;
497     }
498     return true;
499 }
500 
GetMaxSize(std::shared_ptr<DfxMemory> memory)501 uint64_t DfxElf::GetMaxSize(std::shared_ptr<DfxMemory> memory)
502 {
503     if (memory == nullptr) {
504         return 0;
505     }
506 
507     uint64_t size = 0;
508     auto elfImpl = std::make_shared<DfxElfImpl>(memory);
509     elfImpl->GetMaxSize(&size);
510     return size;
511 }
512 
GetReadableBuildID(const std::string & buildIdHex)513 std::string DfxElf::GetReadableBuildID(const std::string &buildIdHex)
514 {
515     if (buildIdHex.empty()) {
516         return "";
517     }
518     static const char HEXTABLE[] = "0123456789abcdef";
519     static const int HEXLENGTH = 16;
520     static const int HEX_EXPAND_PARAM = 2;
521     const size_t len = buildIdHex.length();
522     std::string buildId(len * HEX_EXPAND_PARAM, '\0');
523 
524     for (size_t i = 0; i < len; i++) {
525         unsigned int n = buildIdHex[i];
526         buildId[i * HEX_EXPAND_PARAM] = HEXTABLE[(n >> 4) % HEXLENGTH]; // 4 : higher 4 bit of uint8
527         buildId[i * HEX_EXPAND_PARAM + 1] = HEXTABLE[n % HEXLENGTH];
528     }
529     return buildId;
530 }
531 } // namespace HiviewDFX
532 } // namespace OHOS
533