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