• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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