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