| /* |
| * Copyright (C) 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #ifndef ART_LIBELFFILE_ELF_ELF_DEBUG_READER_H_ |
| #define ART_LIBELFFILE_ELF_ELF_DEBUG_READER_H_ |
| |
| #include "base/array_ref.h" |
| #include "dwarf/headers.h" |
| #include "elf/elf_utils.h" |
| #include "xz_utils.h" |
| |
| #include <map> |
| #include <string_view> |
| |
| namespace art { |
| |
| // Trivial ELF file reader. |
| // |
| // It is the bare minimum needed to read mini-debug-info symbols for unwinding. |
| // We use it to merge JIT mini-debug-infos together or to prune them after GC. |
| template <typename ElfTypes> |
| class ElfDebugReader { |
| public: |
| // Note that the input buffer might be misaligned. |
| using Elf_Ehdr ALIGNED(1) = typename ElfTypes::Ehdr; |
| using Elf_Phdr ALIGNED(1) = typename ElfTypes::Phdr; |
| using Elf_Shdr ALIGNED(1) = typename ElfTypes::Shdr; |
| using Elf_Sym ALIGNED(1) = typename ElfTypes::Sym; |
| using Elf_Addr ALIGNED(1) = typename ElfTypes::Addr; |
| |
| // Call Frame Information. |
| struct CFI { |
| uint32_t length; // Length excluding the size of this field. |
| int32_t cie_pointer; // Offset in the section or -1 for CIE. |
| |
| const uint8_t* data() const { return reinterpret_cast<const uint8_t*>(this); } |
| size_t size() const { return sizeof(uint32_t) + length; } |
| } PACKED(1); |
| |
| // Common Information Entry. |
| struct CIE : public CFI { |
| } PACKED(1); |
| |
| // Frame Description Entry. |
| struct FDE : public CFI { |
| Elf_Addr sym_addr; |
| Elf_Addr sym_size; |
| } PACKED(1); |
| |
| explicit ElfDebugReader(ArrayRef<const uint8_t> file) : file_(file) { |
| header_ = Read<Elf_Ehdr>(/*offset=*/ 0); |
| CHECK_EQ(header_->e_ident[0], ELFMAG0); |
| CHECK_EQ(header_->e_ident[1], ELFMAG1); |
| CHECK_EQ(header_->e_ident[2], ELFMAG2); |
| CHECK_EQ(header_->e_ident[3], ELFMAG3); |
| CHECK_EQ(header_->e_ident[4], sizeof(Elf_Addr) / sizeof(uint32_t)); |
| CHECK_EQ(header_->e_ehsize, sizeof(Elf_Ehdr)); |
| |
| // Find all ELF sections. |
| CHECK_EQ(header_->e_shentsize, sizeof(Elf_Shdr)); |
| sections_ = Read<Elf_Shdr>(header_->e_shoff, header_->e_shnum); |
| for (const Elf_Shdr& section : sections_) { |
| const char* name = Read<char>(sections_[header_->e_shstrndx].sh_offset + section.sh_name); |
| section_map_[std::string_view(name)] = §ion; |
| } |
| |
| // Decompressed embedded debug symbols, if any. |
| const Elf_Shdr* gnu_debugdata = section_map_[".gnu_debugdata"]; |
| if (gnu_debugdata != nullptr) { |
| auto compressed = Read<uint8_t>(gnu_debugdata->sh_offset, gnu_debugdata->sh_size); |
| XzDecompress(compressed, &decompressed_gnu_debugdata_); |
| gnu_debugdata_reader_.reset(new ElfDebugReader(decompressed_gnu_debugdata_)); |
| } |
| } |
| |
| explicit ElfDebugReader(const std::vector<uint8_t>& file) |
| : ElfDebugReader(ArrayRef<const uint8_t>(file)) { |
| } |
| |
| // Check that ELF signature is present at the start of the files, |
| // and that the ELF bitness matches the ElfTypes template arguments. |
| static bool IsValidElfHeader(const std::vector<uint8_t>& data) { |
| static constexpr bool kIs64Bit = sizeof(Elf_Addr) == sizeof(uint64_t); |
| static constexpr char kMagic[] = { 0x7f, 'E', 'L', 'F', kIs64Bit ? 2 : 1 }; |
| return data.size() >= sizeof(kMagic) && memcmp(data.data(), kMagic, sizeof(kMagic)) == 0; |
| } |
| |
| const Elf_Ehdr* GetHeader() { return header_; } |
| |
| ArrayRef<Elf_Shdr> GetSections() { return sections_; } |
| |
| const Elf_Shdr* GetSection(const char* name) { return section_map_[name]; } |
| |
| // Find the base address where the ELF file wants to be loaded. |
| // This is generally zero (therefore always requiring relocation). |
| Elf_Addr GetLoadAddress() { |
| std::optional<Elf_Addr> addr; |
| CHECK_EQ(header_->e_phentsize, sizeof(Elf_Phdr)); |
| for (const Elf_Phdr& phdr : Read<Elf_Phdr>(header_->e_phoff, header_->e_phnum)) { |
| if (phdr.p_type == PT_LOAD) { |
| addr = addr.has_value() ? std::min(addr.value(), phdr.p_vaddr) : phdr.p_vaddr; |
| } |
| } |
| CHECK(addr.has_value()); |
| return addr.value(); |
| } |
| |
| template <typename VisitSym> |
| void VisitFunctionSymbols(VisitSym&& visit_sym) { |
| const Elf_Shdr* symtab = GetSection(".symtab"); |
| const Elf_Shdr* strtab = GetSection(".strtab"); |
| const Elf_Shdr* text = GetSection(".text"); |
| if (symtab != nullptr && strtab != nullptr) { |
| CHECK_EQ(symtab->sh_entsize, sizeof(Elf_Sym)); |
| size_t count = symtab->sh_size / sizeof(Elf_Sym); |
| for (const Elf_Sym& symbol : Read<Elf_Sym>(symtab->sh_offset, count)) { |
| if (ELF_ST_TYPE(symbol.st_info) == STT_FUNC && |
| symbol.st_shndx < sections_.size() && // Ignore ABS section. |
| §ions_[symbol.st_shndx] == text) { |
| visit_sym(symbol, Read<char>(strtab->sh_offset + symbol.st_name)); |
| } |
| } |
| } |
| if (gnu_debugdata_reader_ != nullptr) { |
| gnu_debugdata_reader_->VisitFunctionSymbols(std::forward<VisitSym>(visit_sym)); |
| } |
| } |
| |
| template <typename VisitSym> |
| void VisitDynamicSymbols(VisitSym&& visit_sym) { |
| const Elf_Shdr* dynsym = GetSection(".dynsym"); |
| const Elf_Shdr* dynstr = GetSection(".dynstr"); |
| if (dynsym != nullptr && dynstr != nullptr) { |
| CHECK_EQ(dynsym->sh_entsize, sizeof(Elf_Sym)); |
| size_t count = dynsym->sh_size / sizeof(Elf_Sym); |
| for (const Elf_Sym& symbol : Read<Elf_Sym>(dynsym->sh_offset, count)) { |
| visit_sym(symbol, Read<char>(dynstr->sh_offset + symbol.st_name)); |
| } |
| } |
| } |
| |
| template <typename VisitCIE, typename VisitFDE> |
| void VisitDebugFrame(VisitCIE&& visit_cie, VisitFDE&& visit_fde) { |
| const Elf_Shdr* debug_frame = GetSection(".debug_frame"); |
| if (debug_frame != nullptr) { |
| for (size_t offset = 0; offset < debug_frame->sh_size;) { |
| const CFI* entry = Read<CFI>(debug_frame->sh_offset + offset); |
| DCHECK_LE(entry->size(), debug_frame->sh_size - offset); |
| if (entry->cie_pointer == -1) { |
| visit_cie(Read<CIE>(debug_frame->sh_offset + offset)); |
| } else { |
| const FDE* fde = Read<FDE>(debug_frame->sh_offset + offset); |
| visit_fde(fde, Read<CIE>(debug_frame->sh_offset + fde->cie_pointer)); |
| } |
| offset += entry->size(); |
| } |
| } |
| if (gnu_debugdata_reader_ != nullptr) { |
| gnu_debugdata_reader_->VisitDebugFrame(std::forward<VisitCIE>(visit_cie), |
| std::forward<VisitFDE>(visit_fde)); |
| } |
| } |
| |
| private: |
| template<typename T> |
| const T* Read(size_t offset) { |
| DCHECK_LE(offset + sizeof(T), file_.size()); |
| return reinterpret_cast<const T*>(file_.data() + offset); |
| } |
| |
| template<typename T> |
| ArrayRef<const T> Read(size_t offset, size_t count) { |
| DCHECK_LE(offset + count * sizeof(T), file_.size()); |
| return ArrayRef<const T>(Read<T>(offset), count); |
| } |
| |
| ArrayRef<const uint8_t> const file_; |
| const Elf_Ehdr* header_; |
| ArrayRef<const Elf_Shdr> sections_; |
| std::unordered_map<std::string_view, const Elf_Shdr*> section_map_; |
| std::vector<uint8_t> decompressed_gnu_debugdata_; |
| std::unique_ptr<ElfDebugReader> gnu_debugdata_reader_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ElfDebugReader); |
| }; |
| |
| } // namespace art |
| #endif // ART_LIBELFFILE_ELF_ELF_DEBUG_READER_H_ |