• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 The Chromium Authors
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.h"
6 
7 #include <arpa/inet.h>
8 #include <elf.h>
9 #include <string.h>
10 
11 #include "base/bits.h"
12 #include "base/containers/span.h"
13 #include "base/hash/sha1.h"
14 #include "base/strings/safe_sprintf.h"
15 #include "build/build_config.h"
16 #include "third_party/abseil-cpp/absl/types/optional.h"
17 
18 // NOTE: This code may be used in crash handling code, so the implementation
19 // must avoid dynamic memory allocation or using data structures which rely on
20 // dynamic allocation.
21 
22 namespace base {
23 namespace debug {
24 namespace {
25 
26 // See https://refspecs.linuxbase.org/elf/elf.pdf for the ELF specification.
27 
28 #if __SIZEOF_POINTER__ == 4
29 using Ehdr = Elf32_Ehdr;
30 using Dyn = Elf32_Dyn;
31 using Half = Elf32_Half;
32 using Nhdr = Elf32_Nhdr;
33 using Word = Elf32_Word;
34 using Xword = Elf32_Word;
35 #else
36 using Ehdr = Elf64_Ehdr;
37 using Dyn = Elf64_Dyn;
38 using Half = Elf64_Half;
39 using Nhdr = Elf64_Nhdr;
40 using Word = Elf64_Word;
41 using Xword = Elf64_Xword;
42 #endif
43 
44 constexpr char kGnuNoteName[] = "GNU";
45 
46 // Returns a pointer to the header of the ELF binary mapped into memory, or a
47 // null pointer if the header is invalid. Here and below |elf_mapped_base| is a
48 // pointer to the start of the ELF image.
GetElfHeader(const void * elf_mapped_base)49 const Ehdr* GetElfHeader(const void* elf_mapped_base) {
50   if (strncmp(reinterpret_cast<const char*>(elf_mapped_base), ELFMAG,
51               SELFMAG) != 0)
52     return nullptr;
53 
54   return reinterpret_cast<const Ehdr*>(elf_mapped_base);
55 }
56 
57 }  // namespace
58 
ReadElfBuildId(const void * elf_mapped_base,bool uppercase,ElfBuildIdBuffer build_id)59 size_t ReadElfBuildId(const void* elf_mapped_base,
60                       bool uppercase,
61                       ElfBuildIdBuffer build_id) {
62   // NOTE: Function should use async signal safe calls only.
63 
64   const Ehdr* elf_header = GetElfHeader(elf_mapped_base);
65   if (!elf_header)
66     return 0;
67 
68   const size_t relocation_offset = GetRelocationOffset(elf_mapped_base);
69   for (const Phdr& header : GetElfProgramHeaders(elf_mapped_base)) {
70     if (header.p_type != PT_NOTE)
71       continue;
72 
73     // Look for a NT_GNU_BUILD_ID note with name == "GNU".
74     const char* current_section =
75         reinterpret_cast<const char*>(header.p_vaddr + relocation_offset);
76     const char* section_end = current_section + header.p_memsz;
77     const Nhdr* current_note = nullptr;
78     bool found = false;
79     while (current_section < section_end) {
80       current_note = reinterpret_cast<const Nhdr*>(current_section);
81       if (current_note->n_type == NT_GNU_BUILD_ID) {
82         StringPiece note_name(current_section + sizeof(Nhdr),
83                               current_note->n_namesz);
84         // Explicit constructor is used to include the '\0' character.
85         if (note_name == StringPiece(kGnuNoteName, sizeof(kGnuNoteName))) {
86           found = true;
87           break;
88         }
89       }
90 
91       size_t section_size = bits::AlignUp(current_note->n_namesz, 4u) +
92                             bits::AlignUp(current_note->n_descsz, 4u) +
93                             sizeof(Nhdr);
94       if (section_size > static_cast<size_t>(section_end - current_section))
95         return 0;
96       current_section += section_size;
97     }
98 
99     if (!found)
100       continue;
101 
102     // Validate that the serialized build ID will fit inside |build_id|.
103     size_t note_size = current_note->n_descsz;
104     if ((note_size * 2) > kMaxBuildIdStringLength)
105       continue;
106 
107     // Write out the build ID as a null-terminated hex string.
108     const uint8_t* build_id_raw =
109         reinterpret_cast<const uint8_t*>(current_note) + sizeof(Nhdr) +
110         bits::AlignUp(current_note->n_namesz, 4u);
111     size_t i = 0;
112     for (i = 0; i < current_note->n_descsz; ++i) {
113       strings::SafeSNPrintf(&build_id[i * 2], 3, (uppercase ? "%02X" : "%02x"),
114                             build_id_raw[i]);
115     }
116     build_id[i * 2] = '\0';
117 
118     // Return the length of the string.
119     return i * 2;
120   }
121 
122   return 0;
123 }
124 
ReadElfLibraryName(const void * elf_mapped_base)125 absl::optional<StringPiece> ReadElfLibraryName(const void* elf_mapped_base) {
126   // NOTE: Function should use async signal safe calls only.
127 
128   const Ehdr* elf_header = GetElfHeader(elf_mapped_base);
129   if (!elf_header)
130     return {};
131 
132   const size_t relocation_offset = GetRelocationOffset(elf_mapped_base);
133   for (const Phdr& header : GetElfProgramHeaders(elf_mapped_base)) {
134     if (header.p_type != PT_DYNAMIC)
135       continue;
136 
137     // Read through the ELF dynamic sections to find the string table and
138     // SONAME offsets, which are used to compute the offset of the library
139     // name string.
140     const Dyn* dynamic_start =
141         reinterpret_cast<const Dyn*>(header.p_vaddr + relocation_offset);
142     const Dyn* dynamic_end = reinterpret_cast<const Dyn*>(
143         header.p_vaddr + relocation_offset + header.p_memsz);
144     Xword soname_strtab_offset = 0;
145     const char* strtab_addr = 0;
146     for (const Dyn* dynamic_iter = dynamic_start; dynamic_iter < dynamic_end;
147          ++dynamic_iter) {
148       if (dynamic_iter->d_tag == DT_STRTAB) {
149 #if BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_ANDROID)
150         // Fuchsia and Android do not relocate the symtab pointer on ELF load.
151         strtab_addr = static_cast<size_t>(dynamic_iter->d_un.d_ptr) +
152                       reinterpret_cast<const char*>(relocation_offset);
153 #else
154         strtab_addr = reinterpret_cast<const char*>(dynamic_iter->d_un.d_ptr);
155 #endif
156       } else if (dynamic_iter->d_tag == DT_SONAME) {
157         // The Android NDK wrongly defines `d_val` as an Elf32_Sword for 32 bits
158         // and thus needs this cast.
159         soname_strtab_offset = static_cast<Xword>(dynamic_iter->d_un.d_val);
160       }
161     }
162     if (soname_strtab_offset && strtab_addr)
163       return StringPiece(strtab_addr + soname_strtab_offset);
164   }
165 
166   return absl::nullopt;
167 }
168 
GetElfProgramHeaders(const void * elf_mapped_base)169 span<const Phdr> GetElfProgramHeaders(const void* elf_mapped_base) {
170   // NOTE: Function should use async signal safe calls only.
171 
172   const Ehdr* elf_header = GetElfHeader(elf_mapped_base);
173   if (!elf_header)
174     return {};
175 
176   const char* phdr_start =
177       reinterpret_cast<const char*>(elf_header) + elf_header->e_phoff;
178   return span<const Phdr>(reinterpret_cast<const Phdr*>(phdr_start),
179                           elf_header->e_phnum);
180 }
181 
182 // Returns the offset to add to virtual addresses in the image to compute the
183 // mapped virtual address.
GetRelocationOffset(const void * elf_mapped_base)184 size_t GetRelocationOffset(const void* elf_mapped_base) {
185   span<const Phdr> headers = GetElfProgramHeaders(elf_mapped_base);
186   for (const Phdr& header : headers) {
187     if (header.p_type == PT_LOAD) {
188       // |elf_mapped_base| + |header.p_offset| is the mapped address of this
189       // segment. |header.p_vaddr| is the specified virtual address within the
190       // ELF image.
191       const char* const mapped_address =
192           reinterpret_cast<const char*>(elf_mapped_base) + header.p_offset;
193       return reinterpret_cast<uintptr_t>(mapped_address) - header.p_vaddr;
194     }
195   }
196 
197   // Assume the virtual addresses in the image start at 0, so the offset is
198   // from 0 to the actual mapped base address.
199   return static_cast<size_t>(reinterpret_cast<uintptr_t>(elf_mapped_base) -
200                              reinterpret_cast<uintptr_t>(nullptr));
201 }
202 
203 }  // namespace debug
204 }  // namespace base
205