blob: 2565ea8f2c53803211a14ac58bc840e8e496bc66 [file] [log] [blame]
/*
* Copyright (c) 2015, Intel Corporation
* Author: Andi Kleen
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "elf.h"
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <lib/syslog/cpp/macros.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "garnet/lib/debugger_utils/byte_block_file.h"
#include "garnet/lib/debugger_utils/elf_reader.h"
#include "garnet/lib/debugger_utils/util.h"
#include "third_party/processor-trace/libipt/include/intel-pt.h"
namespace simple_pt {
// Read in the symbol table(s) of |elf|.
// |base| is the address at which the file was loaded, and |len|
// is the length of the text segment.
// |offset| is the different between where the file was actually
// loaded (|base|) and address recorded in the file.
static bool ReadSymtabs(debugger_utils::ElfReader* elf, uint64_t cr3, uint64_t base, uint64_t len,
uint64_t offset, bool is_kernel, std::unique_ptr<SymbolTable>* out_symtab,
std::unique_ptr<SymbolTable>* out_dynsym) {
size_t num_sections = elf->GetNumSections();
debugger_utils::ElfError rc = elf->ReadSectionHeaders();
if (rc != debugger_utils::ElfError::OK) {
FX_LOGS(ERROR) << "Error reading ELF section headers: " << ElfErrorName(rc);
return false;
}
std::unique_ptr<SymbolTable> symtab;
std::unique_ptr<SymbolTable> dynsym;
for (size_t i = 0; i < num_sections; ++i) {
const debugger_utils::ElfSectionHeader& shdr = elf->GetSectionHeader(i);
SymbolTable* st;
switch (shdr.sh_type) {
case SHT_SYMTAB:
symtab.reset(new SymbolTable(elf, "symtab", cr3, base, offset, is_kernel));
st = symtab.get();
break;
case SHT_DYNSYM:
dynsym.reset(new SymbolTable(elf, "dynsym", cr3, base, offset, is_kernel));
st = dynsym.get();
break;
default:
continue;
}
if (!st->Populate(elf, shdr.sh_type))
return false;
// Compute the last address used by symbols in the table.
size_t num_symbols = st->num_symbols();
uint64_t end = 0;
for (size_t j = 0; j < num_symbols; j++) {
const debugger_utils::ElfSymbol& sym = st->GetSymbol(j);
if (end < sym.addr + sym.size)
end = sym.addr + sym.size;
}
// Assign the full range of symbols to the symtab so that even if a symbol
// isn't found, we still know the pc came from this file.
// Other segments technically needn't be contiguous, which one would have
// to deal with to handle more than just the (assumed) one text segment.
if (len > end)
st->set_end(offset + len);
else
st->set_end(offset + end);
}
if (!symtab && !dynsym)
FX_LOGS(WARNING) << elf->file_name() << " has no symbols";
*out_symtab = std::move(symtab);
*out_dynsym = std::move(dynsym);
return true;
}
// Find the base address, length, and file offset of the text segment
// of a non-PIC ELF.
static void FindBaseLenFileoff(debugger_utils::ElfReader* elf, uint64_t* base, uint64_t* len,
uint64_t* fileoff) {
size_t num_segments = elf->GetNumSegments();
for (size_t i = 0; i < num_segments; ++i) {
const debugger_utils::ElfSegmentHeader& phdr = elf->GetSegmentHeader(i);
if (phdr.p_type == PT_LOAD && (phdr.p_flags & PF_X) != 0) {
*base = phdr.p_vaddr;
*len = phdr.p_memsz;
*fileoff = phdr.p_offset;
return;
}
}
}
// Given a potential PIC ELF loaded at |base|, compute the offset from where
// the file says segments are loaded to where they were actually loaded.
static void FindOffset(debugger_utils::ElfReader* elf, uint64_t base, uint64_t* offset) {
uint64_t minaddr = UINT64_MAX;
if (!base) {
*offset = 0;
return;
}
size_t num_segments = elf->GetNumSegments();
for (size_t i = 0; i < num_segments; ++i) {
const debugger_utils::ElfSegmentHeader& phdr = elf->GetSegmentHeader(i);
if (phdr.p_type == PT_LOAD && phdr.p_vaddr < minaddr) {
minaddr = phdr.p_vaddr;
}
}
// Punt if no loadable segments found.
if (minaddr == UINT64_MAX) {
*offset = 0;
return;
}
*offset = base - minaddr;
}
static void AddProgbits(debugger_utils::ElfReader* elf, struct pt_image* image,
const char* file_name, uint64_t base, uint64_t cr3, uint64_t offset,
uint64_t file_off, uint64_t map_len) {
size_t num_segments = elf->GetNumSegments();
for (size_t i = 0; i < num_segments; ++i) {
const debugger_utils::ElfSegmentHeader& phdr = elf->GetSegmentHeader(i);
if ((phdr.p_type == PT_LOAD) && (phdr.p_flags & PF_X) && phdr.p_offset >= file_off &&
(!map_len || phdr.p_offset + phdr.p_filesz <= file_off + map_len)) {
struct pt_asid asid;
int err;
/* The first loadable section in zircon.elf is
unusable to us. Plus we want to ignore it here.
This test is an attempt to not be too zircon specific. */
if (phdr.p_vaddr < phdr.p_paddr)
continue;
pt_asid_init(&asid);
asid.cr3 = cr3;
errno = 0;
err = pt_image_add_file(image, file_name, phdr.p_offset, phdr.p_filesz, &asid,
phdr.p_vaddr + offset);
/* Duplicate. Just ignore. */
if (err == -pte_bad_image)
continue;
if (err < 0) {
fprintf(stderr, "reading prog code at %" PRIx64 ":%" PRIx64 " from %s: %s (%s): %d\n",
phdr.p_vaddr, phdr.p_filesz, file_name, pt_errstr(pt_errcode(err)),
errno ? strerror(errno) : "", err);
return;
}
}
}
}
static bool ElfOpen(const char* file_name, std::unique_ptr<debugger_utils::ElfReader>* out_elf) {
int fd = open(file_name, O_RDONLY);
if (fd < 0) {
FX_LOGS(ERROR) << file_name << ", " << debugger_utils::ErrnoString(errno);
return false;
}
auto bb = std::shared_ptr<debugger_utils::FileByteBlock>(new debugger_utils::FileByteBlock(fd));
std::unique_ptr<debugger_utils::ElfReader> elf;
debugger_utils::ElfError rc = debugger_utils::ElfReader::Create(file_name, bb, 0, 0, &elf);
if (rc != debugger_utils::ElfError::OK) {
FX_LOGS(ERROR) << "Error creating ELF reader: " << debugger_utils::ElfErrorName(rc);
return false;
}
rc = elf->ReadSegmentHeaders();
if (rc != debugger_utils::ElfError::OK) {
FX_LOGS(ERROR) << "Error reading ELF segment headers: " << ElfErrorName(rc);
return false;
}
*out_elf = std::move(elf);
return true;
}
bool ReadElf(const char* file_name, struct pt_image* image, uint64_t base, uint64_t cr3,
uint64_t file_off, uint64_t map_len, std::unique_ptr<SymbolTable>* out_symtab,
std::unique_ptr<SymbolTable>* out_dynsym) {
std::unique_ptr<debugger_utils::ElfReader> elf;
if (!ElfOpen(file_name, &elf))
return false;
bool pic = false;
const debugger_utils::ElfHeader& hdr = elf->header();
pic = hdr.e_type == ET_DYN;
if (pic && base == 0) {
FX_LOGS(ERROR) << "PIC/PIE ELF with base 0 is not supported";
return false;
}
uint64_t offset = 0;
if (pic)
FindOffset(elf.get(), base, &offset);
if (!ReadSymtabs(elf.get(), cr3, base, map_len, offset, false, out_symtab, out_dynsym))
return false;
AddProgbits(elf.get(), image, file_name, base, cr3, offset, file_off, map_len);
return true;
}
bool ReadNonPicElf(const char* file_name, pt_image* image, uint64_t cr3, bool is_kernel,
std::unique_ptr<SymbolTable>* out_symtab,
std::unique_ptr<SymbolTable>* out_dynsym) {
std::unique_ptr<debugger_utils::ElfReader> elf;
if (!ElfOpen(file_name, &elf))
return false;
// TODO(dje): kernel pc values can appear in traces with userspace cr3
// values, e.g., when performing a syscall. For now, ignore cr3 for
// kernel pcs. The original value of zero was odd anyway.
uint64_t kernel_cr3_for_symtab = pt_asid_no_cr3;
// TODO(dje): Need to handle PIE kernels.
uint64_t base = 0, len = 0;
uint64_t offset = 0, file_off = 0;
FindBaseLenFileoff(elf.get(), &base, &len, &file_off);
if (!ReadSymtabs(elf.get(), kernel_cr3_for_symtab, base, len, offset, is_kernel, out_symtab,
out_dynsym))
return false;
AddProgbits(elf.get(), image, file_name, base, cr3 ? cr3 : pt_asid_no_cr3, offset, file_off, len);
return true;
}
} // namespace simple_pt