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