1 /*
2 * Copyright (c) 2021 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 #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
36 #include "elf_parser.h"
37 #include "dwarf_encoding.h"
38 #include "utilities.h"
39 #include "logging.h"
40
41 using namespace OHOS::Developtools::NativeDaemon::ELF;
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 if (filePath.empty()) {
94 HLOGW("nothing to found");
95 return filePath;
96 }
97 for (auto searchPath : searchPaths) {
98 if (searchPath.back() != PATH_SEPARATOR) {
99 searchPath += PATH_SEPARATOR;
100 }
101 std::string PossibleFilePath = searchPath + filePath;
102 if (CheckPathReadable(PossibleFilePath)) {
103 return PossibleFilePath;
104 }
105 HLOGW("have not found '%s' in search paths %s", filePath.c_str(), searchPath.c_str());
106 }
107 return EMPTY_STRING;
108 }
109
FindSymbolFile(const std::vector<std::string> & symbolsFileSearchPaths,std::string symboleFilePath) const110 const std::string SymbolsFile::FindSymbolFile(
111 const std::vector<std::string> &symbolsFileSearchPaths, std::string symboleFilePath) const
112 {
113 /*
114 this function do 2 things:
115 find by name:
116 1 find dso path
117 2 find search path
118 a) search path + dso path
119 b) search path + dso name
120
121 show we should return filePath_ as default ?
122 */
123 if (symboleFilePath.empty()) {
124 symboleFilePath = filePath_;
125 HLOGD("use default filename: %s ", symboleFilePath.c_str());
126 }
127 symboleFilePath = PlatformPathConvert(symboleFilePath);
128 std::string foundPath;
129 // search fisrt if we have path
130 if (symbolsFileSearchPaths.size() != 0) {
131 foundPath = SearchReadableFile(symbolsFileSearchPaths, symboleFilePath);
132 if (foundPath.empty()) {
133 HLOGV("try base name for: %s split with %s", symboleFilePath.c_str(),
134 PATH_SEPARATOR_STR.c_str());
135 auto pathSplit = StringSplit(symboleFilePath, PATH_SEPARATOR_STR);
136 if (pathSplit.size() > 1) {
137 HLOGV("base name is: %s ", pathSplit.back().c_str());
138 // found it again with base name , split it and get last name
139 foundPath = SearchReadableFile(symbolsFileSearchPaths, pathSplit.back());
140 }
141 }
142 }
143
144 // only access the patch in onRecording_
145 // in report mode we don't load any thing in runtime path
146 if (foundPath.empty() and onRecording_) {
147 // try access direct at last
148 if (CheckPathReadable(symboleFilePath)) {
149 // found direct folder
150 HLOGD("find %s in current work dir", symboleFilePath.c_str());
151 return symboleFilePath;
152 }
153 }
154 return foundPath;
155 }
156
157 class ElfFileSymbols : public SymbolsFile {
158 public:
ElfFileSymbols(const std::string & symbolFilePath,const SymbolsFileType symbolsFileType=SYMBOL_ELF_FILE)159 explicit ElfFileSymbols(const std::string &symbolFilePath,
160 const SymbolsFileType symbolsFileType = SYMBOL_ELF_FILE)
161 : SymbolsFile(symbolsFileType, symbolFilePath)
162 {
163 }
164
~ElfFileSymbols()165 virtual ~ElfFileSymbols()
166 {
167 if (mmap_ != MMAP_FAILED) {
168 if (munmap(mmap_, mmapSize_) != 0) {
169 HLOGE("munmap failed with %p", munmap);
170 }
171 }
172 }
173
LoadSymbols(const std::string & symbolFilePath)174 bool LoadSymbols(const std::string &symbolFilePath) override
175 {
176 symbolsLoaded_ = true;
177 std::string findPath = FindSymbolFile(symbolsFileSearchPaths_, symbolFilePath);
178 if (findPath.empty()) {
179 HLOGW("elf found failed (belong to %s)", filePath_.c_str());
180 return false;
181 }
182 if (LoadElfSymbols(findPath)) {
183 return true;
184 } else {
185 HLOGW("elf open failed with '%s'", findPath.c_str());
186 return false;
187 }
188 return false;
189 }
190
ReadRoMemory(uint64_t addr,uint8_t * const data,size_t size) const191 size_t ReadRoMemory(uint64_t addr, uint8_t * const data, size_t size) const override
192 {
193 size_t readSize = 0;
194
195 if (mmap_ != MMAP_FAILED) {
196 if ((addr + size) <= mmapSize_) {
197 std::copy_n(static_cast<uint8_t *>(mmap_) + addr, size, data);
198 readSize = size;
199 } else {
200 HLOGW("read out of range.");
201 HLOGW("try read 0x%" PRIx64 "(elf offset)+%zu max is 0x%" PRIx64 "", addr, size,
202 mmapSize_);
203 }
204 } else {
205 if (readFd_ != nullptr) {
206 if (fseek(readFd_.get(), addr, SEEK_SET) != 0) {
207 return 0;
208 }
209 if (fread(data, size, 1u, readFd_.get())) {
210 readSize = size;
211 } else {
212 HLOGEP("read at %" PRIx64 " failed for %s", addr, filePath_.c_str());
213 }
214 }
215 HLOGM("no mmap files loaded");
216 }
217
218 HLOGM("read %zu/%zu bytes at %" PRIx64 "(elf offset)", readSize, size, addr);
219
220 return readSize;
221 }
222
223 protected:
CovertByteBufferToHexString(const unsigned char * buffer,size_t size) const224 std::string CovertByteBufferToHexString(const unsigned char *buffer, size_t size) const
225 {
226 std::string descString;
227 size_t i = 0;
228 while (i < size) {
229 descString.append(ToHex(buffer[i]));
230 i++; // move to next char
231 }
232 return descString;
233 }
234
ElfGetBuildId(const unsigned char * buffer,size_t size) const235 std::string ElfGetBuildId(const unsigned char *buffer, size_t size) const
236 {
237 const unsigned char *end = buffer + size;
238 HLOGV("size:%zu", size);
239
240 /*
241 Note Section
242 A vendor or system engineer might need to mark an object file with special
243 information that other programs can check for conformance or compatibility. Sections
244 of type SHT_NOTE and program header elements of type PT_NOTE can be used for this
245 purpose.
246
247 The note information in sections and program header elements holds any number of
248 entries, as shown in the following figure. For 64–bit objects and 32–bit objects,
249 each entry is an array of 4-byte words in the format of the target processor. Labels
250 are shown in Figure 12-6 to help explain note information organization, but are not
251 part of the specification.
252
253 Figure 12-5 Note Information
254
255 image:ELF note section information.
256 namesz and name
257 The first namesz bytes in name contain a null-terminated character representation of
258 the entry's owner or originator. No formal mechanism exists for avoiding name
259 conflicts. By convention, vendors use their own name, such as “XYZ Computer
260 Company,” as the identifier. If no name is present, namesz contains the value zero.
261 Padding is present, if necessary, to ensure 4-byte alignment for the descriptor.
262 Such padding is not included in namesz.
263
264 descsz and desc
265 The first descsz bytes in desc hold the note descriptor. If no descriptor is
266 present, descsz contains the value zero. Padding is present, if necessary, to ensure
267 4-byte alignment for the next note entry. Such padding is not included in descsz.
268
269 type
270 Provides the interpretation of the descriptor. Each originator controls its own
271 types. Multiple interpretations of a single type value can exist. A program must
272 recognize both the name and the type to understand a descriptor. Types currently
273 must be nonnegative.
274
275 The note segment that is shown in the following figure holds two entries.
276 */
277
278 // namesz + descsz + type
279 static constexpr const int elfNoteSectionLens = sizeof(uint32_t) * 3;
280
281 while (end - buffer >= elfNoteSectionLens) {
282 uint32_t namesz;
283 uint32_t descsz;
284 uint32_t type;
285 CopyFromBufferAndMove(buffer, &namesz);
286 CopyFromBufferAndMove(buffer, &descsz);
287 CopyFromBufferAndMove(buffer, &type);
288
289 // to ensure 4-byte alignment for the descriptor.
290 constexpr const int elfNoteSectionNameAlign = 4;
291
292 namesz = RoundUp(namesz, elfNoteSectionNameAlign);
293 descsz = RoundUp(descsz, elfNoteSectionNameAlign);
294 HLOGM("namesz:%u descsz:%u type:%u", namesz, descsz, type);
295
296 // size enough ?
297 if (buffer >= end) {
298 return EMPTY_STRING;
299 }
300 if (type == NT_GNU_BUILD_ID) {
301 char name[namesz + 1];
302 CopyFromBufferAndMove(buffer, &name[0], namesz);
303 name[namesz] = 0;
304 HLOGM("found buildid name:%s", name);
305 if (strcmp(name, ELF_NOTE_GNU) == 0) {
306 std::string descString = CovertByteBufferToHexString(buffer, descsz);
307 HLOGD("found buildid:%s", descString.c_str());
308 return descString;
309 } else {
310 // next
311 buffer += descsz;
312 }
313 } else {
314 // next
315 buffer += namesz + descsz;
316 }
317 }
318 return EMPTY_STRING; // found nothing
319 }
320
LoadDebugInfo(const std::string & symbolFilePath)321 bool LoadDebugInfo(const std::string &symbolFilePath) override
322 {
323 if (debugInfoLoaded_) {
324 return true;
325 } else {
326 debugInfoLoaded_ = true;
327 }
328 std::string elfPath = FindSymbolFile(symbolsFileSearchPaths_, symbolFilePath);
329 if (elfPath.empty()) {
330 HLOGW("elf found failed (belong to %s)", filePath_.c_str());
331 return false;
332 }
333 std::unique_ptr<ElfFile> elfFile = LoadElfFile(elfPath);
334 if (elfFile == nullptr) {
335 HLOGD("elf load failed");
336 return false;
337 } else {
338 HLOGD("loaded elf %s", elfPath.c_str());
339 }
340 for (auto &phdr : elfFile->phdrs_) {
341 if ((phdr->type_ == PT_LOAD) && (phdr->flags_ & PF_X)) {
342 // find the min addr
343 if (textExecVaddr_ != std::min(textExecVaddr_, phdr->vaddr_)) {
344 textExecVaddr_ = std::min(textExecVaddr_, phdr->vaddr_);
345 textExecVaddrFileOffset_ = phdr->offset_;
346 }
347 }
348 }
349
350 HLOGD("textExecVaddr_ 0x%016" PRIx64 " file offset 0x%016" PRIx64 "", textExecVaddr_,
351 textExecVaddrFileOffset_);
352
353 if (!ParseShdr(std::move(elfFile))) {
354 return false;
355 }
356
357 // mmap it for later use
358 LoadFileToMemory(elfPath);
359 return true;
360 }
361
362 private:
363 bool EhFrameHDRValid_ {false};
364 uint64_t ehFrameHDRElfOffset_ {0};
365 uint64_t ehFrameHDRFdeCount_ {0};
366 uint64_t ehFrameHDRFdeTableItemSize_ {0};
367 uint64_t ehFrameHDRFdeTableElfOffset_ {0};
368 OHOS::UniqueFd fd_ {-1};
369 std::unique_ptr<FILE, decltype(&fclose)> readFd_ {nullptr, &fclose};
370 struct ShdrInfo {
371 uint64_t sectionVaddr_;
372 uint64_t sectionSize_;
373 uint64_t sectionFileOffset_;
ShdrInfoOHOS::Developtools::NativeDaemon::ElfFileSymbols::ShdrInfo374 ShdrInfo(uint64_t sectionVaddr, uint64_t sectionSize, uint64_t sectionFileOffset)
375 : sectionVaddr_(sectionVaddr),
376 sectionSize_(sectionSize),
377 sectionFileOffset_(sectionFileOffset)
378 {
379 }
380 };
381 std::map<const std::string, ShdrInfo> shdrMap_ {};
382 void *mmap_ {MMAP_FAILED};
383 uint64_t mmapSize_ = {0};
384
GetReadableName(const std::string & name) const385 const std::string GetReadableName(const std::string &name) const
386 {
387 int status = 0;
388 const char *nameStart = name.c_str();
389 bool linkerName = false;
390 if (StringStartsWith(name, LINKER_PREFIX)) {
391 nameStart += LINKER_PREFIX.size();
392 linkerName = true;
393 }
394
395 char *demangle = abi::__cxa_demangle(nameStart, nullptr, nullptr, &status);
396 if (status == 0) {
397 std::string demangleName = demangle;
398 free(static_cast<void *>(demangle));
399 return linkerName ? (LINKER_PREFIX_NAME + demangleName) : demangleName;
400 } else {
401 return linkerName ? (LINKER_PREFIX_NAME + nameStart) : nameStart;
402 }
403 }
404
ElfStTypeName(unsigned char stt) const405 const std::string ElfStTypeName(unsigned char stt) const
406 {
407 switch (stt) {
408 case STT_FUNC:
409 return "function";
410 case STT_GNU_IFUNC:
411 return "gun_func";
412 case STT_OBJECT:
413 return " object";
414 default:
415 return " unknow";
416 }
417 }
418
GetSectionInfo(const std::string & name,uint64_t & sectionVaddr,uint64_t & sectionSize,uint64_t & sectionFileOffset) const419 bool GetSectionInfo(const std::string &name, uint64_t §ionVaddr, uint64_t §ionSize,
420 uint64_t §ionFileOffset) const override
421 {
422 HLOGM("Section '%s' found in %zu", name.c_str(), shdrMap_.size());
423 if (shdrMap_.count(name) > 0) {
424 HLOGM("Section '%s' found", name.c_str());
425 const auto &shdrInfo = shdrMap_.at(name);
426 sectionVaddr = shdrInfo.sectionVaddr_;
427 sectionSize = shdrInfo.sectionSize_;
428 sectionFileOffset = shdrInfo.sectionFileOffset_;
429 HLOGM("Get Section '%s' %" PRIx64 " - %" PRIx64 "", name.c_str(), sectionVaddr,
430 sectionSize);
431 return true;
432 } else {
433 HLOGW("Section '%s' not found", name.c_str());
434 return false;
435 }
436 }
437
438 #ifndef __arm__
GetHDRSectionInfo(uint64_t & ehFrameHdrElfOffset,uint64_t & fdeTableElfOffset,uint64_t & fdeTableSize) const439 bool GetHDRSectionInfo(uint64_t &ehFrameHdrElfOffset, uint64_t &fdeTableElfOffset,
440 uint64_t &fdeTableSize) const override
441 {
442 if (EhFrameHDRValid_) {
443 ehFrameHdrElfOffset = ehFrameHDRElfOffset_;
444 fdeTableElfOffset = ehFrameHDRFdeTableElfOffset_;
445 fdeTableSize = ehFrameHDRFdeCount_ * ehFrameHDRFdeTableItemSize_;
446 return true;
447 } else {
448 HLOGW("!EhFrameHDRValid_");
449 return false;
450 }
451 }
452 #endif
453
DumpEhFrameHDR() const454 void DumpEhFrameHDR() const
455 {
456 HLOGD(" ehFrameHDRElfOffset_: 0x%" PRIx64 "", ehFrameHDRElfOffset_);
457 HLOGD(" ehFrameHDRFdeCount_: 0x%" PRIx64 "", ehFrameHDRFdeCount_);
458 HLOGD(" ehFrameHDRFdeTableElfOffset_: 0x%" PRIx64 "", ehFrameHDRFdeTableElfOffset_);
459 HLOGD(" ehFrameHDRFdeTableItemSize_: 0x%" PRIx64 "", ehFrameHDRFdeTableItemSize_);
460 }
461
LoadEhFrameHDR(const unsigned char * buffer,size_t bufferSize,uint64_t shdrOffset)462 bool LoadEhFrameHDR(const unsigned char *buffer, size_t bufferSize, uint64_t shdrOffset)
463 {
464 eh_frame_hdr *ehFrameHdr = (eh_frame_hdr *)buffer;
465 const uint8_t *dataPtr = ehFrameHdr->encode_data;
466 DwarfEncoding dwEhFramePtr(ehFrameHdr->eh_frame_ptr_enc, dataPtr);
467 DwarfEncoding dwFdeCount(ehFrameHdr->fde_count_enc, dataPtr);
468 DwarfEncoding dwTable(ehFrameHdr->table_enc, dataPtr);
469 DwarfEncoding dwTableValue(ehFrameHdr->table_enc, dataPtr);
470
471 HLOGD("eh_frame_hdr:");
472 HexDump(buffer, BITS_OF_FOUR_BYTE, bufferSize);
473 unsigned char version = ehFrameHdr->version;
474 HLOGD(" version: %02x:%s", version, (version == 1) ? "valid" : "invalid");
475 HLOGD(" eh_frame_ptr_enc: %s", dwEhFramePtr.ToString().c_str());
476 HLOGD(" fde_count_enc: %s", dwFdeCount.ToString().c_str());
477 HLOGD(" table_enc: %s", dwTable.ToString().c_str());
478 HLOGD(" table_enc: %s", dwTable.ToString().c_str());
479 HLOGD(" table_value_enc: %s", dwTableValue.ToString().c_str());
480 HLOGD(" table_iteam_size: %zd", dwTable.GetSize() + dwTableValue.GetSize());
481 HLOGD(" table_offset_in_hdr: %zu", dwTable.GetData() - buffer);
482
483 if (version != 1) {
484 HLOGD("eh_frame_hdr version is invalid");
485 return false;
486 }
487 EhFrameHDRValid_ = true;
488 ehFrameHDRElfOffset_ = shdrOffset;
489 ehFrameHDRFdeCount_ = dwFdeCount.GetAppliedValue();
490 ehFrameHDRFdeTableElfOffset_ = dwTable.GetData() - buffer + shdrOffset;
491 ehFrameHDRFdeTableItemSize_ = dwTable.GetSize() + dwTableValue.GetSize();
492 DumpEhFrameHDR();
493
494 if (!dwFdeCount.IsOmit() && dwFdeCount.GetValue() > 0) {
495 return true;
496 } else {
497 HLOGW("fde table not found.");
498 }
499 return false;
500 }
501
LoadFileToMemory(const std::string & loadElfPath)502 void LoadFileToMemory(const std::string &loadElfPath)
503 {
504 #ifndef HIPERF_ELF_READ_USE_MMAP
505 if (readFd_ == nullptr) {
506 FILE *fp = fopen(loadElfPath.c_str(), "rb");
507 if (fp == nullptr) {
508 HILOG_ERROR(LOG_CORE, "CHECK_NOTNULL(fp) in %s:%d FAILED, ", __func__, __LINE__);
509 return;
510 }
511 readFd_ = std::unique_ptr<FILE, decltype(&fclose)>(fp, &fclose);
512 if (readFd_.get() == nullptr) {
513 HILOG_ERROR(LOG_CORE, "CHECK_NOTNULL(fp) in %s:%d FAILED, ", __func__, __LINE__);
514 return;
515 }
516 return;
517 }
518 #else
519 if (fd_ != -1) {
520 return;
521 }
522 #if is_mingw
523 fd_ = OHOS::UniqueFd(open(loadElfPath.c_str(), O_RDONLY | O_BINARY));
524 #else
525 fd_ = OHOS::UniqueFd(open(loadElfPath.c_str(), O_RDONLY));
526 #endif
527 if (fd_ != -1) {
528 struct stat sb = {};
529
530 if (fstat(fd_, &sb) == -1) {
531 HLOGE("unable to check the file size");
532 } else {
533 HLOGV("file stat size %" PRIu64 "", sb.st_size);
534
535 // unmap it first
536 if (mmap_ != MMAP_FAILED) {
537 munmap(mmap_, mmapSize_);
538 }
539
540 mmap_ = mmap(0, sb.st_size, PROT_READ, MAP_PRIVATE, fd_, 0);
541 if (mmap_ == MMAP_FAILED) {
542 HLOGE("unable to map the file size %" PRIu64 " ", sb.st_size);
543 mmapSize_ = 0;
544 } else {
545 mmapSize_ = sb.st_size;
546 HLOGD("mmap build with size %" PRIu64 " ", mmapSize_);
547 }
548 }
549 } else {
550 const int bufSize = 1024;
551 char buf[bufSize] = { 0 };
552 strerror_r(errno, buf, bufSize);
553 HLOGD("elf file open failed with %s by %s", loadElfPath.c_str(), buf);
554 return;
555 }
556 #endif
557 }
558
ReadSymTab(const std::unique_ptr<ElfFile> & elfFile,const ELF::SectionHeader * shdr,std::vector<Symbol> & symbolsTable) const559 bool ReadSymTab(const std::unique_ptr<ElfFile> &elfFile, const ELF::SectionHeader *shdr,
560 std::vector<Symbol> &symbolsTable) const
561 {
562 HLOGV("ParseSymTable");
563 if (!elfFile->ParseSymTable(shdr)) {
564 return false;
565 }
566
567 HLOGV("Symbol Table:%s", shdr->secTypeName_.c_str());
568 HLOGM("%*s|%16s|%4s|%s", MAX_SYMBOLS_TYPE_NAME_LEN, "type", "addr", "size", "name");
569
570 for (const std::unique_ptr<ElfSymbol> &symbol : elfFile->symTable_->symbols_) {
571 if (ELF64_ST_TYPE(symbol->symInfo_) == STT_FUNC or
572 ELF64_ST_TYPE(symbol->symInfo_) == STT_GNU_IFUNC) {
573 /*
574 name| addr|size|name
575 function|00000000c0102b8c| 56|__lookup_processor_type
576 function|00000000c0102bd4| 0|__error_p
577 function|00000000c0008224| 64|__vet_atags
578 function|00000000c0008144| 128|__fixup_smp
579 function|00000000c00081d0| 64|__fixup_pv_table
580 function|00000000c000808c| 168|__create_page_tables
581 function|00000000c0b002e0| 68|__mmap_switched
582 function|00000000c0102acc| 20|__enable_mmu
583 object|00000000c0102ac0| 0|__secondary_data
584 function|00000000c0102ae0| 20|__do_fixup_smp_on_up
585 */
586
587 std::string name = elfFile->GetStrPtr(shdr->link_, symbol->nameIndex_);
588 std::string type = ElfStTypeName(ELF64_ST_TYPE(symbol->symInfo_));
589 // this will cause malloc , maybe need do this in report ?
590 std::string demangle = GetReadableName(name);
591 HLOGV("%10s|%016" PRIx64 "|%4" PRIu64 "|%s", type.c_str(), symbol->symValue_,
592 symbol->symSize_, demangle.c_str());
593
594 if (symbol->symValue_ == 0) {
595 continue; // we dont need 0 addr symbol
596 }
597 symbolsTable.emplace_back(symbol->symValue_, symbol->symSize_, name, demangle,
598 filePath_);
599 } else {
600 continue;
601 }
602 } // for symbols
603 return true;
604 }
ParseShdr(const std::unique_ptr<ElfFile> elfFile)605 bool ParseShdr(const std::unique_ptr<ElfFile> elfFile)
606 {
607 // only save the section info , not actually read any file content
608 for (const auto &shdrPair : elfFile->shdrs_) {
609 const auto &shdr = shdrPair.second;
610 const char *sh_name =
611 elfFile->GetStrPtr(elfFile->ehdr_->shdrStrTabIdx_, shdr->nameIndex_);
612 const unsigned char *data = elfFile->GetSectionData(shdr->secIndex_);
613
614 if (sh_name == nullptr || data == nullptr) {
615 HLOGE("name %p or data %p get failed.", sh_name, data);
616 return false;
617 }
618
619 HLOGV("shdr name '%s' vaddr 0x%" PRIx64 " offset 0x%" PRIx64 " size 0x%" PRIx64
620 " type 0x%" PRIx64 "(%s) index %u link 0x%u entry 0x%" PRIx64 "",
621 sh_name, shdr->secVaddr_, shdr->fileOffset_, shdr->secSize_, shdr->secType_,
622 shdr->secTypeName_.c_str(), shdr->secIndex_, shdr->link_, shdr->secEntrySize_);
623
624 shdrMap_.emplace(sh_name, ShdrInfo(shdr->secVaddr_, shdr->secSize_, shdr->fileOffset_));
625 #ifndef __arm__
626 if (shdr->secType_ == SHT_PROGBITS) {
627 if (EH_FRAME_HR == sh_name) {
628 LoadEhFrameHDR(data, shdr->secSize_, shdr->fileOffset_);
629 }
630 } // for shdr
631 #endif
632 }
633 return true;
634 }
635
AddSectionAsSymbol(const std::unique_ptr<ELF::SectionHeader> & shdr,const char * name,std::vector<Symbol> & symbolsTable) const636 void AddSectionAsSymbol(const std::unique_ptr<ELF::SectionHeader> &shdr, const char *name,
637 std::vector<Symbol> &symbolsTable) const
638 {
639 HLOGV("add section %s as function symbol from 0x%" PRIx64 " size 0x%" PRIx64 "", name,
640 shdr->secVaddr_, shdr->secSize_);
641 symbolsTable.emplace_back(shdr->secVaddr_, shdr->secSize_, name, name, filePath_);
642 }
643
ParseShdr(const std::unique_ptr<ElfFile> elfFile,std::vector<Symbol> & symbolsTable,std::string & buildIdFound)644 bool ParseShdr(const std::unique_ptr<ElfFile> elfFile, std::vector<Symbol> &symbolsTable,
645 std::string &buildIdFound)
646 {
647 const ELF::SectionHeader *symTableShdr = nullptr;
648 // walkthough
649 for (const auto &shdrPair : elfFile->shdrs_) {
650 const auto &shdr = shdrPair.second;
651 const char *sh_name = elfFile->GetStrPtr(elfFile->ehdr_->shdrStrTabIdx_, shdr->nameIndex_);
652 const unsigned char *data = elfFile->GetSectionData(shdr->secIndex_);
653
654 CHECK_NOTNULL(sh_name, false, "name %p get failed.", sh_name);
655 CHECK_NOTNULL(data, false, "data %p get failed.", data);
656
657 HLOGVVV("shdr name '%s' vaddr 0x%" PRIx64 " offset 0x%" PRIx64 " size 0x%" PRIx64
658 " type 0x%" PRIx64 "(%s) index %u link 0x%u entry 0x%" PRIx64 "",
659 sh_name, shdr->secVaddr_, shdr->fileOffset_, shdr->secSize_, shdr->secType_,
660 shdr->secTypeName_.c_str(), shdr->secIndex_, shdr->link_, shdr->secEntrySize_);
661
662 shdrMap_.emplace(sh_name, ShdrInfo(shdr->secVaddr_, shdr->secSize_, shdr->fileOffset_));
663 switch (shdr->secType_) {
664 case SHT_SYMTAB:
665 symTableShdr = shdr.get();
666 break;
667 case SHT_DYNSYM:
668 // if we already have SHT_SYMTAB ?
669 if (symTableShdr == nullptr) {
670 symTableShdr = shdr.get();
671 }
672 break;
673 case SHT_NOTE:
674 // notes
675 if (buildIdFound.empty()) {
676 // we use our function, not from gelf_getnote
677 HLOGM("found NOTE_GNU_BUILD_ID size: %" PRIu64 "", shdr->secSize_);
678
679 // there will be a log of note sh , we just need the right one
680 buildIdFound = ElfGetBuildId(data, shdr->secSize_);
681 }
682 break;
683 case SHT_PROGBITS:
684 #ifndef __arm__
685 if (EH_FRAME_HR == sh_name) {
686 LoadEhFrameHDR(data, shdr->secSize_, shdr->fileOffset_);
687 break;
688 }
689 #endif
690 if (PLT == sh_name) {
691 // this is a plt section, PLT table will put here
692 // we make it as named PLT function
693 AddSectionAsSymbol(shdr, sh_name, symbolsTable);
694 }
695 break;
696 default:
697 HLOGM("skip shdr.sh_type %" PRIx64 "", shdr->secType_);
698 break;
699 } // for shdr
700 } // for each shdrs_
701 // load symtab
702 if (symTableShdr != nullptr) {
703 if (!ReadSymTab(elfFile, symTableShdr, symbolsTable)) {
704 return false;
705 }
706 }
707 return true;
708 }
709
LoadElfFile(std::string & elfPath) const710 std::unique_ptr<ElfFile> LoadElfFile(std::string &elfPath) const
711 {
712 HLOGD("try load elf %s", elfPath.c_str());
713 if (elfPath.empty()) {
714 elfPath = filePath_;
715 HLOGD("use default elf path %s\n", elfPath.c_str());
716 }
717 return ElfFile::MakeUnique(elfPath);
718 }
719
UpdateSymbols(std::vector<Symbol> & symbolsTable,const std::string & elfPath)720 void UpdateSymbols(std::vector<Symbol> &symbolsTable, const std::string &elfPath)
721 {
722 symbols_.clear();
723 HLOGD("%zu symbols loadded from symbolsTable.", symbolsTable.size());
724
725 symbols_.swap(symbolsTable);
726
727 AdjustSymbols();
728 HLOGD("%zu symbols loadded from elf '%s'.", symbols_.size(), elfPath.c_str());
729 if (buildId_.empty()) {
730 HLOGD("buildId not found from elf '%s'.", elfPath.c_str());
731 // dont failed. some time the lib have not got the build id
732 // buildId not found from elf '/system/bin/ld-musl-arm.so.1'.
733 }
734 }
735
LoadElfSymbols(std::string elfPath)736 bool LoadElfSymbols(std::string elfPath)
737 {
738 #ifdef HIPERF_DEBUG_TIME
739 const auto startTime = steady_clock::now();
740 #endif
741 std::unique_ptr<ElfFile> elfFile = LoadElfFile(elfPath);
742 if (elfFile == nullptr) {
743 HLOGD("elf load failed");
744 return false;
745 } else {
746 HLOGD("loaded elf %s", elfPath.c_str());
747 }
748 // we prepare two table here
749 // only one we will push in to symbols_
750 // or both drop if build id is not same
751 std::vector<Symbol> symbolsTable;
752 std::string buildIdFound;
753 for (auto &phdr : elfFile->phdrs_) {
754 if ((phdr->type_ == PT_LOAD) && (phdr->flags_ & PF_X)) {
755 // find the min addr
756 if (textExecVaddr_ != std::min(textExecVaddr_, phdr->vaddr_)) {
757 textExecVaddr_ = std::min(textExecVaddr_, phdr->vaddr_);
758 textExecVaddrFileOffset_ = phdr->offset_;
759 }
760 }
761 }
762
763 HLOGD("textExecVaddr_ 0x%016" PRIx64 " file offset 0x%016" PRIx64 "", textExecVaddr_,
764 textExecVaddrFileOffset_);
765
766 if (!ParseShdr(std::move(elfFile), symbolsTable, buildIdFound)) {
767 return false;
768 }
769
770 if (UpdateBuildIdIfMatch(buildIdFound)) {
771 UpdateSymbols(symbolsTable, elfPath);
772 } else {
773 HLOGW("symbols will not update for '%s' because buildId is not match.",
774 elfPath.c_str());
775 // this mean failed . we dont goon for this.
776 return false;
777 }
778
779 // mmap it for later use
780 LoadFileToMemory(elfPath);
781 #ifdef HIPERF_DEBUG_TIME
782 auto usedTime = duration_cast<microseconds>(steady_clock::now() - startTime);
783 if (usedTime.count() != 0) {
784 HLOGV("cost %0.3f ms to load symbols '%s'",
785 usedTime.count() / static_cast<double>(milliseconds::duration::period::den),
786 elfPath.c_str());
787 }
788 #endif
789 return true;
790 }
791
GetVaddrInSymbols(uint64_t ip,uint64_t mapStart,uint64_t mapPageOffset) const792 uint64_t GetVaddrInSymbols(uint64_t ip, uint64_t mapStart,
793 uint64_t mapPageOffset) const override
794 {
795 /*
796 00200000-002c5000 r--p 00000000 08:02 46400311
797 002c5000-00490000 r-xp 000c5000 08:02 4640031
798
799 [14] .text PROGBITS 00000000002c5000 000c5000
800
801 if ip is 0x46e6ab
802 1. find the map range is 002c5000-00490000
803 2. ip - map start(002c5000) = map section offset
804 3. map section offset + map page offset(000c5000) = elf file offset
805 4. elf file offset - exec file offset(000c5000)
806 = ip offset (ip always in exec file offset)
807 5. ip offset + exec begin vaddr(2c5000) = virtual ip in elf
808 */
809 uint64_t vaddr = ip - mapStart + mapPageOffset - textExecVaddrFileOffset_ + textExecVaddr_;
810 HLOGM(" ip :0x%016" PRIx64 " -> elf offset :0x%016" PRIx64 " -> vaddr :0x%016" PRIx64 " ",
811 ip, ip - mapStart + mapPageOffset, vaddr);
812 HLOGM("(minExecAddrFileOffset_ is 0x%" PRIx64 " textExecVaddr_ is 0x%" PRIx64 ")",
813 textExecVaddrFileOffset_, textExecVaddr_);
814 return vaddr;
815 }
816 };
817
818 class KernelSymbols : public ElfFileSymbols {
819 public:
KernelSymbols(const std::string & symbolFilePath)820 explicit KernelSymbols(const std::string &symbolFilePath)
821 : ElfFileSymbols(symbolFilePath, SYMBOL_KERNEL_FILE)
822 {
823 }
824
825 static constexpr const int KSYM_MIN_TOKENS = 3;
826 static constexpr const int KSYM_DEFAULT_LINE = 35000;
827 static constexpr const int KSYM_DEFAULT_SIZE = 1024 * 1024 * 1; // 1MB
828
ParseKallsymsLine()829 bool ParseKallsymsLine()
830 {
831 size_t lines = 0;
832 std::string kallsym;
833 if (!ReadFileToString("/proc/kallsyms", kallsym, KSYM_DEFAULT_SIZE)) {
834 HLOGW("/proc/kallsyms load failed.");
835 return false;
836 }
837 // reduce the mem alloc
838 symbols_.reserve(KSYM_DEFAULT_LINE);
839
840 char *lineBegin = kallsym.data();
841 char *dataEnd = lineBegin + kallsym.size();
842 while (lineBegin < dataEnd) {
843 char *lineEnd = strchr(lineBegin, '\n');
844 if (lineEnd != nullptr) {
845 *lineEnd = '\0';
846 }
847 size_t lineSize = (lineEnd != nullptr) ? (lineEnd - lineBegin) : (dataEnd - lineBegin);
848
849 lines++;
850 uint64_t addr = 0;
851 char type = '\0';
852
853 char nameRaw[lineSize];
854 char moduleRaw[lineSize];
855 int ret = sscanf_s(lineBegin, "%" PRIx64 " %c %s%s", &addr, &type, sizeof(type),
856 nameRaw, sizeof(nameRaw), moduleRaw, sizeof(moduleRaw));
857
858 lineBegin = lineEnd + 1;
859 if (ret >= KSYM_MIN_TOKENS) {
860 if (ret == KSYM_MIN_TOKENS) {
861 moduleRaw[0] = '\0';
862 }
863 HLOGM(" 0x%016" PRIx64 " %c '%s' '%s'", addr, type, nameRaw, moduleRaw);
864 } else {
865 HLOGW("unknow line %d: '%s'", ret, lineBegin);
866 continue;
867 }
868 std::string name = nameRaw;
869 std::string module = moduleRaw;
870
871 /*
872 T
873 The symbol is in the text (code) section.
874
875 W
876 The symbol is a weak symbol that has not been specifically
877 tagged as a weak object symbol. When a weak defined symbol is
878 linked with a normal defined symbol, the normal defined symbol
879 is used with no error. When a weak undefined symbol is linked
880 and the symbol is not defined, the value of the weak symbol
881 becomes zero with no error.
882 */
883 if (addr != 0 && strchr("TtWw", type)) {
884 // we only need text symbols
885 symbols_.emplace_back(addr, name, module.empty() ? filePath_ : module);
886 }
887 }
888 HLOGD("%zu line processed(%zu symbols)", lines, symbols_.size());
889 return true;
890 }
891
892 const std::string KPTR_RESTRICT = "/proc/sys/kernel/kptr_restrict";
893
LoadKernelSyms()894 bool LoadKernelSyms()
895 {
896 HLOGD("try read /proc/kallsyms");
897 if (access("/proc/kallsyms", R_OK) != 0) {
898 printf("No vmlinux path is given, and kallsyms cannot be opened\n");
899 return false;
900 }
901
902 if (ReadFileToString(KPTR_RESTRICT).front() != '0') {
903 printf("/proc/sys/kernel/kptr_restrict is NOT 0, will try set it to 0.\n");
904 if (!WriteStringToFile(KPTR_RESTRICT, "0")) {
905 printf("/proc/sys/kernel/kptr_restrict write failed and we cant not change it.\n");
906 }
907 }
908
909 // getline end
910 if (!ParseKallsymsLine()) {
911 return false;
912 }
913
914 if (symbols_.empty()) {
915 printf("The symbol table addresses in /proc/kallsyms are all 0.\n"
916 "Please check the value of /proc/sys/kernel/kptr_restrict, it "
917 "should be 0.\n"
918 "Or provide a separate vmlinux path.\n");
919
920 if (buildId_.size() != 0) {
921 // but we got the buildid , so we make a dummpy symbols
922 HLOGD("kallsyms not found. but we have the buildid");
923 return true;
924 } else {
925 // we got nothing
926 return false;
927 }
928 } else {
929 AdjustSymbols();
930 HLOGV("%zu symbols_ loadded from kallsyms.\n", symbols_.size());
931 return true;
932 }
933 }
LoadSymbols(const std::string & symbolFilePath)934 bool LoadSymbols(const std::string &symbolFilePath) override
935 {
936 symbolsLoaded_ = true;
937 HLOGV("KernelSymbols try read '%s' search paths size %zu, inDeviceRecord %d",
938 symbolFilePath.c_str(), symbolsFileSearchPaths_.size(), onRecording_);
939
940 if (onRecording_) {
941 // try read
942 HLOGD("try read /sys/kernel/notes");
943 std::string notes = ReadFileToString("/sys/kernel/notes");
944 if (notes.empty()) {
945 printf("notes cannot be opened, unable get buildid\n");
946 return false;
947 } else {
948 HLOGD("kernel notes size: %zu", notes.size());
949 buildId_ = ElfGetBuildId((const unsigned char *)notes.data(), notes.size());
950 }
951
952 const auto startTime = std::chrono::steady_clock::now();
953 if (!LoadKernelSyms()) {
954 printf("parse kalsyms failed.\n");
955 return false;
956 } else {
957 const auto thisTime = std::chrono::steady_clock::now();
958 const auto usedTimeMsTick =
959 std::chrono::duration_cast<std::chrono::milliseconds>(thisTime - startTime);
960 HLOGV("Load kernel symbols (total %" PRId64 " ms)\n", (int64_t)usedTimeMsTick.count());
961 // load complete
962 return true;
963 }
964 } // no search path
965
966 // try vmlinux
967 return ElfFileSymbols::LoadSymbols(KERNEL_ELF_NAME);
968 }
GetVaddrInSymbols(uint64_t ip,uint64_t mapStart,uint64_t) const969 uint64_t GetVaddrInSymbols(uint64_t ip, uint64_t mapStart, uint64_t) const override
970 {
971 // ip is vaddr in /proc/kallsyms
972 return ip;
973 }
~KernelSymbols()974 ~KernelSymbols() override {}
975 };
976
977 class KernelModuleSymbols : public ElfFileSymbols {
978 public:
KernelModuleSymbols(const std::string & symbolFilePath)979 explicit KernelModuleSymbols(const std::string &symbolFilePath) : ElfFileSymbols(symbolFilePath)
980 {
981 HLOGV("create %s", symbolFilePath.c_str());
982 symbolFileType_ = SYMBOL_KERNEL_MODULE_FILE;
983 module_ = symbolFilePath;
984 }
~KernelModuleSymbols()985 ~KernelModuleSymbols() override {};
986
LoadSymbols(const std::string & symbolFilePath)987 bool LoadSymbols(const std::string &symbolFilePath) override
988 {
989 symbolsLoaded_ = true;
990 if (module_ == filePath_ and onRecording_) {
991 // file name sitll not convert to ko file path
992 // this is in record mode
993 HLOGV("find ko name %s", module_.c_str());
994 for (const std::string &path : kernelModulePaths) {
995 if (access(path.c_str(), R_OK) == 0) {
996 std::string koPath = path + module_ + KERNEL_MODULES_EXT_NAME;
997 HLOGV("found ko in %s", koPath.c_str());
998 if (access(koPath.c_str(), R_OK) == 0) {
999 // create symbol
1000 filePath_ = koPath;
1001 break; // find next ko
1002 }
1003 }
1004 }
1005 LoadBuildId();
1006 } else {
1007 HLOGV("we have file path, load with %s", filePath_.c_str());
1008 return ElfFileSymbols::LoadSymbols(filePath_);
1009 }
1010 return false;
1011 }
GetVaddrInSymbols(uint64_t ip,uint64_t mapStart,uint64_t) const1012 uint64_t GetVaddrInSymbols(uint64_t ip, uint64_t mapStart, uint64_t) const override
1013 {
1014 return ip - mapStart;
1015 }
1016
1017 private:
LoadBuildId()1018 bool LoadBuildId()
1019 {
1020 std::string sysFile = "/sys/module/" + module_ + "/notes/.note.gnu.build-id";
1021 std::string buildIdRaw = ReadFileToString(sysFile);
1022 if (!buildIdRaw.empty()) {
1023 buildId_ = ElfGetBuildId((const unsigned char *)buildIdRaw.data(), buildIdRaw.size());
1024 HLOGD("kerne module %s(%s) build id %s", module_.c_str(), filePath_.c_str(),
1025 buildId_.c_str());
1026 return buildId_.empty() ? false : true;
1027 }
1028 return false;
1029 }
1030
1031 const std::vector<std::string> kernelModulePaths = {"/vendor/modules/"};
1032 std::string module_ = "";
1033 };
1034
1035 class JavaFileSymbols : public ElfFileSymbols {
1036 public:
JavaFileSymbols(const std::string & symbolFilePath)1037 explicit JavaFileSymbols(const std::string &symbolFilePath) : ElfFileSymbols(symbolFilePath)
1038 {
1039 symbolFileType_ = SYMBOL_KERNEL_FILE;
1040 }
LoadSymbols(const std::string & symbolFilePath)1041 bool LoadSymbols(const std::string &symbolFilePath) override
1042 {
1043 symbolsLoaded_ = true;
1044 return false;
1045 }
~JavaFileSymbols()1046 ~JavaFileSymbols() override {}
1047
GetVaddrInSymbols(uint64_t ip,uint64_t mapStart,uint64_t mapPageOffset) const1048 uint64_t GetVaddrInSymbols(uint64_t ip, uint64_t mapStart,
1049 uint64_t mapPageOffset) const override
1050 {
1051 // this is different with elf
1052 // elf use ip - mapStart + mapPageOffset - minExecAddrFileOffset_ + textExecVaddr_
1053 return ip - mapStart + mapPageOffset;
1054 }
1055 };
1056
1057 class JSFileSymbols : public ElfFileSymbols {
1058 public:
JSFileSymbols(const std::string & symbolFilePath)1059 explicit JSFileSymbols(const std::string &symbolFilePath) : ElfFileSymbols(symbolFilePath)
1060 {
1061 symbolFileType_ = SYMBOL_KERNEL_FILE;
1062 }
LoadSymbols(const std::string & symbolFilePath)1063 bool LoadSymbols(const std::string &symbolFilePath) override
1064 {
1065 symbolsLoaded_ = true;
1066 return false;
1067 }
~JSFileSymbols()1068 ~JSFileSymbols() override {}
1069 };
1070
1071 class UnknowFileSymbols : public SymbolsFile {
1072 public:
UnknowFileSymbols(const std::string & symbolFilePath)1073 explicit UnknowFileSymbols(const std::string &symbolFilePath)
1074 : SymbolsFile(SYMBOL_UNKNOW_FILE, symbolFilePath)
1075 {
1076 }
LoadSymbols(const std::string & symbolFilePath)1077 bool LoadSymbols(const std::string &symbolFilePath) override
1078 {
1079 symbolsLoaded_ = true;
1080 return false;
1081 }
~UnknowFileSymbols()1082 ~UnknowFileSymbols() override {}
1083 };
1084
~SymbolsFile()1085 SymbolsFile::~SymbolsFile() {}
1086
CreateSymbolsFile(SymbolsFileType symbolType,const std::string symbolFilePath)1087 std::unique_ptr<SymbolsFile> SymbolsFile::CreateSymbolsFile(SymbolsFileType symbolType,
1088 const std::string symbolFilePath)
1089 {
1090 switch (symbolType) {
1091 case SYMBOL_KERNEL_FILE:
1092 return std::make_unique<KernelSymbols>(symbolFilePath.empty() ? KERNEL_MMAP_NAME
1093 : symbolFilePath);
1094 case SYMBOL_KERNEL_MODULE_FILE:
1095 return std::make_unique<KernelModuleSymbols>(symbolFilePath);
1096 case SYMBOL_ELF_FILE:
1097 return std::make_unique<ElfFileSymbols>(symbolFilePath);
1098 case SYMBOL_JAVA_FILE:
1099 return std::make_unique<JavaFileSymbols>(symbolFilePath);
1100 case SYMBOL_JS_FILE:
1101 return std::make_unique<JSFileSymbols>(symbolFilePath);
1102 default:
1103 return std::make_unique<SymbolsFile>(SYMBOL_UNKNOW_FILE, symbolFilePath);
1104 }
1105 }
1106
CreateSymbolsFile(const std::string & symbolFilePath)1107 std::unique_ptr<SymbolsFile> SymbolsFile::CreateSymbolsFile(const std::string &symbolFilePath)
1108 {
1109 // we need check file name here
1110 if (symbolFilePath == KERNEL_MMAP_NAME) {
1111 return SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_FILE, symbolFilePath);
1112 } else if (StringEndsWith(symbolFilePath, KERNEL_MODULES_EXT_NAME)) {
1113 return SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_MODULE_FILE, symbolFilePath);
1114 } else {
1115 // default is elf
1116 return SymbolsFile::CreateSymbolsFile(SYMBOL_ELF_FILE, symbolFilePath);
1117 }
1118 }
1119
AdjustSymbols()1120 void SymbolsFile::AdjustSymbols()
1121 {
1122 if (symbols_.size() <= 1u) {
1123 return;
1124 }
1125
1126 // order
1127 sort(symbols_.begin(), symbols_.end(), Symbol::CompareLessThen);
1128 HLOGV("sort completed");
1129
1130 size_t fullSize = symbols_.size();
1131 size_t erased = 0;
1132
1133 // Check for duplicate vaddr
1134 auto last = std::unique(symbols_.begin(), symbols_.end(), &Symbol::SameVaddr);
1135 symbols_.erase(last, symbols_.end());
1136 erased = fullSize - symbols_.size();
1137 HLOGV("uniqued completed");
1138 auto it = symbols_.begin();
1139 while (it != symbols_.end()) {
1140 it->index_ = it - symbols_.begin();
1141 it++;
1142 }
1143 HLOGV("indexed completed");
1144
1145 HLOG_ASSERT(symbols_.size() != 0);
1146
1147 if (textExecVaddrRange_ == maxVaddr) {
1148 textExecVaddrRange_ = symbols_.back().funcVaddr_ - symbols_.front().funcVaddr_;
1149 }
1150
1151 HLOGDDD("%zu symbols after adjust (%zu erased) 0x%016" PRIx64 " - 0x%016" PRIx64
1152 " @0x%016" PRIx64 " ",
1153 symbols_.size(), erased, symbols_.front().funcVaddr_, symbols_.back().funcVaddr_,
1154 textExecVaddrFileOffset_);
1155 }
1156
SortMatchedSymbols()1157 void SymbolsFile::SortMatchedSymbols()
1158 {
1159 if (matchedSymbols_.size() <= 1u) {
1160 return;
1161 }
1162 sort(matchedSymbols_.begin(), matchedSymbols_.end(), Symbol::CompareByPointer);
1163 }
1164
GetSymbols()1165 const std::vector<Symbol> &SymbolsFile::GetSymbols()
1166 {
1167 return symbols_;
1168 }
1169
GetMatchedSymbols()1170 const std::vector<Symbol *> &SymbolsFile::GetMatchedSymbols()
1171 {
1172 return matchedSymbols_;
1173 }
1174
GetSymbolWithVaddr(uint64_t vaddrInFile)1175 const Symbol SymbolsFile::GetSymbolWithVaddr(uint64_t vaddrInFile)
1176 {
1177 #ifdef HIPERF_DEBUG_TIME
1178 const auto startTime = steady_clock::now();
1179 #endif
1180 Symbol symbol;
1181 // it should be already order from small to large
1182 auto found =
1183 std::upper_bound(symbols_.begin(), symbols_.end(), vaddrInFile, Symbol::ValueLessThen);
1184 /*
1185 if data is { 1, 2, 4, 5, 5, 6 };
1186 upper_bound for each val :
1187 0 < 1 at index 0
1188 1 < 2 at index 1
1189 2 < 4 at index 2
1190 3 < 4 at index 2
1191 4 < 5 at index 3
1192 5 < 6 at index 5
1193 6 < not found
1194 if key symbol vaddr is { 1, 2, 4, 5, 5, 6 };
1195 check ip vaddr for each val :
1196 ip sym
1197 0 not found
1198 1 1
1199 1 1
1200 2 2
1201 3 3
1202 4 4
1203 5 5
1204 6 6
1205 7 7
1206 */
1207 if (found != symbols_.begin()) {
1208 found = std::prev(found);
1209 if (found->Contain(vaddrInFile)) {
1210 if (!found->HasMatched()) {
1211 found->SetMatchFlag();
1212 matchedSymbols_.push_back(&(*found));
1213 }
1214 symbol = *found; // copy
1215 HLOGV("found '%s' for vaddr 0x%016" PRIx64 "", symbol.ToString().c_str(), vaddrInFile);
1216 }
1217 }
1218
1219 if (!symbol.isValid()) {
1220 HLOGV("NOT found vaddr 0x%" PRIx64 " in symbole file %s(%zu)", vaddrInFile,
1221 filePath_.c_str(), symbols_.size());
1222 }
1223 symbol.setIpVAddress(vaddrInFile);
1224
1225 #ifdef HIPERF_DEBUG_TIME
1226 auto usedTime = duration_cast<milliseconds>(steady_clock::now() - startTime);
1227 if (usedTime > 1ms) {
1228 HLOGW("cost %" PRId64 "ms to search ", usedTime.count());
1229 }
1230 #endif
1231 return symbol;
1232 }
1233
CheckPathReadable(const std::string & path) const1234 bool SymbolsFile::CheckPathReadable(const std::string &path) const
1235 {
1236 if (access(path.c_str(), R_OK) == 0) {
1237 return true;
1238 } else {
1239 HLOGM("'%s' is unable read", path.c_str());
1240 return false;
1241 }
1242 }
1243
setSymbolsFilePath(const std::vector<std::string> & symbolsSearchPaths)1244 bool SymbolsFile::setSymbolsFilePath(const std::vector<std::string> &symbolsSearchPaths)
1245 {
1246 symbolsFileSearchPaths_.clear();
1247 for (auto &symbolsSearchPath : symbolsSearchPaths) {
1248 if (CheckPathReadable(symbolsSearchPath)) {
1249 symbolsFileSearchPaths_.emplace_back(symbolsSearchPath);
1250 HLOGV("'%s' is add to symbolsSearchPath", symbolsSearchPath.c_str());
1251 }
1252 }
1253 return (symbolsFileSearchPaths_.size() > 0);
1254 }
1255
LoadSymbolsFromSaved(const SymbolFileStruct & symbolFileStruct)1256 std::unique_ptr<SymbolsFile> SymbolsFile::LoadSymbolsFromSaved(
1257 const SymbolFileStruct &symbolFileStruct)
1258 {
1259 auto symbolsFile = CreateSymbolsFile(symbolFileStruct.filePath_);
1260 symbolsFile->filePath_ = symbolFileStruct.filePath_;
1261 symbolsFile->symbolFileType_ = (SymbolsFileType)symbolFileStruct.symbolType_;
1262 symbolsFile->textExecVaddr_ = symbolFileStruct.textExecVaddr_;
1263 symbolsFile->textExecVaddrFileOffset_ = symbolFileStruct.textExecVaddrFileOffset_;
1264 symbolsFile->buildId_ = symbolFileStruct.buildId_;
1265 for (auto &symbolStruct : symbolFileStruct.symbolStructs_) {
1266 symbolsFile->symbols_.emplace_back(symbolStruct.vaddr_, symbolStruct.len_,
1267 symbolStruct.symbolName_, symbolFileStruct.filePath_);
1268 }
1269 symbolsFile->AdjustSymbols(); // reorder
1270 HLOGV("load %zu symbol from SymbolFileStruct for file '%s'", symbolsFile->symbols_.size(),
1271 symbolsFile->filePath_.c_str());
1272 return symbolsFile;
1273 }
1274
ExportSymbolToFileFormat(SymbolFileStruct & symbolFileStruct)1275 void SymbolsFile::ExportSymbolToFileFormat(SymbolFileStruct &symbolFileStruct)
1276 {
1277 symbolFileStruct.filePath_ = filePath_;
1278 symbolFileStruct.symbolType_ = symbolFileType_;
1279 symbolFileStruct.textExecVaddr_ = textExecVaddr_;
1280 symbolFileStruct.textExecVaddrFileOffset_ = textExecVaddrFileOffset_;
1281 symbolFileStruct.buildId_ = buildId_;
1282
1283 SortMatchedSymbols();
1284 auto symbols = GetMatchedSymbols();
1285 symbolFileStruct.symbolStructs_.reserve(symbols.size());
1286 for (auto symbol : symbols) {
1287 auto &symbolStruct = symbolFileStruct.symbolStructs_.emplace_back();
1288 symbolStruct.vaddr_ = symbol->funcVaddr_;
1289 symbolStruct.len_ = symbol->len_;
1290 symbolStruct.symbolName_ = symbol->Name();
1291 }
1292
1293 HLOGV("export %zu symbol to SymbolFileStruct from %s", symbolFileStruct.symbolStructs_.size(),
1294 filePath_.c_str());
1295 }
1296
GetVaddrInSymbols(uint64_t ip,uint64_t,uint64_t) const1297 uint64_t SymbolsFile::GetVaddrInSymbols(uint64_t ip, uint64_t, uint64_t) const
1298 {
1299 // no convert
1300 return ip;
1301 }
1302 } // namespace NativeDaemon
1303 } // namespace Developtools
1304 } // namespace OHOS
1305