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