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 
41 using namespace OHOS::HiviewDFX;
42 using namespace std::chrono;
43 
44 namespace OHOS {
45 namespace Developtools {
46 namespace NativeDaemon {
47 bool SymbolsFile::onRecording_ = true;
GetBuildId() const48 const std::string SymbolsFile::GetBuildId() const
49 {
50     return buildId_;
51 }
52 
UpdateBuildIdIfMatch(std::string buildId)53 bool 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 
SearchReadableFile(const std::vector<std::string> &searchPaths, const std::string &filePath) const90 std::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 
FindSymbolFile( const std::vector<std::string> &symbolsFileSearchPaths, std::string symboleFilePath) const107 const 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 
160 class ElfFileSymbols : public SymbolsFile {
161 public:
ElfFileSymbols(const std::string &symbolFilePath, const SymbolsFileType symbolsFileType = SYMBOL_ELF_FILE)162     explicit ElfFileSymbols(const std::string &symbolFilePath,
163                             const SymbolsFileType symbolsFileType = SYMBOL_ELF_FILE)
164         : SymbolsFile(symbolsFileType, symbolFilePath)
165     {
166     }
167 
~ElfFileSymbols()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 
191 protected:
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 
244 private:
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 
DumpEhFrameHDR() const298     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 
LoadEhFrameHDR(const unsigned char *buffer, size_t bufferSize, uint64_t shdrOffset)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 
UpdateSymbols(std::vector<DfxSymbol> &symbolsTable, const std::string &elfPath)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 
LoadElfSymbols(std::shared_ptr<DfxMap> map, std::string elfPath)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 
445 class KernelSymbols : public ElfFileSymbols {
446 public:
KernelSymbols(const std::string &symbolFilePath)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 
ParseKallsymsLine()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 
LoadKernelSyms()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 
608 class KernelModuleSymbols : public ElfFileSymbols {
609 public:
KernelModuleSymbols(const std::string &symbolFilePath)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 
648 private:
LoadBuildId()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 
666 class JavaFileSymbols : public ElfFileSymbols {
667 public:
JavaFileSymbols(const std::string &symbolFilePath)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 
688 class JSFileSymbols : public ElfFileSymbols {
689 public:
JSFileSymbols(const std::string &symbolFilePath)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 
702 class HapFileSymbols : public ElfFileSymbols {
703 private:
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;
714 public:
HapFileSymbols(const std::string &symbolFilePath, pid_t pid)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
IsHapAbc()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 
873 class UnknowFileSymbols : public SymbolsFile {
874 public:
UnknowFileSymbols(const std::string &symbolFilePath)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 
~SymbolsFile()887 SymbolsFile::~SymbolsFile() {}
888 
CreateSymbolsFile(SymbolsFileType symbolType, const std::string symbolFilePath, pid_t pid)889 std::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 
CreateSymbolsFile(const std::string &symbolFilePath, pid_t pid)911 std::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 
AdjustSymbols()926 void 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 
SortMatchedSymbols()967 void 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 
GetSymbols()977 const std::vector<DfxSymbol> &SymbolsFile::GetSymbols()
978 {
979     return symbols_;
980 }
981 
GetMatchedSymbols()982 const std::vector<DfxSymbol *> &SymbolsFile::GetMatchedSymbols()
983 {
984     return matchedSymbols_;
985 }
986 
GetSymbolWithVaddr(uint64_t vaddrInFile)987 const 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 
CheckPathReadable(const std::string &path) const1046 bool 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 
setSymbolsFilePath(const std::vector<std::string> &symbolsSearchPaths)1056 bool 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 
LoadSymbolsFromSaved( const SymbolFileStruct &symbolFileStruct)1068 std::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 
SetBoolValue(bool value)1087 void SymbolsFile::SetBoolValue(bool value)
1088 {
1089 }
1090 
ExportSymbolToFileFormat(SymbolFileStruct &symbolFileStruct)1091 void 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 
GetVaddrInSymbols(uint64_t ip, uint64_t, uint64_t) const1113 uint64_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