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