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
ElfSegmentBuildIDNoteAsString(const ElfSegment & segment)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
FindElfSegments(const void * elf_mapped_base,uint32_t segment_type)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
ReadElfBuildId(const void * elf_base)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
ReadElfLibraryName(const void * elf_base)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