| // Copyright 2018 The Chromium Authors. All rights reserved. |
| // 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_linux.h" |
| |
| #include <arpa/inet.h> |
| #include <elf.h> |
| |
| #include <vector> |
| |
| #include "base/bits.h" |
| #include "base/containers/span.h" |
| #include "base/sha1.h" |
| #include "base/strings/stringprintf.h" |
| #include "starboard/types.h" |
| |
| namespace base { |
| namespace debug { |
| |
| namespace { |
| |
| #if __SIZEOF_POINTER__ == 4 |
| using Ehdr = Elf32_Ehdr; |
| using Dyn = Elf32_Dyn; |
| using Half = Elf32_Half; |
| using Nhdr = Elf32_Nhdr; |
| using Phdr = Elf32_Phdr; |
| using Word = Elf32_Word; |
| #else |
| using Ehdr = Elf64_Ehdr; |
| using Dyn = Elf64_Dyn; |
| using Half = Elf64_Half; |
| using Nhdr = Elf64_Nhdr; |
| using Phdr = Elf64_Phdr; |
| using Word = Elf64_Word; |
| #endif |
| |
| using ElfSegment = span<const char>; |
| |
| Optional<std::string> ElfSegmentBuildIDNoteAsString(const ElfSegment& segment) { |
| const void* section_end = segment.data() + segment.size_bytes(); |
| const Nhdr* note_header = reinterpret_cast<const Nhdr*>(segment.data()); |
| while (note_header < section_end) { |
| if (note_header->n_type == NT_GNU_BUILD_ID) |
| break; |
| note_header = reinterpret_cast<const Nhdr*>( |
| reinterpret_cast<const char*>(note_header) + sizeof(Nhdr) + |
| bits::Align(note_header->n_namesz, 4) + |
| bits::Align(note_header->n_descsz, 4)); |
| } |
| |
| if (note_header >= section_end || note_header->n_descsz != kSHA1Length) |
| return nullopt; |
| |
| const uint8_t* guid = reinterpret_cast<const uint8_t*>(note_header) + |
| sizeof(Nhdr) + bits::Align(note_header->n_namesz, 4); |
| |
| uint32_t dword = htonl(*reinterpret_cast<const int32_t*>(guid)); |
| uint16_t word1 = htons(*reinterpret_cast<const int16_t*>(guid + 4)); |
| uint16_t word2 = htons(*reinterpret_cast<const int16_t*>(guid + 6)); |
| std::string identifier; |
| identifier.reserve(kSHA1Length * 2); // as hex string |
| SStringPrintf(&identifier, "%08X%04X%04X", dword, word1, word2); |
| for (size_t i = 8; i < note_header->n_descsz; ++i) |
| StringAppendF(&identifier, "%02X", guid[i]); |
| |
| return identifier; |
| } |
| |
| std::vector<ElfSegment> FindElfSegments(const void* elf_mapped_base, |
| uint32_t segment_type) { |
| const char* elf_base = reinterpret_cast<const char*>(elf_mapped_base); |
| if (strncmp(elf_base, ELFMAG, SELFMAG) != 0) |
| return std::vector<ElfSegment>(); |
| |
| const Ehdr* elf_header = reinterpret_cast<const Ehdr*>(elf_base); |
| const Phdr* phdrs = |
| reinterpret_cast<const Phdr*>(elf_base + elf_header->e_phoff); |
| std::vector<ElfSegment> segments; |
| for (Half i = 0; i < elf_header->e_phnum; ++i) { |
| if (phdrs[i].p_type == segment_type) |
| segments.push_back({elf_base + phdrs[i].p_offset, phdrs[i].p_filesz}); |
| } |
| return segments; |
| } |
| |
| } // namespace |
| |
| Optional<std::string> ReadElfBuildId(const void* elf_base) { |
| // Elf program headers can have multiple PT_NOTE arrays. |
| std::vector<ElfSegment> segs = FindElfSegments(elf_base, PT_NOTE); |
| if (segs.empty()) |
| return nullopt; |
| Optional<std::string> id; |
| for (const ElfSegment& seg : segs) { |
| id = ElfSegmentBuildIDNoteAsString(seg); |
| if (id) |
| return id; |
| } |
| |
| return nullopt; |
| } |
| |
| Optional<std::string> ReadElfLibraryName(const void* elf_base) { |
| std::vector<ElfSegment> segs = FindElfSegments(elf_base, PT_DYNAMIC); |
| if (segs.empty()) |
| return nullopt; |
| DCHECK_EQ(1u, segs.size()); |
| |
| const ElfSegment& dynamic_seg = segs.front(); |
| const Dyn* dynamic_start = reinterpret_cast<const Dyn*>(dynamic_seg.data()); |
| const Dyn* dynamic_end = reinterpret_cast<const Dyn*>( |
| dynamic_seg.data() + dynamic_seg.size_bytes()); |
| Optional<std::string> soname; |
| Word 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) { |
| strtab_addr = |
| dynamic_iter->d_un.d_ptr + reinterpret_cast<const char*>(elf_base); |
| } else if (dynamic_iter->d_tag == DT_SONAME) { |
| soname_strtab_offset = dynamic_iter->d_un.d_val; |
| } |
| } |
| if (soname_strtab_offset && strtab_addr) |
| return std::string(strtab_addr + soname_strtab_offset); |
| return nullopt; |
| } |
| |
| } // namespace debug |
| } // namespace base |