blob: cb71e43eebb395b81d5ad2d359580d0c531afb32 [file] [log] [blame]
// Copyright 2017 The Fuchsia 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 "elf_symtab.h"
#include <lib/syslog/cpp/macros.h>
namespace debugger_utils {
ElfSymbolTable::ElfSymbolTable(const std::string& file_name, const std::string& contents)
: file_name_(file_name), contents_(contents) {}
ElfSymbolTable::~ElfSymbolTable() {
if (symbols_)
delete[] symbols_;
}
bool ElfSymbolTable::Populate(ElfReader* elf, unsigned symtab_type) {
FX_DCHECK(symtab_type == SHT_SYMTAB || symtab_type == SHT_DYNSYM);
// TODO(dje): Add support for loading both SHT_SYMTAB and SHT_DYNSYM.
if (symbols_) {
FX_LOGS(ERROR) << "Already populated";
return false;
}
ElfError rc = elf->ReadSectionHeaders();
if (rc != ElfError::OK) {
FX_LOGS(ERROR) << "Error reading ELF section headers: " << ElfErrorName(rc);
return false;
}
const ElfSectionHeader* shdr = elf->GetSectionHeaderByType(symtab_type);
if (!shdr)
return true; // empty symbol table
size_t num_sections = elf->GetNumSections();
size_t string_section = shdr->sh_link;
if (string_section >= num_sections) {
FX_LOGS(ERROR) << "Bad string section: " << string_section;
return false;
}
const ElfSectionHeader& str_shdr = elf->GetSectionHeader(string_section);
std::unique_ptr<ElfSectionContents> contents;
rc = elf->GetSectionContents(*shdr, &contents);
if (rc != ElfError::OK) {
FX_LOGS(ERROR) << "Error reading ELF section: " << ElfErrorName(rc);
return false;
}
rc = elf->GetSectionContents(str_shdr, &string_section_);
if (rc != ElfError::OK) {
FX_LOGS(ERROR) << "Error reading ELF string section: " << ElfErrorName(rc);
return false;
}
auto strings = reinterpret_cast<const char*>(string_section_->contents());
size_t max_string_offset = string_section_->GetSize();
size_t num_raw_symbols = contents->GetNumEntries();
symbols_ = new ElfSymbol[num_raw_symbols];
size_t num_symbols = 0;
for (size_t i = 0; i < num_raw_symbols; ++i) {
const ElfRawSymbol& sym = contents->GetSymbolEntry(i);
if (sym.st_name >= max_string_offset) {
FX_LOGS(ERROR) << "Bad symbol string name offset: " << sym.st_name;
continue;
}
ElfSymbol* s = &symbols_[num_symbols++];
// TODO(dje): IWBN to have a convenience function for getting symbol
// names, not sure what it will look like yet.
s->name = strings + sym.st_name;
s->addr = sym.st_value;
s->size = sym.st_size;
}
num_symbols_ = num_symbols;
Finalize();
return true;
}
static int CompareSymbol(const void* ap, const void* bp) {
auto a = reinterpret_cast<const ElfSymbol*>(ap);
auto b = reinterpret_cast<const ElfSymbol*>(bp);
if (a->addr >= b->addr && a->addr < b->addr + b->size)
return 0;
if (b->addr >= a->addr && b->addr < a->addr + a->size)
return 0;
return a->addr - b->addr;
}
void ElfSymbolTable::Finalize() { qsort(symbols_, num_symbols_, sizeof(ElfSymbol), CompareSymbol); }
const ElfSymbol* ElfSymbolTable::FindSymbol(uint64_t addr) const {
ElfSymbol search = {.addr = addr};
/* add last hit cache here */
auto s = reinterpret_cast<const ElfSymbol*>(
bsearch(&search, symbols_, num_symbols_, sizeof(ElfSymbol), CompareSymbol));
return s;
}
void ElfSymbolTable::Dump(FILE* f) const {
fprintf(f, "file: %s\n", file_name_.c_str());
fprintf(f, "contents: %s\n", contents_.c_str());
for (size_t i = 0; i < num_symbols_; i++) {
ElfSymbol* s = &symbols_[i];
if (s->addr && s->name[0])
fprintf(f, "%p %s\n", (void*)s->addr, s->name);
}
}
} // namespace debugger_utils