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
47namespace OHOS {
48namespace HiviewDFX {
49namespace {
50#undef LOG_DOMAIN
51#undef LOG_TAG
52#define LOG_DOMAIN 0xD002D11
53#define LOG_TAG "DfxElf"
54}
55
56std::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
65std::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
119DfxElf::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
155DfxElf::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
166DfxElf::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
177void DfxElf::Init()
178{
179    uti_.namePtr = 0;
180    uti_.format = -1;
181    hasTableInfo_ = false;
182}
183
184void 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
198bool 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
209std::shared_ptr<DfxElf> DfxElf::GetEmbeddedElf()
210{
211    return embeddedElf_;
212}
213
214std::shared_ptr<MiniDebugInfo> DfxElf::GetMiniDebugInfo()
215{
216    return miniDebugInfo_;
217}
218
219bool 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
248bool 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
285bool DfxElf::IsValid()
286{
287    if (valid_ == false) {
288        InitHeaders();
289    }
290    return valid_;
291}
292
293uint8_t DfxElf::GetClassType()
294{
295    if (IsValid()) {
296        return classType_;
297    }
298    return ELFCLASSNONE;
299}
300
301ArchType DfxElf::GetArchType()
302{
303    if (IsValid()) {
304        elfParse_->GetArchType();
305    }
306    return ARCH_UNKNOWN;
307}
308
309int64_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
320uint64_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
333void DfxElf::SetLoadBase(uint64_t base)
334{
335    loadBase_ = base;
336}
337
338void DfxElf::SetBaseOffset(uint64_t offset)
339{
340    baseOffset_ = offset;
341}
342
343uint64_t DfxElf::GetBaseOffset()
344{
345    return baseOffset_;
346}
347
348uint64_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
362uint64_t DfxElf::GetStartVaddr()
363{
364    if (IsValid()) {
365        return elfParse_->GetStartVaddr();
366    }
367    return 0;
368}
369
370uint64_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
384uint64_t DfxElf::GetEndVaddr()
385{
386    if (IsValid()) {
387        return elfParse_->GetEndVaddr();
388    }
389    return 0;
390}
391
392uint64_t DfxElf::GetStartOffset()
393{
394    if (IsValid()) {
395        return elfParse_->GetStartOffset();
396    }
397    return 0;
398}
399
400uint64_t DfxElf::GetRelPc(uint64_t pc, uint64_t mapStart, uint64_t mapOffset)
401{
402    return (pc - GetLoadBase(mapStart, mapOffset));
403}
404
405uint64_t DfxElf::GetElfSize()
406{
407    if (!IsValid()) {
408        return 0;
409    }
410    return elfParse_->GetElfSize();
411}
412
413std::string DfxElf::GetElfName()
414{
415    if (!IsValid()) {
416        return "";
417    }
418    return elfParse_->GetElfName();
419}
420
421void DfxElf::SetBuildId(const std::string& buildId)
422{
423    buildId_ = buildId;
424}
425
426std::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
444std::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
500uintptr_t DfxElf::GetGlobalPointer()
501{
502    if (!IsValid()) {
503        return 0;
504    }
505    return elfParse_->GetGlobalPointer();
506}
507
508std::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
527bool DfxElf::GetSectionInfo(ShdrInfo& shdr, const std::string secName)
528{
529    if (!IsValid()) {
530        return false;
531    }
532    return elfParse_->GetSectionInfo(shdr, secName);
533}
534
535bool 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
543const 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
566const 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
589bool 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
622bool 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
632bool 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
655const std::unordered_map<uint64_t, ElfLoadInfo>& DfxElf::GetPtLoads()
656{
657    return elfParse_->GetPtLoads();
658}
659
660bool 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
676bool 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
725bool 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
774int 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
829int 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
850bool 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
868int 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
968bool 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
976const uint8_t* DfxElf::GetMmapPtr()
977{
978    if (mmap_ == nullptr) {
979        return nullptr;
980    }
981    return static_cast<uint8_t *>(mmap_->Get());
982}
983
984size_t DfxElf::GetMmapSize()
985{
986    if (mmap_ == nullptr) {
987        return 0;
988    }
989    return mmap_->Size();
990}
991
992bool 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
1005size_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