1 /*
2 * Copyright (c) 2021-2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "dfx_elf.h"
17
18 #include <cstdlib>
19 #include <elf.h>
20 #include <fcntl.h>
21 #include <link.h>
22 #include <securec.h>
23 #include <string>
24 #include <sys/mman.h>
25 #include <sys/stat.h>
26 #include <sys/types.h>
27 #include <unistd.h>
28 #include <utility>
29 #include "dfx_define.h"
30 #include "dfx_log.h"
31 #include "dfx_util.h"
32
33 #ifndef PAGE_SIZE
34 #define PAGE_SIZE 4096
35 #endif
36
37 namespace OHOS {
38 namespace HiviewDFX {
39 namespace {
40 #undef LOG_DOMAIN
41 #undef LOG_TAG
42 #define LOG_DOMAIN 0xD002D11
43 #define LOG_TAG "DfxElf"
44 }
45
GetNameFromPath(const std::string & path)46 std::string ElfFileInfo::GetNameFromPath(const std::string &path)
47 {
48 size_t pos = path.find_last_of("/");
49 return path.substr(pos + 1);
50 }
51
IsValidPc(uint64_t pc)52 bool DfxElfImpl::IsValidPc(uint64_t pc)
53 {
54 if (!ptLoads_.empty()) {
55 for (const auto& iter : ptLoads_) {
56 uint64_t start = iter.second.tableVaddr;
57 uint64_t end = start + iter.second.tableSize;
58 if (pc >= start && pc < end) {
59 return true;
60 }
61 }
62 return false;
63 }
64 return false;
65 }
66
GetRealLoadOffset(uint64_t offset) const67 uint64_t DfxElfImpl::GetRealLoadOffset(uint64_t offset) const
68 {
69 if (!ptLoads_.empty()) {
70 for (auto& iter : ptLoads_) {
71 if ((iter.second.offset & -PAGE_SIZE) == offset) {
72 return offset + (iter.second.tableVaddr - iter.second.offset);
73 }
74 }
75 }
76 return offset;
77 }
78
GetMaxSize(uint64_t * size)79 void DfxElfImpl::GetMaxSize(uint64_t* size)
80 {
81 ElfW(Ehdr) ehdr;
82 if (!ReadElfHeaders(ehdr)) {
83 return;
84 }
85
86 if (ehdr.e_shnum == 0) {
87 return;
88 }
89 *size = ehdr.e_shoff + ehdr.e_shentsize * ehdr.e_shnum;
90 }
91
Init()92 bool DfxElfImpl::Init()
93 {
94 return ReadAllHeaders();
95 }
96
ReadAllHeaders()97 bool DfxElfImpl::ReadAllHeaders()
98 {
99 ElfW(Ehdr) ehdr;
100 if (!ReadElfHeaders(ehdr)) {
101 DFXLOG_ERROR("%s : Failed to read elf headers.", __func__);
102 return false;
103 }
104 ReadProgramHeaders(ehdr);
105 ReadSectionHeaders(ehdr);
106 return true;
107 }
108
ReadElfHeaders(ElfW (Ehdr)& ehdr)109 bool DfxElfImpl::ReadElfHeaders(ElfW(Ehdr)& ehdr)
110 {
111 if (!memory_->ReadFully(0, &ehdr, sizeof(ehdr))) {
112 return false;
113 }
114 return true;
115 }
116
ReadProgramHeaders(const ElfW (Ehdr)& ehdr)117 void DfxElfImpl::ReadProgramHeaders(const ElfW(Ehdr)& ehdr)
118 {
119 uint64_t offset = ehdr.e_phoff;
120 bool firstExecLoadHeader = true;
121 for (size_t i = 0; i < ehdr.e_phnum; i++, offset += ehdr.e_phentsize) {
122 ElfW(Phdr) phdr;
123 if (!memory_->ReadFully(offset, &phdr, sizeof(phdr))) {
124 DFXLOG_ERROR("%s : Failed to read memory of program headers.", __func__);
125 return;
126 }
127
128 switch (phdr.p_type) {
129 case PT_LOAD: {
130 if ((phdr.p_flags & PF_X) == 0) {
131 continue;
132 }
133
134 ElfLoadInfo elfLoadInfo;
135 elfLoadInfo.offset = phdr.p_offset;
136 elfLoadInfo.tableVaddr = phdr.p_vaddr;
137 elfLoadInfo.tableSize = static_cast<size_t>(phdr.p_memsz);
138 ptLoads_[phdr.p_offset] = elfLoadInfo;
139
140 if (firstExecLoadHeader) {
141 loadBias_ = static_cast<int64_t>(phdr.p_vaddr) - static_cast<int64_t>(phdr.p_offset);
142 }
143 firstExecLoadHeader = false;
144 break;
145 }
146 default:
147 DFXLOG_WARN("phdr type(%u) error", phdr.p_type);
148 break;
149 }
150 }
151 }
152
ReadSectionHeaders(const ElfW (Ehdr)& ehdr)153 void DfxElfImpl::ReadSectionHeaders(const ElfW(Ehdr)& ehdr)
154 {
155 uint64_t offset = ehdr.e_shoff;
156 uint64_t secOffset = 0;
157 uint64_t secSize = 0;
158 ElfW(Shdr) shdr;
159 if (ehdr.e_shstrndx < ehdr.e_shnum) {
160 uint64_t shOffset = offset + ehdr.e_shstrndx * ehdr.e_shentsize;
161 if (memory_->ReadFully(shOffset, &shdr, sizeof(shdr))) {
162 secOffset = shdr.sh_offset;
163 secSize = shdr.sh_size;
164 }
165 }
166
167 // Skip the first header, it's always going to be NULL.
168 offset += ehdr.e_shentsize;
169
170 for (size_t i = 1; i < ehdr.e_shnum; i++, offset += ehdr.e_shentsize) {
171 if (!memory_->ReadFully(offset, &shdr, sizeof(shdr))) {
172 DFXLOG_ERROR("%s : Failed to read memory of section headers.", __func__);
173 return;
174 }
175
176 if (shdr.sh_type == SHT_SYMTAB || shdr.sh_type == SHT_DYNSYM) {
177 // Need to go get the information about the section that contains the string terminated names.
178 ElfW(Shdr) strShdr;
179 if (shdr.sh_link >= ehdr.e_shnum || shdr.sh_entsize == 0) {
180 continue;
181 }
182 uint64_t strOffset = ehdr.e_shoff + shdr.sh_link * ehdr.e_shentsize;
183 if (!memory_->ReadFully(strOffset, &strShdr, sizeof(strShdr))) {
184 continue;
185 }
186 if (strShdr.sh_type != SHT_STRTAB) {
187 continue;
188 }
189 uint32_t shCount = static_cast<uint32_t>(shdr.sh_size / shdr.sh_entsize);
190 for (uint32_t j = 0; j < shCount; j++) {
191 ElfW(Sym) sym;
192 if (!memory_->ReadFully(shdr.sh_offset + j * shdr.sh_entsize, &sym, sizeof(sym))) {
193 continue;
194 }
195
196 SymbolInfo info;
197 info.start = sym.st_value;
198 info.end = sym.st_value + sym.st_size;
199 info.name = sym.st_name;
200 info.ndx = sym.st_shndx;
201 info.type = sym.st_info;
202 symbols_.push_back(info);
203 }
204 } else if (shdr.sh_type == SHT_NOTE) {
205 if (shdr.sh_name < secSize) {
206 std::string name;
207 if (memory_->ReadString(secOffset + shdr.sh_name, &name, secSize - shdr.sh_name) &&
208 name == ".note.gnu.build-id") {
209 buildIdOffset_ = shdr.sh_offset;
210 buildIdSize_ = shdr.sh_size;
211 }
212 }
213 }
214 }
215 }
216
GetFuncNameAndOffset(uint64_t addr,std::string * funcName,uint64_t * start,uint64_t * end)217 bool DfxElfImpl::GetFuncNameAndOffset(uint64_t addr, std::string* funcName, uint64_t* start, uint64_t* end)
218 {
219 if (symbols_.empty()) {
220 return false;
221 }
222
223 std::vector<SymbolInfo>::iterator it;
224 for (it = symbols_.begin(); it != symbols_.end(); ++it) {
225 SymbolInfo& symbol = *it;
226 if (symbol.ndx == SHN_UNDEF || ELF32_ST_TYPE(symbol.type) != STT_FUNC) {
227 continue;
228 }
229
230 if ((addr >= symbol.start) && (addr < symbol.end)) {
231 *start = symbol.start;
232 *end = symbol.end;
233 uint64_t nameAddr = symbol.start + symbol.name;
234 if (nameAddr < symbol.end) {
235 memory_->ReadString(nameAddr, funcName, static_cast<size_t>(symbol.end - nameAddr));
236 }
237 return true;
238 }
239 }
240 return false;
241 }
242
GetFuncNameAndOffset(uint64_t addr,std::string * funcName,uint64_t * funcOffset)243 bool DfxElfImpl::GetFuncNameAndOffset(uint64_t addr, std::string* funcName, uint64_t* funcOffset)
244 {
245 if (symbols_.empty()) {
246 return false;
247 }
248
249 std::vector<SymbolInfo>::iterator it;
250 for (it = symbols_.begin(); it != symbols_.end(); ++it) {
251 SymbolInfo& symbol = *it;
252 if (symbol.ndx == SHN_UNDEF || ELF32_ST_TYPE(symbol.type) != STT_FUNC) {
253 continue;
254 }
255
256 if ((addr >= symbol.start) && (addr < symbol.end)) {
257 *funcOffset = addr - symbol.start;
258 uint64_t nameAddr = symbol.start + symbol.name;
259 if (nameAddr < symbol.end) {
260 memory_->ReadString(nameAddr, funcName, static_cast<size_t>(symbol.end - nameAddr));
261 }
262 return true;
263 }
264 }
265 return false;
266 }
267
GetGlobalVariableOffset(const std::string & name,uint64_t * offset)268 bool DfxElfImpl::GetGlobalVariableOffset(const std::string& name, uint64_t* offset)
269 {
270 if (symbols_.empty()) {
271 return false;
272 }
273
274 std::vector<SymbolInfo>::iterator it;
275 for (it = symbols_.begin(); it != symbols_.end(); ++it) {
276 SymbolInfo& symbol = *it;
277 if (symbol.ndx == SHN_UNDEF || ELF32_ST_TYPE(symbol.type) != STT_OBJECT ||
278 ELF32_ST_BIND(symbol.type) != STB_GLOBAL) {
279 continue;
280 }
281
282 uint64_t nameAddr = symbol.start + symbol.name;
283 if (nameAddr < symbol.end) {
284 std::string tmpName;
285 if (memory_->ReadString(nameAddr, &tmpName, static_cast<size_t>(symbol.end - nameAddr))
286 && tmpName == name) {
287 *offset = symbol.start;
288 return true;
289 }
290 }
291 }
292 return false;
293 }
294
GetLoadBias()295 int64_t DfxElfImpl::GetLoadBias()
296 {
297 return loadBias_;
298 }
299
GetBuildID()300 std::string DfxElfImpl::GetBuildID()
301 {
302 // Ensure there is no overflow in any of the calculations below.
303 uint64_t tmp;
304 if (__builtin_add_overflow(buildIdOffset_, buildIdSize_, &tmp)) {
305 return "";
306 }
307
308 uint64_t offset = 0;
309 while (offset < buildIdSize_) {
310 ElfW(Nhdr) nhdr;
311 if (buildIdSize_ - offset < sizeof(nhdr)) {
312 return "";
313 }
314
315 if (!memory_->ReadFully(buildIdOffset_ + offset, &nhdr, sizeof(nhdr))) {
316 return "";
317 }
318 offset += sizeof(nhdr);
319
320 if (buildIdSize_ - offset < nhdr.n_namesz) {
321 return "";
322 }
323
324 if (nhdr.n_namesz > 0) {
325 std::string name(nhdr.n_namesz, '\0');
326 if (!memory_->ReadFully(buildIdOffset_ + offset, &(name[0]), nhdr.n_namesz)) {
327 return "";
328 }
329
330 // Trim trailing \0 as GNU is stored as a C string in the ELF file.
331 if (name.back() == '\0') {
332 name.resize(name.size() - 1);
333 }
334
335 offset += ALIGN_VALUE(nhdr.n_namesz, 4); // Align hdr.n_namesz to next power multiple of 4. See man 5 elf.
336 if (name == "GNU" && nhdr.n_type == NT_GNU_BUILD_ID) {
337 if (buildIdSize_ - offset < nhdr.n_descsz || nhdr.n_descsz == 0) {
338 return "";
339 }
340 std::string buildId(nhdr.n_descsz, '\0');
341 if (memory_->ReadFully(buildIdOffset_ + offset, &buildId[0], nhdr.n_descsz)) {
342 return buildId;
343 }
344 return "";
345 }
346 }
347 offset += ALIGN_VALUE(nhdr.n_descsz, 4); // Align hdr.n_descsz to next power multiple of 4. See man 5 elf.
348 }
349 return "";
350 }
351
Init()352 bool DfxElf::Init()
353 {
354 if (memory_ == nullptr) {
355 return false;
356 }
357
358 if (!ReadElfInfo()) {
359 DFXLOG_ERROR("%s : Failed to read elf info.", __func__);
360 return false;
361 }
362
363 elfImpl_ = std::unique_ptr<DfxElfImpl>(new DfxElfImpl(memory_));
364 if (elfImpl_ == nullptr) {
365 return false;
366 }
367 valid_ = elfImpl_->Init();
368 return valid_;
369 }
370
ReadElfInfo()371 bool DfxElf::ReadElfInfo()
372 {
373 if (memory_ == nullptr) {
374 return false;
375 }
376
377 if (!IsValidElf(memory_)) {
378 return false;
379 }
380
381 uint8_t eiClass;
382 if (!memory_->ReadFully(EI_CLASS, &eiClass, sizeof(eiClass))) {
383 return false;
384 }
385 class_ = eiClass;
386
387 uint16_t machine;
388 if (!memory_->ReadFully(EI_NIDENT + sizeof(uint16_t), &machine, sizeof(machine))) {
389 return false;
390 }
391 machine_ = machine;
392
393 if (class_ == ELFCLASS32) {
394 if (machine == EM_ARM) {
395 arch_ = ARCH_ARM;
396 } else if (machine == EM_386) {
397 arch_ = ARCH_X86;
398 } else {
399 LOGI("32 bit elf that is neither arm nor x86: machine = %d\n", machine);
400 return false;
401 }
402 } else if (class_ == ELFCLASS64) {
403 if (machine == EM_AARCH64) {
404 arch_ = ARCH_ARM64;
405 } else if (machine == EM_X86_64) {
406 arch_ = ARCH_X86_64;
407 } else {
408 LOGI("64 bit elf that is neither aarch64 nor x86_64: machine = %d\n", machine);
409 return false;
410 }
411 }
412 return true;
413 }
414
GetFuncNameAndOffset(uint64_t addr,std::string * funcName,uint64_t * start,uint64_t * end)415 bool DfxElf::GetFuncNameAndOffset(uint64_t addr, std::string* funcName, uint64_t* start, uint64_t* end)
416 {
417 std::lock_guard<std::mutex> guard(lock_);
418 if (!valid_) {
419 return false;
420 }
421 return elfImpl_->GetFuncNameAndOffset(addr, funcName, start, end);
422 }
423
GetFuncNameAndOffset(uint64_t addr,std::string * funcName,uint64_t * funcOffset)424 bool DfxElf::GetFuncNameAndOffset(uint64_t addr, std::string* funcName, uint64_t* funcOffset)
425 {
426 std::lock_guard<std::mutex> guard(lock_);
427 if (!valid_) {
428 return false;
429 }
430 return elfImpl_->GetFuncNameAndOffset(addr, funcName, funcOffset);
431 }
432
GetGlobalVariableOffset(const std::string & name,uint64_t * offset)433 bool DfxElf::GetGlobalVariableOffset(const std::string& name, uint64_t* offset)
434 {
435 std::lock_guard<std::mutex> guard(lock_);
436 if (!valid_) {
437 return false;
438 }
439
440 uint64_t vaddr;
441 if (!elfImpl_->GetGlobalVariableOffset(name, &vaddr)) {
442 return false;
443 }
444 return true;
445 }
446
GetLoadBias()447 int64_t DfxElf::GetLoadBias()
448 {
449 if (!valid_) {
450 return 0;
451 }
452 return elfImpl_->GetLoadBias();
453 }
454
GetBuildID()455 std::string DfxElf::GetBuildID()
456 {
457 if (!valid_) {
458 return "";
459 }
460 return elfImpl_->GetBuildID();
461 }
462
IsValidPc(uint64_t pc)463 bool DfxElf::IsValidPc(uint64_t pc)
464 {
465 if (!valid_ || (GetLoadBias() > 0 && pc < static_cast<uint64_t>(GetLoadBias()))) {
466 return false;
467 }
468
469 if (elfImpl_->IsValidPc(pc)) {
470 return true;
471 }
472 return false;
473 }
474
GetRealLoadOffset(uint64_t offset) const475 uint64_t DfxElf::GetRealLoadOffset(uint64_t offset) const
476 {
477 if (!valid_) {
478 return offset;
479 }
480 return elfImpl_->GetRealLoadOffset(offset);
481 }
482
IsValidElf(std::shared_ptr<DfxMemory> memory)483 bool DfxElf::IsValidElf(std::shared_ptr<DfxMemory> memory)
484 {
485 if (memory == nullptr) {
486 return false;
487 }
488
489 // Verify that this is a valid elf file.
490 uint8_t e_ident[SELFMAG + 1];
491 if (!memory->ReadFully(0, e_ident, SELFMAG)) {
492 return false;
493 }
494
495 if (memcmp(e_ident, ELFMAG, SELFMAG) != 0) {
496 return false;
497 }
498 return true;
499 }
500
GetMaxSize(std::shared_ptr<DfxMemory> memory)501 uint64_t DfxElf::GetMaxSize(std::shared_ptr<DfxMemory> memory)
502 {
503 if (memory == nullptr) {
504 return 0;
505 }
506
507 uint64_t size = 0;
508 auto elfImpl = std::make_shared<DfxElfImpl>(memory);
509 elfImpl->GetMaxSize(&size);
510 return size;
511 }
512
GetReadableBuildID(const std::string & buildIdHex)513 std::string DfxElf::GetReadableBuildID(const std::string &buildIdHex)
514 {
515 if (buildIdHex.empty()) {
516 return "";
517 }
518 static const char HEXTABLE[] = "0123456789abcdef";
519 static const int HEXLENGTH = 16;
520 static const int HEX_EXPAND_PARAM = 2;
521 const size_t len = buildIdHex.length();
522 std::string buildId(len * HEX_EXPAND_PARAM, '\0');
523
524 for (size_t i = 0; i < len; i++) {
525 unsigned int n = buildIdHex[i];
526 buildId[i * HEX_EXPAND_PARAM] = HEXTABLE[(n >> 4) % HEXLENGTH]; // 4 : higher 4 bit of uint8
527 buildId[i * HEX_EXPAND_PARAM + 1] = HEXTABLE[n % HEXLENGTH];
528 }
529 return buildId;
530 }
531 } // namespace HiviewDFX
532 } // namespace OHOS
533