Home | History | Annotate | Download | only in debug
      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 
     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 
     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 
     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 
    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