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