1 /*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <elf.h>
18 #include <stdint.h>
19
20 #include <memory>
21 #include <string>
22 #include <utility>
23
24 #include <zlib.h>
25 #include <zstd.h>
26
27 #include <unwindstack/DwarfError.h>
28 #include <unwindstack/DwarfSection.h>
29 #include <unwindstack/ElfInterface.h>
30 #include <unwindstack/Log.h>
31 #include <unwindstack/Memory.h>
32 #include <unwindstack/Regs.h>
33
34 #include "DwarfDebugFrame.h"
35 #include "DwarfEhFrame.h"
36 #include "DwarfEhFrameWithHdr.h"
37 #include "MemoryBuffer.h"
38 #include "MemoryXz.h"
39 #include "Symbols.h"
40
41 namespace unwindstack {
42
~ElfInterface()43 ElfInterface::~ElfInterface() {
44 for (auto symbol : symbols_) {
45 delete symbol;
46 }
47 }
48
IsValidPc(uint64_t pc)49 bool ElfInterface::IsValidPc(uint64_t pc) {
50 if (!pt_loads_.empty()) {
51 for (auto& entry : pt_loads_) {
52 uint64_t start = entry.second.table_offset;
53 uint64_t end = start + entry.second.table_size;
54 if (pc >= start && pc < end) {
55 return true;
56 }
57 }
58 return false;
59 }
60
61 // No PT_LOAD data, look for a fde for this pc in the section data.
62 if (debug_frame_ != nullptr && debug_frame_->GetFdeFromPc(pc) != nullptr) {
63 return true;
64 }
65
66 if (eh_frame_ != nullptr && eh_frame_->GetFdeFromPc(pc) != nullptr) {
67 return true;
68 }
69
70 return false;
71 }
72
GetTextRange(uint64_t * addr,uint64_t * size)73 bool ElfInterface::GetTextRange(uint64_t* addr, uint64_t* size) {
74 if (text_size_ != 0) {
75 *addr = text_addr_;
76 *size = text_size_;
77 return true;
78 }
79 return false;
80 }
81
CreateGnuDebugdataMemory()82 std::shared_ptr<Memory> ElfInterface::CreateGnuDebugdataMemory() {
83 if (gnu_debugdata_offset_ == 0 || gnu_debugdata_size_ == 0) {
84 return nullptr;
85 }
86
87 auto decompressed = std::make_shared<MemoryXz>(memory_.get(), gnu_debugdata_offset_,
88 gnu_debugdata_size_, GetSoname());
89 if (!decompressed || !decompressed->Init()) {
90 gnu_debugdata_offset_ = 0;
91 gnu_debugdata_size_ = 0;
92 return nullptr;
93 }
94 return decompressed;
95 }
96
ZlibDecompress(uint8_t * compressed_data,size_t compressed_size,MemoryBuffer * memory)97 static bool ZlibDecompress(uint8_t* compressed_data, size_t compressed_size, MemoryBuffer* memory) {
98 z_stream stream;
99 stream.zalloc = Z_NULL;
100 stream.zfree = Z_NULL;
101 stream.opaque = Z_NULL;
102 if (inflateInit(&stream) != Z_OK) {
103 return false;
104 }
105 stream.next_in = compressed_data;
106 stream.avail_in = compressed_size;
107 stream.next_out = memory->Data();
108 stream.avail_out = memory->Size();
109 int ret = inflate(&stream, Z_FINISH);
110 if (inflateEnd(&stream) != Z_OK) {
111 return false;
112 }
113 return ret == Z_STREAM_END;
114 }
115
ZstdDecompress(uint8_t * compressed_data,size_t compressed_size,MemoryBuffer * memory)116 static bool ZstdDecompress(uint8_t* compressed_data, size_t compressed_size, MemoryBuffer* memory) {
117 size_t decompress_size =
118 ZSTD_decompress(memory->Data(), memory->Size(), compressed_data, compressed_size);
119 return memory->Size() == decompress_size;
120 }
121
122 template <typename ChdrType>
CreateMemoryFromCompressedSection(SectionInfo & info,std::shared_ptr<Memory> & elf_memory)123 std::shared_ptr<Memory> CreateMemoryFromCompressedSection(SectionInfo& info,
124 std::shared_ptr<Memory>& elf_memory) {
125 if (info.size < sizeof(ChdrType)) {
126 return nullptr;
127 }
128
129 uint8_t* compressed_data = elf_memory->GetPtr(info.offset);
130 std::vector<uint8_t> compressed;
131 if (compressed_data == nullptr || elf_memory->GetPtr(info.offset + info.size - 1) == nullptr) {
132 compressed.resize(info.size);
133 if (!elf_memory->ReadFully(info.offset, compressed.data(), info.size)) {
134 return nullptr;
135 }
136 compressed_data = compressed.data();
137 }
138
139 ChdrType* chdr = reinterpret_cast<ChdrType*>(compressed_data);
140 std::shared_ptr<MemoryBuffer> memory(new MemoryBuffer(chdr->ch_size, info.offset));
141
142 bool ret = false;
143 if (chdr->ch_type == ELFCOMPRESS_ZLIB) {
144 ret = ZlibDecompress(&compressed_data[sizeof(ChdrType)], info.size - sizeof(ChdrType),
145 memory.get());
146 } else if (chdr->ch_type == ELFCOMPRESS_ZSTD) {
147 ret = ZstdDecompress(&compressed_data[sizeof(ChdrType)], info.size - sizeof(ChdrType),
148 memory.get());
149 }
150 if (!ret) {
151 return nullptr;
152 }
153 // Set the section info to match the uncompressed section data.
154 info.size = chdr->ch_size;
155 info.flags &= ~SHF_COMPRESSED;
156 return memory;
157 }
158
159 template <typename ElfTypes>
InitHeaders()160 void ElfInterfaceImpl<ElfTypes>::InitHeaders() {
161 if (eh_frame_hdr_info_.offset != 0) {
162 DwarfEhFrameWithHdr<AddressType>* eh_frame_hdr = new DwarfEhFrameWithHdr<AddressType>(memory_);
163 eh_frame_.reset(eh_frame_hdr);
164 if (!eh_frame_hdr->EhFrameInit(eh_frame_info_) || !eh_frame_->Init(eh_frame_hdr_info_)) {
165 eh_frame_hdr_info_ = {};
166 eh_frame_.reset(nullptr);
167 }
168 }
169
170 if (eh_frame_.get() == nullptr && eh_frame_info_.offset != 0) {
171 // If there is an eh_frame section without an eh_frame_hdr section,
172 // or using the frame hdr object failed to init.
173 eh_frame_.reset(new DwarfEhFrame<AddressType>(memory_));
174 if (!eh_frame_->Init(eh_frame_info_)) {
175 eh_frame_info_ = {};
176 eh_frame_.reset(nullptr);
177 }
178 }
179
180 if (debug_frame_info_.offset != 0) {
181 std::shared_ptr<Memory> debug_memory = memory_;
182 if (debug_frame_info_.flags & SHF_COMPRESSED) {
183 debug_memory = CreateMemoryFromCompressedSection<ChdrType>(debug_frame_info_, memory_);
184 }
185 debug_frame_.reset(new DwarfDebugFrame<AddressType>(debug_memory));
186 if (!debug_frame_->Init(debug_frame_info_)) {
187 debug_frame_.reset(nullptr);
188 debug_frame_info_ = {};
189 }
190 }
191 }
192
193 template <typename ElfTypes>
ReadAllHeaders(int64_t * load_bias)194 bool ElfInterfaceImpl<ElfTypes>::ReadAllHeaders(int64_t* load_bias) {
195 EhdrType ehdr;
196 if (!memory_->ReadFully(0, &ehdr, sizeof(ehdr))) {
197 last_error_.code = ERROR_MEMORY_INVALID;
198 last_error_.address = 0;
199 return false;
200 }
201
202 // If we have enough information that this is an elf file, then allow
203 // malformed program and section headers.
204 ReadProgramHeaders(ehdr, load_bias);
205 ReadSectionHeaders(ehdr);
206 return true;
207 }
208
209 template <typename EhdrType, typename PhdrType>
GetLoadBias(Memory * memory)210 int64_t ElfInterface::GetLoadBias(Memory* memory) {
211 EhdrType ehdr;
212 if (!memory->ReadFully(0, &ehdr, sizeof(ehdr))) {
213 return false;
214 }
215
216 uint64_t offset = ehdr.e_phoff;
217 for (size_t i = 0; i < ehdr.e_phnum; i++, offset += ehdr.e_phentsize) {
218 PhdrType phdr;
219 if (!memory->ReadFully(offset, &phdr, sizeof(phdr))) {
220 return 0;
221 }
222
223 // Find the first executable load when looking for the load bias.
224 if (phdr.p_type == PT_LOAD && (phdr.p_flags & PF_X)) {
225 return static_cast<uint64_t>(phdr.p_vaddr) - phdr.p_offset;
226 }
227 }
228 return 0;
229 }
230
231 template <typename ElfTypes>
ReadProgramHeaders(const EhdrType & ehdr,int64_t * load_bias)232 void ElfInterfaceImpl<ElfTypes>::ReadProgramHeaders(const EhdrType& ehdr, int64_t* load_bias) {
233 uint64_t offset = ehdr.e_phoff;
234 bool first_exec_load_header = true;
235 for (size_t i = 0; i < ehdr.e_phnum; i++, offset += ehdr.e_phentsize) {
236 PhdrType phdr;
237 if (!memory_->ReadFully(offset, &phdr, sizeof(phdr))) {
238 return;
239 }
240
241 switch (phdr.p_type) {
242 case PT_LOAD:
243 {
244 if ((phdr.p_flags & PF_X) == 0) {
245 continue;
246 }
247
248 pt_loads_[phdr.p_offset] = LoadInfo{phdr.p_offset, phdr.p_vaddr,
249 static_cast<size_t>(phdr.p_memsz)};
250 // Only set the load bias from the first executable load header.
251 if (first_exec_load_header) {
252 *load_bias = static_cast<uint64_t>(phdr.p_vaddr) - phdr.p_offset;
253 }
254 first_exec_load_header = false;
255 break;
256 }
257
258 case PT_GNU_EH_FRAME:
259 // This is really the pointer to the .eh_frame_hdr section.
260 eh_frame_hdr_info_ = {
261 .offset = phdr.p_offset,
262 .size = phdr.p_memsz,
263 .flags = phdr.p_flags,
264 .bias = static_cast<int64_t>(static_cast<uint64_t>(phdr.p_vaddr) - phdr.p_offset)};
265 break;
266
267 case PT_DYNAMIC:
268 dynamic_offset_ = phdr.p_offset;
269 dynamic_vaddr_start_ = phdr.p_vaddr;
270 if (__builtin_add_overflow(dynamic_vaddr_start_, phdr.p_memsz, &dynamic_vaddr_end_)) {
271 dynamic_offset_ = 0;
272 dynamic_vaddr_start_ = 0;
273 dynamic_vaddr_end_ = 0;
274 }
275 break;
276
277 default:
278 HandleUnknownType(phdr.p_type, phdr.p_offset, phdr.p_filesz);
279 break;
280 }
281 }
282 }
283
284 template <typename ElfTypes>
ReadBuildID()285 std::string ElfInterfaceImpl<ElfTypes>::ReadBuildID() {
286 // Ensure there is no overflow in any of the calulations below.
287 uint64_t tmp;
288 if (__builtin_add_overflow(gnu_build_id_offset_, gnu_build_id_size_, &tmp)) {
289 return "";
290 }
291
292 uint64_t offset = 0;
293 while (offset < gnu_build_id_size_) {
294 if (gnu_build_id_size_ - offset < sizeof(NhdrType)) {
295 return "";
296 }
297 NhdrType hdr;
298 if (!memory_->ReadFully(gnu_build_id_offset_ + offset, &hdr, sizeof(hdr))) {
299 return "";
300 }
301 offset += sizeof(hdr);
302
303 if (gnu_build_id_size_ - offset < hdr.n_namesz) {
304 return "";
305 }
306 if (hdr.n_namesz > 0) {
307 std::string name(hdr.n_namesz, '\0');
308 if (!memory_->ReadFully(gnu_build_id_offset_ + offset, &(name[0]), hdr.n_namesz)) {
309 return "";
310 }
311
312 // Trim trailing \0 as GNU is stored as a C string in the ELF file.
313 if (name.back() == '\0')
314 name.resize(name.size() - 1);
315
316 // Align hdr.n_namesz to next power multiple of 4. See man 5 elf.
317 offset += (hdr.n_namesz + 3) & ~3;
318
319 if (name == "GNU" && hdr.n_type == NT_GNU_BUILD_ID) {
320 if (gnu_build_id_size_ - offset < hdr.n_descsz || hdr.n_descsz == 0) {
321 return "";
322 }
323 std::string build_id(hdr.n_descsz, '\0');
324 if (memory_->ReadFully(gnu_build_id_offset_ + offset, &build_id[0], hdr.n_descsz)) {
325 return build_id;
326 }
327 return "";
328 }
329 }
330 // Align hdr.n_descsz to next power multiple of 4. See man 5 elf.
331 offset += (hdr.n_descsz + 3) & ~3;
332 }
333 return "";
334 }
335 template <typename ElfTypes>
ReadSectionHeaders(const EhdrType & ehdr)336 void ElfInterfaceImpl<ElfTypes>::ReadSectionHeaders(const EhdrType& ehdr) {
337 uint64_t offset = ehdr.e_shoff;
338 uint64_t sec_offset = 0;
339 uint64_t sec_size = 0;
340
341 // Get the location of the section header names.
342 // If something is malformed in the header table data, we aren't going
343 // to terminate, we'll simply ignore this part.
344 ShdrType shdr;
345 if (ehdr.e_shstrndx < ehdr.e_shnum) {
346 uint64_t sh_offset = offset + ehdr.e_shstrndx * ehdr.e_shentsize;
347 if (memory_->ReadFully(sh_offset, &shdr, sizeof(shdr))) {
348 sec_offset = shdr.sh_offset;
349 sec_size = shdr.sh_size;
350 }
351 }
352
353 // Skip the first header, it's always going to be NULL.
354 offset += ehdr.e_shentsize;
355 for (size_t i = 1; i < ehdr.e_shnum; i++, offset += ehdr.e_shentsize) {
356 if (!memory_->ReadFully(offset, &shdr, sizeof(shdr))) {
357 return;
358 }
359
360 if (shdr.sh_type == SHT_SYMTAB || shdr.sh_type == SHT_DYNSYM) {
361 // Need to go get the information about the section that contains
362 // the string terminated names.
363 ShdrType str_shdr;
364 if (shdr.sh_link >= ehdr.e_shnum) {
365 continue;
366 }
367 uint64_t str_offset = ehdr.e_shoff + shdr.sh_link * ehdr.e_shentsize;
368 if (!memory_->ReadFully(str_offset, &str_shdr, sizeof(str_shdr))) {
369 continue;
370 }
371 if (str_shdr.sh_type != SHT_STRTAB) {
372 continue;
373 }
374 symbols_.push_back(new Symbols(shdr.sh_offset, shdr.sh_size, shdr.sh_entsize,
375 str_shdr.sh_offset, str_shdr.sh_size));
376 } else if ((shdr.sh_type == SHT_PROGBITS || shdr.sh_type == SHT_NOBITS) && sec_size != 0) {
377 // Look for the .debug_frame and .gnu_debugdata.
378 if (shdr.sh_name < sec_size) {
379 std::string name;
380 if (memory_->ReadString(sec_offset + shdr.sh_name, &name, sec_size - shdr.sh_name)) {
381 if (name == ".debug_frame") {
382 debug_frame_info_ = {
383 .offset = shdr.sh_offset,
384 .size = shdr.sh_size,
385 .flags = shdr.sh_flags,
386 .bias = static_cast<int64_t>(static_cast<uint64_t>(shdr.sh_addr) - shdr.sh_offset)};
387 } else if (name == ".gnu_debugdata") {
388 gnu_debugdata_offset_ = shdr.sh_offset;
389 gnu_debugdata_size_ = shdr.sh_size;
390 } else if (name == ".eh_frame") {
391 eh_frame_info_ = {
392 .offset = shdr.sh_offset,
393 .size = shdr.sh_size,
394 .flags = shdr.sh_flags,
395 .bias = static_cast<int64_t>(static_cast<uint64_t>(shdr.sh_addr) - shdr.sh_offset)};
396 } else if (eh_frame_hdr_info_.offset == 0 && name == ".eh_frame_hdr") {
397 eh_frame_hdr_info_ = {
398 .offset = shdr.sh_offset,
399 .size = shdr.sh_size,
400 .flags = shdr.sh_flags,
401 .bias = static_cast<int64_t>(static_cast<uint64_t>(shdr.sh_addr) - shdr.sh_offset)};
402 } else if (name == ".data") {
403 data_offset_ = shdr.sh_offset;
404 data_vaddr_start_ = shdr.sh_addr;
405 if (__builtin_add_overflow(data_vaddr_start_, shdr.sh_size, &data_vaddr_end_)) {
406 data_offset_ = 0;
407 data_vaddr_start_ = 0;
408 data_vaddr_end_ = 0;
409 }
410 } else if (name == ".text") {
411 text_addr_ = shdr.sh_addr;
412 text_size_ = shdr.sh_size;
413 }
414 }
415 }
416 } else if (shdr.sh_type == SHT_STRTAB) {
417 // In order to read soname, keep track of address to offset mapping.
418 strtabs_.push_back(std::make_pair<uint64_t, uint64_t>(static_cast<uint64_t>(shdr.sh_addr),
419 static_cast<uint64_t>(shdr.sh_offset)));
420 } else if (shdr.sh_type == SHT_NOTE) {
421 if (shdr.sh_name < sec_size) {
422 std::string name;
423 if (memory_->ReadString(sec_offset + shdr.sh_name, &name, sec_size - shdr.sh_name) &&
424 name == ".note.gnu.build-id") {
425 gnu_build_id_offset_ = shdr.sh_offset;
426 gnu_build_id_size_ = shdr.sh_size;
427 }
428 }
429 }
430 }
431 }
432
433 template <typename ElfTypes>
GetSoname()434 std::string ElfInterfaceImpl<ElfTypes>::GetSoname() {
435 if (soname_type_ == SONAME_INVALID) {
436 return "";
437 }
438 if (soname_type_ == SONAME_VALID) {
439 return soname_;
440 }
441
442 soname_type_ = SONAME_INVALID;
443
444 uint64_t soname_offset = 0;
445 uint64_t strtab_addr = 0;
446 uint64_t strtab_size = 0;
447
448 // Find the soname location from the dynamic headers section.
449 DynType dyn;
450 uint64_t offset = dynamic_offset_;
451 uint64_t max_offset = offset + dynamic_vaddr_end_ - dynamic_vaddr_start_;
452 for (uint64_t offset = dynamic_offset_; offset < max_offset; offset += sizeof(DynType)) {
453 if (!memory_->ReadFully(offset, &dyn, sizeof(dyn))) {
454 last_error_.code = ERROR_MEMORY_INVALID;
455 last_error_.address = offset;
456 return "";
457 }
458
459 if (dyn.d_tag == DT_STRTAB) {
460 strtab_addr = dyn.d_un.d_ptr;
461 } else if (dyn.d_tag == DT_STRSZ) {
462 strtab_size = dyn.d_un.d_val;
463 } else if (dyn.d_tag == DT_SONAME) {
464 soname_offset = dyn.d_un.d_val;
465 } else if (dyn.d_tag == DT_NULL) {
466 break;
467 }
468 }
469
470 // Need to map the strtab address to the real offset.
471 for (const auto& entry : strtabs_) {
472 if (entry.first == strtab_addr) {
473 soname_offset = entry.second + soname_offset;
474 uint64_t soname_max = entry.second + strtab_size;
475 if (soname_offset >= soname_max) {
476 return "";
477 }
478 if (!memory_->ReadString(soname_offset, &soname_, soname_max - soname_offset)) {
479 return "";
480 }
481 soname_type_ = SONAME_VALID;
482 return soname_;
483 }
484 }
485 return "";
486 }
487
488 template <typename ElfTypes>
GetFunctionName(uint64_t addr,SharedString * name,uint64_t * func_offset)489 bool ElfInterfaceImpl<ElfTypes>::GetFunctionName(uint64_t addr, SharedString* name,
490 uint64_t* func_offset) {
491 if (symbols_.empty()) {
492 return false;
493 }
494
495 for (const auto symbol : symbols_) {
496 if (symbol->template GetName<SymType>(addr, memory_.get(), name, func_offset)) {
497 return true;
498 }
499 }
500 return false;
501 }
502
503 template <typename ElfTypes>
GetGlobalVariable(const std::string & name,uint64_t * memory_address)504 bool ElfInterfaceImpl<ElfTypes>::GetGlobalVariable(const std::string& name,
505 uint64_t* memory_address) {
506 if (symbols_.empty()) {
507 return false;
508 }
509
510 for (const auto symbol : symbols_) {
511 if (symbol->template GetGlobal<SymType>(memory_.get(), name, memory_address)) {
512 return true;
513 }
514 }
515 return false;
516 }
517
Step(uint64_t pc,Regs * regs,Memory * process_memory,bool * finished,bool * is_signal_frame)518 bool ElfInterface::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished,
519 bool* is_signal_frame) {
520 last_error_.code = ERROR_NONE;
521 last_error_.address = 0;
522
523 // Try the debug_frame first since it contains the most specific unwind
524 // information.
525 DwarfSection* debug_frame = debug_frame_.get();
526 if (debug_frame != nullptr &&
527 debug_frame->Step(pc, regs, process_memory, finished, is_signal_frame)) {
528 return true;
529 }
530
531 // Try the eh_frame next.
532 DwarfSection* eh_frame = eh_frame_.get();
533 if (eh_frame != nullptr && eh_frame->Step(pc, regs, process_memory, finished, is_signal_frame)) {
534 return true;
535 }
536
537 if (gnu_debugdata_interface_ != nullptr &&
538 gnu_debugdata_interface_->Step(pc, regs, process_memory, finished, is_signal_frame)) {
539 return true;
540 }
541
542 // Set the error code based on the first error encountered.
543 DwarfSection* section = nullptr;
544 if (debug_frame_ != nullptr) {
545 section = debug_frame_.get();
546 } else if (eh_frame_ != nullptr) {
547 section = eh_frame_.get();
548 } else if (gnu_debugdata_interface_ != nullptr) {
549 last_error_ = gnu_debugdata_interface_->last_error();
550 return false;
551 } else {
552 return false;
553 }
554
555 // Convert the DWARF ERROR to an external error.
556 DwarfErrorCode code = section->LastErrorCode();
557 switch (code) {
558 case DWARF_ERROR_NONE:
559 last_error_.code = ERROR_NONE;
560 break;
561
562 case DWARF_ERROR_MEMORY_INVALID:
563 last_error_.code = ERROR_MEMORY_INVALID;
564 last_error_.address = section->LastErrorAddress();
565 break;
566
567 case DWARF_ERROR_ILLEGAL_VALUE:
568 case DWARF_ERROR_ILLEGAL_STATE:
569 case DWARF_ERROR_STACK_INDEX_NOT_VALID:
570 case DWARF_ERROR_TOO_MANY_ITERATIONS:
571 case DWARF_ERROR_CFA_NOT_DEFINED:
572 case DWARF_ERROR_NO_FDES:
573 last_error_.code = ERROR_UNWIND_INFO;
574 break;
575
576 case DWARF_ERROR_NOT_IMPLEMENTED:
577 case DWARF_ERROR_UNSUPPORTED_VERSION:
578 last_error_.code = ERROR_UNSUPPORTED;
579 break;
580 }
581 return false;
582 }
583
584 // This is an estimation of the size of the elf file using the location
585 // of the section headers and size. This assumes that the section headers
586 // are at the end of the elf file. If the elf has a load bias, the size
587 // will be too large, but this is acceptable.
588 template <typename ElfTypes>
GetMaxSize(Memory * memory,uint64_t * size)589 void ElfInterfaceImpl<ElfTypes>::GetMaxSize(Memory* memory, uint64_t* size) {
590 EhdrType ehdr;
591 if (!memory->ReadFully(0, &ehdr, sizeof(ehdr))) {
592 *size = 0;
593 return;
594 }
595
596 // If this winds up as zero, the PT_LOAD reading will get a better value.
597 uint64_t elf_size = ehdr.e_shoff + ehdr.e_shentsize * ehdr.e_shnum;
598
599 // Search through the PT_LOAD values and if any result in a larger elf
600 // size, use that.
601 uint64_t offset = ehdr.e_phoff;
602 for (size_t i = 0; i < ehdr.e_phnum; i++, offset += ehdr.e_phentsize) {
603 PhdrType phdr;
604 if (!memory->ReadFully(offset, &phdr, sizeof(phdr))) {
605 break;
606 }
607 if (phdr.p_type == PT_LOAD) {
608 uint64_t end_offset;
609 if (__builtin_add_overflow(phdr.p_offset, phdr.p_memsz, &end_offset)) {
610 continue;
611 }
612 if (end_offset > elf_size) {
613 elf_size = end_offset;
614 }
615 }
616 }
617
618 *size = elf_size;
619 }
620
621 template <typename EhdrType, typename ShdrType>
GetBuildIDInfo(Memory * memory,uint64_t * build_id_offset,uint64_t * build_id_size)622 bool GetBuildIDInfo(Memory* memory, uint64_t* build_id_offset, uint64_t* build_id_size) {
623 EhdrType ehdr;
624 if (!memory->ReadFully(0, &ehdr, sizeof(ehdr))) {
625 return false;
626 }
627
628 uint64_t offset = ehdr.e_shoff;
629 uint64_t sec_offset;
630 uint64_t sec_size;
631 ShdrType shdr;
632 if (ehdr.e_shstrndx >= ehdr.e_shnum) {
633 return false;
634 }
635
636 uint64_t sh_offset = offset + ehdr.e_shstrndx * ehdr.e_shentsize;
637 if (!memory->ReadFully(sh_offset, &shdr, sizeof(shdr))) {
638 return false;
639 }
640 sec_offset = shdr.sh_offset;
641 sec_size = shdr.sh_size;
642
643 // Skip the first header, it's always going to be NULL.
644 offset += ehdr.e_shentsize;
645 for (size_t i = 1; i < ehdr.e_shnum; i++, offset += ehdr.e_shentsize) {
646 if (!memory->ReadFully(offset, &shdr, sizeof(shdr))) {
647 return false;
648 }
649 std::string name;
650 if (shdr.sh_type == SHT_NOTE && shdr.sh_name < sec_size &&
651 memory->ReadString(sec_offset + shdr.sh_name, &name, sec_size - shdr.sh_name) &&
652 name == ".note.gnu.build-id") {
653 *build_id_offset = shdr.sh_offset;
654 *build_id_size = shdr.sh_size;
655 return true;
656 }
657 }
658
659 return false;
660 }
661
662 template <typename EhdrType, typename ShdrType, typename NhdrType>
ReadBuildIDFromMemory(Memory * memory)663 std::string ElfInterface::ReadBuildIDFromMemory(Memory* memory) {
664 uint64_t note_offset;
665 uint64_t note_size;
666 if (!GetBuildIDInfo<EhdrType, ShdrType>(memory, ¬e_offset, ¬e_size)) {
667 return "";
668 }
669
670 // Ensure there is no overflow in any of the calculations below.
671 uint64_t tmp;
672 if (__builtin_add_overflow(note_offset, note_size, &tmp)) {
673 return "";
674 }
675
676 uint64_t offset = 0;
677 while (offset < note_size) {
678 if (note_size - offset < sizeof(NhdrType)) {
679 return "";
680 }
681 NhdrType hdr;
682 if (!memory->ReadFully(note_offset + offset, &hdr, sizeof(hdr))) {
683 return "";
684 }
685 offset += sizeof(hdr);
686
687 if (note_size - offset < hdr.n_namesz) {
688 return "";
689 }
690 if (hdr.n_namesz > 0) {
691 std::string name(hdr.n_namesz, '\0');
692 if (!memory->ReadFully(note_offset + offset, &(name[0]), hdr.n_namesz)) {
693 return "";
694 }
695
696 // Trim trailing \0 as GNU is stored as a C string in the ELF file.
697 if (name.back() == '\0') name.resize(name.size() - 1);
698
699 // Align hdr.n_namesz to next power multiple of 4. See man 5 elf.
700 offset += (hdr.n_namesz + 3) & ~3;
701
702 if (name == "GNU" && hdr.n_type == NT_GNU_BUILD_ID) {
703 if (note_size - offset < hdr.n_descsz || hdr.n_descsz == 0) {
704 return "";
705 }
706 std::string build_id(hdr.n_descsz, '\0');
707 if (memory->ReadFully(note_offset + offset, &build_id[0], hdr.n_descsz)) {
708 return build_id;
709 }
710 return "";
711 }
712 }
713 // Align hdr.n_descsz to next power multiple of 4. See man 5 elf.
714 offset += (hdr.n_descsz + 3) & ~3;
715 }
716 return "";
717 }
718
719 // Instantiate all of the needed template functions.
720 template class ElfInterfaceImpl<ElfTypes32>;
721 template class ElfInterfaceImpl<ElfTypes64>;
722
723 template int64_t ElfInterface::GetLoadBias<Elf32_Ehdr, Elf32_Phdr>(Memory*);
724 template int64_t ElfInterface::GetLoadBias<Elf64_Ehdr, Elf64_Phdr>(Memory*);
725
726 template std::string ElfInterface::ReadBuildIDFromMemory<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr>(
727 Memory*);
728 template std::string ElfInterface::ReadBuildIDFromMemory<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr>(
729 Memory*);
730
731 } // namespace unwindstack
732