| // Copyright 2018 The Chromium Authors | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "base/debug/elf_reader.h" | 
 |  | 
 | #include <arpa/inet.h> | 
 | #include <elf.h> | 
 | #include <string.h> | 
 |  | 
 | #include "base/bits.h" | 
 | #include "base/containers/span.h" | 
 | #include "base/hash/sha1.h" | 
 | #include "base/strings/safe_sprintf.h" | 
 | #include "build/build_config.h" | 
 | #include "third_party/abseil-cpp/absl/types/optional.h" | 
 |  | 
 | // NOTE: This code may be used in crash handling code, so the implementation | 
 | // must avoid dynamic memory allocation or using data structures which rely on | 
 | // dynamic allocation. | 
 |  | 
 | namespace base { | 
 | namespace debug { | 
 | namespace { | 
 |  | 
 | // See https://refspecs.linuxbase.org/elf/elf.pdf for the ELF specification. | 
 |  | 
 | #if __SIZEOF_POINTER__ == 4 | 
 | using Ehdr = Elf32_Ehdr; | 
 | using Dyn = Elf32_Dyn; | 
 | using Half = Elf32_Half; | 
 | using Nhdr = Elf32_Nhdr; | 
 | using Word = Elf32_Word; | 
 | using Xword = Elf32_Word; | 
 | #else | 
 | using Ehdr = Elf64_Ehdr; | 
 | using Dyn = Elf64_Dyn; | 
 | using Half = Elf64_Half; | 
 | using Nhdr = Elf64_Nhdr; | 
 | using Word = Elf64_Word; | 
 | using Xword = Elf64_Xword; | 
 | #endif | 
 |  | 
 | constexpr char kGnuNoteName[] = "GNU"; | 
 |  | 
 | // Returns a pointer to the header of the ELF binary mapped into memory, or a | 
 | // null pointer if the header is invalid. Here and below |elf_mapped_base| is a | 
 | // pointer to the start of the ELF image. | 
 | const Ehdr* GetElfHeader(const void* elf_mapped_base) { | 
 |   if (strncmp(reinterpret_cast<const char*>(elf_mapped_base), ELFMAG, | 
 |               SELFMAG) != 0) | 
 |     return nullptr; | 
 |  | 
 |   return reinterpret_cast<const Ehdr*>(elf_mapped_base); | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | size_t ReadElfBuildId(const void* elf_mapped_base, | 
 |                       bool uppercase, | 
 |                       ElfBuildIdBuffer build_id) { | 
 |   // NOTE: Function should use async signal safe calls only. | 
 |  | 
 |   const Ehdr* elf_header = GetElfHeader(elf_mapped_base); | 
 |   if (!elf_header) | 
 |     return 0; | 
 |  | 
 |   const size_t relocation_offset = GetRelocationOffset(elf_mapped_base); | 
 |   for (const Phdr& header : GetElfProgramHeaders(elf_mapped_base)) { | 
 |     if (header.p_type != PT_NOTE) | 
 |       continue; | 
 |  | 
 |     // Look for a NT_GNU_BUILD_ID note with name == "GNU". | 
 |     const char* current_section = | 
 |         reinterpret_cast<const char*>(header.p_vaddr + relocation_offset); | 
 |     const char* section_end = current_section + header.p_memsz; | 
 |     const Nhdr* current_note = nullptr; | 
 |     bool found = false; | 
 |     while (current_section < section_end) { | 
 |       current_note = reinterpret_cast<const Nhdr*>(current_section); | 
 |       if (current_note->n_type == NT_GNU_BUILD_ID) { | 
 |         StringPiece note_name(current_section + sizeof(Nhdr), | 
 |                               current_note->n_namesz); | 
 |         // Explicit constructor is used to include the '\0' character. | 
 |         if (note_name == StringPiece(kGnuNoteName, sizeof(kGnuNoteName))) { | 
 |           found = true; | 
 |           break; | 
 |         } | 
 |       } | 
 |  | 
 |       size_t section_size = bits::AlignUp(current_note->n_namesz, 4u) + | 
 |                             bits::AlignUp(current_note->n_descsz, 4u) + | 
 |                             sizeof(Nhdr); | 
 |       if (section_size > static_cast<size_t>(section_end - current_section)) | 
 |         return 0; | 
 |       current_section += section_size; | 
 |     } | 
 |  | 
 |     if (!found) | 
 |       continue; | 
 |  | 
 |     // Validate that the serialized build ID will fit inside |build_id|. | 
 |     size_t note_size = current_note->n_descsz; | 
 |     if ((note_size * 2) > kMaxBuildIdStringLength) | 
 |       continue; | 
 |  | 
 |     // Write out the build ID as a null-terminated hex string. | 
 |     const uint8_t* build_id_raw = | 
 |         reinterpret_cast<const uint8_t*>(current_note) + sizeof(Nhdr) + | 
 |         bits::AlignUp(current_note->n_namesz, 4u); | 
 |     size_t i = 0; | 
 |     for (i = 0; i < current_note->n_descsz; ++i) { | 
 |       strings::SafeSNPrintf(&build_id[i * 2], 3, (uppercase ? "%02X" : "%02x"), | 
 |                             build_id_raw[i]); | 
 |     } | 
 |     build_id[i * 2] = '\0'; | 
 |  | 
 |     // Return the length of the string. | 
 |     return i * 2; | 
 |   } | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | absl::optional<StringPiece> ReadElfLibraryName(const void* elf_mapped_base) { | 
 |   // NOTE: Function should use async signal safe calls only. | 
 |  | 
 |   const Ehdr* elf_header = GetElfHeader(elf_mapped_base); | 
 |   if (!elf_header) | 
 |     return {}; | 
 |  | 
 |   const size_t relocation_offset = GetRelocationOffset(elf_mapped_base); | 
 |   for (const Phdr& header : GetElfProgramHeaders(elf_mapped_base)) { | 
 |     if (header.p_type != PT_DYNAMIC) | 
 |       continue; | 
 |  | 
 |     // Read through the ELF dynamic sections to find the string table and | 
 |     // SONAME offsets, which are used to compute the offset of the library | 
 |     // name string. | 
 |     const Dyn* dynamic_start = | 
 |         reinterpret_cast<const Dyn*>(header.p_vaddr + relocation_offset); | 
 |     const Dyn* dynamic_end = reinterpret_cast<const Dyn*>( | 
 |         header.p_vaddr + relocation_offset + header.p_memsz); | 
 |     Xword soname_strtab_offset = 0; | 
 |     const char* strtab_addr = 0; | 
 |     for (const Dyn* dynamic_iter = dynamic_start; dynamic_iter < dynamic_end; | 
 |          ++dynamic_iter) { | 
 |       if (dynamic_iter->d_tag == DT_STRTAB) { | 
 | #if BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_ANDROID) | 
 |         // Fuchsia and Android do not relocate the symtab pointer on ELF load. | 
 |         strtab_addr = static_cast<size_t>(dynamic_iter->d_un.d_ptr) + | 
 |                       reinterpret_cast<const char*>(relocation_offset); | 
 | #else | 
 |         strtab_addr = reinterpret_cast<const char*>(dynamic_iter->d_un.d_ptr); | 
 | #endif | 
 |       } else if (dynamic_iter->d_tag == DT_SONAME) { | 
 |         // The Android NDK wrongly defines `d_val` as an Elf32_Sword for 32 bits | 
 |         // and thus needs this cast. | 
 |         soname_strtab_offset = static_cast<Xword>(dynamic_iter->d_un.d_val); | 
 |       } | 
 |     } | 
 |     if (soname_strtab_offset && strtab_addr) | 
 |       return StringPiece(strtab_addr + soname_strtab_offset); | 
 |   } | 
 |  | 
 |   return absl::nullopt; | 
 | } | 
 |  | 
 | span<const Phdr> GetElfProgramHeaders(const void* elf_mapped_base) { | 
 |   // NOTE: Function should use async signal safe calls only. | 
 |  | 
 |   const Ehdr* elf_header = GetElfHeader(elf_mapped_base); | 
 |   if (!elf_header) | 
 |     return {}; | 
 |  | 
 |   const char* phdr_start = | 
 |       reinterpret_cast<const char*>(elf_header) + elf_header->e_phoff; | 
 |   return span<const Phdr>(reinterpret_cast<const Phdr*>(phdr_start), | 
 |                           elf_header->e_phnum); | 
 | } | 
 |  | 
 | // Returns the offset to add to virtual addresses in the image to compute the | 
 | // mapped virtual address. | 
 | size_t GetRelocationOffset(const void* elf_mapped_base) { | 
 |   span<const Phdr> headers = GetElfProgramHeaders(elf_mapped_base); | 
 |   for (const Phdr& header : headers) { | 
 |     if (header.p_type == PT_LOAD) { | 
 |       // |elf_mapped_base| + |header.p_offset| is the mapped address of this | 
 |       // segment. |header.p_vaddr| is the specified virtual address within the | 
 |       // ELF image. | 
 |       const char* const mapped_address = | 
 |           reinterpret_cast<const char*>(elf_mapped_base) + header.p_offset; | 
 |       return reinterpret_cast<uintptr_t>(mapped_address) - header.p_vaddr; | 
 |     } | 
 |   } | 
 |  | 
 |   // Assume the virtual addresses in the image start at 0, so the offset is | 
 |   // from 0 to the actual mapped base address. | 
 |   return static_cast<size_t>(reinterpret_cast<uintptr_t>(elf_mapped_base) - | 
 |                              reinterpret_cast<uintptr_t>(nullptr)); | 
 | } | 
 |  | 
 | }  // namespace debug | 
 | }  // namespace base |