blob: 91b1b3ea819136efbbf5463a8c30215db761bf14 [file] [log] [blame]
/*
* 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_COMPILER_DEBUG_ELF_DEBUG_READER_H_
#define ART_COMPILER_DEBUG_ELF_DEBUG_READER_H_
#include "base/array_ref.h"
#include "debug/dwarf/headers.h"
#include "elf.h"
#include "xz_utils.h"
namespace art {
namespace debug {
// 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.
// The consumed ELF file comes from ART JIT.
template <typename ElfTypes, typename VisitSym, typename VisitFde>
static void ReadElfSymbols(const uint8_t* elf, VisitSym visit_sym, VisitFde visit_fde) {
// Note that the input buffer might be misaligned.
typedef typename ElfTypes::Ehdr ALIGNED(1) Elf_Ehdr;
typedef typename ElfTypes::Shdr ALIGNED(1) Elf_Shdr;
typedef typename ElfTypes::Sym ALIGNED(1) Elf_Sym;
typedef typename ElfTypes::Addr ALIGNED(1) Elf_Addr;
// Read and check the elf header.
const Elf_Ehdr* header = reinterpret_cast<const Elf_Ehdr*>(elf);
CHECK(header->checkMagic());
// Find sections that we are interested in.
const Elf_Shdr* sections = reinterpret_cast<const Elf_Shdr*>(elf + header->e_shoff);
const Elf_Shdr* strtab = nullptr;
const Elf_Shdr* symtab = nullptr;
const Elf_Shdr* debug_frame = nullptr;
const Elf_Shdr* gnu_debugdata = nullptr;
for (size_t i = 1 /* skip null section */; i < header->e_shnum; i++) {
const Elf_Shdr* section = sections + i;
const char* name = reinterpret_cast<const char*>(
elf + sections[header->e_shstrndx].sh_offset + section->sh_name);
if (strcmp(name, ".strtab") == 0) {
strtab = section;
} else if (strcmp(name, ".symtab") == 0) {
symtab = section;
} else if (strcmp(name, ".debug_frame") == 0) {
debug_frame = section;
} else if (strcmp(name, ".gnu_debugdata") == 0) {
gnu_debugdata = section;
}
}
// Visit symbols.
if (symtab != nullptr && strtab != nullptr) {
const Elf_Sym* symbols = reinterpret_cast<const Elf_Sym*>(elf + symtab->sh_offset);
DCHECK_EQ(symtab->sh_entsize, sizeof(Elf_Sym));
size_t count = symtab->sh_size / sizeof(Elf_Sym);
for (size_t i = 1 /* skip null symbol */; i < count; i++) {
Elf_Sym symbol = symbols[i];
if (symbol.getBinding() != STB_LOCAL) { // Ignore local symbols (e.g. "$t").
const uint8_t* name = elf + strtab->sh_offset + symbol.st_name;
visit_sym(symbol, reinterpret_cast<const char*>(name));
}
}
}
// Visit CFI (unwind) data.
if (debug_frame != nullptr) {
const uint8_t* data = elf + debug_frame->sh_offset;
const uint8_t* end = data + debug_frame->sh_size;
while (data < end) {
Elf_Addr addr, size;
ArrayRef<const uint8_t> opcodes;
if (dwarf::ReadFDE<Elf_Addr>(&data, &addr, &size, &opcodes)) {
visit_fde(addr, size, opcodes);
}
}
}
// Process embedded compressed ELF file.
if (gnu_debugdata != nullptr) {
ArrayRef<const uint8_t> compressed(elf + gnu_debugdata->sh_offset, gnu_debugdata->sh_size);
std::vector<uint8_t> decompressed;
XzDecompress(compressed, &decompressed);
ReadElfSymbols<ElfTypes>(decompressed.data(), visit_sym, visit_fde);
}
}
} // namespace debug
} // namespace art
#endif // ART_COMPILER_DEBUG_ELF_DEBUG_READER_H_