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         LOGE("%s", "current hap mapitem has no prev mapitem, maybe pc is wrong?");
75         return nullptr;
76     }
77     if (!StartsWith(file, "/proc") || !EndsWith(file, ".hap")) {
78         LOGD("Illegal file path, please check file: %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         LOGE("Failed to open hap file, errno(%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             LOGE("%s", "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             LOGE("Invalid elf size? elf size: %d, hap size: %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         LOGU("elfSize: %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         LOGU("file: %s", file.c_str());
123 #if defined(is_ohos) && is_ohos
124         if (!DfxMaps::IsLegalMapItem(file)) {
125             LOGD("Illegal map file, please check file: %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                 LOGW("Failed to realpath %s, errno(%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                 LOGE("%s", "Failed to mmap init.");
146             }
147             close(fd);
148         } else {
149             LOGE("Failed to open file: %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             LOGE("%s", "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         LOGE("%s", "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             LOGE("%s", "Failed to parse Embedded Elf.");
240         }
241     } else {
242         LOGE("%s", "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         LOGW("InitHeaders failed, classType: %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             LOGU("Elf loadBias: %" 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             LOGU("mapStart: %" PRIx64 ", mapOffset: %" PRIx64 "", (uint64_t)mapStart, (uint64_t)mapOffset);
325             loadBase_ = mapStart - mapOffset - static_cast<uint64_t>(GetLoadBias());
326             LOGU("Elf loadBase: %" PRIx64 "", (uint64_t)loadBase_);
327         }
328     }
329     return loadBase_;
330 }
331 
SetLoadBase(uint64_t base)332 void DfxElf::SetLoadBase(uint64_t base)
333 {
334     loadBase_ = base;
335 }
336 
SetBaseOffset(uint64_t offset)337 void DfxElf::SetBaseOffset(uint64_t offset)
338 {
339     baseOffset_ = offset;
340 }
341 
GetBaseOffset()342 uint64_t DfxElf::GetBaseOffset()
343 {
344     return baseOffset_;
345 }
346 
GetStartPc()347 uint64_t DfxElf::GetStartPc()
348 {
349     if (startPc_ == static_cast<uint64_t>(-1)) {
350         if (IsValid()) {
351             auto startVaddr = elfParse_->GetStartVaddr();
352             if (loadBase_ != static_cast<uint64_t>(-1) && startVaddr != static_cast<uint64_t>(-1)) {
353                 startPc_ = startVaddr + loadBase_;
354                 LOGU("Elf startPc: %" PRIx64 "", (uint64_t)startPc_);
355             }
356         }
357     }
358     return startPc_;
359 }
360 
GetStartVaddr()361 uint64_t DfxElf::GetStartVaddr()
362 {
363     if (IsValid()) {
364         return elfParse_->GetStartVaddr();
365     }
366     return 0;
367 }
368 
GetEndPc()369 uint64_t DfxElf::GetEndPc()
370 {
371     if (endPc_ == 0) {
372         if (IsValid()) {
373             auto endVaddr = elfParse_->GetEndVaddr();
374             if (loadBase_ != static_cast<uint64_t>(-1) && endVaddr != 0) {
375                 endPc_ = endVaddr + loadBase_;
376                 LOGU("Elf endPc: %" PRIx64 "", (uint64_t)endPc_);
377             }
378         }
379     }
380     return endPc_;
381 }
382 
GetEndVaddr()383 uint64_t DfxElf::GetEndVaddr()
384 {
385     if (IsValid()) {
386         return elfParse_->GetEndVaddr();
387     }
388     return 0;
389 }
390 
GetStartOffset()391 uint64_t DfxElf::GetStartOffset()
392 {
393     if (IsValid()) {
394         return elfParse_->GetStartOffset();
395     }
396     return 0;
397 }
398 
GetRelPc(uint64_t pc,uint64_t mapStart,uint64_t mapOffset)399 uint64_t DfxElf::GetRelPc(uint64_t pc, uint64_t mapStart, uint64_t mapOffset)
400 {
401     return (pc - GetLoadBase(mapStart, mapOffset));
402 }
403 
GetElfSize()404 uint64_t DfxElf::GetElfSize()
405 {
406     if (!IsValid()) {
407         return 0;
408     }
409     return elfParse_->GetElfSize();
410 }
411 
GetElfName()412 std::string DfxElf::GetElfName()
413 {
414     if (!IsValid()) {
415         return "";
416     }
417     return elfParse_->GetElfName();
418 }
419 
SetBuildId(const std::string & buildId)420 void DfxElf::SetBuildId(const std::string& buildId)
421 {
422     buildId_ = buildId;
423 }
424 
GetBuildId()425 std::string DfxElf::GetBuildId()
426 {
427     if (buildId_.empty()) {
428         if (!IsValid()) {
429             return "";
430         }
431         ShdrInfo shdr;
432         if ((GetSectionInfo(shdr, NOTE_GNU_BUILD_ID) || GetSectionInfo(shdr, NOTES)) && GetMmapPtr() != nullptr) {
433             std::string buildIdHex = GetBuildId((uint64_t)((char*)GetMmapPtr() + shdr.offset), shdr.size);
434             if (!buildIdHex.empty()) {
435                 buildId_ = ToReadableBuildId(buildIdHex);
436                 LOGU("Elf buildId: %s", buildId_.c_str());
437             }
438         }
439     }
440     return buildId_;
441 }
442 
GetBuildId(uint64_t noteAddr,uint64_t noteSize)443 std::string DfxElf::GetBuildId(uint64_t noteAddr, uint64_t noteSize)
444 {
445     uint64_t tmp;
446     if (__builtin_add_overflow(noteAddr, noteSize, &tmp)) {
447         LOGE("%s", "noteAddr overflow");
448         return "";
449     }
450     uint64_t offset = 0;
451     uint64_t ptr = noteAddr;
452     while (offset < noteSize) {
453         ElfW(Nhdr) nhdr;
454         if (noteSize - offset < sizeof(nhdr)) {
455             return "";
456         }
457         ptr = noteAddr + offset;
458         if (memcpy_s(&nhdr, sizeof(nhdr), reinterpret_cast<void*>(ptr), sizeof(nhdr)) != 0) {
459             LOGE("%s", "memcpy_s nhdr failed");
460             return "";
461         }
462         offset += sizeof(nhdr);
463         if (noteSize - offset < nhdr.n_namesz) {
464             return "";
465         }
466         if (nhdr.n_namesz > 0) {
467             std::string name(nhdr.n_namesz, '\0');
468             ptr = noteAddr + offset;
469             if (memcpy_s(&(name[0]), nhdr.n_namesz, reinterpret_cast<void*>(ptr), nhdr.n_namesz) != 0) {
470                 LOGE("%s", "memcpy_s note name failed");
471                 return "";
472             }
473             // Trim trailing \0 as GNU is stored as a C string in the ELF file.
474             if (name.size() != 0 && name.back() == '\0') {
475                 name.resize(name.size() - 1);
476             }
477             // Align nhdr.n_namesz to next power multiple of 4. See man 5 elf.
478             offset += (nhdr.n_namesz + 3) & ~3; // 3 : Align the offset to a 4-byte boundary
479             if (name != "GNU" || nhdr.n_type != NT_GNU_BUILD_ID) {
480                 offset += (nhdr.n_descsz + 3) & ~3; // 3 : Align the offset to a 4-byte boundary
481                 continue;
482             }
483             if (noteSize - offset < nhdr.n_descsz || nhdr.n_descsz == 0) {
484                 return "";
485             }
486             std::string buildIdRaw(nhdr.n_descsz, '\0');
487             ptr = noteAddr + offset;
488             if (memcpy_s(&buildIdRaw[0], nhdr.n_descsz, reinterpret_cast<void*>(ptr), nhdr.n_descsz) != 0) {
489                 return "";
490             }
491             return buildIdRaw;
492         }
493         // Align hdr.n_descsz to next power multiple of 4. See man 5 elf.
494         offset += (nhdr.n_descsz + 3) & ~3; // 3 : Align the offset to a 4-byte boundary
495     }
496     return "";
497 }
498 
GetGlobalPointer()499 uintptr_t DfxElf::GetGlobalPointer()
500 {
501     if (!IsValid()) {
502         return 0;
503     }
504     return elfParse_->GetGlobalPointer();
505 }
506 
ToReadableBuildId(const std::string & buildIdHex)507 std::string DfxElf::ToReadableBuildId(const std::string& buildIdHex)
508 {
509     if (buildIdHex.empty()) {
510         return "";
511     }
512     static const char HEXTABLE[] = "0123456789abcdef";
513     static const int HEXLENGTH = 16;
514     static const int HEX_EXPAND_PARAM = 2;
515     const size_t len = buildIdHex.length();
516     std::string buildId(len * HEX_EXPAND_PARAM, '\0');
517 
518     for (size_t i = 0; i < len; i++) {
519         unsigned int n = buildIdHex[i];
520         buildId[i * HEX_EXPAND_PARAM] = HEXTABLE[(n >> 4) % HEXLENGTH]; // 4 : higher 4 bit of uint8
521         buildId[i * HEX_EXPAND_PARAM + 1] = HEXTABLE[n % HEXLENGTH];
522     }
523     return buildId;
524 }
525 
GetSectionInfo(ShdrInfo & shdr,const std::string secName)526 bool DfxElf::GetSectionInfo(ShdrInfo& shdr, const std::string secName)
527 {
528     if (!IsValid()) {
529         return false;
530     }
531     return elfParse_->GetSectionInfo(shdr, secName);
532 }
533 
GetSectionData(unsigned char * buf,uint64_t size,std::string secName)534 bool DfxElf::GetSectionData(unsigned char *buf, uint64_t size, std::string secName)
535 {
536     if (!IsValid()) {
537         return false;
538     }
539     return elfParse_->GetSectionData(buf, size, secName);
540 }
541 
GetElfSymbols()542 const std::vector<ElfSymbol>& DfxElf::GetElfSymbols()
543 {
544     if (!elfSymbols_.empty()) {
545         return elfSymbols_;
546     }
547     elfSymbols_ = elfParse_->GetElfSymbols(false);
548 #if defined(ENABLE_MINIDEBUGINFO)
549     if (IsEmbeddedElfValid()) {
550         auto symbols = embeddedElf_->elfParse_->GetElfSymbols(false);
551         LOGU("Get EmbeddedElf ElfSymbols, size: %zu", symbols.size());
552         elfSymbols_.insert(elfSymbols_.end(), symbols.begin(), symbols.end());
553     }
554 #endif
555     std::sort(elfSymbols_.begin(), elfSymbols_.end(), [](const ElfSymbol& sym1, const ElfSymbol& sym2) {
556         return sym1.value < sym2.value;
557     });
558     auto pred = [](ElfSymbol a, ElfSymbol b) { return a.value == b.value; };
559     elfSymbols_.erase(std::unique(elfSymbols_.begin(), elfSymbols_.end(), pred), elfSymbols_.end());
560     elfSymbols_.shrink_to_fit();
561     LOGU("GetElfSymbols, size: %zu", elfSymbols_.size());
562     return elfSymbols_;
563 }
564 
GetFuncSymbols()565 const std::vector<ElfSymbol>& DfxElf::GetFuncSymbols()
566 {
567     if (!funcSymbols_.empty()) {
568         return funcSymbols_;
569     }
570     funcSymbols_ = elfParse_->GetElfSymbols(true);
571 #if defined(ENABLE_MINIDEBUGINFO)
572     if (IsEmbeddedElfValid()) {
573         auto symbols = embeddedElf_->elfParse_->GetElfSymbols(true);
574         LOGU("Get EmbeddedElf FuncSymbols, size: %zu", symbols.size());
575         funcSymbols_.insert(funcSymbols_.end(), symbols.begin(), symbols.end());
576     }
577 #endif
578     std::sort(funcSymbols_.begin(), funcSymbols_.end(), [](const ElfSymbol& sym1, const ElfSymbol& sym2) {
579         return sym1.value < sym2.value;
580     });
581     auto pred = [](ElfSymbol a, ElfSymbol b) { return a.value == b.value; };
582     funcSymbols_.erase(std::unique(funcSymbols_.begin(), funcSymbols_.end(), pred), funcSymbols_.end());
583     funcSymbols_.shrink_to_fit();
584     LOGU("GetFuncSymbols, size: %zu", funcSymbols_.size());
585     return funcSymbols_;
586 }
587 
GetFuncInfoLazily(uint64_t addr,ElfSymbol & elfSymbol)588 bool DfxElf::GetFuncInfoLazily(uint64_t addr, ElfSymbol& elfSymbol)
589 {
590     DFX_TRACE_SCOPED_DLSYM("GetFuncInfoLazily");
591     if (FindFuncSymbol(addr, funcSymbols_, elfSymbol)) {
592         return true;
593     }
594     bool findSymbol = false;
595 #if defined(ENABLE_MINIDEBUGINFO)
596     if (IsEmbeddedElfValid() &&
597         embeddedElf_->elfParse_->GetElfSymbolByAddr(addr, elfSymbol)) {
598         funcSymbols_.emplace_back(elfSymbol);
599         findSymbol = true;
600     }
601 #endif
602 
603     if (!findSymbol && elfParse_->GetElfSymbolByAddr(addr, elfSymbol)) {
604         funcSymbols_.emplace_back(elfSymbol);
605         findSymbol = true;
606     }
607 
608     if (findSymbol) {
609         std::sort(funcSymbols_.begin(), funcSymbols_.end(), [](const ElfSymbol& sym1, const ElfSymbol& sym2) {
610             return sym1.value < sym2.value;
611         });
612         auto pred = [](ElfSymbol a, ElfSymbol b) { return a.value == b.value; };
613         funcSymbols_.erase(std::unique(funcSymbols_.begin(), funcSymbols_.end(), pred), funcSymbols_.end());
614         funcSymbols_.shrink_to_fit();
615         LOGU("GetFuncInfoLazily, size: %zu", funcSymbols_.size());
616         return true;
617     }
618     return false;
619 }
620 
GetFuncInfo(uint64_t addr,ElfSymbol & elfSymbol)621 bool DfxElf::GetFuncInfo(uint64_t addr, ElfSymbol& elfSymbol)
622 {
623     if (UnwinderConfig::GetEnableLoadSymbolLazily()) {
624         return GetFuncInfoLazily(addr, elfSymbol);
625     }
626 
627     auto symbols = GetFuncSymbols();
628     return FindFuncSymbol(addr, symbols, elfSymbol);
629 }
630 
FindFuncSymbol(uint64_t addr,const std::vector<ElfSymbol> & symbols,ElfSymbol & elfSymbol)631 bool DfxElf::FindFuncSymbol(uint64_t addr, const std::vector<ElfSymbol>& symbols, ElfSymbol& elfSymbol)
632 {
633     DFX_TRACE_SCOPED_DLSYM("FindFuncSymbol");
634     if (symbols.empty()) {
635         return false;
636     }
637     size_t begin = 0;
638     size_t end = symbols.size();
639     while (begin < end) {
640         size_t mid = begin + (end - begin) / 2;
641         const auto& symbol = symbols[mid];
642         if (addr < symbol.value) {
643             end = mid;
644         } else if (addr < (symbol.value + symbol.size)) {
645             elfSymbol = symbol;
646             return true;
647         } else {
648             begin = mid + 1;
649         }
650     }
651     return false;
652 }
653 
GetPtLoads()654 const std::unordered_map<uint64_t, ElfLoadInfo>& DfxElf::GetPtLoads()
655 {
656     return elfParse_->GetPtLoads();
657 }
658 
FillUnwindTableByExidx(ShdrInfo shdr,uintptr_t loadBase,struct UnwindTableInfo * uti)659 bool DfxElf::FillUnwindTableByExidx(ShdrInfo shdr, uintptr_t loadBase, struct UnwindTableInfo* uti)
660 {
661     if (uti == nullptr) {
662         return false;
663     }
664     uti->gp = 0;
665     uti->tableData = loadBase + shdr.addr;
666     uti->tableLen = shdr.size;
667     INSTR_STATISTIC(InstructionEntriesArmExidx, shdr.size, 0);
668     uti->format = UNW_INFO_FORMAT_ARM_EXIDX;
669     LOGU("tableData: %" PRIx64 ", tableLen: %d", (uint64_t)uti->tableData, (int)uti->tableLen);
670     return true;
671 }
672 
673 #if is_ohos && !is_mingw
FillUnwindTableByEhhdrLocal(struct DwarfEhFrameHdr * hdr,struct UnwindTableInfo * uti)674 bool DfxElf::FillUnwindTableByEhhdrLocal(struct DwarfEhFrameHdr* hdr, struct UnwindTableInfo* uti)
675 {
676     if (hdr == nullptr) {
677         return false;
678     }
679     if (hdr->version != DW_EH_VERSION) {
680         LOGE("version(%d) error", hdr->version);
681         return false;
682     }
683 
684     uintptr_t ptr = (uintptr_t)(&(hdr->ehFrame));
685     LOGU("hdr: %" PRIx64 ", ehFrame: %" PRIx64 "", (uint64_t)hdr, (uint64_t)ptr);
686 
687     auto acc = std::make_shared<DfxAccessorsLocal>();
688     auto memory = std::make_shared<DfxMemory>(acc);
689     LOGU("gp: %" PRIx64 ", ehFramePtrEnc: %x, fdeCountEnc: %x",
690         (uint64_t)uti->gp, hdr->ehFramePtrEnc, hdr->fdeCountEnc);
691     memory->SetDataOffset(uti->gp);
692     MAYBE_UNUSED uintptr_t ehFrameStart = memory->ReadEncodedValue(ptr, hdr->ehFramePtrEnc);
693     uintptr_t fdeCount = memory->ReadEncodedValue(ptr, hdr->fdeCountEnc);
694     LOGU("ehFrameStart: %" PRIx64 ", fdeCount: %d", (uint64_t)ehFrameStart, (int)fdeCount);
695 
696     if (hdr->tableEnc != (DW_EH_PE_datarel | DW_EH_PE_sdata4)) {
697         LOGU("tableEnc: %x", hdr->tableEnc);
698         if (hdr->fdeCountEnc == DW_EH_PE_omit) {
699             fdeCount = ~0UL;
700         }
701         if (hdr->ehFramePtrEnc == DW_EH_PE_omit) {
702             LOGE("ehFramePtrEnc(%x) error", hdr->ehFramePtrEnc);
703             return false;
704         }
705         uti->isLinear = true;
706         uti->tableLen = fdeCount;
707         uti->tableData = ehFrameStart;
708     } else {
709         uti->isLinear = false;
710         uti->tableLen = (fdeCount * sizeof(DwarfTableEntry)) / sizeof(uintptr_t);
711         uti->tableData = ptr;
712         uti->segbase = (uintptr_t)hdr;
713     }
714     uti->format = UNW_INFO_FORMAT_REMOTE_TABLE;
715     LOGU("tableData: %" PRIx64 ", tableLen: %d", (uint64_t)uti->tableData, (int)uti->tableLen);
716     return true;
717 }
718 #endif
719 
FillUnwindTableByEhhdr(struct DwarfEhFrameHdr * hdr,uintptr_t shdrBase,struct UnwindTableInfo * uti)720 bool DfxElf::FillUnwindTableByEhhdr(struct DwarfEhFrameHdr* hdr, uintptr_t shdrBase, struct UnwindTableInfo* uti)
721 {
722     if ((hdr == nullptr) || (uti == nullptr)) {
723         return false;
724     }
725     if (hdr->version != DW_EH_VERSION) {
726         LOGE("version(%d) error", hdr->version);
727         return false;
728     }
729     uintptr_t ptr = (uintptr_t)(&(hdr->ehFrame));
730     LOGU("hdr: %" PRIx64 ", ehFrame: %" PRIx64 "", (uint64_t)hdr, (uint64_t)ptr);
731 
732     uti->gp = GetGlobalPointer();
733     LOGU("gp: %" PRIx64 ", ehFramePtrEnc: %x, fdeCountEnc: %x",
734         (uint64_t)uti->gp, hdr->ehFramePtrEnc, hdr->fdeCountEnc);
735     mmap_->SetDataOffset(uti->gp);
736     auto ptrOffset = ptr - reinterpret_cast<uintptr_t>(GetMmapPtr());
737     MAYBE_UNUSED uintptr_t ehFrameStart = mmap_->ReadEncodedValue(ptrOffset, hdr->ehFramePtrEnc);
738     uintptr_t fdeCount = mmap_->ReadEncodedValue(ptrOffset, hdr->fdeCountEnc);
739     LOGU("ehFrameStart: %" PRIx64 ", fdeCount: %d", (uint64_t)ehFrameStart, (int)fdeCount);
740     ptr = reinterpret_cast<uintptr_t>(GetMmapPtr()) + ptrOffset;
741 
742     if (hdr->tableEnc != (DW_EH_PE_datarel | DW_EH_PE_sdata4)) {
743         LOGU("tableEnc: %x", hdr->tableEnc);
744         if (hdr->fdeCountEnc == DW_EH_PE_omit) {
745             fdeCount = ~0UL;
746         }
747         if (hdr->ehFramePtrEnc == DW_EH_PE_omit) {
748             LOGE("ehFramePtrEnc(%x) error", hdr->ehFramePtrEnc);
749             return false;
750         }
751         uti->isLinear = true;
752         uti->tableLen = fdeCount;
753         uti->tableData = shdrBase + ehFrameStart;
754         uti->segbase = shdrBase;
755     } else {
756         uti->isLinear = false;
757         uti->tableLen = (fdeCount * sizeof(DwarfTableEntry)) / sizeof(uintptr_t);
758         uti->tableData = shdrBase + ptr - (uintptr_t)hdr;
759         uti->segbase = shdrBase;
760     }
761     uti->format = UNW_INFO_FORMAT_REMOTE_TABLE;
762     LOGU("tableData: %" PRIx64 ", tableLen: %d", (uint64_t)uti->tableData, (int)uti->tableLen);
763     return true;
764 }
765 
FindUnwindTableInfo(uintptr_t pc,std::shared_ptr<DfxMap> map,struct UnwindTableInfo & uti)766 int DfxElf::FindUnwindTableInfo(uintptr_t pc, std::shared_ptr<DfxMap> map, struct UnwindTableInfo& uti)
767 {
768     if (hasTableInfo_) {
769         if (pc >= uti_.startPc && pc < uti_.endPc) {
770             uti = uti_;
771             LOGU("%s", "FindUnwindTableInfo had found");
772             return UNW_ERROR_NONE;
773         }
774     }
775     if (map == nullptr) {
776         return UNW_ERROR_INVALID_MAP;
777     }
778     uintptr_t loadBase = GetLoadBase(map->begin, map->offset);
779     uti.startPc = GetStartPc();
780     uti.endPc = GetEndPc();
781     if (pc < uti.startPc || pc >= uti.endPc) {
782         LOGU("Elf startPc: %" PRIx64 ", endPc: %" PRIx64 "", (uint64_t)uti.startPc, (uint64_t)uti.endPc);
783         return UNW_ERROR_PC_NOT_IN_UNWIND_INFO;
784     }
785 
786     ShdrInfo shdr;
787 #if defined(__arm__)
788     if (GetSectionInfo(shdr, ARM_EXIDX)) {
789         hasTableInfo_ = FillUnwindTableByExidx(shdr, loadBase, &uti);
790     }
791 #endif
792 
793     if (!hasTableInfo_) {
794         struct DwarfEhFrameHdr* hdr = nullptr;
795         struct DwarfEhFrameHdr synthHdr;
796         if (GetSectionInfo(shdr, EH_FRAME_HDR) && GetMmapPtr() != nullptr) {
797             INSTR_STATISTIC(InstructionEntriesEhFrame, shdr.size, 0);
798             hdr = (struct DwarfEhFrameHdr *) (shdr.offset + (char *)GetMmapPtr());
799         } else if (GetSectionInfo(shdr, EH_FRAME) && GetMmapPtr() != nullptr) {
800             LOGW("Elf(%s) no found .eh_frame_hdr section, using synthetic .eh_frame section", map->name.c_str());
801             INSTR_STATISTIC(InstructionEntriesEhFrame, shdr.size, 0);
802             synthHdr.version = DW_EH_VERSION;
803             synthHdr.ehFramePtrEnc = DW_EH_PE_absptr |
804                 ((sizeof(ElfW(Addr)) == 4) ? DW_EH_PE_udata4 : DW_EH_PE_udata8); // 4 : four bytes
805             synthHdr.fdeCountEnc = DW_EH_PE_omit;
806             synthHdr.tableEnc = DW_EH_PE_omit;
807             synthHdr.ehFrame = (ElfW(Addr))(shdr.offset + (char*)GetMmapPtr());
808             hdr = &synthHdr;
809         }
810         uintptr_t shdrBase = static_cast<uintptr_t>(loadBase + shdr.addr);
811         hasTableInfo_ = FillUnwindTableByEhhdr(hdr, shdrBase, &uti);
812     }
813 
814     if (hasTableInfo_) {
815         uti_ = uti;
816         return UNW_ERROR_NONE;
817     }
818     return UNW_ERROR_NO_UNWIND_INFO;
819 }
820 
FindUnwindTableLocal(uintptr_t pc,struct UnwindTableInfo & uti)821 int DfxElf::FindUnwindTableLocal(uintptr_t pc, struct UnwindTableInfo& uti)
822 {
823 #if is_ohos && !is_mingw
824     DlCbData cbData;
825     (void)memset_s(&cbData, sizeof(cbData), 0, sizeof(cbData));
826     cbData.pc = pc;
827     cbData.uti.format = -1;
828     int ret = dl_iterate_phdr(DlPhdrCb, &cbData);
829     if (ret > 0) {
830         if (cbData.uti.format != -1) {
831             uti = cbData.uti;
832             return UNW_ERROR_NONE;
833         }
834     }
835     return UNW_ERROR_NO_UNWIND_INFO;
836 #else
837     return UNW_ERROR_UNSUPPORTED_VERSION;
838 #endif
839 }
840 
841 #if is_ohos && !is_mingw
FindSection(struct dl_phdr_info * info,const std::string secName,ShdrInfo & shdr)842 bool DfxElf::FindSection(struct dl_phdr_info *info, const std::string secName, ShdrInfo& shdr)
843 {
844     if (info == nullptr) {
845         return false;
846     }
847     const char *file = info->dlpi_name;
848     if (strlen(file) == 0) {
849         file = PROC_SELF_EXE_PATH;
850     }
851 
852     auto elf = Create(file);
853     if (elf == nullptr) {
854         return false;
855     }
856 
857     return elf->GetSectionInfo(shdr, secName);
858 }
859 
DlPhdrCb(struct dl_phdr_info * info,size_t size,void * data)860 int DfxElf::DlPhdrCb(struct dl_phdr_info *info, size_t size, void *data)
861 {
862     struct DlCbData *cbData = (struct DlCbData *)data;
863     if ((info == nullptr) || (cbData == nullptr)) {
864         return -1;
865     }
866     UnwindTableInfo* uti = &cbData->uti;
867     uintptr_t pc = cbData->pc;
868     const ElfW(Phdr) *pText = nullptr;
869     const ElfW(Phdr) *pDynamic = nullptr;
870 #if defined(__arm__)
871     const ElfW(Phdr) *pArmExidx = nullptr;
872 #endif
873     const ElfW(Phdr) *pEhHdr = nullptr;
874 
875     const ElfW(Phdr) *phdr = info->dlpi_phdr;
876     ElfW(Addr) loadBase = info->dlpi_addr;
877     for (size_t i = 0; i < info->dlpi_phnum && phdr != nullptr; i++, phdr++) {
878         switch (phdr->p_type) {
879             case PT_LOAD: {
880                 ElfW(Addr) vaddr = phdr->p_vaddr + loadBase;
881                 if (pc >= vaddr && pc < vaddr + phdr->p_memsz) {
882                     pText = phdr;
883                 }
884                 break;
885             }
886 #if defined(__arm__)
887             case PT_ARM_EXIDX: {
888                 pArmExidx = phdr;
889                 break;
890             }
891 #endif
892             case PT_GNU_EH_FRAME: {
893                 pEhHdr = phdr;
894                 break;
895             }
896             case PT_DYNAMIC: {
897                 pDynamic = phdr;
898                 break;
899             }
900             default:
901                 break;
902         }
903     }
904     if (pText == nullptr) {
905         return 0;
906     }
907     uti->startPc = pText->p_vaddr + loadBase;
908     uti->endPc = uti->startPc + pText->p_memsz;
909     LOGU("Elf name: %s", info->dlpi_name);
910     uti->namePtr = (uintptr_t) info->dlpi_name;
911 
912 #if defined(__arm__)
913     if (pArmExidx) {
914         ShdrInfo shdr;
915         shdr.addr = pArmExidx->p_vaddr;
916         shdr.size = pArmExidx->p_memsz;
917         return FillUnwindTableByExidx(shdr, loadBase, uti);
918     }
919 #endif
920 
921     if (pDynamic) {
922         ElfW(Dyn) *dyn = (ElfW(Dyn) *)(pDynamic->p_vaddr + loadBase);
923         if (dyn == nullptr) {
924             return 0;
925         }
926         for (; dyn->d_tag != DT_NULL; ++dyn) {
927             if (dyn->d_tag == DT_PLTGOT) {
928                 uti->gp = dyn->d_un.d_ptr;
929                 break;
930             }
931         }
932     } else {
933         uti->gp = 0;
934     }
935 
936     struct DwarfEhFrameHdr *hdr = nullptr;
937     struct DwarfEhFrameHdr synthHdr;
938     if (pEhHdr) {
939         INSTR_STATISTIC(InstructionEntriesEhFrame, pEhHdr->p_memsz, 0);
940         hdr = (struct DwarfEhFrameHdr *) (pEhHdr->p_vaddr + loadBase);
941     } else {
942         ShdrInfo shdr;
943         if (FindSection(info, EH_FRAME, shdr)) {
944             LOGW("Elf(%s) no found .eh_frame_hdr section, using synthetic .eh_frame section", info->dlpi_name);
945             INSTR_STATISTIC(InstructionEntriesEhFrame, shdr.size, 0);
946             synthHdr.version = DW_EH_VERSION;
947             synthHdr.ehFramePtrEnc = DW_EH_PE_absptr |
948                 ((sizeof(ElfW(Addr)) == 4) ? DW_EH_PE_udata4 : DW_EH_PE_udata8); // 4 : four bytes
949             synthHdr.fdeCountEnc = DW_EH_PE_omit;
950             synthHdr.tableEnc = DW_EH_PE_omit;
951             synthHdr.ehFrame = (ElfW(Addr))(shdr.addr + info->dlpi_addr);
952             hdr = &synthHdr;
953         }
954     }
955     return FillUnwindTableByEhhdrLocal(hdr, uti);
956 }
957 #endif
958 
Read(uintptr_t pos,void * buf,size_t size)959 bool DfxElf::Read(uintptr_t pos, void *buf, size_t size)
960 {
961     if ((mmap_ != nullptr) && (mmap_->Read(pos, buf, size) == size)) {
962         return true;
963     }
964     return false;
965 }
966 
GetMmapPtr()967 const uint8_t* DfxElf::GetMmapPtr()
968 {
969     if (mmap_ == nullptr) {
970         return nullptr;
971     }
972     return static_cast<uint8_t *>(mmap_->Get());
973 }
974 
GetMmapSize()975 size_t DfxElf::GetMmapSize()
976 {
977     if (mmap_ == nullptr) {
978         return 0;
979     }
980     return mmap_->Size();
981 }
982 
IsValidElf(const void * ptr,size_t size)983 bool DfxElf::IsValidElf(const void* ptr, size_t size)
984 {
985     if (ptr == nullptr) {
986         return false;
987     }
988 
989     if (memcmp(ptr, ELFMAG, size) != 0) {
990         LOGD("%s", "Invalid elf hdr?");
991         return false;
992     }
993     return true;
994 }
995 
GetElfSize(const void * ptr)996 size_t DfxElf::GetElfSize(const void* ptr)
997 {
998     if (!IsValidElf(ptr, SELFMAG)) {
999         return 0;
1000     }
1001 
1002     const uint8_t* data = static_cast<const uint8_t*>(ptr);
1003     uint8_t classType = data[EI_CLASS];
1004     if (classType == ELFCLASS32) {
1005         Elf32_Ehdr *ehdr = (Elf32_Ehdr *)data;
1006         return static_cast<size_t>(ehdr->e_shoff + (ehdr->e_shentsize * ehdr->e_shnum));
1007     } else if (classType == ELFCLASS64) {
1008         Elf64_Ehdr *ehdr = (Elf64_Ehdr *)data;
1009         return static_cast<size_t>(ehdr->e_shoff + (ehdr->e_shentsize * ehdr->e_shnum));
1010     }
1011     LOGW("classType(%d) error", classType);
1012     return 0;
1013 }
1014 } // namespace HiviewDFX
1015 } // namespace OHOS
1016