// 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 #include #include #include "base/bits.h" #include "base/containers/span.h" #include "base/sha1.h" #include "base/strings/stringprintf.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; Optional ElfSegmentBuildIDNoteAsString(const ElfSegment& segment) { const void* section_end = segment.data() + segment.size_bytes(); const Nhdr* note_header = reinterpret_cast(segment.data()); while (note_header < section_end) { if (note_header->n_type == NT_GNU_BUILD_ID) break; note_header = reinterpret_cast( reinterpret_cast(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(note_header) + sizeof(Nhdr) + bits::Align(note_header->n_namesz, 4); uint32_t dword = htonl(*reinterpret_cast(guid)); uint16_t word1 = htons(*reinterpret_cast(guid + 4)); uint16_t word2 = htons(*reinterpret_cast(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 FindElfSegments(const void* elf_mapped_base, uint32_t segment_type) { const char* elf_base = reinterpret_cast(elf_mapped_base); if (strncmp(elf_base, ELFMAG, SELFMAG) != 0) return std::vector(); const Ehdr* elf_header = reinterpret_cast(elf_base); const Phdr* phdrs = reinterpret_cast(elf_base + elf_header->e_phoff); std::vector 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 ReadElfBuildId(const void* elf_base) { // Elf program headers can have multiple PT_NOTE arrays. std::vector segs = FindElfSegments(elf_base, PT_NOTE); if (segs.empty()) return nullopt; Optional id; for (const ElfSegment& seg : segs) { id = ElfSegmentBuildIDNoteAsString(seg); if (id) return id; } return nullopt; } Optional ReadElfLibraryName(const void* elf_base) { std::vector 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(dynamic_seg.data()); const Dyn* dynamic_end = reinterpret_cast( dynamic_seg.data() + dynamic_seg.size_bytes()); Optional 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(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