1 // Copyright 2018 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "base/debug/elf_reader_linux.h" 6 7 #include <arpa/inet.h> 8 #include <elf.h> 9 10 #include <vector> 11 12 #include "base/bits.h" 13 #include "base/containers/span.h" 14 #include "base/sha1.h" 15 #include "base/strings/stringprintf.h" 16 17 namespace base { 18 namespace debug { 19 20 namespace { 21 22 #if __SIZEOF_POINTER__ == 4 23 using Ehdr = Elf32_Ehdr; 24 using Dyn = Elf32_Dyn; 25 using Half = Elf32_Half; 26 using Nhdr = Elf32_Nhdr; 27 using Phdr = Elf32_Phdr; 28 using Word = Elf32_Word; 29 #else 30 using Ehdr = Elf64_Ehdr; 31 using Dyn = Elf64_Dyn; 32 using Half = Elf64_Half; 33 using Nhdr = Elf64_Nhdr; 34 using Phdr = Elf64_Phdr; 35 using Word = Elf64_Word; 36 #endif 37 38 using ElfSegment = span<const char>; 39 40 Optional<std::string> ElfSegmentBuildIDNoteAsString(const ElfSegment& segment) { 41 const void* section_end = segment.data() + segment.size_bytes(); 42 const Nhdr* note_header = reinterpret_cast<const Nhdr*>(segment.data()); 43 while (note_header < section_end) { 44 if (note_header->n_type == NT_GNU_BUILD_ID) 45 break; 46 note_header = reinterpret_cast<const Nhdr*>( 47 reinterpret_cast<const char*>(note_header) + sizeof(Nhdr) + 48 bits::Align(note_header->n_namesz, 4) + 49 bits::Align(note_header->n_descsz, 4)); 50 } 51 52 if (note_header >= section_end || note_header->n_descsz != kSHA1Length) 53 return nullopt; 54 55 const uint8_t* guid = reinterpret_cast<const uint8_t*>(note_header) + 56 sizeof(Nhdr) + bits::Align(note_header->n_namesz, 4); 57 58 uint32_t dword = htonl(*reinterpret_cast<const int32_t*>(guid)); 59 uint16_t word1 = htons(*reinterpret_cast<const int16_t*>(guid + 4)); 60 uint16_t word2 = htons(*reinterpret_cast<const int16_t*>(guid + 6)); 61 std::string identifier; 62 identifier.reserve(kSHA1Length * 2); // as hex string 63 SStringPrintf(&identifier, "%08X%04X%04X", dword, word1, word2); 64 for (size_t i = 8; i < note_header->n_descsz; ++i) 65 StringAppendF(&identifier, "%02X", guid[i]); 66 67 return identifier; 68 } 69 70 std::vector<ElfSegment> FindElfSegments(const void* elf_mapped_base, 71 uint32_t segment_type) { 72 const char* elf_base = reinterpret_cast<const char*>(elf_mapped_base); 73 if (strncmp(elf_base, ELFMAG, SELFMAG) != 0) 74 return std::vector<ElfSegment>(); 75 76 const Ehdr* elf_header = reinterpret_cast<const Ehdr*>(elf_base); 77 const Phdr* phdrs = 78 reinterpret_cast<const Phdr*>(elf_base + elf_header->e_phoff); 79 std::vector<ElfSegment> segments; 80 for (Half i = 0; i < elf_header->e_phnum; ++i) { 81 if (phdrs[i].p_type == segment_type) 82 segments.push_back({elf_base + phdrs[i].p_offset, phdrs[i].p_filesz}); 83 } 84 return segments; 85 } 86 87 } // namespace 88 89 Optional<std::string> ReadElfBuildId(const void* elf_base) { 90 // Elf program headers can have multiple PT_NOTE arrays. 91 std::vector<ElfSegment> segs = FindElfSegments(elf_base, PT_NOTE); 92 if (segs.empty()) 93 return nullopt; 94 Optional<std::string> id; 95 for (const ElfSegment& seg : segs) { 96 id = ElfSegmentBuildIDNoteAsString(seg); 97 if (id) 98 return id; 99 } 100 101 return nullopt; 102 } 103 104 Optional<std::string> ReadElfLibraryName(const void* elf_base) { 105 std::vector<ElfSegment> segs = FindElfSegments(elf_base, PT_DYNAMIC); 106 if (segs.empty()) 107 return nullopt; 108 DCHECK_EQ(1u, segs.size()); 109 110 const ElfSegment& dynamic_seg = segs.front(); 111 const Dyn* dynamic_start = reinterpret_cast<const Dyn*>(dynamic_seg.data()); 112 const Dyn* dynamic_end = reinterpret_cast<const Dyn*>( 113 dynamic_seg.data() + dynamic_seg.size_bytes()); 114 Optional<std::string> soname; 115 Word soname_strtab_offset = 0; 116 const char* strtab_addr = 0; 117 for (const Dyn* dynamic_iter = dynamic_start; dynamic_iter < dynamic_end; 118 ++dynamic_iter) { 119 if (dynamic_iter->d_tag == DT_STRTAB) { 120 strtab_addr = 121 dynamic_iter->d_un.d_ptr + reinterpret_cast<const char*>(elf_base); 122 } else if (dynamic_iter->d_tag == DT_SONAME) { 123 soname_strtab_offset = dynamic_iter->d_un.d_val; 124 } 125 } 126 if (soname_strtab_offset && strtab_addr) 127 return std::string(strtab_addr + soname_strtab_offset); 128 return nullopt; 129 } 130 131 } // namespace debug 132 } // namespace base 133