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