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