| // 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/fxl/logging.h" |
| |
| namespace debugserver { |
| namespace elf { |
| |
| SymbolTable::SymbolTable(const std::string& file_name, |
| const std::string& contents) |
| : file_name_(file_name), contents_(contents) {} |
| |
| SymbolTable::~SymbolTable() { |
| if (symbols_) |
| delete[] symbols_; |
| } |
| |
| bool SymbolTable::Populate(elf::Reader* elf, unsigned symtab_type) { |
| FXL_DCHECK(symtab_type == SHT_SYMTAB || symtab_type == SHT_DYNSYM); |
| |
| // TODO(dje): Add support for loading both SHT_SYMTAB and SHT_DYNSYM. |
| if (symbols_) { |
| FXL_LOG(ERROR) << "Already populated"; |
| return false; |
| } |
| |
| elf::Error rc = elf->ReadSectionHeaders(); |
| if (rc != Error::OK) { |
| FXL_LOG(ERROR) << "Error reading ELF section headers: " << ErrorName(rc); |
| return false; |
| } |
| |
| const SectionHeader* 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) { |
| FXL_LOG(ERROR) << "Bad string section: " << string_section; |
| return false; |
| } |
| const SectionHeader& str_shdr = elf->GetSectionHeader(string_section); |
| |
| std::unique_ptr<SectionContents> contents; |
| rc = elf->GetSectionContents(*shdr, &contents); |
| if (rc != Error::OK) { |
| FXL_LOG(ERROR) << "Error reading ELF section: " << ErrorName(rc); |
| return false; |
| } |
| |
| rc = elf->GetSectionContents(str_shdr, &string_section_); |
| if (rc != Error::OK) { |
| FXL_LOG(ERROR) << "Error reading ELF string section: " << ErrorName(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 Symbol[num_raw_symbols]; |
| |
| size_t num_symbols = 0; |
| for (size_t i = 0; i < num_raw_symbols; ++i) { |
| const RawSymbol& sym = contents->GetSymbolEntry(i); |
| if (sym.st_name >= max_string_offset) { |
| FXL_LOG(ERROR) << "Bad symbol string name offset: " << sym.st_name; |
| continue; |
| } |
| Symbol* 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 Symbol*>(ap); |
| auto b = reinterpret_cast<const Symbol*>(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 SymbolTable::Finalize() { |
| qsort(symbols_, num_symbols_, sizeof(Symbol), CompareSymbol); |
| } |
| |
| const Symbol* SymbolTable::FindSymbol(uint64_t addr) const { |
| Symbol search = {.addr = addr}; |
| |
| /* add last hit cache here */ |
| |
| auto s = reinterpret_cast<const Symbol*>( |
| bsearch(&search, symbols_, num_symbols_, sizeof(Symbol), CompareSymbol)); |
| return s; |
| } |
| |
| void SymbolTable::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++) { |
| Symbol* s = &symbols_[i]; |
| if (s->addr && s->name[0]) |
| fprintf(f, "%p %s\n", (void*)s->addr, s->name); |
| } |
| } |
| |
| } // namespace elf |
| } // namespace debugserver |