• 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_parser.h"
17 
18 #include <algorithm>
19 #include <cstdlib>
20 #include <iostream>
21 #include <securec.h>
22 #include <string>
23 #include <sys/types.h>
24 #include <unistd.h>
25 #include <utility>
26 
27 #include "dfx_define.h"
28 #include "dfx_log.h"
29 #include "dfx_util.h"
30 
31 #ifndef PAGE_SIZE
32 #define PAGE_SIZE 4096
33 #endif
34 
35 namespace OHOS {
36 namespace HiviewDFX {
37 namespace {
38 #undef LOG_DOMAIN
39 #undef LOG_TAG
40 #define LOG_DOMAIN 0xD002D11
41 #define LOG_TAG "DfxElfParser"
42 }
43 
Read(uintptr_t pos,void * buf,size_t size)44 bool ElfParser::Read(uintptr_t pos, void *buf, size_t size)
45 {
46     if (mmap_->Read(pos, buf, size) == size) {
47         return true;
48     }
49     return false;
50 }
51 
MmapSize()52 size_t ElfParser::MmapSize()
53 {
54     return mmap_->Size();
55 }
56 
GetElfSize()57 uint64_t ElfParser::GetElfSize()
58 {
59     return elfSize_;
60 }
61 
62 template <typename EhdrType, typename PhdrType, typename ShdrType>
ParseAllHeaders()63 bool ElfParser::ParseAllHeaders()
64 {
65     EhdrType ehdr;
66     if (!Read(0, &ehdr, sizeof(ehdr))) {
67         return false;
68     }
69 
70     if (!ParseElfHeaders<EhdrType>(ehdr)) {
71         DFXLOG_WARN("ParseElfHeaders failed");
72         return false;
73     }
74 
75     if (!ParseProgramHeaders<EhdrType, PhdrType>(ehdr)) {
76         DFXLOG_WARN("ParseProgramHeaders failed");
77         return false;
78     }
79 
80     if (!ParseSectionHeaders<EhdrType, ShdrType>(ehdr)) {
81         DFXLOG_WARN("ParseSectionHeaders failed");
82         return false;
83     }
84     return true;
85 }
86 
87 template <typename EhdrType>
ParseElfHeaders(const EhdrType & ehdr)88 bool ElfParser::ParseElfHeaders(const EhdrType& ehdr)
89 {
90     if (ehdr.e_shnum == 0) {
91         return false;
92     }
93 
94     auto machine = ehdr.e_machine;
95     if (machine == EM_ARM) {
96         archType_ = ARCH_ARM;
97     } else if (machine == EM_386) {
98         archType_ = ARCH_X86;
99     } else if (machine == EM_AARCH64) {
100         archType_ = ARCH_ARM64;
101     } else if (machine == EM_RISCV) {
102         archType_ = ARCH_RISCV64;
103     } else if (machine == EM_X86_64) {
104         archType_ = ARCH_X86_64;
105     } else {
106         LOGW("Failed the machine = %d", machine);
107     }
108     elfSize_ = ehdr.e_shoff + ehdr.e_shentsize * ehdr.e_shnum;
109     return true;
110 }
111 
112 template <typename EhdrType, typename PhdrType>
ParseProgramHeaders(const EhdrType & ehdr)113 bool ElfParser::ParseProgramHeaders(const EhdrType& ehdr)
114 {
115     uint64_t offset = ehdr.e_phoff;
116     bool firstLoadHeader = true;
117     for (size_t i = 0; i < ehdr.e_phnum; i++, offset += ehdr.e_phentsize) {
118         PhdrType phdr;
119         if (!Read((uintptr_t)offset, &phdr, sizeof(phdr))) {
120             return false;
121         }
122 
123         switch (phdr.p_type) {
124             case PT_LOAD: {
125                 ElfLoadInfo loadInfo;
126                 loadInfo.offset = phdr.p_offset;
127                 loadInfo.tableVaddr = phdr.p_vaddr;
128                 loadInfo.tableSize = static_cast<size_t>(phdr.p_memsz);
129                 loadInfo.align = phdr.p_align;
130                 uint64_t len = loadInfo.tableSize + (loadInfo.tableVaddr & (loadInfo.align - 1));
131                 loadInfo.mmapLen = len - (len & (loadInfo.align - 1)) + loadInfo.align;
132                 ptLoads_[phdr.p_offset] = loadInfo;
133                 if ((phdr.p_flags & PF_X) == 0) {
134                     continue;
135                 }
136                 // Only set the load bias from the first executable load header.
137                 if (firstLoadHeader) {
138                     loadBias_ = static_cast<int64_t>(static_cast<uint64_t>(phdr.p_vaddr) - phdr.p_offset);
139                 }
140                 firstLoadHeader = false;
141 
142                 if (phdr.p_vaddr < startVaddr_) {
143                     startVaddr_ = phdr.p_vaddr;
144                     startOffset_ = phdr.p_offset;
145                 }
146                 if (phdr.p_vaddr + phdr.p_memsz > endVaddr_) {
147                     endVaddr_ = phdr.p_vaddr + phdr.p_memsz;
148                 }
149                 LOGU("Elf startVaddr: %" PRIx64 ", endVaddr: %" PRIx64 "", (uint64_t)startVaddr_, (uint64_t)endVaddr_);
150                 break;
151             }
152             case PT_DYNAMIC: {
153                 dynamicOffset_ = phdr.p_offset;
154                 break;
155             }
156             default:
157                 break;
158         }
159     }
160     return true;
161 }
162 
GetMiniDebugInfo()163 std::shared_ptr<MiniDebugInfo> ElfParser::GetMiniDebugInfo()
164 {
165     return minidebugInfo_;
166 }
167 
168 template <typename EhdrType, typename ShdrType>
ParseSectionHeaders(const EhdrType & ehdr)169 bool ElfParser::ParseSectionHeaders(const EhdrType& ehdr)
170 {
171     uint64_t offset = ehdr.e_shoff;
172 
173     ShdrType shdr;
174     //section header string table index. include section header table with section name string table.
175     if (ehdr.e_shstrndx < ehdr.e_shnum) {
176         uint64_t secOffset = 0;
177         uint64_t secSize = 0;
178         uint64_t shNdxOffset = offset + ehdr.e_shstrndx * ehdr.e_shentsize;
179         if (!Read((uintptr_t)shNdxOffset, &shdr, sizeof(shdr))) {
180             LOGE("Read section header string table failed");
181             return false;
182         }
183         secOffset = shdr.sh_offset;
184         secSize = shdr.sh_size;
185         if (!ParseStrTab(sectionNames_, secOffset, secSize)) {
186             return false;
187         }
188     } else {
189         LOGE("e_shstrndx(%u) cannot greater than or equal e_shnum(%u)", ehdr.e_shstrndx, ehdr.e_shnum);
190         return false;
191     }
192 
193     offset += ehdr.e_shentsize;
194     for (size_t i = 1; i < ehdr.e_shnum; i++, offset += ehdr.e_shentsize) {
195         if (i == ehdr.e_shstrndx) {
196             continue;
197         }
198         if (!Read((uintptr_t)offset, &shdr, sizeof(shdr))) {
199             return false;
200         }
201 
202         std::string secName;
203         if (!GetSectionNameByIndex(secName, shdr.sh_name)) {
204             LOGE("Failed to get section name");
205             continue;
206         }
207 
208         if (shdr.sh_size != 0 && secName == GNU_DEBUGDATA) {
209             minidebugInfo_ = std::make_shared<MiniDebugInfo>();
210             minidebugInfo_->offset = static_cast<uint64_t>(shdr.sh_offset);
211             minidebugInfo_->size = static_cast<uintptr_t>(shdr.sh_size);
212         }
213 
214         ShdrInfo shdrInfo;
215         shdrInfo.addr = static_cast<uint64_t>(shdr.sh_addr);
216         shdrInfo.entSize = static_cast<uint64_t>(shdr.sh_entsize);
217         shdrInfo.size = static_cast<uint64_t>(shdr.sh_size);
218         shdrInfo.offset = static_cast<uint64_t>(shdr.sh_offset);
219         shdrInfoPairs_.emplace(std::make_pair(i, secName), shdrInfo);
220 
221         if (shdr.sh_type == SHT_SYMTAB || shdr.sh_type == SHT_DYNSYM) {
222             if (shdr.sh_link >= ehdr.e_shnum) {
223                 continue;
224             }
225             ElfShdr elfShdr;
226             elfShdr.name = static_cast<uint32_t>(shdr.sh_name);
227             elfShdr.type = static_cast<uint32_t>(shdr.sh_type);
228             elfShdr.flags = static_cast<uint64_t>(shdr.sh_flags);
229             elfShdr.addr = static_cast<uint64_t>(shdr.sh_addr);
230             elfShdr.offset = static_cast<uint64_t>(shdr.sh_offset);
231             elfShdr.size = static_cast<uint64_t>(shdr.sh_size);
232             elfShdr.link = static_cast<uint32_t>(shdr.sh_link);
233             elfShdr.info = static_cast<uint32_t>(shdr.sh_info);
234             elfShdr.addrAlign = static_cast<uint64_t>(shdr.sh_addralign);
235             elfShdr.entSize = static_cast<uint64_t>(shdr.sh_entsize);
236             symShdrs_.emplace_back(elfShdr);
237         }
238     }
239     return true;
240 }
241 
242 template <typename DynType>
ParseElfDynamic()243 bool ElfParser::ParseElfDynamic()
244 {
245     if (dynamicOffset_ == 0) {
246         return false;
247     }
248 
249     DynType *dyn = (DynType *)(dynamicOffset_ + static_cast<char*>(mmap_->Get()));
250     for (; dyn->d_tag != DT_NULL; ++dyn) {
251         if (dyn->d_tag == DT_PLTGOT) {
252             // Assume that _DYNAMIC is writable and GLIBC has relocated it (true for x86 at least).
253             dtPltGotAddr_ = dyn->d_un.d_ptr;
254             break;
255         } else if (dyn->d_tag == DT_STRTAB) {
256             dtStrtabAddr_ = dyn->d_un.d_ptr;
257         } else if (dyn->d_tag == DT_STRSZ) {
258             dtStrtabSize_ = dyn->d_un.d_val;
259         } else if (dyn->d_tag == DT_SONAME) {
260             dtSonameOffset_ = dyn->d_un.d_val;
261         }
262     }
263     return true;
264 }
265 
266 template <typename DynType>
ParseElfName()267 bool ElfParser::ParseElfName()
268 {
269     if (!ParseElfDynamic<DynType>()) {
270         return false;
271     }
272     ShdrInfo shdrInfo;
273     if (!GetSectionInfo(shdrInfo, DYNSTR)) {
274         return false;
275     }
276     uintptr_t sonameOffset = shdrInfo.offset + dtSonameOffset_;
277     uint64_t sonameOffsetMax = shdrInfo.offset + dtStrtabSize_;
278     size_t maxStrSize = static_cast<size_t>(sonameOffsetMax - sonameOffset);
279     mmap_->ReadString(sonameOffset, &soname_, maxStrSize);
280     LOGU("parse current elf file soname is %s.", soname_.c_str());
281     return true;
282 }
283 
284 template <typename SymType>
IsFunc(const SymType sym)285 bool ElfParser::IsFunc(const SymType sym)
286 {
287     return ((sym.st_shndx != SHN_UNDEF) &&
288         (ELF32_ST_TYPE(sym.st_info) == STT_FUNC || ELF32_ST_TYPE(sym.st_info) == STT_GNU_IFUNC));
289 }
290 
291 template <typename SymType>
ParseElfSymbols(bool isFunc)292 bool ElfParser::ParseElfSymbols(bool isFunc)
293 {
294     if (symShdrs_.empty()) {
295         return false;
296     }
297 
298     elfSymbols_.clear();
299     for (const auto &iter : symShdrs_) {
300         const auto &shdr = iter;
301         ParseElfSymbols<SymType>(shdr, isFunc);
302     }
303     return (elfSymbols_.size() > 0);
304 }
305 
306 template <typename SymType>
ParseElfSymbols(ElfShdr shdr,bool isFunc)307 bool ElfParser::ParseElfSymbols(ElfShdr shdr, bool isFunc)
308 {
309     ShdrInfo linkShdrInfo;
310     if (!GetSectionInfo(linkShdrInfo, shdr.link)) {
311         return false;
312     }
313 
314     uint32_t count = static_cast<uint32_t>((shdr.entSize != 0) ? (shdr.size / shdr.entSize) : 0);
315     for (uint32_t idx = 0; idx < count; ++idx) {
316         uintptr_t offset = static_cast<uintptr_t>(shdr.offset + idx * shdr.entSize);
317         SymType sym;
318         if (!Read(offset, &sym, sizeof(sym))) {
319             continue;
320         }
321         if (sym.st_value == 0 || sym.st_size == 0) {
322             continue;
323         }
324         ElfSymbol elfSymbol;
325         if (isFunc && (!ParseElfSymbolName(linkShdrInfo, sym, elfSymbol.nameStr))) {
326             continue;
327         }
328         elfSymbol.value = static_cast<uint64_t>(sym.st_value);
329         elfSymbol.size = static_cast<uint64_t>(sym.st_size);
330         elfSymbol.name = static_cast<uint32_t>(sym.st_name);
331         elfSymbols_.emplace_back(elfSymbol);
332     }
333     LOGU("elfSymbols.size: %" PRIuPTR "", elfSymbols_.size());
334     return true;
335 }
336 
337 template <typename SymType>
ParseElfSymbolName(ShdrInfo linkShdr,SymType sym,std::string & nameStr)338 bool ElfParser::ParseElfSymbolName(ShdrInfo linkShdr, SymType sym, std::string& nameStr)
339 {
340     if (!IsFunc(sym) || (static_cast<uint64_t>(sym.st_name) >= linkShdr.size)) {
341         return false;
342     }
343     uintptr_t nameOffset = static_cast<uintptr_t>(linkShdr.offset + sym.st_name);
344     nameStr = std::string(static_cast<char*>(mmap_->Get()) + nameOffset);
345     return true;
346 }
347 
348 template <typename SymType>
ParseElfSymbolByAddr(uint64_t addr,ElfSymbol & elfSymbol)349 bool ElfParser::ParseElfSymbolByAddr(uint64_t addr, ElfSymbol& elfSymbol)
350 {
351     if (symShdrs_.empty()) {
352         return false;
353     }
354 
355     for (const auto &shdr : symShdrs_) {
356         ShdrInfo linkShdrInfo;
357         if (!GetSectionInfo(linkShdrInfo, shdr.link)) {
358             return false;
359         }
360 
361         uint32_t count = static_cast<uint32_t>((shdr.entSize != 0) ? (shdr.size / shdr.entSize) : 0);
362         for (uint32_t idx = 0; idx < count; ++idx) {
363             uintptr_t offset = static_cast<uintptr_t>(shdr.offset + idx * shdr.entSize);
364             SymType sym;
365             if (!Read(offset, &sym, sizeof(sym))) { // todo inplace search
366                 continue;
367             }
368             if (sym.st_value == 0 || sym.st_size == 0) {
369                 continue;
370             }
371 
372             if ((sym.st_value <= addr) && (addr < (sym.st_value + sym.st_size)) &&
373                 ParseElfSymbolName(linkShdrInfo, sym, elfSymbol.nameStr)) {
374                 elfSymbol.value = static_cast<uint64_t>(sym.st_value);
375                 elfSymbol.size = static_cast<uint64_t>(sym.st_size);
376                 elfSymbol.name = static_cast<uint32_t>(sym.st_name);
377                 LOGU("Parse elf symbol nameStr: %s", elfSymbol.nameStr.c_str());
378                 return true;
379             }
380         }
381     }
382     return false;
383 }
384 
GetSectionNameByIndex(std::string & nameStr,const uint32_t name)385 bool ElfParser::GetSectionNameByIndex(std::string& nameStr, const uint32_t name)
386 {
387     if (sectionNames_.empty() || name >= sectionNames_.size()) {
388         LOGE("name index(%u) out of range, size: %" PRIuPTR "", name, sectionNames_.size());
389         return false;
390     }
391 
392     size_t endIndex = sectionNames_.find('\0', name);
393     if (endIndex != std::string::npos) {
394         nameStr = sectionNames_.substr(name, endIndex - name);
395         return true;
396     }
397     return false;
398 }
399 
ParseStrTab(std::string & nameStr,const uint64_t offset,const uint64_t size)400 bool ElfParser::ParseStrTab(std::string& nameStr, const uint64_t offset, const uint64_t size)
401 {
402     if (size > MmapSize()) {
403         LOGE("size(%" PRIu64 ") is too large.", size);
404         return false;
405     }
406     char *namesBuf = new char[size];
407     if (namesBuf == nullptr) {
408         LOGE("New failed");
409         return false;
410     }
411     (void)memset_s(namesBuf, size, '\0', size);
412     if (!Read((uintptr_t)offset, namesBuf, size)) {
413         LOGE("Read failed");
414         delete[] namesBuf;
415         namesBuf = nullptr;
416         return false;
417     }
418     nameStr = std::string(namesBuf, namesBuf + size);
419     delete[] namesBuf;
420     namesBuf = nullptr;
421     return true;
422 }
423 
GetSectionInfo(ShdrInfo & shdr,const uint32_t idx)424 bool ElfParser::GetSectionInfo(ShdrInfo& shdr, const uint32_t idx)
425 {
426     for (const auto &iter: shdrInfoPairs_) {
427         auto tmpPair = iter.first;
428         if (tmpPair.first == idx) {
429             shdr = iter.second;
430             return true;
431         }
432     }
433     return false;
434 }
435 
GetSectionInfo(ShdrInfo & shdr,const std::string & secName)436 bool ElfParser::GetSectionInfo(ShdrInfo& shdr, const std::string& secName)
437 {
438     for (const auto &iter: shdrInfoPairs_) {
439         auto tmpPair = iter.first;
440         if (tmpPair.second == secName) {
441             shdr = iter.second;
442             return true;
443         }
444     }
445     return false;
446 }
447 
GetSectionData(unsigned char * buf,uint64_t size,std::string secName)448 bool ElfParser::GetSectionData(unsigned char *buf, uint64_t size, std::string secName)
449 {
450     ShdrInfo shdr;
451     if (GetSectionInfo(shdr, secName)) {
452         if (Read(shdr.offset, buf, size)) {
453             return true;
454         }
455     } else {
456         LOGE("Failed to get data from secName %s", secName.c_str());
457     }
458     return false;
459 }
460 
InitHeaders()461 bool ElfParser32::InitHeaders()
462 {
463     return ParseAllHeaders<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>();
464 }
465 
InitHeaders()466 bool ElfParser64::InitHeaders()
467 {
468     return ParseAllHeaders<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>();
469 }
470 
GetElfName()471 std::string ElfParser32::GetElfName()
472 {
473     if (soname_ == "") {
474         ParseElfName<Elf32_Dyn>();
475     }
476     return soname_;
477 }
478 
GetElfName()479 std::string ElfParser64::GetElfName()
480 {
481     if (soname_ == "") {
482         ParseElfName<Elf64_Dyn>();
483     }
484     return soname_;
485 }
486 
GetGlobalPointer()487 uintptr_t ElfParser32::GetGlobalPointer()
488 {
489     if (dtPltGotAddr_ == 0) {
490         ParseElfDynamic<Elf32_Dyn>();
491     }
492     return dtPltGotAddr_;
493 }
494 
GetGlobalPointer()495 uintptr_t ElfParser64::GetGlobalPointer()
496 {
497     if (dtPltGotAddr_ == 0) {
498         ParseElfDynamic<Elf64_Dyn>();
499     }
500     return dtPltGotAddr_;
501 }
502 
GetElfSymbols(bool isFunc)503 const std::vector<ElfSymbol>& ElfParser32::GetElfSymbols(bool isFunc)
504 {
505     ParseElfSymbols<Elf32_Sym>(isFunc);
506     return elfSymbols_;
507 }
508 
GetElfSymbols(bool isFunc)509 const std::vector<ElfSymbol>& ElfParser64::GetElfSymbols(bool isFunc)
510 {
511     ParseElfSymbols<Elf64_Sym>(isFunc);
512     return elfSymbols_;
513 }
514 
GetElfSymbolByAddr(uint64_t addr,ElfSymbol & elfSymbol)515 bool ElfParser32::GetElfSymbolByAddr(uint64_t addr, ElfSymbol& elfSymbol)
516 {
517     return ParseElfSymbolByAddr<Elf32_Sym>(addr, elfSymbol);
518 }
519 
GetElfSymbolByAddr(uint64_t addr,ElfSymbol & elfSymbol)520 bool ElfParser64::GetElfSymbolByAddr(uint64_t addr, ElfSymbol& elfSymbol)
521 {
522     return ParseElfSymbolByAddr<Elf64_Sym>(addr, elfSymbol);
523 }
524 } // namespace HiviewDFX
525 } // namespace OHOS
526