1/*
2 * Copyright (c) Huawei Technologies Co., Ltd. 2021. All rights reserved.
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#define HILOG_TAG "Symbols"
17
18#include "symbols_file.h"
19
20#include <algorithm>
21#include <chrono>
22#include <cxxabi.h>
23#include <fcntl.h>
24#include <fstream>
25
26#if is_mingw
27#include <memoryapi.h>
28#else
29#include <sys/mman.h>
30#include <sys/stat.h>
31#endif
32
33#include <cstdlib>
34#include <unistd.h>
35#include "dfx_ark.h"
36#include "dfx_extractor_utils.h"
37#include "dfx_symbols.h"
38#include "dwarf_encoding.h"
39#include "utilities.h"
40
41using namespace OHOS::HiviewDFX;
42using namespace std::chrono;
43
44namespace OHOS {
45namespace Developtools {
46namespace NativeDaemon {
47bool SymbolsFile::onRecording_ = true;
48const std::string SymbolsFile::GetBuildId() const
49{
50    return buildId_;
51}
52
53bool SymbolsFile::UpdateBuildIdIfMatch(std::string buildId)
54{
55    /*
56        here we have two case
57        1 buildId_ is empty
58            a) always return match
59        2 buildId_ is not empty
60            a) really check if the same one
61    */
62
63    if (buildId_.empty()) {
64        // we have new empty build
65        if (buildId.empty()) {
66            // both empty , no build id provided
67            HLOGD("build id is empty.");
68            return true;
69        } else {
70            buildId_ = buildId;
71            HLOGD("new buildId %s", buildId_.c_str());
72            return true;
73        }
74    } else {
75        // we already have a build id
76        // so this is not the first time load symbol
77        // we need check if it match
78        HLOGV("expected buildid: %s vs %s", buildId_.c_str(), buildId.c_str());
79
80        if (buildId_ != buildId) {
81            HLOGW("id not match");
82            return false;
83        } else {
84            HLOGD("id match");
85            return true;
86        }
87    }
88}
89
90std::string SymbolsFile::SearchReadableFile(const std::vector<std::string> &searchPaths,
91                                            const std::string &filePath) const
92{
93    UNWIND_CHECK_TRUE(!filePath.empty(), filePath, "nothing to found");
94    for (auto searchPath : searchPaths) {
95        if (searchPath.back() != PATH_SEPARATOR) {
96            searchPath += PATH_SEPARATOR;
97        }
98        std::string PossibleFilePath = searchPath + filePath;
99        if (CheckPathReadable(PossibleFilePath)) {
100            return PossibleFilePath;
101        }
102        HLOGW("have not found '%s' in search paths %s", filePath.c_str(), searchPath.c_str());
103    }
104    return EMPTY_STRING;
105}
106
107const std::string SymbolsFile::FindSymbolFile(
108    const std::vector<std::string> &symbolsFileSearchPaths, std::string symboleFilePath) const
109{
110    /*
111        this function do 2 things:
112        find by name:
113            1 find dso path
114            2 find search path
115                a) search path + dso path
116                b) search path + dso name
117
118        show we should return filePath_ as default ?
119    */
120    if (symboleFilePath.empty()) {
121        symboleFilePath = filePath_;
122        HLOGD("use default filename: %s ", symboleFilePath.c_str());
123    }
124    symboleFilePath = PlatformPathConvert(symboleFilePath);
125    std::string foundPath;
126    // search fisrt if we have path
127    if (symbolsFileSearchPaths.size() != 0) {
128        foundPath = SearchReadableFile(symbolsFileSearchPaths, symboleFilePath);
129        if (foundPath.empty()) {
130            HLOGV("try base name for: %s split with %s", symboleFilePath.c_str(),
131                  PATH_SEPARATOR_STR.c_str());
132            auto pathSplit = StringSplit(symboleFilePath, PATH_SEPARATOR_STR);
133            if (pathSplit.size() > 1) {
134                HLOGV("base name is: %s ", pathSplit.back().c_str());
135                // found it again with base name , split it and get last name
136                foundPath = SearchReadableFile(symbolsFileSearchPaths, pathSplit.back());
137            }
138        }
139    }
140    auto pathSplit = StringSplit(symboleFilePath, "!");
141    if (pathSplit.size() > 1) {
142        HLOGV("base name is: %s ", pathSplit.back().c_str());
143        if (StringEndsWith(pathSplit[0], ".hap")) {
144            foundPath = pathSplit[0];
145        }
146    }
147    // only access the patch in onRecording_
148    // in report mode we don't load any thing in runtime path
149    if (foundPath.empty() and onRecording_) {
150        // try access direct at last
151        if (CheckPathReadable(symboleFilePath)) {
152            // found direct folder
153            HLOGD("find %s in current work dir", symboleFilePath.c_str());
154            return symboleFilePath;
155        }
156    }
157    return foundPath;
158}
159
160class ElfFileSymbols : public SymbolsFile {
161public:
162    explicit ElfFileSymbols(const std::string &symbolFilePath,
163                            const SymbolsFileType symbolsFileType = SYMBOL_ELF_FILE)
164        : SymbolsFile(symbolsFileType, symbolFilePath)
165    {
166    }
167
168    virtual ~ElfFileSymbols()
169    {
170    }
171
172    bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
173    {
174        symbolsLoaded_ = true;
175        std::string findPath = FindSymbolFile(symbolsFileSearchPaths_, symbolFilePath);
176        UNWIND_CHECK_TRUE(!findPath.empty(), false, "elf found failed (belong to %s)", filePath_.c_str());
177        if (LoadElfSymbols(map, findPath)) {
178            return true;
179        } else {
180            HLOGW("elf open failed with '%s'", findPath.c_str());
181            return false;
182        }
183        return false;
184    }
185
186    std::shared_ptr<DfxElf> GetElfFile() override
187    {
188        return elfFile_;
189    }
190
191protected:
192    bool LoadDebugInfo(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
193    {
194        if (debugInfoLoaded_) {
195            return true;
196        } else {
197            debugInfoLoaded_ = true;
198        }
199        std::string elfPath = FindSymbolFile(symbolsFileSearchPaths_, symbolFilePath);
200        UNWIND_CHECK_TRUE(!elfPath.empty(), false, "elf found failed (belong to %s)", filePath_.c_str());
201        if (elfFile_ == nullptr) {
202            if (StringEndsWith(elfPath, ".hap")) {
203                elfFile_ = DfxElf::CreateFromHap(elfPath, map->prevMap, map->offset);
204                map->elf = elfFile_;
205            } else {
206                elfFile_ = std::make_shared<DfxElf>(elfPath);
207            }
208        }
209
210        if (elfFile_ == nullptr) {
211            HLOGE("Failed to create elf file for %s.", elfPath.c_str());
212            return false;
213        }
214
215        if (!elfFile_->IsValid()) {
216            HLOGE("parse elf file failed.");
217            return false;
218        }
219
220        HLOGD("loaded elf %s", elfPath.c_str());
221        // update path for so in hap
222        if (StringEndsWith(elfPath, ".hap")) {
223            filePath_ = elfPath + "!" + elfFile_->GetElfName();
224            HLOGD("update path for so in hap %s.", filePath_.c_str());
225            map->name = filePath_;
226            map->elf = elfFile_;
227            map->prevMap->name = filePath_;
228            map->prevMap->elf = elfFile_;
229        }
230        textExecVaddr_ = elfFile_->GetStartVaddr();
231        textExecVaddrFileOffset_ = elfFile_->GetStartOffset();
232        HLOGD("textExecVaddr_ 0x%016" PRIx64 " file offset 0x%016" PRIx64 "", textExecVaddr_,
233              textExecVaddrFileOffset_);
234
235#ifndef __arm__
236        ShdrInfo shinfo;
237        if (elfFile_->GetSectionInfo(shinfo, ".eh_frame_hdr")) {
238            LoadEhFrameHDR(elfFile_->GetMmapPtr() + shinfo.offset, shinfo.size, shinfo.offset);
239        }
240#endif
241        return true;
242    }
243
244private:
245    bool ehFrameHDRValid_ {false};
246    uint64_t ehFrameHDRElfOffset_ {0};
247    uint64_t ehFrameHDRFdeCount_ {0};
248    uint64_t ehFrameHDRFdeTableItemSize_ {0};
249    uint64_t ehFrameHDRFdeTableElfOffset_ {0};
250    std::shared_ptr<DfxElf> elfFile_;
251    bool GetSectionInfo(const std::string &name, uint64_t &sectionVaddr, uint64_t &sectionSize,
252                        uint64_t &sectionFileOffset) const override
253    {
254        struct ShdrInfo shdrInfo;
255        if (elfFile_->GetSectionInfo(shdrInfo, name)) {
256            sectionVaddr = shdrInfo.addr;
257            sectionSize = shdrInfo.size;
258            sectionFileOffset = shdrInfo.offset;
259            HLOGM("Get Section '%s' %" PRIx64 " - %" PRIx64 "", name.c_str(), sectionVaddr, sectionSize);
260            return true;
261        } else {
262            HLOGW("Section '%s' not found", name.c_str());
263            return false;
264        }
265    }
266
267#ifndef __arm__
268    bool GetHDRSectionInfo(uint64_t &ehFrameHdrElfOffset, uint64_t &fdeTableElfOffset,
269                           uint64_t &fdeTableSize) override
270    {
271        ShdrInfo shinfo;
272        if (!elfFile_->GetSectionInfo(shinfo, ".eh_frame_hdr")) {
273            return false;
274        }
275
276        ehFrameHDRElfOffset_ = shinfo.offset;
277        if (ehFrameHDRValid_) {
278            ehFrameHdrElfOffset = ehFrameHDRElfOffset_;
279            fdeTableElfOffset = ehFrameHDRFdeTableElfOffset_;
280            fdeTableSize = ehFrameHDRFdeCount_;
281            return true;
282        } else {
283            HLOGW("!ehFrameHDRValid_");
284            return false;
285        }
286        if (!LoadEhFrameHDR(elfFile_->GetMmapPtr() + shinfo.offset, elfFile_->GetMmapSize(), shinfo.offset)) {
287            HLOGW("Failed to load eh_frame_hdr");
288            return false;
289        }
290
291        ehFrameHdrElfOffset = ehFrameHDRElfOffset_;
292        fdeTableElfOffset = ehFrameHDRFdeTableElfOffset_;
293        fdeTableSize = ehFrameHDRFdeCount_;
294        return true;
295    }
296#endif
297
298    void DumpEhFrameHDR() const
299    {
300        HLOGD("  ehFrameHDRElfOffset_:          0x%" PRIx64 "", ehFrameHDRElfOffset_);
301        HLOGD("  ehFrameHDRFdeCount_:           0x%" PRIx64 "", ehFrameHDRFdeCount_);
302        HLOGD("  ehFrameHDRFdeTableElfOffset_:  0x%" PRIx64 "", ehFrameHDRFdeTableElfOffset_);
303        HLOGD("  ehFrameHDRFdeTableItemSize_:   0x%" PRIx64 "", ehFrameHDRFdeTableItemSize_);
304    }
305
306    bool LoadEhFrameHDR(const unsigned char *buffer, size_t bufferSize, uint64_t shdrOffset)
307    {
308        eh_frame_hdr *ehFrameHdr = (eh_frame_hdr *)buffer;
309        const uint8_t *dataPtr = ehFrameHdr->encode_data;
310        DwarfEncoding dwEhFramePtr(ehFrameHdr->eh_frame_ptr_enc, dataPtr);
311        DwarfEncoding dwFdeCount(ehFrameHdr->fde_count_enc, dataPtr);
312        DwarfEncoding dwTable(ehFrameHdr->table_enc, dataPtr);
313        DwarfEncoding dwTableValue(ehFrameHdr->table_enc, dataPtr);
314
315        HLOGD("eh_frame_hdr:");
316        HexDump(buffer, BITS_OF_FOUR_BYTE, bufferSize);
317        unsigned char version = ehFrameHdr->version;
318        HLOGD("  version:             %02x:%s", version, (version == 1) ? "valid" : "invalid");
319        HLOGD("  eh_frame_ptr_enc:    %s", dwEhFramePtr.ToString().c_str());
320        HLOGD("  fde_count_enc:       %s", dwFdeCount.ToString().c_str());
321        HLOGD("  table_enc:           %s", dwTable.ToString().c_str());
322        HLOGD("  table_value_enc:     %s", dwTableValue.ToString().c_str());
323        HLOGD("  table_item_size:     %zd", dwTable.GetSize() + dwTableValue.GetSize());
324        HLOGD("  table_offset_in_hdr: %zu", dwTable.GetData() - buffer);
325
326        if (version != 1) {
327            HLOGD("eh_frame_hdr version is invalid");
328            return false;
329        }
330        ehFrameHDRValid_ = true;
331        ehFrameHDRElfOffset_ = shdrOffset;
332        ehFrameHDRFdeCount_ = dwFdeCount.GetAppliedValue();
333        ehFrameHDRFdeTableElfOffset_ = dwTable.GetData() - buffer + shdrOffset;
334        ehFrameHDRFdeTableItemSize_ = dwTable.GetSize() + dwTableValue.GetSize();
335        DumpEhFrameHDR();
336
337        if (!dwFdeCount.IsOmit() && dwFdeCount.GetValue() > 0) {
338            return true;
339        } else {
340            HLOGW("fde table not found.\n");
341        }
342        return false;
343    }
344
345    void UpdateSymbols(std::vector<DfxSymbol> &symbolsTable, const std::string &elfPath)
346    {
347        symbols_.clear();
348        HLOGD("%zu symbols loadded from symbolsTable.", symbolsTable.size());
349
350        symbols_.swap(symbolsTable);
351
352        AdjustSymbols();
353        HLOGD("%zu symbols loadded from elf '%s'.", symbols_.size(), elfPath.c_str());
354        for (auto& symbol: symbols_) {
355            HLOGD("symbol %s", symbol.ToDebugString().c_str());
356        }
357        if (buildId_.empty()) {
358            HLOGD("buildId not found from elf '%s'.", elfPath.c_str());
359            // dont failed. some time the lib have not got the build id
360            // buildId not found from elf '/system/bin/ld-musl-arm.so.1'.
361        }
362    }
363
364    bool LoadElfSymbols(std::shared_ptr<DfxMap> map, std::string elfPath)
365    {
366#ifdef HIPERF_DEBUG_TIME
367        const auto startTime = steady_clock::now();
368#endif
369        if (elfFile_ == nullptr) {
370            if (StringEndsWith(elfPath, ".hap") && map != nullptr) {
371                elfFile_ = DfxElf::CreateFromHap(elfPath, map->prevMap, map->offset);
372                map->elf = elfFile_;
373                HLOGD("loaded map %s", elfPath.c_str());
374            } else {
375                elfFile_ = std::make_shared<DfxElf>(elfPath);
376                HLOGD("loaded elf %s", elfPath.c_str());
377            }
378        }
379
380        if (elfFile_ == nullptr || !elfFile_->IsValid()) {
381            HLOGD("parser elf file failed.");
382            return false;
383        }
384        textExecVaddr_ = elfFile_->GetStartVaddr();
385        textExecVaddrFileOffset_ = elfFile_->GetStartOffset();
386        HLOGD("textExecVaddr_ 0x%016" PRIx64 " file offset 0x%016" PRIx64 "", textExecVaddr_,
387              textExecVaddrFileOffset_);
388
389        // we prepare two table here
390        // only one we will push in to symbols_
391        // or both drop if build id is not same
392        std::string buildIdFound = elfFile_->GetBuildId();
393        std::vector<DfxSymbol> symbolsTable;
394
395        // use elfFile_ to get symbolsTable
396        DfxSymbols::ParseSymbols(symbolsTable, elfFile_, elfPath);
397        DfxSymbols::AddSymbolsByPlt(symbolsTable, elfFile_, elfPath);
398
399        if (UpdateBuildIdIfMatch(buildIdFound)) {
400            UpdateSymbols(symbolsTable, elfPath);
401        } else {
402            HLOGW("symbols will not update for '%s' because buildId is not match.",
403                  elfPath.c_str());
404            // this mean failed . we dont goon for this.
405            return false;
406        }
407
408#ifdef HIPERF_DEBUG_TIME
409        auto usedTime = duration_cast<microseconds>(steady_clock::now() - startTime);
410        if (usedTime.count() != 0) {
411            HLOGV("cost %0.3f ms to load symbols '%s'",
412                  usedTime.count() / static_cast<double>(milliseconds::duration::period::den),
413                  elfPath.c_str());
414        }
415#endif
416        return true;
417    }
418
419    uint64_t GetVaddrInSymbols(uint64_t ip, uint64_t mapStart,
420                               uint64_t mapPageOffset) const override
421    {
422        /*
423            00200000-002c5000 r--p 00000000 08:02 46400311
424            002c5000-00490000 r-xp 000c5000 08:02 4640031
425
426            [14] .text             PROGBITS         00000000002c5000  000c5000
427
428            if ip is 0x46e6ab
429            1. find the map range is 002c5000-00490000
430            2. ip - map start(002c5000) = map section offset
431            3. map section offset + map page offset(000c5000) = elf file offset
432            4. elf file offset - exec file offset(000c5000)
433                = ip offset (ip always in exec file offset)
434            5. ip offset + exec begin vaddr(2c5000) = virtual ip in elf
435        */
436        uint64_t vaddr = ip - mapStart + mapPageOffset - textExecVaddrFileOffset_ + textExecVaddr_;
437        HLOGM(" ip :0x%016" PRIx64 " -> elf offset :0x%016" PRIx64 " -> vaddr :0x%016" PRIx64 " ",
438              ip, ip - mapStart + mapPageOffset, vaddr);
439        HLOGM("(minExecAddrFileOffset_ is 0x%" PRIx64 " textExecVaddr_ is 0x%" PRIx64 ")",
440              textExecVaddrFileOffset_, textExecVaddr_);
441        return vaddr;
442    }
443};
444
445class KernelSymbols : public ElfFileSymbols {
446public:
447    explicit KernelSymbols(const std::string &symbolFilePath)
448        : ElfFileSymbols(symbolFilePath, SYMBOL_KERNEL_FILE)
449    {
450    }
451
452    static constexpr const int KSYM_MIN_TOKENS = 3;
453    static constexpr const int KSYM_DEFAULT_LINE = 35000;
454    static constexpr const int KSYM_DEFAULT_SIZE = 1024 * 1024 * 1; // 1MB
455
456    bool ParseKallsymsLine()
457    {
458        size_t lines = 0;
459        std::string kallsym;
460        if (!ReadFileToString("/proc/kallsyms", kallsym, KSYM_DEFAULT_SIZE)) {
461            HLOGW("/proc/kallsyms load failed.");
462            return false;
463        }
464        // reduce the mem alloc
465        symbols_.reserve(KSYM_DEFAULT_LINE);
466
467        char *lineBegin = kallsym.data();
468        char *dataEnd = lineBegin + kallsym.size();
469        while (lineBegin < dataEnd) {
470            char *lineEnd = strchr(lineBegin, '\n');
471            if (lineEnd != nullptr) {
472                *lineEnd = '\0';
473            }
474            size_t lineSize = (lineEnd != nullptr) ? (lineEnd - lineBegin) : (dataEnd - lineBegin);
475
476            lines++;
477            uint64_t addr = 0;
478            char type = '\0';
479
480            char nameRaw[lineSize];
481            char moduleRaw[lineSize];
482            int ret = sscanf_s(lineBegin, "%" PRIx64 " %c %s%s", &addr, &type, sizeof(type),
483                               nameRaw, sizeof(nameRaw), moduleRaw, sizeof(moduleRaw));
484
485            lineBegin = lineEnd + 1;
486            if (ret >= KSYM_MIN_TOKENS) {
487                if (ret == KSYM_MIN_TOKENS) {
488                    moduleRaw[0] = '\0';
489                }
490                HLOGM(" 0x%016" PRIx64 " %c '%s' '%s'", addr, type, nameRaw, moduleRaw);
491            } else {
492                HLOGW("unknow line %d: '%s'", ret, lineBegin);
493                continue;
494            }
495            std::string name = nameRaw;
496            std::string module = moduleRaw;
497
498            /*
499            T
500            The symbol is in the text (code) section.
501
502            W
503            The symbol is a weak symbol that has not been specifically
504            tagged as a weak object symbol. When a weak defined symbol is
505            linked with a normal defined symbol, the normal defined symbol
506            is used with no error. When a weak undefined symbol is linked
507            and the symbol is not defined, the value of the weak symbol
508            becomes zero with no error.
509            */
510            if (addr != 0 && strchr("TtWw", type)) {
511                // we only need text symbols
512                symbols_.emplace_back(addr, name, module.empty() ? filePath_ : module);
513            }
514        }
515        HLOGD("%zu line processed(%zu symbols)", lines, symbols_.size());
516        return true;
517    }
518
519    const std::string KPTR_RESTRICT = "/proc/sys/kernel/kptr_restrict";
520
521    bool LoadKernelSyms()
522    {
523        if (!IsRoot()) {
524            HLOGE("only root mode can access kernel symbols");
525            return false;
526        }
527        HLOGD("try read /proc/kallsyms");
528        if (access("/proc/kallsyms", R_OK) != 0) {
529            printf("No vmlinux path is given, and kallsyms cannot be opened\n");
530            return false;
531        }
532
533        if (ReadFileToString(KPTR_RESTRICT).front() != '0') {
534            printf("/proc/sys/kernel/kptr_restrict is NOT 0, will try set it to 0.\n");
535            if (!WriteStringToFile(KPTR_RESTRICT, "0")) {
536                printf("/proc/sys/kernel/kptr_restrict write failed and we cant not change it.\n");
537            }
538        }
539
540        // getline end
541        if (!ParseKallsymsLine()) {
542            return false;
543        }
544
545        if (symbols_.empty()) {
546            printf("The symbol table addresses in /proc/kallsyms are all 0.\n"
547                   "Please check the value of /proc/sys/kernel/kptr_restrict, it "
548                   "should be 0.\n"
549                   "Or provide a separate vmlinux path.\n");
550
551            if (buildId_.size() != 0) {
552                // but we got the buildid , so we make a dummpy symbols
553                HLOGD("kallsyms not found. but we have the buildid");
554                return true;
555            } else {
556                // we got nothing
557                return false;
558            }
559        } else {
560            AdjustSymbols();
561            HLOGV("%zu symbols_ loadded from kallsyms.\n", symbols_.size());
562            return true;
563        }
564    }
565    bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
566    {
567        symbolsLoaded_ = true;
568        HLOGV("KernelSymbols try read '%s' search paths size %zu, inDeviceRecord %d",
569              symbolFilePath.c_str(), symbolsFileSearchPaths_.size(), onRecording_);
570
571        if (onRecording_) {
572            // try read
573            HLOGD("try read /sys/kernel/notes");
574            std::string notes = ReadFileToString("/sys/kernel/notes");
575            if (notes.empty()) {
576                printf("notes cannot be opened, unable get buildid\n");
577                return false;
578            } else {
579                HLOGD("kernel notes size: %zu", notes.size());
580                buildId_ = DfxElf::GetBuildId((uint64_t)notes.data(), (uint64_t)notes.size());
581            }
582
583            const auto startTime = std::chrono::steady_clock::now();
584            if (!LoadKernelSyms()) {
585                printf("parse kalsyms failed.\n");
586                return false;
587            } else {
588                const auto thisTime = std::chrono::steady_clock::now();
589                const auto usedTimeMsTick =
590                    std::chrono::duration_cast<std::chrono::milliseconds>(thisTime - startTime);
591                HLOGV("Load kernel symbols (total %" PRId64 " ms)\n", (int64_t)usedTimeMsTick.count());
592                // load complete
593                return true;
594            }
595        } // no search path
596
597        // try vmlinux
598        return ElfFileSymbols::LoadSymbols(nullptr, KERNEL_ELF_NAME);
599    }
600    uint64_t GetVaddrInSymbols(uint64_t ip, uint64_t mapStart, uint64_t) const override
601    {
602        // ip is vaddr in /proc/kallsyms
603        return ip;
604    }
605    ~KernelSymbols() override {}
606};
607
608class KernelModuleSymbols : public ElfFileSymbols {
609public:
610    explicit KernelModuleSymbols(const std::string &symbolFilePath) : ElfFileSymbols(symbolFilePath)
611    {
612        HLOGV("create %s", symbolFilePath.c_str());
613        symbolFileType_ = SYMBOL_KERNEL_MODULE_FILE;
614        module_ = symbolFilePath;
615    }
616    ~KernelModuleSymbols() override {};
617
618    bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
619    {
620        symbolsLoaded_ = true;
621        if (module_ == filePath_ and onRecording_) {
622            // file name sitll not convert to ko file path
623            // this is in record mode
624            HLOGV("find ko name %s", module_.c_str());
625            for (const std::string &path : kernelModulePaths) {
626                if (access(path.c_str(), R_OK) == 0) {
627                    std::string koPath = path + module_ + KERNEL_MODULES_EXT_NAME;
628                    HLOGV("found ko in %s", koPath.c_str());
629                    if (access(koPath.c_str(), R_OK) == 0) {
630                        // create symbol
631                        filePath_ = koPath;
632                        break; // find next ko
633                    }
634                }
635            }
636            LoadBuildId();
637        } else {
638            HLOGV("we have file path, load with %s", filePath_.c_str());
639            return ElfFileSymbols::LoadSymbols(nullptr, filePath_);
640        }
641        return false;
642    }
643    uint64_t GetVaddrInSymbols(uint64_t ip, uint64_t mapStart, uint64_t) const override
644    {
645        return ip - mapStart;
646    }
647
648private:
649    bool LoadBuildId()
650    {
651        std::string sysFile = "/sys/module/" + module_ + "/notes/.note.gnu.build-id";
652        std::string buildIdRaw = ReadFileToString(sysFile);
653        if (!buildIdRaw.empty()) {
654            buildId_ = DfxElf::GetBuildId((uint64_t)buildIdRaw.data(), (uint64_t)buildIdRaw.size());
655            HLOGD("kerne module %s(%s) build id %s", module_.c_str(), filePath_.c_str(),
656                  buildId_.c_str());
657            return buildId_.empty() ? false : true;
658        }
659        return false;
660    }
661
662    const std::vector<std::string> kernelModulePaths = {"/vendor/modules/"};
663    std::string module_ = "";
664};
665
666class JavaFileSymbols : public ElfFileSymbols {
667public:
668    explicit JavaFileSymbols(const std::string &symbolFilePath) : ElfFileSymbols(symbolFilePath)
669    {
670        symbolFileType_ = SYMBOL_KERNEL_FILE;
671    }
672    bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
673    {
674        symbolsLoaded_ = true;
675        return false;
676    }
677    ~JavaFileSymbols() override {}
678
679    uint64_t GetVaddrInSymbols(uint64_t ip, uint64_t mapStart,
680                                       uint64_t mapPageOffset) const override
681    {
682        // this is different with elf
683        // elf use  ip - mapStart + mapPageOffset - minExecAddrFileOffset_ + textExecVaddr_
684        return ip - mapStart + mapPageOffset;
685    }
686};
687
688class JSFileSymbols : public ElfFileSymbols {
689public:
690    explicit JSFileSymbols(const std::string &symbolFilePath) : ElfFileSymbols(symbolFilePath)
691    {
692        symbolFileType_ = SYMBOL_KERNEL_FILE;
693    }
694    bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
695    {
696        symbolsLoaded_ = true;
697        return false;
698    }
699    ~JSFileSymbols() override {}
700};
701
702class HapFileSymbols : public ElfFileSymbols {
703private:
704#if defined(is_ohos) && is_ohos
705    std::unique_ptr<DfxExtractor> dfxExtractor_;
706    bool hapExtracted_ = false;
707#endif
708    std::unique_ptr<uint8_t[]> abcDataPtr_ = nullptr;
709    [[maybe_unused]] uintptr_t loadOffSet_ = 0;
710    [[maybe_unused]] size_t abcDataSize_ = 0;
711    [[maybe_unused]] uintptr_t arkExtractorptr_ = 0;
712    bool isHapAbc_ = false;
713    pid_t pid_ = 0;
714public:
715    explicit HapFileSymbols(const std::string &symbolFilePath, pid_t pid)
716        : ElfFileSymbols(symbolFilePath, SYMBOL_HAP_FILE)
717    {
718        pid_ = pid;
719    }
720
721    ~HapFileSymbols() override
722    {
723#if defined(is_ohos) && is_ohos
724        if (arkExtractorptr_ != 0) {
725            DfxArk::ArkDestoryJsSymbolExtractor(arkExtractorptr_);
726            arkExtractorptr_ = 0;
727        }
728#endif
729    }
730    // abc is the ark bytecode
731    bool IsHapAbc()
732    {
733#if defined(is_ohos) && is_ohos
734        if (hapExtracted_) {
735            return isHapAbc_;
736        }
737        hapExtracted_ = true;
738        HLOGD("the symbol file is %s.", filePath_.c_str());
739        if (StringEndsWith(filePath_, ".hap") && map_->IsMapExec()) {
740            HLOGD("map is exec not abc file , the symbol file is:%s", map_->name.c_str());
741            return false;
742        }
743        if (StringEndsWith(filePath_, ".hap") || StringEndsWith(filePath_, ".hsp")) {
744            dfxExtractor_ = std::make_unique<DfxExtractor>(filePath_);
745            if (dfxExtractor_ == nullptr) {
746                HLOGD("DfxExtractor create failed.");
747                return false;
748            }
749            // extract the bytecode information of the ark in the hap package
750            if (!dfxExtractor_->GetHapAbcInfo(loadOffSet_, abcDataPtr_, abcDataSize_)) {
751                HLOGD("failed to call GetHapAbcInfo, the symbol file is:%s", filePath_.c_str());
752                return false;
753            }
754            HLOGD("loadOffSet %u", (uint32_t)loadOffSet_);
755            if (abcDataPtr_ != nullptr) {
756                isHapAbc_ = true;
757                HLOGD("input abcDataPtr : %s, isAbc: %d", abcDataPtr_.get(), isHapAbc_);
758            }
759        } else {
760            if (map_ == nullptr) {
761                return false;
762            }
763            loadOffSet_ = map_->offset;
764            abcDataSize_ = map_->end - map_->begin;
765            abcDataPtr_ = std::make_unique<uint8_t[]>(abcDataSize_);
766            auto size = DfxMemory::ReadProcMemByPid(pid_, map_->begin, abcDataPtr_.get(), map_->end - map_->begin);
767            if (size != abcDataSize_) {
768                HLOGD("return size is small abcDataPtr : %s, isAbc: %d", abcDataPtr_.get(), isHapAbc_);
769                return false;
770            }
771            isHapAbc_ = true;
772            HLOGD("symbol file name %s loadOffSet %u abcDataSize_ %u abcDataPtr_ %s",
773                  filePath_.c_str(), (uint32_t)loadOffSet_, (uint32_t)abcDataSize_, abcDataPtr_.get());
774        }
775        auto ret = DfxArk::ArkCreateJsSymbolExtractor(&arkExtractorptr_);
776        if (ret < 0) {
777            arkExtractorptr_ = 0;
778            HLOGE("failed to call ArkCreateJsSymbolExtractor, the symbol file is:%s", filePath_.c_str());
779        }
780#endif
781        return isHapAbc_;
782    }
783
784    bool IsAbc() override
785    {
786        return isHapAbc_ == true;
787    }
788
789    void SetBoolValue(bool value) override
790    {
791        isHapAbc_ = value;
792    }
793
794    bool LoadDebugInfo(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
795    {
796        HLOGD("map ptr:%p, map name:%s", map.get(), map->name.c_str());
797        if (debugInfoLoaded_) {
798            return true;
799        }
800        if (!onRecording_) {
801            return true;
802        }
803
804        if (!IsHapAbc()) {
805            ElfFileSymbols::LoadDebugInfo(map, "");
806        }
807        debugInfoLoaded_ = true;
808        debugInfoLoadResult_ = true;
809        return true;
810    }
811
812    bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
813    {
814        HLOGD("map ptr:%p, map name:%s", map.get(), map->name.c_str());
815        if (symbolsLoaded_ || !onRecording_) {
816            return true;
817        }
818        symbolsLoaded_ = true;
819        if (!IsHapAbc()) {
820            ElfFileSymbols::LoadSymbols(map, "");
821        }
822        return true;
823    }
824
825    DfxSymbol GetSymbolWithPcAndMap(uint64_t ip, std::shared_ptr<DfxMap> map) override
826    {
827        // get cache
828        auto iter = symbolsMap_.find(ip);
829        if (iter != symbolsMap_.end()) {
830            return iter->second;
831        }
832        if (map == nullptr) {
833            return DfxSymbol(ip, "");
834        }
835        HLOGD("map ptr:%p, map name:%s", map.get(), map->name.c_str());
836
837#if defined(is_ohos) && is_ohos
838        if (IsAbc()) {
839            JsFunction jsFunc;
840            std::string module = map->name;
841            HLOGD("map->name module:%s", module.c_str());
842            // symbolization based on ark bytecode
843            auto ret = DfxArk::ParseArkFrameInfo(static_cast<uintptr_t>(ip), static_cast<uintptr_t>(map->begin),
844                                                 loadOffSet_, abcDataPtr_.get(), abcDataSize_,
845                                                 arkExtractorptr_, &jsFunc);
846            if (ret == -1) {
847                HLOGD("failed to call ParseArkFrameInfo, the symbol file is : %s", map->name.c_str());
848                return DfxSymbol(ip, "");
849            }
850            this->symbolsMap_.insert(std::make_pair(ip,
851                                                    DfxSymbol(ip,
852                                                    jsFunc.codeBegin,
853                                                    jsFunc.functionName,
854                                                    jsFunc.ToString(),
855                                                    map->name)));
856
857            DfxSymbol &foundSymbol = symbolsMap_[ip];
858            if (!foundSymbol.matched_) {
859                foundSymbol.matched_ = true;
860                matchedSymbols_.push_back(&(symbolsMap_[ip]));
861            }
862
863            HLOGD("ip : 0x%" PRIx64 " the symbol file is : %s, function is %s demangle_ : %s", ip,
864                  symbolsMap_[ip].module_.data(), jsFunc.functionName, matchedSymbols_.back()->demangle_.data());
865            return symbolsMap_[ip];
866        }
867#endif
868        DfxSymbol symbol(ip, "");
869        return symbol;
870    }
871};
872
873class UnknowFileSymbols : public SymbolsFile {
874public:
875    explicit UnknowFileSymbols(const std::string &symbolFilePath)
876        : SymbolsFile(SYMBOL_UNKNOW_FILE, symbolFilePath)
877    {
878    }
879    bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
880    {
881        symbolsLoaded_ = true;
882        return false;
883    }
884    ~UnknowFileSymbols() override {}
885};
886
887SymbolsFile::~SymbolsFile() {}
888
889std::unique_ptr<SymbolsFile> SymbolsFile::CreateSymbolsFile(SymbolsFileType symbolType,
890                                                            const std::string symbolFilePath, pid_t pid)
891{
892    switch (symbolType) {
893        case SYMBOL_KERNEL_FILE:
894            return std::make_unique<KernelSymbols>(symbolFilePath.empty() ? KERNEL_MMAP_NAME
895                                                                          : symbolFilePath);
896        case SYMBOL_KERNEL_MODULE_FILE:
897            return std::make_unique<KernelModuleSymbols>(symbolFilePath);
898        case SYMBOL_ELF_FILE:
899            return std::make_unique<ElfFileSymbols>(symbolFilePath);
900        case SYMBOL_JAVA_FILE:
901            return std::make_unique<JavaFileSymbols>(symbolFilePath);
902        case SYMBOL_JS_FILE:
903            return std::make_unique<JSFileSymbols>(symbolFilePath);
904        case SYMBOL_HAP_FILE:
905            return std::make_unique<HapFileSymbols>(symbolFilePath, pid);
906        default:
907            return std::make_unique<SymbolsFile>(SYMBOL_UNKNOW_FILE, symbolFilePath);
908    }
909}
910
911std::unique_ptr<SymbolsFile> SymbolsFile::CreateSymbolsFile(const std::string &symbolFilePath, pid_t pid)
912{
913    // we need check file name here
914    if (symbolFilePath == KERNEL_MMAP_NAME) {
915        return SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_FILE, symbolFilePath);
916    } else if (StringEndsWith(symbolFilePath, KERNEL_MODULES_EXT_NAME)) {
917        return SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_MODULE_FILE, symbolFilePath);
918    } else if (IsArkJsFile(symbolFilePath)) {
919        return SymbolsFile::CreateSymbolsFile(SYMBOL_HAP_FILE, symbolFilePath, pid);
920    } else {
921        // default is elf
922        return SymbolsFile::CreateSymbolsFile(SYMBOL_ELF_FILE, symbolFilePath);
923    }
924}
925
926void SymbolsFile::AdjustSymbols()
927{
928    if (symbols_.size() <= 1u) {
929        return;
930    }
931
932    // order
933    sort(symbols_.begin(), symbols_.end(), [](const DfxSymbol& a, const DfxSymbol& b) {
934        return a.funcVaddr_ < b.funcVaddr_;
935    });
936    HLOGV("sort completed");
937
938    size_t fullSize = symbols_.size();
939    size_t erased = 0;
940
941    // Check for duplicate vaddr
942    auto last = std::unique(symbols_.begin(), symbols_.end(), [](const DfxSymbol &a, const DfxSymbol &b) {
943        return (a.funcVaddr_ == b.funcVaddr_);
944    });
945    symbols_.erase(last, symbols_.end());
946    erased = fullSize - symbols_.size();
947    HLOGV("uniqued completed");
948    auto it = symbols_.begin();
949    while (it != symbols_.end()) {
950        it->index_ = it - symbols_.begin();
951        it++;
952    }
953    HLOGV("indexed completed");
954
955    HLOG_ASSERT(symbols_.size() != 0);
956
957    if (textExecVaddrRange_ == maxVaddr) {
958        textExecVaddrRange_ = symbols_.back().funcVaddr_ - symbols_.front().funcVaddr_;
959    }
960
961    HLOGDDD("%zu symbols after adjust (%zu erased) 0x%016" PRIx64 " - 0x%016" PRIx64
962            " @0x%016" PRIx64 " ",
963            symbols_.size(), erased, symbols_.front().funcVaddr_, symbols_.back().funcVaddr_,
964            textExecVaddrFileOffset_);
965}
966
967void SymbolsFile::SortMatchedSymbols()
968{
969    if (matchedSymbols_.size() <= 1u) {
970        return;
971    }
972    sort(matchedSymbols_.begin(), matchedSymbols_.end(), [](const DfxSymbol* a, const DfxSymbol* b) {
973        return a->funcVaddr_ < b->funcVaddr_;
974    });
975}
976
977const std::vector<DfxSymbol> &SymbolsFile::GetSymbols()
978{
979    return symbols_;
980}
981
982const std::vector<DfxSymbol *> &SymbolsFile::GetMatchedSymbols()
983{
984    return matchedSymbols_;
985}
986
987const DfxSymbol SymbolsFile::GetSymbolWithVaddr(uint64_t vaddrInFile)
988{
989#ifdef HIPERF_DEBUG_TIME
990    const auto startTime = steady_clock::now();
991#endif
992    DfxSymbol symbol;
993    // it should be already order from small to large
994    auto found =
995        std::upper_bound(symbols_.begin(), symbols_.end(), vaddrInFile, DfxSymbol::ValueLessThen);
996    /*
997    if data is { 1, 2, 4, 5, 5, 6 };
998    upper_bound for each val :
999        0 < 1 at index 0
1000        1 < 2 at index 1
1001        2 < 4 at index 2
1002        3 < 4 at index 2
1003        4 < 5 at index 3
1004        5 < 6 at index 5
1005        6 < not found
1006    if key symbol vaddr is { 1, 2, 4, 5, 5, 6 };
1007     check ip vaddr for each val :
1008       ip   sym
1009        0   not found
1010        1   1
1011        1   1
1012        2   2
1013        3   3
1014        4   4
1015        5   5
1016        6   6
1017        7   7
1018    */
1019    if (found != symbols_.begin()) {
1020        found = std::prev(found);
1021        if (found->Contain(vaddrInFile)) {
1022            if (!found->matched_) {
1023                found->matched_ = true;
1024                matchedSymbols_.push_back(&(*found));
1025            }
1026            symbol = *found; // copy
1027            HLOGV("found '%s' for vaddr 0x%016" PRIx64 "", symbol.ToString().c_str(), vaddrInFile);
1028        }
1029    }
1030
1031    if (!symbol.IsValid()) {
1032        HLOGV("NOT found vaddr 0x%" PRIx64 " in symbole file %s(%zu)", vaddrInFile,
1033              filePath_.c_str(), symbols_.size());
1034    }
1035    symbol.SetIpVAddress(vaddrInFile);
1036
1037#ifdef HIPERF_DEBUG_TIME
1038    auto usedTime = duration_cast<milliseconds>(steady_clock::now() - startTime);
1039    if (usedTime > 1ms) {
1040        HLOGW("cost %" PRId64 "ms to search ", usedTime.count());
1041    }
1042#endif
1043    return symbol;
1044}
1045
1046bool SymbolsFile::CheckPathReadable(const std::string &path) const
1047{
1048    if (access(path.c_str(), R_OK) == 0) {
1049        return true;
1050    } else {
1051        HLOGM("'%s' is unable read", path.c_str());
1052        return false;
1053    }
1054}
1055
1056bool SymbolsFile::setSymbolsFilePath(const std::vector<std::string> &symbolsSearchPaths)
1057{
1058    symbolsFileSearchPaths_.clear();
1059    for (auto &symbolsSearchPath : symbolsSearchPaths) {
1060        if (CheckPathReadable(symbolsSearchPath)) {
1061            symbolsFileSearchPaths_.emplace_back(symbolsSearchPath);
1062            HLOGV("'%s' is add to symbolsSearchPath", symbolsSearchPath.c_str());
1063        }
1064    }
1065    return (symbolsFileSearchPaths_.size() > 0);
1066}
1067
1068std::unique_ptr<SymbolsFile> SymbolsFile::LoadSymbolsFromSaved(
1069    const SymbolFileStruct &symbolFileStruct)
1070{
1071    auto symbolsFile = CreateSymbolsFile(symbolFileStruct.filePath_);
1072    symbolsFile->filePath_ = symbolFileStruct.filePath_;
1073    symbolsFile->symbolFileType_ = (SymbolsFileType)symbolFileStruct.symbolType_;
1074    symbolsFile->textExecVaddr_ = symbolFileStruct.textExecVaddr_;
1075    symbolsFile->textExecVaddrFileOffset_ = symbolFileStruct.textExecVaddrFileOffset_;
1076    symbolsFile->buildId_ = symbolFileStruct.buildId_;
1077    for (auto &symbolStruct : symbolFileStruct.symbolStructs_) {
1078        symbolsFile->symbols_.emplace_back(symbolStruct.vaddr_, symbolStruct.len_,
1079                                           symbolStruct.symbolName_, symbolFileStruct.filePath_);
1080    }
1081    symbolsFile->AdjustSymbols(); // reorder
1082    HLOGV("load %zu symbol from SymbolFileStruct for file '%s'", symbolsFile->symbols_.size(),
1083          symbolsFile->filePath_.c_str());
1084    return symbolsFile;
1085}
1086
1087void SymbolsFile::SetBoolValue(bool value)
1088{
1089}
1090
1091void SymbolsFile::ExportSymbolToFileFormat(SymbolFileStruct &symbolFileStruct)
1092{
1093    symbolFileStruct.filePath_ = filePath_;
1094    symbolFileStruct.symbolType_ = symbolFileType_;
1095    symbolFileStruct.textExecVaddr_ = textExecVaddr_;
1096    symbolFileStruct.textExecVaddrFileOffset_ = textExecVaddrFileOffset_;
1097    symbolFileStruct.buildId_ = buildId_;
1098
1099    SortMatchedSymbols();
1100    auto symbols = GetMatchedSymbols();
1101    symbolFileStruct.symbolStructs_.reserve(symbols.size());
1102    for (auto symbol : symbols) {
1103        auto &symbolStruct = symbolFileStruct.symbolStructs_.emplace_back();
1104        symbolStruct.vaddr_ = symbol->funcVaddr_;
1105        symbolStruct.len_ = symbol->size_;
1106        symbolStruct.symbolName_ = symbol->GetName();
1107    }
1108
1109    HLOGV("export %zu symbol to SymbolFileStruct from %s", symbolFileStruct.symbolStructs_.size(),
1110          filePath_.c_str());
1111}
1112
1113uint64_t SymbolsFile::GetVaddrInSymbols(uint64_t ip, uint64_t, uint64_t) const
1114{
1115    // no convert
1116    return ip;
1117}
1118} // namespace NativeDaemon
1119} // namespace Developtools
1120} // namespace OHOS
1121