1 /*
2 * Copyright (c) 2021-2024 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.h"
17
18 #include <algorithm>
19 #include <cstdlib>
20 #include <fcntl.h>
21 #include <securec.h>
22 #include <string>
23 #if is_mingw
24 #include "dfx_nonlinux_define.h"
25 #else
26 #include <elf.h>
27 #include <sys/mman.h>
28 #endif
29 #include <sys/stat.h>
30 #include <sys/types.h>
31 #include <unistd.h>
32 #include <utility>
33
34 #include "dfx_define.h"
35 #include "dfx_log.h"
36 #include "dfx_instr_statistic.h"
37 #include "dfx_util.h"
38 #include "dfx_maps.h"
39 #include "dfx_trace_dlsym.h"
40 #include "dwarf_define.h"
41 #if defined(ENABLE_MINIDEBUGINFO)
42 #include "dfx_xz_utils.h"
43 #endif
44 #include "string_util.h"
45 #include "unwinder_config.h"
46
47 namespace OHOS {
48 namespace HiviewDFX {
49 namespace {
50 #undef LOG_DOMAIN
51 #undef LOG_TAG
52 #define LOG_DOMAIN 0xD002D11
53 #define LOG_TAG "DfxElf"
54
55 #if is_ohos && !is_mingw
56 enum HdrSection {
57 SECTION_TEXT = 0,
58 SECTION_ARMEXIDX = 1,
59 SECTION_DYNAMIC = 2,
60 SECTION_EHFRAMEHDR = 3
61 };
62 #endif
63 }
64
Create(const std::string & path)65 std::shared_ptr<DfxElf> DfxElf::Create(const std::string& path)
66 {
67 auto elf = std::make_shared<DfxElf>(path);
68 if (elf->IsValid()) {
69 return elf;
70 }
71 return nullptr;
72 }
73
CreateFromHap(const std::string & file,std::shared_ptr<DfxMap> prevMap,uint64_t & offset)74 std::shared_ptr<DfxElf> DfxElf::CreateFromHap(const std::string& file, std::shared_ptr<DfxMap> prevMap,
75 uint64_t& offset)
76 {
77 // elf header is in the first mmap area
78 // c3840000-c38a6000 r--p 00174000 /data/storage/el1/bundle/entry.hap <- program header
79 // c38a6000-c3945000 r-xp 001d9000 /data/storage/el1/bundle/entry.hap <- pc is in this region
80 // c3945000-c394b000 r--p 00277000 /data/storage/el1/bundle/entry.hap
81 // c394b000-c394c000 rw-p 0027c000 /data/storage/el1/bundle/entry.hap
82 if (prevMap == nullptr) {
83 DFXLOGE("current hap mapitem has no prev mapitem, maybe pc is wrong?");
84 return nullptr;
85 }
86 if (!StartsWith(file, "/proc") || !EndsWith(file, ".hap")) {
87 DFXLOGD("Illegal file path, please check file: %{public}s", file.c_str());
88 return nullptr;
89 }
90 int fd = OHOS_TEMP_FAILURE_RETRY(open(file.c_str(), O_RDONLY));
91 if (fd < 0) {
92 DFXLOGE("Failed to open hap file, errno(%{public}d)", errno);
93 return nullptr;
94 }
95 auto fileSize = GetFileSize(fd);
96 size_t elfSize = 0;
97 size_t size = prevMap->end - prevMap->begin;
98 do {
99 auto mmap = std::make_shared<DfxMmap>();
100 if (!mmap->Init(fd, size, (off_t)prevMap->offset)) {
101 DFXLOGE("Failed to mmap program header in hap.");
102 break;
103 }
104
105 elfSize = GetElfSize(mmap->Get());
106 if (elfSize <= 0 || elfSize + prevMap->offset > static_cast<uint64_t>(fileSize)) {
107 DFXLOGE("Invalid elf size? elf size: %{public}d, hap size: %{public}d", (int)elfSize, (int)fileSize);
108 elfSize = 0;
109 break;
110 }
111
112 offset -= prevMap->offset;
113 } while (false);
114
115 if (elfSize != 0) {
116 DFXLOGU("elfSize: %{public}zu", elfSize);
117 auto elf = std::make_shared<DfxElf>(fd, elfSize, prevMap->offset);
118 if (elf->IsValid()) {
119 close(fd);
120 elf->SetBaseOffset(prevMap->offset);
121 return elf;
122 }
123 }
124 close(fd);
125 return nullptr;
126 }
127
DfxElf(const std::string & file)128 DfxElf::DfxElf(const std::string& file)
129 {
130 if (mmap_ == nullptr && (!file.empty())) {
131 DFXLOGU("file: %{public}s", file.c_str());
132 #if defined(is_ohos) && is_ohos
133 if (!DfxMaps::IsLegalMapItem(file)) {
134 DFXLOGD("Illegal map file, please check file: %{public}s", file.c_str());
135 return;
136 }
137 #endif
138 std::string realPath = file;
139 if (!StartsWith(file, "/proc/")) { // sandbox file should not be check by realpath function
140 if (!RealPath(file, realPath)) {
141 DFXLOGW("Failed to realpath %{public}s, errno(%{public}d)", file.c_str(), errno);
142 return;
143 }
144 }
145 #if defined(is_mingw) && is_mingw
146 int fd = OHOS_TEMP_FAILURE_RETRY(open(realPath.c_str(), O_RDONLY | O_BINARY));
147 #else
148 int fd = OHOS_TEMP_FAILURE_RETRY(open(realPath.c_str(), O_RDONLY));
149 #endif
150 if (fd > 0) {
151 auto size = static_cast<size_t>(GetFileSize(fd));
152 mmap_ = std::make_shared<DfxMmap>();
153 if (!mmap_->Init(fd, size, 0)) {
154 DFXLOGE("Failed to mmap init.");
155 }
156 close(fd);
157 } else {
158 DFXLOGE("Failed to open file: %{public}s", file.c_str());
159 }
160 }
161 Init();
162 }
163
DfxElf(const int fd,const size_t elfSz,const off_t offset)164 DfxElf::DfxElf(const int fd, const size_t elfSz, const off_t offset)
165 {
166 if (mmap_ == nullptr) {
167 mmap_ = std::make_shared<DfxMmap>();
168 if (!mmap_->Init(fd, elfSz, offset)) {
169 DFXLOGE("Failed to mmap init elf in hap.");
170 }
171 }
172 Init();
173 }
174
DfxElf(uint8_t * decompressedData,size_t size)175 DfxElf::DfxElf(uint8_t* decompressedData, size_t size)
176 {
177 if (mmap_ == nullptr) {
178 mmap_ = std::make_shared<DfxMmap>();
179 // this mean the embedded elf initialization.
180 mmap_->Init(decompressedData, size);
181 mmap_->SetNeedUnmap(false);
182 }
183 Init();
184 }
185
Init()186 void DfxElf::Init()
187 {
188 uti_.namePtr = 0;
189 uti_.format = -1;
190 hasTableInfo_ = false;
191 }
192
Clear()193 void DfxElf::Clear()
194 {
195 if (elfParse_ != nullptr) {
196 elfParse_.reset();
197 elfParse_ = nullptr;
198 }
199
200 if (mmap_ != nullptr) {
201 mmap_->Clear();
202 mmap_.reset();
203 mmap_ = nullptr;
204 }
205 }
206
IsEmbeddedElfValid()207 bool DfxElf::IsEmbeddedElfValid()
208 {
209 #if defined(ENABLE_MINIDEBUGINFO)
210 if (embeddedElf_ == nullptr) {
211 return InitEmbeddedElf();
212 }
213 return embeddedElf_ != nullptr && embeddedElf_->IsValid();
214 #endif
215 return false;
216 }
217
GetEmbeddedElf()218 std::shared_ptr<DfxElf> DfxElf::GetEmbeddedElf()
219 {
220 return embeddedElf_;
221 }
222
GetMiniDebugInfo()223 std::shared_ptr<MiniDebugInfo> DfxElf::GetMiniDebugInfo()
224 {
225 return miniDebugInfo_;
226 }
227
InitEmbeddedElf()228 bool DfxElf::InitEmbeddedElf()
229 {
230 #if defined(ENABLE_MINIDEBUGINFO)
231 DFX_TRACE_SCOPED_DLSYM("InitEmbeddedElf");
232 if (!UnwinderConfig::GetEnableMiniDebugInfo() || miniDebugInfo_ == nullptr || GetMmapPtr() == nullptr) {
233 return false;
234 }
235 uint8_t* addr = miniDebugInfo_->offset + const_cast<uint8_t*>(GetMmapPtr());
236 embeddedElfData_ = std::make_shared<std::vector<uint8_t>>();
237 if (embeddedElfData_ == nullptr) {
238 DFXLOGE("Create embeddedElfData failed.");
239 return false;
240 }
241 if (XzDecompress(addr, miniDebugInfo_->size, embeddedElfData_)) {
242 // embeddedElfData_ store the decompressed bytes.
243 // use these bytes to construct an elf.
244 embeddedElf_ = std::make_shared<DfxElf>(embeddedElfData_->data(), embeddedElfData_->size());
245 if (embeddedElf_ != nullptr && embeddedElf_->IsValid()) {
246 return true;
247 } else {
248 DFXLOGE("Failed to parse Embedded Elf.");
249 }
250 } else {
251 DFXLOGE("Failed to decompressed .gnu_debugdata seciton.");
252 }
253 #endif
254 return false;
255 }
256
InitHeaders()257 bool DfxElf::InitHeaders()
258 {
259 if (mmap_ == nullptr) {
260 return false;
261 }
262
263 if (elfParse_ != nullptr) {
264 return true;
265 }
266
267 uint8_t ident[SELFMAG + 1];
268 if (!Read(0, ident, SELFMAG) || !IsValidElf(ident, SELFMAG)) {
269 return false;
270 }
271
272 if (!Read(EI_CLASS, &classType_, sizeof(uint8_t))) {
273 return false;
274 }
275
276 if (classType_ == ELFCLASS32) {
277 elfParse_ = std::unique_ptr<ElfParser>(new ElfParser32(mmap_));
278 } else if (classType_ == ELFCLASS64) {
279 elfParse_ = std::unique_ptr<ElfParser>(new ElfParser64(mmap_));
280 } else {
281 DFXLOGW("InitHeaders failed, classType: %{public}d", classType_);
282 return false;
283 }
284 if (elfParse_ != nullptr) {
285 valid_ = true;
286 elfParse_->InitHeaders();
287 #if defined(ENABLE_MINIDEBUGINFO)
288 miniDebugInfo_ = elfParse_->GetMiniDebugInfo();
289 #endif
290 }
291 return valid_;
292 }
293
IsValid()294 bool DfxElf::IsValid()
295 {
296 if (valid_ == false) {
297 InitHeaders();
298 }
299 return valid_;
300 }
301
GetClassType()302 uint8_t DfxElf::GetClassType()
303 {
304 if (IsValid()) {
305 return classType_;
306 }
307 return ELFCLASSNONE;
308 }
309
GetArchType()310 ArchType DfxElf::GetArchType()
311 {
312 if (IsValid()) {
313 elfParse_->GetArchType();
314 }
315 return ARCH_UNKNOWN;
316 }
317
GetLoadBias()318 int64_t DfxElf::GetLoadBias()
319 {
320 if (loadBias_ == 0) {
321 if (IsValid()) {
322 loadBias_ = elfParse_->GetLoadBias();
323 DFXLOGU("Elf loadBias: %{public}" PRIx64 "", (uint64_t)loadBias_);
324 }
325 }
326 return loadBias_;
327 }
328
GetLoadBase(uint64_t mapStart,uint64_t mapOffset)329 uint64_t DfxElf::GetLoadBase(uint64_t mapStart, uint64_t mapOffset)
330 {
331 if (loadBase_ == static_cast<uint64_t>(-1)) {
332 if (IsValid()) {
333 DFXLOGU("mapStart: %{public}" PRIx64 ", mapOffset: %{public}" PRIx64 "",
334 (uint64_t)mapStart, (uint64_t)mapOffset);
335 loadBase_ = mapStart - mapOffset - static_cast<uint64_t>(GetLoadBias());
336 DFXLOGU("Elf loadBase: %{public}" PRIx64 "", (uint64_t)loadBase_);
337 }
338 }
339 return loadBase_;
340 }
341
SetLoadBase(uint64_t base)342 void DfxElf::SetLoadBase(uint64_t base)
343 {
344 loadBase_ = base;
345 }
346
SetBaseOffset(uint64_t offset)347 void DfxElf::SetBaseOffset(uint64_t offset)
348 {
349 baseOffset_ = offset;
350 }
351
GetBaseOffset()352 uint64_t DfxElf::GetBaseOffset()
353 {
354 return baseOffset_;
355 }
356
GetStartPc()357 uint64_t DfxElf::GetStartPc()
358 {
359 if (startPc_ == static_cast<uint64_t>(-1)) {
360 if (IsValid()) {
361 auto startVaddr = elfParse_->GetStartVaddr();
362 if (loadBase_ != static_cast<uint64_t>(-1) && startVaddr != static_cast<uint64_t>(-1)) {
363 startPc_ = startVaddr + loadBase_;
364 DFXLOGU("Elf startPc: %{public}" PRIx64 "", (uint64_t)startPc_);
365 }
366 }
367 }
368 return startPc_;
369 }
370
GetStartVaddr()371 uint64_t DfxElf::GetStartVaddr()
372 {
373 if (IsValid()) {
374 return elfParse_->GetStartVaddr();
375 }
376 return 0;
377 }
378
GetEndPc()379 uint64_t DfxElf::GetEndPc()
380 {
381 if (endPc_ == 0) {
382 if (IsValid()) {
383 auto endVaddr = elfParse_->GetEndVaddr();
384 if (loadBase_ != static_cast<uint64_t>(-1) && endVaddr != 0) {
385 endPc_ = endVaddr + loadBase_;
386 DFXLOGU("Elf endPc: %{public}" PRIx64 "", (uint64_t)endPc_);
387 }
388 }
389 }
390 return endPc_;
391 }
392
GetEndVaddr()393 uint64_t DfxElf::GetEndVaddr()
394 {
395 if (IsValid()) {
396 return elfParse_->GetEndVaddr();
397 }
398 return 0;
399 }
400
GetStartOffset()401 uint64_t DfxElf::GetStartOffset()
402 {
403 if (IsValid()) {
404 return elfParse_->GetStartOffset();
405 }
406 return 0;
407 }
408
GetRelPc(uint64_t pc,uint64_t mapStart,uint64_t mapOffset)409 uint64_t DfxElf::GetRelPc(uint64_t pc, uint64_t mapStart, uint64_t mapOffset)
410 {
411 return (pc - GetLoadBase(mapStart, mapOffset));
412 }
413
GetElfSize()414 uint64_t DfxElf::GetElfSize()
415 {
416 if (!IsValid()) {
417 return 0;
418 }
419 return elfParse_->GetElfSize();
420 }
421
GetElfName()422 std::string DfxElf::GetElfName()
423 {
424 if (!IsValid()) {
425 return "";
426 }
427 return elfParse_->GetElfName();
428 }
429
SetBuildId(const std::string & buildId)430 void DfxElf::SetBuildId(const std::string& buildId)
431 {
432 buildId_ = buildId;
433 }
434
GetBuildId()435 std::string DfxElf::GetBuildId()
436 {
437 if (buildId_.empty()) {
438 if (!IsValid()) {
439 return "";
440 }
441 ShdrInfo shdr;
442 if ((GetSectionInfo(shdr, NOTE_GNU_BUILD_ID) || GetSectionInfo(shdr, NOTES)) && GetMmapPtr() != nullptr) {
443 std::string buildIdHex = GetBuildId((uint64_t)((char*)GetMmapPtr() + shdr.offset), shdr.size);
444 if (!buildIdHex.empty()) {
445 buildId_ = ToReadableBuildId(buildIdHex);
446 DFXLOGU("Elf buildId: %{public}s", buildId_.c_str());
447 }
448 }
449 }
450 return buildId_;
451 }
452
GetBuildId(uint64_t noteAddr,uint64_t noteSize)453 std::string DfxElf::GetBuildId(uint64_t noteAddr, uint64_t noteSize)
454 {
455 uint64_t tmp;
456 if (__builtin_add_overflow(noteAddr, noteSize, &tmp)) {
457 DFXLOGE("noteAddr overflow");
458 return "";
459 }
460 uint64_t offset = 0;
461 while (offset < noteSize) {
462 ElfW(Nhdr) nhdr;
463 if (noteSize - offset < sizeof(nhdr)) {
464 return "";
465 }
466 uint64_t ptr = noteAddr + offset;
467 if (memcpy_s(&nhdr, sizeof(nhdr), reinterpret_cast<void*>(ptr), sizeof(nhdr)) != 0) {
468 DFXLOGE("memcpy_s nhdr failed");
469 return "";
470 }
471 offset += sizeof(nhdr);
472 if (noteSize - offset < nhdr.n_namesz) {
473 return "";
474 }
475 if (nhdr.n_namesz > 0) {
476 std::string name(nhdr.n_namesz, '\0');
477 ptr = noteAddr + offset;
478 if (memcpy_s(&(name[0]), nhdr.n_namesz, reinterpret_cast<void*>(ptr), nhdr.n_namesz) != 0) {
479 DFXLOGE("memcpy_s note name failed");
480 return "";
481 }
482 // Trim trailing \0 as GNU is stored as a C string in the ELF file.
483 if (name.size() != 0 && name.back() == '\0') {
484 name.resize(name.size() - 1);
485 }
486 // Align nhdr.n_namesz to next power multiple of 4. See man 5 elf.
487 offset += (nhdr.n_namesz + 3) & ~3; // 3 : Align the offset to a 4-byte boundary
488 if (name != "GNU" || nhdr.n_type != NT_GNU_BUILD_ID) {
489 offset += (nhdr.n_descsz + 3) & ~3; // 3 : Align the offset to a 4-byte boundary
490 continue;
491 }
492 if (noteSize - offset < nhdr.n_descsz || nhdr.n_descsz == 0) {
493 return "";
494 }
495 std::string buildIdRaw(nhdr.n_descsz, '\0');
496 ptr = noteAddr + offset;
497 if (memcpy_s(&buildIdRaw[0], nhdr.n_descsz, reinterpret_cast<void*>(ptr), nhdr.n_descsz) != 0) {
498 return "";
499 }
500 return buildIdRaw;
501 }
502 // Align hdr.n_descsz to next power multiple of 4. See man 5 elf.
503 offset += (nhdr.n_descsz + 3) & ~3; // 3 : Align the offset to a 4-byte boundary
504 }
505 return "";
506 }
507
GetGlobalPointer()508 uintptr_t DfxElf::GetGlobalPointer()
509 {
510 if (!IsValid()) {
511 return 0;
512 }
513 return elfParse_->GetGlobalPointer();
514 }
515
ToReadableBuildId(const std::string & buildIdHex)516 std::string DfxElf::ToReadableBuildId(const std::string& buildIdHex)
517 {
518 if (buildIdHex.empty()) {
519 return "";
520 }
521 static const char HEXTABLE[] = "0123456789abcdef";
522 static const int HEXLENGTH = 16;
523 static const int HEX_EXPAND_PARAM = 2;
524 const size_t len = buildIdHex.length();
525 std::string buildId(len * HEX_EXPAND_PARAM, '\0');
526
527 for (size_t i = 0; i < len; i++) {
528 unsigned int n = buildIdHex[i];
529 buildId[i * HEX_EXPAND_PARAM] = HEXTABLE[(n >> 4) % HEXLENGTH]; // 4 : higher 4 bit of uint8
530 buildId[i * HEX_EXPAND_PARAM + 1] = HEXTABLE[n % HEXLENGTH];
531 }
532 return buildId;
533 }
534
GetSectionInfo(ShdrInfo & shdr,const std::string secName)535 bool DfxElf::GetSectionInfo(ShdrInfo& shdr, const std::string secName)
536 {
537 if (!IsValid()) {
538 return false;
539 }
540 return elfParse_->GetSectionInfo(shdr, secName);
541 }
542
GetSectionData(unsigned char * buf,uint64_t size,std::string secName)543 bool DfxElf::GetSectionData(unsigned char* buf, uint64_t size, std::string secName)
544 {
545 if (!IsValid()) {
546 return false;
547 }
548 return elfParse_->GetSectionData(buf, size, secName);
549 }
550
GetElfSymbols()551 const std::vector<ElfSymbol>& DfxElf::GetElfSymbols()
552 {
553 if (!elfSymbols_.empty()) {
554 return elfSymbols_;
555 }
556 elfSymbols_ = elfParse_->GetElfSymbols(false);
557 #if defined(ENABLE_MINIDEBUGINFO)
558 if (IsEmbeddedElfValid()) {
559 auto symbols = embeddedElf_->elfParse_->GetElfSymbols(false);
560 DFXLOGU("Get EmbeddedElf ElfSymbols, size: %{public}zu", symbols.size());
561 elfSymbols_.insert(elfSymbols_.end(), symbols.begin(), symbols.end());
562 }
563 #endif
564 std::sort(elfSymbols_.begin(), elfSymbols_.end(), [](const ElfSymbol& sym1, const ElfSymbol& sym2) {
565 return sym1.value < sym2.value;
566 });
567 auto pred = [](ElfSymbol a, ElfSymbol b) { return a.value == b.value; };
568 elfSymbols_.erase(std::unique(elfSymbols_.begin(), elfSymbols_.end(), pred), elfSymbols_.end());
569 elfSymbols_.shrink_to_fit();
570 DFXLOGU("GetElfSymbols, size: %{public}zu", elfSymbols_.size());
571 return elfSymbols_;
572 }
573
GetFuncSymbols()574 const std::vector<ElfSymbol>& DfxElf::GetFuncSymbols()
575 {
576 if (!funcSymbols_.empty()) {
577 return funcSymbols_;
578 }
579 funcSymbols_ = elfParse_->GetElfSymbols(true);
580 #if defined(ENABLE_MINIDEBUGINFO)
581 if (IsEmbeddedElfValid()) {
582 auto symbols = embeddedElf_->elfParse_->GetElfSymbols(true);
583 DFXLOGU("Get EmbeddedElf FuncSymbols, size: %{public}zu", symbols.size());
584 funcSymbols_.insert(funcSymbols_.end(), symbols.begin(), symbols.end());
585 }
586 #endif
587 std::sort(funcSymbols_.begin(), funcSymbols_.end(), [](const ElfSymbol& sym1, const ElfSymbol& sym2) {
588 return sym1.value < sym2.value;
589 });
590 auto pred = [](ElfSymbol a, ElfSymbol b) { return a.value == b.value; };
591 funcSymbols_.erase(std::unique(funcSymbols_.begin(), funcSymbols_.end(), pred), funcSymbols_.end());
592 funcSymbols_.shrink_to_fit();
593 DFXLOGU("GetFuncSymbols, size: %{public}zu", funcSymbols_.size());
594 return funcSymbols_;
595 }
596
GetFuncInfoLazily(uint64_t addr,ElfSymbol & elfSymbol)597 bool DfxElf::GetFuncInfoLazily(uint64_t addr, ElfSymbol& elfSymbol)
598 {
599 DFX_TRACE_SCOPED_DLSYM("GetFuncInfoLazily");
600 if (FindFuncSymbol(addr, funcSymbols_, elfSymbol)) {
601 return true;
602 }
603 bool findSymbol = false;
604 #if defined(ENABLE_MINIDEBUGINFO)
605 if (IsEmbeddedElfValid() &&
606 embeddedElf_->elfParse_->GetElfSymbolByAddr(addr, elfSymbol)) {
607 funcSymbols_.emplace_back(elfSymbol);
608 findSymbol = true;
609 }
610 #endif
611
612 if (!findSymbol && elfParse_->GetElfSymbolByAddr(addr, elfSymbol)) {
613 funcSymbols_.emplace_back(elfSymbol);
614 findSymbol = true;
615 }
616
617 if (findSymbol) {
618 std::sort(funcSymbols_.begin(), funcSymbols_.end(), [](const ElfSymbol& sym1, const ElfSymbol& sym2) {
619 return sym1.value < sym2.value;
620 });
621 auto pred = [](ElfSymbol a, ElfSymbol b) { return a.value == b.value; };
622 funcSymbols_.erase(std::unique(funcSymbols_.begin(), funcSymbols_.end(), pred), funcSymbols_.end());
623 funcSymbols_.shrink_to_fit();
624 DFXLOGU("GetFuncInfoLazily, size: %{public}zu", funcSymbols_.size());
625 return true;
626 }
627 return false;
628 }
629
GetFuncInfo(uint64_t addr,ElfSymbol & elfSymbol)630 bool DfxElf::GetFuncInfo(uint64_t addr, ElfSymbol& elfSymbol)
631 {
632 if (UnwinderConfig::GetEnableLoadSymbolLazily()) {
633 return GetFuncInfoLazily(addr, elfSymbol);
634 }
635
636 auto symbols = GetFuncSymbols();
637 return FindFuncSymbol(addr, symbols, elfSymbol);
638 }
639
FindFuncSymbol(uint64_t addr,const std::vector<ElfSymbol> & symbols,ElfSymbol & elfSymbol)640 bool DfxElf::FindFuncSymbol(uint64_t addr, const std::vector<ElfSymbol>& symbols, ElfSymbol& elfSymbol)
641 {
642 DFX_TRACE_SCOPED_DLSYM("FindFuncSymbol");
643 if (symbols.empty()) {
644 return false;
645 }
646 size_t begin = 0;
647 size_t end = symbols.size();
648 while (begin < end) {
649 size_t mid = begin + (end - begin) / 2;
650 const auto& symbol = symbols[mid];
651 if (addr < symbol.value) {
652 end = mid;
653 } else if (addr < (symbol.value + symbol.size)) {
654 elfSymbol = symbol;
655 return true;
656 } else {
657 begin = mid + 1;
658 }
659 }
660 return false;
661 }
662
GetPtLoads()663 const std::unordered_map<uint64_t, ElfLoadInfo>& DfxElf::GetPtLoads()
664 {
665 return elfParse_->GetPtLoads();
666 }
667
FillUnwindTableByExidx(ShdrInfo shdr,uintptr_t loadBase,struct UnwindTableInfo * uti)668 bool DfxElf::FillUnwindTableByExidx(ShdrInfo shdr, uintptr_t loadBase, struct UnwindTableInfo* uti)
669 {
670 if (uti == nullptr) {
671 return false;
672 }
673 uti->gp = 0;
674 uti->tableData = loadBase + shdr.addr;
675 uti->tableLen = shdr.size;
676 INSTR_STATISTIC(InstructionEntriesArmExidx, shdr.size, 0);
677 uti->format = UNW_INFO_FORMAT_ARM_EXIDX;
678 DFXLOGU("[%{public}d]: tableData: %{public}" PRIx64 ", tableLen: %{public}d", __LINE__,
679 (uint64_t)uti->tableData, (int)uti->tableLen);
680 return true;
681 }
682
683 #if is_ohos && !is_mingw
FillUnwindTableByEhhdrLocal(struct DwarfEhFrameHdr * hdr,struct UnwindTableInfo * uti)684 bool DfxElf::FillUnwindTableByEhhdrLocal(struct DwarfEhFrameHdr* hdr, struct UnwindTableInfo* uti)
685 {
686 if (hdr == nullptr) {
687 return false;
688 }
689 if (hdr->version != DW_EH_VERSION) {
690 DFXLOGE("[%{public}d]: version(%{public}d) error", __LINE__, hdr->version);
691 return false;
692 }
693
694 uintptr_t ptr = (uintptr_t)(&(hdr->ehFrame));
695 DFXLOGU("[%{public}d]: hdr: %{public}" PRIx64 ", ehFrame: %{public}" PRIx64 "", __LINE__,
696 (uint64_t)hdr, (uint64_t)ptr);
697
698 auto acc = std::make_shared<DfxAccessorsLocal>();
699 auto memory = std::make_shared<DfxMemory>(acc);
700 DFXLOGU("[%{public}d]: gp: %{public}" PRIx64 ", ehFramePtrEnc: %{public}x, fdeCountEnc: %{public}x", __LINE__,
701 (uint64_t)uti->gp, hdr->ehFramePtrEnc, hdr->fdeCountEnc);
702 memory->SetDataOffset(uti->gp);
703 MAYBE_UNUSED uintptr_t ehFrameStart = memory->ReadEncodedValue(ptr, hdr->ehFramePtrEnc);
704 uintptr_t fdeCount = memory->ReadEncodedValue(ptr, hdr->fdeCountEnc);
705 DFXLOGU("[%{public}d]: ehFrameStart: %{public}" PRIx64 ", fdeCount: %{public}d", __LINE__,
706 (uint64_t)ehFrameStart, (int)fdeCount);
707
708 if (hdr->tableEnc != (DW_EH_PE_datarel | DW_EH_PE_sdata4)) {
709 DFXLOGU("tableEnc: %{public}x", hdr->tableEnc);
710 if (hdr->fdeCountEnc == DW_EH_PE_omit) {
711 fdeCount = ~0UL;
712 }
713 if (hdr->ehFramePtrEnc == DW_EH_PE_omit) {
714 DFXLOGE("ehFramePtrEnc(%{public}x) error", hdr->ehFramePtrEnc);
715 return false;
716 }
717 uti->isLinear = true;
718 uti->tableLen = fdeCount;
719 uti->tableData = ehFrameStart;
720 } else {
721 uti->isLinear = false;
722 uti->tableLen = (fdeCount * sizeof(DwarfTableEntry)) / sizeof(uintptr_t);
723 uti->tableData = ptr;
724 uti->segbase = (uintptr_t)hdr;
725 }
726 uti->format = UNW_INFO_FORMAT_REMOTE_TABLE;
727 DFXLOGU("[%{public}d]: tableData: %{public}" PRIx64 ", tableLen: %{public}d", __LINE__,
728 (uint64_t)uti->tableData, (int)uti->tableLen);
729 return true;
730 }
731 #endif
732
FillUnwindTableByEhhdr(struct DwarfEhFrameHdr * hdr,uintptr_t shdrBase,struct UnwindTableInfo * uti)733 bool DfxElf::FillUnwindTableByEhhdr(struct DwarfEhFrameHdr* hdr, uintptr_t shdrBase, struct UnwindTableInfo* uti)
734 {
735 if ((hdr == nullptr) || (uti == nullptr)) {
736 return false;
737 }
738 if (hdr->version != DW_EH_VERSION) {
739 DFXLOGE("[%{public}d]: version(%{public}d) error", __LINE__, hdr->version);
740 return false;
741 }
742 uintptr_t ptr = (uintptr_t)(&(hdr->ehFrame));
743 DFXLOGU("[%{public}d]: hdr: %{public}" PRIx64 ", ehFrame: %{public}" PRIx64 "", __LINE__,
744 (uint64_t)hdr, (uint64_t)ptr);
745
746 uti->gp = GetGlobalPointer();
747 DFXLOGU("[%{public}d]: gp: %{public}" PRIx64 ", ehFramePtrEnc: %{public}x, fdeCountEnc: %{public}x", __LINE__,
748 (uint64_t)uti->gp, hdr->ehFramePtrEnc, hdr->fdeCountEnc);
749 mmap_->SetDataOffset(uti->gp);
750 auto ptrOffset = ptr - reinterpret_cast<uintptr_t>(GetMmapPtr());
751 MAYBE_UNUSED uintptr_t ehFrameStart = mmap_->ReadEncodedValue(ptrOffset, hdr->ehFramePtrEnc);
752 uintptr_t fdeCount = mmap_->ReadEncodedValue(ptrOffset, hdr->fdeCountEnc);
753 DFXLOGU("[%{public}d]: ehFrameStart: %{public}" PRIx64 ", fdeCount: %{public}d", __LINE__,
754 (uint64_t)ehFrameStart, (int)fdeCount);
755 ptr = reinterpret_cast<uintptr_t>(GetMmapPtr()) + ptrOffset;
756
757 if (hdr->tableEnc != (DW_EH_PE_datarel | DW_EH_PE_sdata4)) {
758 DFXLOGU("[%{public}d]: tableEnc: %{public}x", __LINE__, hdr->tableEnc);
759 if (hdr->fdeCountEnc == DW_EH_PE_omit) {
760 fdeCount = ~0UL;
761 }
762 if (hdr->ehFramePtrEnc == DW_EH_PE_omit) {
763 DFXLOGE("[%{public}d]: ehFramePtrEnc(%{public}x) error", __LINE__, hdr->ehFramePtrEnc);
764 return false;
765 }
766 uti->isLinear = true;
767 uti->tableLen = fdeCount;
768 uti->tableData = shdrBase + ehFrameStart;
769 uti->segbase = shdrBase;
770 } else {
771 uti->isLinear = false;
772 uti->tableLen = (fdeCount * sizeof(DwarfTableEntry)) / sizeof(uintptr_t);
773 uti->tableData = shdrBase + ptr - (uintptr_t)hdr;
774 uti->segbase = shdrBase;
775 }
776 uti->format = UNW_INFO_FORMAT_REMOTE_TABLE;
777 DFXLOGU("[%{public}d]: tableData: %{public}" PRIx64 ", tableLen: %{public}d", __LINE__,
778 (uint64_t)uti->tableData, (int)uti->tableLen);
779 return true;
780 }
781
FindUnwindTableInfo(uintptr_t pc,std::shared_ptr<DfxMap> map,struct UnwindTableInfo & uti)782 int DfxElf::FindUnwindTableInfo(uintptr_t pc, std::shared_ptr<DfxMap> map, struct UnwindTableInfo& uti)
783 {
784 if (hasTableInfo_ && pc >= uti_.startPc && pc < uti_.endPc) {
785 uti = uti_;
786 DFXLOGU("FindUnwindTableInfo had found");
787 return UNW_ERROR_NONE;
788 }
789 if (map == nullptr) {
790 return UNW_ERROR_INVALID_MAP;
791 }
792 uintptr_t loadBase = GetLoadBase(map->begin, map->offset);
793 uti.startPc = GetStartPc();
794 uti.endPc = GetEndPc();
795 if (pc < uti.startPc || pc >= uti.endPc) {
796 DFXLOGU("Elf startPc: %{public}" PRIx64 ", endPc: %{public}" PRIx64 "",
797 (uint64_t)uti.startPc, (uint64_t)uti.endPc);
798 return UNW_ERROR_PC_NOT_IN_UNWIND_INFO;
799 }
800
801 ShdrInfo shdr;
802 #if defined(__arm__)
803 if (GetSectionInfo(shdr, ARM_EXIDX)) {
804 hasTableInfo_ = FillUnwindTableByExidx(shdr, loadBase, &uti);
805 }
806 #endif
807
808 if (!hasTableInfo_) {
809 struct DwarfEhFrameHdr* hdr = nullptr;
810 struct DwarfEhFrameHdr synthHdr;
811 if (GetSectionInfo(shdr, EH_FRAME_HDR) && GetMmapPtr() != nullptr) {
812 INSTR_STATISTIC(InstructionEntriesEhFrame, shdr.size, 0);
813 hdr = (struct DwarfEhFrameHdr *) (shdr.offset + (char *)GetMmapPtr());
814 } else if (GetSectionInfo(shdr, EH_FRAME) && GetMmapPtr() != nullptr) {
815 DFXLOGW("[%{public}d]: Elf(%{public}s) no found .eh_frame_hdr section, " \
816 "using synthetic .eh_frame section", __LINE__, map->name.c_str());
817 INSTR_STATISTIC(InstructionEntriesEhFrame, shdr.size, 0);
818 synthHdr.version = DW_EH_VERSION;
819 synthHdr.ehFramePtrEnc = DW_EH_PE_absptr |
820 ((sizeof(ElfW(Addr)) == 4) ? DW_EH_PE_udata4 : DW_EH_PE_udata8); // 4 : four bytes
821 synthHdr.fdeCountEnc = DW_EH_PE_omit;
822 synthHdr.tableEnc = DW_EH_PE_omit;
823 synthHdr.ehFrame = (ElfW(Addr))(shdr.offset + (char*)GetMmapPtr());
824 hdr = &synthHdr;
825 }
826 uintptr_t shdrBase = static_cast<uintptr_t>(loadBase + shdr.addr);
827 hasTableInfo_ = FillUnwindTableByEhhdr(hdr, shdrBase, &uti);
828 }
829
830 if (hasTableInfo_) {
831 uti_ = uti;
832 return UNW_ERROR_NONE;
833 }
834 return UNW_ERROR_NO_UNWIND_INFO;
835 }
836
FindUnwindTableLocal(uintptr_t pc,struct UnwindTableInfo & uti)837 int DfxElf::FindUnwindTableLocal(uintptr_t pc, struct UnwindTableInfo& uti)
838 {
839 #if is_ohos && !is_mingw
840 DlCbData cbData;
841 (void)memset_s(&cbData, sizeof(cbData), 0, sizeof(cbData));
842 cbData.pc = pc;
843 cbData.uti.format = -1;
844 int ret = dl_iterate_phdr(DlPhdrCb, &cbData);
845 if (ret > 0) {
846 if (cbData.uti.format != -1) {
847 uti = cbData.uti;
848 return UNW_ERROR_NONE;
849 }
850 }
851 return UNW_ERROR_NO_UNWIND_INFO;
852 #else
853 return UNW_ERROR_UNSUPPORTED_VERSION;
854 #endif
855 }
856
857 #if is_ohos && !is_mingw
FindSection(struct dl_phdr_info * info,const std::string secName,ShdrInfo & shdr)858 bool DfxElf::FindSection(struct dl_phdr_info* info, const std::string secName, ShdrInfo& shdr)
859 {
860 if (info == nullptr) {
861 return false;
862 }
863 const char* file = info->dlpi_name;
864 if (strlen(file) == 0) {
865 file = PROC_SELF_EXE_PATH;
866 }
867
868 auto elf = Create(file);
869 if (elf == nullptr) {
870 return false;
871 }
872
873 return elf->GetSectionInfo(shdr, secName);
874 }
875
ParsePhdr(struct dl_phdr_info * info,const ElfW (Phdr)* (& pHdrSections)[4],const uintptr_t pc)876 void DfxElf::ParsePhdr(struct dl_phdr_info* info, const ElfW(Phdr)* (&pHdrSections)[4], const uintptr_t pc)
877 {
878 const ElfW(Phdr)* phdr = info->dlpi_phdr;
879 for (size_t i = 0; i < info->dlpi_phnum && phdr != nullptr; i++, phdr++) {
880 switch (phdr->p_type) {
881 case PT_LOAD: {
882 ElfW(Addr) vaddr = phdr->p_vaddr + info->dlpi_addr;
883 if (pc >= vaddr && pc < vaddr + phdr->p_memsz) {
884 pHdrSections[SECTION_TEXT] = phdr;
885 }
886 break;
887 }
888 #if defined(__arm__)
889 case PT_ARM_EXIDX: {
890 pHdrSections[SECTION_ARMEXIDX] = phdr;
891 break;
892 }
893 #endif
894 case PT_GNU_EH_FRAME: {
895 pHdrSections[SECTION_EHFRAMEHDR] = phdr;
896 break;
897 }
898 case PT_DYNAMIC: {
899 pHdrSections[SECTION_DYNAMIC] = phdr;
900 break;
901 }
902 default:
903 break;
904 }
905 }
906 }
907
ProccessDynamic(const ElfW (Phdr)* pDynamic,ElfW (Addr)loadBase,UnwindTableInfo * uti)908 bool DfxElf::ProccessDynamic(const ElfW(Phdr)* pDynamic, ElfW(Addr) loadBase, UnwindTableInfo* uti)
909 {
910 ElfW(Dyn)* dyn = reinterpret_cast<ElfW(Dyn) *>(pDynamic->p_vaddr + loadBase);
911 if (dyn == nullptr) {
912 return false;
913 }
914 for (; dyn->d_tag != DT_NULL; ++dyn) {
915 if (dyn->d_tag == DT_PLTGOT) {
916 uti->gp = dyn->d_un.d_ptr;
917 break;
918 }
919 }
920 return true;
921 }
922
InitHdr(struct DwarfEhFrameHdr & synthHdr,struct dl_phdr_info * info,const ElfW (Phdr)* pEhHdr)923 struct DwarfEhFrameHdr* DfxElf::InitHdr(struct DwarfEhFrameHdr& synthHdr,
924 struct dl_phdr_info* info, const ElfW(Phdr)* pEhHdr)
925 {
926 struct DwarfEhFrameHdr* hdr = nullptr;
927 if (pEhHdr) {
928 INSTR_STATISTIC(InstructionEntriesEhFrame, pEhHdr->p_memsz, 0);
929 hdr = reinterpret_cast<struct DwarfEhFrameHdr *>(pEhHdr->p_vaddr + info->dlpi_addr);
930 } else {
931 ShdrInfo shdr;
932 if (FindSection(info, EH_FRAME, shdr)) {
933 DFXLOGW("[%{public}d]: Elf(%{public}s) no found .eh_frame_hdr section, " \
934 "using synthetic .eh_frame section", __LINE__, info->dlpi_name);
935 INSTR_STATISTIC(InstructionEntriesEhFrame, shdr.size, 0);
936 synthHdr.version = DW_EH_VERSION;
937 synthHdr.ehFramePtrEnc = DW_EH_PE_absptr |
938 ((sizeof(ElfW(Addr)) == 4) ? DW_EH_PE_udata4 : DW_EH_PE_udata8); // 4 : four bytes
939 synthHdr.fdeCountEnc = DW_EH_PE_omit;
940 synthHdr.tableEnc = DW_EH_PE_omit;
941 synthHdr.ehFrame = (ElfW(Addr))(shdr.addr + info->dlpi_addr);
942 hdr = &synthHdr;
943 }
944 }
945 return hdr;
946 }
947
DlPhdrCb(struct dl_phdr_info * info,size_t size,void * data)948 int DfxElf::DlPhdrCb(struct dl_phdr_info* info, size_t size, void* data)
949 {
950 struct DlCbData* cbData = reinterpret_cast<struct DlCbData *>(data);
951 if ((info == nullptr) || (cbData == nullptr)) {
952 return -1;
953 }
954 UnwindTableInfo* uti = &cbData->uti;
955 uintptr_t pc = cbData->pc;
956 const int numOfPhdrSections = 4;
957 const ElfW(Phdr)* pHdrSections[numOfPhdrSections] = {nullptr};
958 ParsePhdr(info, pHdrSections, pc);
959
960 if (pHdrSections[SECTION_TEXT] == nullptr) {
961 return 0;
962 }
963 ElfW(Addr) loadBase = info->dlpi_addr;
964 uti->startPc = pHdrSections[SECTION_TEXT]->p_vaddr + loadBase;
965 uti->endPc = uti->startPc + pHdrSections[SECTION_TEXT]->p_memsz;
966 DFXLOGU("Elf name: %{public}s", info->dlpi_name);
967 uti->namePtr = reinterpret_cast<uintptr_t>(info->dlpi_name);
968
969 #if defined(__arm__)
970 if (pHdrSections[SECTION_ARMEXIDX]) {
971 ShdrInfo shdr;
972 shdr.addr = pHdrSections[SECTION_ARMEXIDX]->p_vaddr;
973 shdr.size = pHdrSections[SECTION_ARMEXIDX]->p_memsz;
974 return FillUnwindTableByExidx(shdr, loadBase, uti);
975 }
976 #endif
977
978 if (pHdrSections[SECTION_DYNAMIC]) {
979 if (!ProccessDynamic(pHdrSections[SECTION_DYNAMIC], loadBase, uti)) {
980 return 0;
981 }
982 } else {
983 uti->gp = 0;
984 }
985
986 struct DwarfEhFrameHdr synthHdr;
987 struct DwarfEhFrameHdr* hdr = InitHdr(synthHdr, info, pHdrSections[SECTION_EHFRAMEHDR]);
988
989 return FillUnwindTableByEhhdrLocal(hdr, uti);
990 }
991 #endif
992
Read(uintptr_t pos,void * buf,size_t size)993 bool DfxElf::Read(uintptr_t pos, void* buf, size_t size)
994 {
995 if ((mmap_ != nullptr) && (mmap_->Read(pos, buf, size) == size)) {
996 return true;
997 }
998 return false;
999 }
1000
GetMmapPtr()1001 const uint8_t* DfxElf::GetMmapPtr()
1002 {
1003 if (mmap_ == nullptr) {
1004 return nullptr;
1005 }
1006 return static_cast<uint8_t *>(mmap_->Get());
1007 }
1008
GetMmapSize()1009 size_t DfxElf::GetMmapSize()
1010 {
1011 if (mmap_ == nullptr) {
1012 return 0;
1013 }
1014 return mmap_->Size();
1015 }
1016
IsValidElf(const void * ptr,size_t size)1017 bool DfxElf::IsValidElf(const void* ptr, size_t size)
1018 {
1019 if (ptr == nullptr) {
1020 return false;
1021 }
1022
1023 if (memcmp(ptr, ELFMAG, size) != 0) {
1024 DFXLOGD("Invalid elf hdr?");
1025 return false;
1026 }
1027 return true;
1028 }
1029
GetElfSize(const void * ptr)1030 size_t DfxElf::GetElfSize(const void* ptr)
1031 {
1032 if (!IsValidElf(ptr, SELFMAG)) {
1033 return 0;
1034 }
1035
1036 const uint8_t* data = static_cast<const uint8_t*>(ptr);
1037 uint8_t classType = data[EI_CLASS];
1038 if (classType == ELFCLASS32) {
1039 const Elf32_Ehdr* ehdr = reinterpret_cast<const Elf32_Ehdr *>(data);
1040 return static_cast<size_t>(ehdr->e_shoff + (ehdr->e_shentsize * ehdr->e_shnum));
1041 } else if (classType == ELFCLASS64) {
1042 const Elf64_Ehdr* ehdr = reinterpret_cast<const Elf64_Ehdr *>(data);
1043 return static_cast<size_t>(ehdr->e_shoff + (ehdr->e_shentsize * ehdr->e_shnum));
1044 }
1045 DFXLOGW("classType(%{public}d) error", classType);
1046 return 0;
1047 }
1048 } // namespace HiviewDFX
1049 } // namespace OHOS
1050