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