Source code

Revision control

Copy as Markdown

Other Tools

// Copyright 2005 Google Inc. All Rights Reserved.
// Author: chatham@google.com (Andrew Chatham)
// Author: satorux@google.com (Satoru Takabayashi)
//
// Code for reading in ELF files.
//
// For information on the ELF format, see
//
// I also liked:
//
// A note about types: When dealing with the file format, we use types
// like Elf32_Word, but in the public interfaces we treat all
// addresses as uint64. As a result, we should be able to symbolize
// 64-bit binaries from a 32-bit process (which we don't do,
// anyway). size_t should therefore be avoided, except where required
// by things like mmap().
//
// Although most of this code can deal with arbitrary ELF files of
// either word size, the public ElfReader interface only examines
// files loaded into the current address space, which must all match
// the machine's native word size. This code cannot handle ELF files
// with a non-native byte ordering.
//
// TODO(chatham): It would be nice if we could accomplish this task
// without using malloc(), so we could use it as the process is dying.
#ifndef _GNU_SOURCE
#define _GNU_SOURCE // needed for pread()
#endif
#include <fcntl.h>
#include <limits.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <algorithm>
#include <map>
#include <string>
#include <vector>
// TODO(saugustine): Add support for compressed debug.
// Also need to add configure tests for zlib.
//#include "zlib.h"
#include "third_party/musl/include/elf.h"
#include "elf_reader.h"
#include "common/using_std_string.h"
// EM_AARCH64 is not defined by elf.h of GRTE v3 on x86.
// TODO(dougkwan): Remove this when v17 is retired.
#if !defined(EM_AARCH64)
#define EM_AARCH64 183 /* ARM AARCH64 */
#endif
// Map Linux macros to their Apple equivalents.
#if __APPLE__ || __FreeBSD__
#ifndef __LITTLE_ENDIAN
#define __LITTLE_ENDIAN __ORDER_LITTLE_ENDIAN__
#endif // __LITTLE_ENDIAN
#ifndef __BIG_ENDIAN
#define __BIG_ENDIAN __ORDER_BIG_ENDIAN__
#endif // __BIG_ENDIAN
#ifndef __BYTE_ORDER
#define __BYTE_ORDER __BYTE_ORDER__
#endif // __BYTE_ORDER
#endif // __APPLE__
// TODO(dthomson): Can be removed once all Java code is using the Google3
// launcher. We need to avoid processing PLT functions as it causes memory
// fragmentation in malloc, which is fixed in tcmalloc - and if the Google3
// launcher is used the JVM will then use tcmalloc. b/13735638
//DEFINE_bool(elfreader_process_dynsyms, true,
// "Activate PLT function processing");
using std::vector;
namespace {
// The lowest bit of an ARM symbol value is used to indicate a Thumb address.
const int kARMThumbBitOffset = 0;
// Converts an ARM Thumb symbol value to a true aligned address value.
template <typename T>
T AdjustARMThumbSymbolValue(const T& symbol_table_value) {
return symbol_table_value & ~(1 << kARMThumbBitOffset);
}
// Names of PLT-related sections.
const char kElfPLTRelSectionName[] = ".rel.plt"; // Use Rel struct.
const char kElfPLTRelaSectionName[] = ".rela.plt"; // Use Rela struct.
const char kElfPLTSectionName[] = ".plt";
const char kElfDynSymSectionName[] = ".dynsym";
const int kX86PLTCodeSize = 0x10; // Size of one x86 PLT function in bytes.
const int kARMPLTCodeSize = 0xc;
const int kAARCH64PLTCodeSize = 0x10;
const int kX86PLT0Size = 0x10; // Size of the special PLT0 entry.
const int kARMPLT0Size = 0x14;
const int kAARCH64PLT0Size = 0x20;
// Suffix for PLT functions when it needs to be explicitly identified as such.
const char kPLTFunctionSuffix[] = "@plt";
} // namespace
namespace dwarf2reader {
template <class ElfArch> class ElfReaderImpl;
// 32-bit and 64-bit ELF files are processed exactly the same, except
// for various field sizes. Elf32 and Elf64 encompass all of the
// differences between the two formats, and all format-specific code
// in this file is templated on one of them.
class Elf32 {
public:
typedef Elf32_Ehdr Ehdr;
typedef Elf32_Shdr Shdr;
typedef Elf32_Phdr Phdr;
typedef Elf32_Word Word;
typedef Elf32_Sym Sym;
typedef Elf32_Rel Rel;
typedef Elf32_Rela Rela;
// What should be in the EI_CLASS header.
static const int kElfClass = ELFCLASS32;
// Given a symbol pointer, return the binding type (eg STB_WEAK).
static char Bind(const Elf32_Sym *sym) {
return ELF32_ST_BIND(sym->st_info);
}
// Given a symbol pointer, return the symbol type (eg STT_FUNC).
static char Type(const Elf32_Sym *sym) {
return ELF32_ST_TYPE(sym->st_info);
}
// Extract the symbol index from the r_info field of a relocation.
static int r_sym(const Elf32_Word r_info) {
return ELF32_R_SYM(r_info);
}
};
class Elf64 {
public:
typedef Elf64_Ehdr Ehdr;
typedef Elf64_Shdr Shdr;
typedef Elf64_Phdr Phdr;
typedef Elf64_Word Word;
typedef Elf64_Sym Sym;
typedef Elf64_Rel Rel;
typedef Elf64_Rela Rela;
// What should be in the EI_CLASS header.
static const int kElfClass = ELFCLASS64;
static char Bind(const Elf64_Sym *sym) {
return ELF64_ST_BIND(sym->st_info);
}
static char Type(const Elf64_Sym *sym) {
return ELF64_ST_TYPE(sym->st_info);
}
static int r_sym(const Elf64_Xword r_info) {
return ELF64_R_SYM(r_info);
}
};
// ElfSectionReader mmaps a section of an ELF file ("section" is ELF
// terminology). The ElfReaderImpl object providing the section header
// must exist for the lifetime of this object.
//
// The motivation for mmaping individual sections of the file is that
// many Google executables are large enough when unstripped that we
// have to worry about running out of virtual address space.
//
// For compressed sections we have no choice but to allocate memory.
template<class ElfArch>
class ElfSectionReader {
public:
ElfSectionReader(const char *name, const string &path, int fd,
const typename ElfArch::Shdr &section_header)
: contents_aligned_(NULL),
contents_(NULL),
header_(section_header) {
// Back up to the beginning of the page we're interested in.
const size_t additional = header_.sh_offset % getpagesize();
const size_t offset_aligned = header_.sh_offset - additional;
section_size_ = header_.sh_size;
size_aligned_ = section_size_ + additional;
// If the section has been stripped or is empty, do not attempt
// to process its contents.
if (header_.sh_type == SHT_NOBITS || header_.sh_size == 0)
return;
contents_aligned_ = mmap(NULL, size_aligned_, PROT_READ, MAP_SHARED,
fd, offset_aligned);
// Set where the offset really should begin.
contents_ = reinterpret_cast<char *>(contents_aligned_) +
(header_.sh_offset - offset_aligned);
// Check for and handle any compressed contents.
//if (strncmp(name, ".zdebug_", strlen(".zdebug_")) == 0)
// DecompressZlibContents();
// TODO(saugustine): Add support for proposed elf-section flag
// "SHF_COMPRESS".
}
~ElfSectionReader() {
if (contents_aligned_ != NULL)
munmap(contents_aligned_, size_aligned_);
else
delete[] contents_;
}
// Return the section header for this section.
typename ElfArch::Shdr const &header() const { return header_; }
// Return memory at the given offset within this section.
const char *GetOffset(typename ElfArch::Word bytes) const {
return contents_ + bytes;
}
const char *contents() const { return contents_; }
size_t section_size() const { return section_size_; }
private:
// page-aligned file contents
void *contents_aligned_;
// contents as usable by the client. For non-compressed sections,
// pointer within contents_aligned_ to where the section data
// begins; for compressed sections, pointer to the decompressed
// data.
char *contents_;
// size of contents_aligned_
size_t size_aligned_;
// size of contents.
size_t section_size_;
const typename ElfArch::Shdr header_;
};
// An iterator over symbols in a given section. It handles walking
// through the entries in the specified section and mapping symbol
// entries to their names in the appropriate string table (in
// another section).
template<class ElfArch>
class SymbolIterator {
public:
SymbolIterator(ElfReaderImpl<ElfArch> *reader,
typename ElfArch::Word section_type)
: symbol_section_(reader->GetSectionByType(section_type)),
string_section_(NULL),
num_symbols_in_section_(0),
symbol_within_section_(0) {
// If this section type doesn't exist, leave
// num_symbols_in_section_ as zero, so this iterator is already
// done().
if (symbol_section_ != NULL) {
num_symbols_in_section_ = symbol_section_->header().sh_size /
symbol_section_->header().sh_entsize;
// Symbol sections have sh_link set to the section number of
// the string section containing the symbol names.
string_section_ = reader->GetSection(symbol_section_->header().sh_link);
}
}
// Return true iff we have passed all symbols in this section.
bool done() const {
return symbol_within_section_ >= num_symbols_in_section_;
}
// Advance to the next symbol in this section.
// REQUIRES: !done()
void Next() { ++symbol_within_section_; }
// Return a pointer to the current symbol.
// REQUIRES: !done()
const typename ElfArch::Sym *GetSymbol() const {
return reinterpret_cast<const typename ElfArch::Sym*>(
symbol_section_->GetOffset(symbol_within_section_ *
symbol_section_->header().sh_entsize));
}
// Return the name of the current symbol, NULL if it has none.
// REQUIRES: !done()
const char *GetSymbolName() const {
int name_offset = GetSymbol()->st_name;
if (name_offset == 0)
return NULL;
return string_section_->GetOffset(name_offset);
}
int GetCurrentSymbolIndex() const {
return symbol_within_section_;
}
private:
const ElfSectionReader<ElfArch> *const symbol_section_;
const ElfSectionReader<ElfArch> *string_section_;
int num_symbols_in_section_;
int symbol_within_section_;
};
// Copied from strings/strutil.h. Per chatham,
// this library should not depend on strings.
static inline bool MyHasSuffixString(const string& str, const string& suffix) {
int len = str.length();
int suflen = suffix.length();
return (suflen <= len) && (str.compare(len-suflen, suflen, suffix) == 0);
}
// ElfReader loads an ELF binary and can provide information about its
// contents. It is most useful for matching addresses to function
// names. It does not understand debugging formats (eg dwarf2), so it
// can't print line numbers. It takes a path to an elf file and a
// readable file descriptor for that file, which it does not assume
// ownership of.
template<class ElfArch>
class ElfReaderImpl {
public:
explicit ElfReaderImpl(const string &path, int fd)
: path_(path),
fd_(fd),
section_headers_(NULL),
program_headers_(NULL),
opd_section_(NULL),
base_for_text_(0),
plts_supported_(false),
plt_code_size_(0),
plt0_size_(0),
visited_relocation_entries_(false) {
string error;
is_dwp_ = MyHasSuffixString(path, ".dwp");
ParseHeaders(fd, path);
// Currently we need some extra information for PowerPC64 binaries
// including a way to read the .opd section for function descriptors and a
// way to find the linked base for function symbols.
if (header_.e_machine == EM_PPC64) {
// "opd_section_" must always be checked for NULL before use.
opd_section_ = GetSectionInfoByName(".opd", &opd_info_);
for (unsigned int k = 0u; k < GetNumSections(); ++k) {
const char *name = GetSectionName(section_headers_[k].sh_name);
if (strncmp(name, ".text", strlen(".text")) == 0) {
base_for_text_ =
section_headers_[k].sh_addr - section_headers_[k].sh_offset;
break;
}
}
}
// Turn on PLTs.
if (header_.e_machine == EM_386 || header_.e_machine == EM_X86_64) {
plt_code_size_ = kX86PLTCodeSize;
plt0_size_ = kX86PLT0Size;
plts_supported_ = true;
} else if (header_.e_machine == EM_ARM) {
plt_code_size_ = kARMPLTCodeSize;
plt0_size_ = kARMPLT0Size;
plts_supported_ = true;
} else if (header_.e_machine == EM_AARCH64) {
plt_code_size_ = kAARCH64PLTCodeSize;
plt0_size_ = kAARCH64PLT0Size;
plts_supported_ = true;
}
}
~ElfReaderImpl() {
for (unsigned int i = 0u; i < sections_.size(); ++i)
delete sections_[i];
delete [] section_headers_;
delete [] program_headers_;
}
// Examine the headers of the file and return whether the file looks
// like an ELF file for this architecture. Takes an already-open
// file descriptor for the candidate file, reading in the prologue
// to see if the ELF file appears to match the current
// architecture. If error is non-NULL, it will be set with a reason
// in case of failure.
static bool IsArchElfFile(int fd, string *error) {
unsigned char header[EI_NIDENT];
if (pread(fd, header, sizeof(header), 0) != sizeof(header)) {
if (error != NULL) *error = "Could not read header";
return false;
}
if (memcmp(header, ELFMAG, SELFMAG) != 0) {
if (error != NULL) *error = "Missing ELF magic";
return false;
}
if (header[EI_CLASS] != ElfArch::kElfClass) {
if (error != NULL) *error = "Different word size";
return false;
}
int endian = 0;
if (header[EI_DATA] == ELFDATA2LSB)
endian = __LITTLE_ENDIAN;
else if (header[EI_DATA] == ELFDATA2MSB)
endian = __BIG_ENDIAN;
if (endian != __BYTE_ORDER) {
if (error != NULL) *error = "Different byte order";
return false;
}
return true;
}
// Return true if we can use this symbol in Address-to-Symbol map.
bool CanUseSymbol(const char *name, const typename ElfArch::Sym *sym) {
// For now we only save FUNC and NOTYPE symbols. For now we just
// care about functions, but some functions written in assembler
// don't have a proper ELF type attached to them, so we store
// NOTYPE symbols as well. The remaining significant type is
// OBJECT (eg global variables), which represent about 25% of
// the symbols in a typical google3 binary.
if (ElfArch::Type(sym) != STT_FUNC &&
ElfArch::Type(sym) != STT_NOTYPE) {
return false;
}
// Target specific filtering.
switch (header_.e_machine) {
case EM_AARCH64:
case EM_ARM:
// Filter out '$x' special local symbols used by tools
return name[0] != '$' || ElfArch::Bind(sym) != STB_LOCAL;
case EM_X86_64:
// Filter out read-only constants like .LC123.
return name[0] != '.' || ElfArch::Bind(sym) != STB_LOCAL;
default:
return true;
}
}
// Iterate over the symbols in a section, either SHT_DYNSYM or
// SHT_SYMTAB. Add all symbols to the given SymbolMap.
/*
void GetSymbolPositions(SymbolMap *symbols,
typename ElfArch::Word section_type,
uint64 mem_offset,
uint64 file_offset) {
// This map is used to filter out "nested" functions.
// See comment below.
AddrToSymMap addr_to_sym_map;
for (SymbolIterator<ElfArch> it(this, section_type);
!it.done(); it.Next()) {
const char *name = it.GetSymbolName();
if (name == NULL)
continue;
const typename ElfArch::Sym *sym = it.GetSymbol();
if (CanUseSymbol(name, sym)) {
const int sec = sym->st_shndx;
// We don't support special section indices. The most common
// is SHN_ABS, for absolute symbols used deep in the bowels of
// glibc. Also ignore any undefined symbols.
if (sec == SHN_UNDEF ||
(sec >= SHN_LORESERVE && sec <= SHN_HIRESERVE)) {
continue;
}
const typename ElfArch::Shdr& hdr = section_headers_[sec];
// Adjust for difference between where we expected to mmap
// this section, and where it was actually mmapped.
const int64 expected_base = hdr.sh_addr - hdr.sh_offset;
const int64 real_base = mem_offset - file_offset;
const int64 adjust = real_base - expected_base;
uint64 start = sym->st_value + adjust;
// Adjust function symbols for PowerPC64 by dereferencing and adjusting
// the function descriptor to get the function address.
if (header_.e_machine == EM_PPC64 && ElfArch::Type(sym) == STT_FUNC) {
const uint64 opd_addr =
AdjustPPC64FunctionDescriptorSymbolValue(sym->st_value);
// Only adjust the returned value if the function address was found.
if (opd_addr != sym->st_value) {
const int64 adjust_function_symbols =
real_base - base_for_text_;
start = opd_addr + adjust_function_symbols;
}
}
addr_to_sym_map.push_back(std::make_pair(start, sym));
}
}
std::sort(addr_to_sym_map.begin(), addr_to_sym_map.end(), &AddrToSymSorter);
addr_to_sym_map.erase(std::unique(addr_to_sym_map.begin(),
addr_to_sym_map.end(), &AddrToSymEquals),
addr_to_sym_map.end());
// Squeeze out any "nested functions".
// Nested functions are not allowed in C, but libc plays tricks.
//
// For example, here is disassembly of /lib64/tls/libc-2.3.5.so:
// 0x00000000000aa380 <read+0>: cmpl $0x0,0x2781b9(%rip)
// 0x00000000000aa387 <read+7>: jne 0xaa39b <read+27>
// 0x00000000000aa389 <__read_nocancel+0>: mov $0x0,%rax
// 0x00000000000aa390 <__read_nocancel+7>: syscall
// 0x00000000000aa392 <__read_nocancel+9>: cmp $0xfffffffffffff001,%rax
// 0x00000000000aa398 <__read_nocancel+15>: jae 0xaa3ef <read+111>
// 0x00000000000aa39a <__read_nocancel+17>: retq
// 0x00000000000aa39b <read+27>: sub $0x28,%rsp
// 0x00000000000aa39f <read+31>: mov %rdi,0x8(%rsp)
// ...
// Without removing __read_nocancel, symbolizer will return NULL
// given e.g. 0xaa39f (because the lower bound is __read_nocancel,
// but 0xaa39f is beyond its end.
if (addr_to_sym_map.empty()) {
return;
}
const ElfSectionReader<ElfArch> *const symbol_section =
this->GetSectionByType(section_type);
const ElfSectionReader<ElfArch> *const string_section =
this->GetSection(symbol_section->header().sh_link);
typename AddrToSymMap::iterator curr = addr_to_sym_map.begin();
// Always insert the first symbol.
symbols->AddSymbol(string_section->GetOffset(curr->second->st_name),
curr->first, curr->second->st_size);
typename AddrToSymMap::iterator prev = curr++;
for (; curr != addr_to_sym_map.end(); ++curr) {
const uint64 prev_addr = prev->first;
const uint64 curr_addr = curr->first;
const typename ElfArch::Sym *const prev_sym = prev->second;
const typename ElfArch::Sym *const curr_sym = curr->second;
if (prev_addr + prev_sym->st_size <= curr_addr ||
// The next condition is true if two symbols overlap like this:
//
// Previous symbol |----------------------------|
// Current symbol |-------------------------------|
//
// These symbols are not found in google3 codebase, but in
// jdk1.6.0_01_gg1/jre/lib/i386/server/libjvm.so.
//
// 0619e040 00000046 t CardTableModRefBS::write_region_work()
// 0619e070 00000046 t CardTableModRefBS::write_ref_array_work()
//
// We allow overlapped symbols rather than ignore these.
// Due to the way SymbolMap::GetSymbolAtPosition() works,
// lookup for any address in [curr_addr, curr_addr + its size)
// (e.g. 0619e071) will produce the current symbol,
// which is the desired outcome.
prev_addr + prev_sym->st_size < curr_addr + curr_sym->st_size) {
const char *name = string_section->GetOffset(curr_sym->st_name);
symbols->AddSymbol(name, curr_addr, curr_sym->st_size);
prev = curr;
} else {
// Current symbol is "nested" inside previous one like this:
//
// Previous symbol |----------------------------|
// Current symbol |---------------------|
//
// This happens within glibc, e.g. __read_nocancel is nested
// "inside" __read. Ignore "inner" symbol.
//DCHECK_LE(curr_addr + curr_sym->st_size,
// prev_addr + prev_sym->st_size);
;
}
}
}
*/
void VisitSymbols(typename ElfArch::Word section_type,
ElfReader::SymbolSink *sink) {
VisitSymbols(section_type, sink, -1, -1, false);
}
void VisitSymbols(typename ElfArch::Word section_type,
ElfReader::SymbolSink *sink,
int symbol_binding,
int symbol_type,
bool get_raw_symbol_values) {
for (SymbolIterator<ElfArch> it(this, section_type);
!it.done(); it.Next()) {
const char *name = it.GetSymbolName();
if (!name) continue;
const typename ElfArch::Sym *sym = it.GetSymbol();
if ((symbol_binding < 0 || ElfArch::Bind(sym) == symbol_binding) &&
(symbol_type < 0 || ElfArch::Type(sym) == symbol_type)) {
typename ElfArch::Sym symbol = *sym;
// Add a PLT symbol in addition to the main undefined symbol.
// Only do this for SHT_DYNSYM, because PLT symbols are dynamic.
int symbol_index = it.GetCurrentSymbolIndex();
// TODO(dthomson): Can be removed once all Java code is using the
// Google3 launcher.
if (section_type == SHT_DYNSYM &&
static_cast<unsigned int>(symbol_index) < symbols_plt_offsets_.size() &&
symbols_plt_offsets_[symbol_index] != 0) {
string plt_name = string(name) + kPLTFunctionSuffix;
if (plt_function_names_[symbol_index].empty()) {
plt_function_names_[symbol_index] = plt_name;
} else if (plt_function_names_[symbol_index] != plt_name) {
;
}
sink->AddSymbol(plt_function_names_[symbol_index].c_str(),
symbols_plt_offsets_[it.GetCurrentSymbolIndex()],
plt_code_size_);
}
if (!get_raw_symbol_values)
AdjustSymbolValue(&symbol);
sink->AddSymbol(name, symbol.st_value, symbol.st_size);
}
}
}
void VisitRelocationEntries() {
if (visited_relocation_entries_) {
return;
}
visited_relocation_entries_ = true;
if (!plts_supported_) {
return;
}
// First determine if PLTs exist. If not, then there is nothing to do.
ElfReader::SectionInfo plt_section_info;
const char* plt_section =
GetSectionInfoByName(kElfPLTSectionName, &plt_section_info);
if (!plt_section) {
return;
}
if (plt_section_info.size == 0) {
return;
}
// The PLTs could be referenced by either a Rel or Rela (Rel with Addend)
// section.
ElfReader::SectionInfo rel_section_info;
ElfReader::SectionInfo rela_section_info;
const char* rel_section =
GetSectionInfoByName(kElfPLTRelSectionName, &rel_section_info);
const char* rela_section =
GetSectionInfoByName(kElfPLTRelaSectionName, &rela_section_info);
const typename ElfArch::Rel* rel =
reinterpret_cast<const typename ElfArch::Rel*>(rel_section);
const typename ElfArch::Rela* rela =
reinterpret_cast<const typename ElfArch::Rela*>(rela_section);
if (!rel_section && !rela_section) {
return;
}
// Use either Rel or Rela section, depending on which one exists.
size_t section_size = rel_section ? rel_section_info.size
: rela_section_info.size;
size_t entry_size = rel_section ? sizeof(typename ElfArch::Rel)
: sizeof(typename ElfArch::Rela);
// Determine the number of entries in the dynamic symbol table.
ElfReader::SectionInfo dynsym_section_info;
const char* dynsym_section =
GetSectionInfoByName(kElfDynSymSectionName, &dynsym_section_info);
// The dynsym section might not exist, or it might be empty. In either case
// there is nothing to be done so return.
if (!dynsym_section || dynsym_section_info.size == 0) {
return;
}
size_t num_dynamic_symbols =
dynsym_section_info.size / dynsym_section_info.entsize;
symbols_plt_offsets_.resize(num_dynamic_symbols, 0);
// TODO(dthomson): Can be removed once all Java code is using the
// Google3 launcher.
// Make storage room for PLT function name strings.
plt_function_names_.resize(num_dynamic_symbols);
for (size_t i = 0; i < section_size / entry_size; ++i) {
// Determine symbol index from the |r_info| field.
int sym_index = ElfArch::r_sym(rel_section ? rel[i].r_info
: rela[i].r_info);
if (static_cast<unsigned int>(sym_index) >= symbols_plt_offsets_.size()) {
continue;
}
symbols_plt_offsets_[sym_index] =
plt_section_info.addr + plt0_size_ + i * plt_code_size_;
}
}
// Return an ElfSectionReader for the first section of the given
// type by iterating through all section headers. Returns NULL if
// the section type is not found.
const ElfSectionReader<ElfArch> *GetSectionByType(
typename ElfArch::Word section_type) {
for (unsigned int k = 0u; k < GetNumSections(); ++k) {
if (section_headers_[k].sh_type == section_type) {
return GetSection(k);
}
}
return NULL;
}
// Return the name of section "shndx". Returns NULL if the section
// is not found.
const char *GetSectionNameByIndex(int shndx) {
return GetSectionName(section_headers_[shndx].sh_name);
}
// Return a pointer to section "shndx", and store the size in
// "size". Returns NULL if the section is not found.
const char *GetSectionContentsByIndex(int shndx, size_t *size) {
const ElfSectionReader<ElfArch> *section = GetSection(shndx);
if (section != NULL) {
*size = section->section_size();
return section->contents();
}
return NULL;
}
// Return a pointer to the first section of the given name by
// iterating through all section headers, and store the size in
// "size". Returns NULL if the section name is not found.
const char *GetSectionContentsByName(const string &section_name,
size_t *size) {
for (unsigned int k = 0u; k < GetNumSections(); ++k) {
// When searching for sections in a .dwp file, the sections
// we're looking for will always be at the end of the section
// table, so reverse the direction of iteration.
int shndx = is_dwp_ ? GetNumSections() - k - 1 : k;
const char *name = GetSectionName(section_headers_[shndx].sh_name);
if (name != NULL && ElfReader::SectionNamesMatch(section_name, name)) {
const ElfSectionReader<ElfArch> *section = GetSection(shndx);
if (section == NULL) {
return NULL;
} else {
*size = section->section_size();
return section->contents();
}
}
}
return NULL;
}
// This is like GetSectionContentsByName() but it returns a lot of extra
// information about the section.
const char *GetSectionInfoByName(const string &section_name,
ElfReader::SectionInfo *info) {
for (unsigned int k = 0u; k < GetNumSections(); ++k) {
// When searching for sections in a .dwp file, the sections
// we're looking for will always be at the end of the section
// table, so reverse the direction of iteration.
int shndx = is_dwp_ ? GetNumSections() - k - 1 : k;
const char *name = GetSectionName(section_headers_[shndx].sh_name);
if (name != NULL && ElfReader::SectionNamesMatch(section_name, name)) {
const ElfSectionReader<ElfArch> *section = GetSection(shndx);
if (section == NULL) {
return NULL;
} else {
info->type = section->header().sh_type;
info->flags = section->header().sh_flags;
info->addr = section->header().sh_addr;
info->offset = section->header().sh_offset;
info->size = section->header().sh_size;
info->link = section->header().sh_link;
info->info = section->header().sh_info;
info->addralign = section->header().sh_addralign;
info->entsize = section->header().sh_entsize;
return section->contents();
}
}
}
return NULL;
}
// p_vaddr of the first PT_LOAD segment (if any), or 0 if no PT_LOAD
// segments are present. This is the address an ELF image was linked
// (by static linker) to be loaded at. Usually (but not always) 0 for
// shared libraries and position-independent executables.
uint64 VaddrOfFirstLoadSegment() const {
// Relocatable objects (of type ET_REL) do not have LOAD segments.
if (header_.e_type == ET_REL) {
return 0;
}
for (int i = 0; i < GetNumProgramHeaders(); ++i) {
if (program_headers_[i].p_type == PT_LOAD) {
return program_headers_[i].p_vaddr;
}
}
return 0;
}
// According to the LSB ("ELF special sections"), sections with debug
// info are prefixed by ".debug". The names are not specified, but they
// look like ".debug_line", ".debug_info", etc.
bool HasDebugSections() {
// Debug sections are likely to be near the end, so reverse the
// direction of iteration.
for (int k = GetNumSections() - 1; k >= 0; --k) {
const char *name = GetSectionName(section_headers_[k].sh_name);
if (strncmp(name, ".debug", strlen(".debug")) == 0) return true;
if (strncmp(name, ".zdebug", strlen(".zdebug")) == 0) return true;
}
return false;
}
bool IsDynamicSharedObject() const {
return header_.e_type == ET_DYN;
}
// Return the number of sections.
uint64_t GetNumSections() const {
if (HasManySections())
return first_section_header_.sh_size;
return header_.e_shnum;
}
private:
typedef vector<pair<uint64, const typename ElfArch::Sym *> > AddrToSymMap;
static bool AddrToSymSorter(const typename AddrToSymMap::value_type& lhs,
const typename AddrToSymMap::value_type& rhs) {
return lhs.first < rhs.first;
}
static bool AddrToSymEquals(const typename AddrToSymMap::value_type& lhs,
const typename AddrToSymMap::value_type& rhs) {
return lhs.first == rhs.first;
}
// Does this ELF file have too many sections to fit in the program header?
bool HasManySections() const {
return header_.e_shnum == SHN_UNDEF;
}
// Return the number of program headers.
int GetNumProgramHeaders() const {
if (HasManySections() && header_.e_phnum == 0xffff &&
first_section_header_.sh_info != 0)
return first_section_header_.sh_info;
return header_.e_phnum;
}
// Return the index of the string table.
int GetStringTableIndex() const {
if (HasManySections()) {
if (header_.e_shstrndx == 0xffff)
return first_section_header_.sh_link;
else if (header_.e_shstrndx >= GetNumSections())
return 0;
}
return header_.e_shstrndx;
}
// Given an offset into the section header string table, return the
// section name.
const char *GetSectionName(typename ElfArch::Word sh_name) {
const ElfSectionReader<ElfArch> *shstrtab =
GetSection(GetStringTableIndex());
if (shstrtab != NULL) {
return shstrtab->GetOffset(sh_name);
}
return NULL;
}
// Return an ElfSectionReader for the given section. The reader will
// be freed when this object is destroyed.
const ElfSectionReader<ElfArch> *GetSection(int num) {
const char *name;
// Hard-coding the name for the section-name string table prevents
// infinite recursion.
if (num == GetStringTableIndex())
name = ".shstrtab";
else
name = GetSectionNameByIndex(num);
ElfSectionReader<ElfArch> *& reader = sections_[num];
if (reader == NULL)
reader = new ElfSectionReader<ElfArch>(name, path_, fd_,
section_headers_[num]);
return reader;
}
// Parse out the overall header information from the file and assert
// that it looks sane. This contains information like the magic
// number and target architecture.
bool ParseHeaders(int fd, const string &path) {
// Read in the global ELF header.
if (pread(fd, &header_, sizeof(header_), 0) != sizeof(header_)) {
return false;
}
// Must be an executable, dynamic shared object or relocatable object
if (header_.e_type != ET_EXEC &&
header_.e_type != ET_DYN &&
header_.e_type != ET_REL) {
return false;
}
// Need a section header.
if (header_.e_shoff == 0) {
return false;
}
if (header_.e_shnum == SHN_UNDEF) {
// The number of sections in the program header is only a 16-bit value. In
// the event of overflow (greater than SHN_LORESERVE sections), e_shnum
// will read SHN_UNDEF and the true number of section header table entries
// is found in the sh_size field of the first section header.
if (pread(fd, &first_section_header_, sizeof(first_section_header_),
header_.e_shoff) != sizeof(first_section_header_)) {
return false;
}
}
// Dynamically allocate enough space to store the section headers
// and read them out of the file.
const int section_headers_size =
GetNumSections() * sizeof(*section_headers_);
section_headers_ = new typename ElfArch::Shdr[section_headers_size];
if (pread(fd, section_headers_, section_headers_size, header_.e_shoff) !=
section_headers_size) {
return false;
}
// Dynamically allocate enough space to store the program headers
// and read them out of the file.
//const int program_headers_size =
// GetNumProgramHeaders() * sizeof(*program_headers_);
program_headers_ = new typename ElfArch::Phdr[GetNumProgramHeaders()];
// Presize the sections array for efficiency.
sections_.resize(GetNumSections(), NULL);
return true;
}
// Given the "value" of a function descriptor return the address of the
// function (i.e. the dereferenced value). Otherwise return "value".
uint64 AdjustPPC64FunctionDescriptorSymbolValue(uint64 value) {
if (opd_section_ != NULL &&
opd_info_.addr <= value &&
value < opd_info_.addr + opd_info_.size) {
uint64 offset = value - opd_info_.addr;
return (*reinterpret_cast<const uint64*>(opd_section_ + offset));
}
return value;
}
void AdjustSymbolValue(typename ElfArch::Sym* sym) {
switch (header_.e_machine) {
case EM_ARM:
// For ARM architecture, if the LSB of the function symbol offset is set,
// it indicates a Thumb function. This bit should not be taken literally.
// Clear it.
if (ElfArch::Type(sym) == STT_FUNC)
sym->st_value = AdjustARMThumbSymbolValue(sym->st_value);
break;
case EM_386:
// No adjustment needed for Intel x86 architecture. However, explicitly
// define this case as we use it quite often.
break;
case EM_PPC64:
// PowerPC64 currently has function descriptors as part of the ABI.
// Function symbols need to be adjusted accordingly.
if (ElfArch::Type(sym) == STT_FUNC)
sym->st_value = AdjustPPC64FunctionDescriptorSymbolValue(sym->st_value);
break;
default:
break;
}
}
friend class SymbolIterator<ElfArch>;
// The file we're reading.
const string path_;
// Open file descriptor for path_. Not owned by this object.
const int fd_;
// The global header of the ELF file.
typename ElfArch::Ehdr header_;
// The header of the first section. This may be used to supplement the ELF
// file header.
typename ElfArch::Shdr first_section_header_;
// Array of GetNumSections() section headers, allocated when we read
// in the global header.
typename ElfArch::Shdr *section_headers_;
// Array of GetNumProgramHeaders() program headers, allocated when we read
// in the global header.
typename ElfArch::Phdr *program_headers_;
// An array of pointers to ElfSectionReaders. Sections are
// mmaped as they're needed and not released until this object is
// destroyed.
vector<ElfSectionReader<ElfArch>*> sections_;
// For PowerPC64 we need to keep track of function descriptors when looking up
// values for funtion symbols values. Function descriptors are kept in the
// .opd section and are dereferenced to find the function address.
ElfReader::SectionInfo opd_info_;
const char *opd_section_; // Must be checked for NULL before use.
int64 base_for_text_;
// Read PLT-related sections for the current architecture.
bool plts_supported_;
// Code size of each PLT function for the current architecture.
size_t plt_code_size_;
// Size of the special first entry in the .plt section that calls the runtime
// loader resolution routine, and that all other entries jump to when doing
// lazy symbol binding.
size_t plt0_size_;
// Maps a dynamic symbol index to a PLT offset.
// The vector entry index is the dynamic symbol index.
std::vector<uint64> symbols_plt_offsets_;
// Container for PLT function name strings. These strings are passed by
// reference to SymbolSink::AddSymbol() so they need to be stored somewhere.
std::vector<string> plt_function_names_;
bool visited_relocation_entries_;
// True if this is a .dwp file.
bool is_dwp_;
};
ElfReader::ElfReader(const string &path)
: path_(path), fd_(-1), impl32_(NULL), impl64_(NULL) {
// linux 2.6.XX kernel can show deleted files like this:
// /var/run/nscd/dbYLJYaE (deleted)
// and the kernel-supplied vdso and vsyscall mappings like this:
// [vdso]
// [vsyscall]
if (MyHasSuffixString(path, " (deleted)"))
return;
if (path == "[vdso]")
return;
if (path == "[vsyscall]")
return;
fd_ = open(path.c_str(), O_RDONLY);
}
ElfReader::~ElfReader() {
if (fd_ != -1)
close(fd_);
if (impl32_ != NULL)
delete impl32_;
if (impl64_ != NULL)
delete impl64_;
}
// The only word-size specific part of this file is IsNativeElfFile().
#if ULONG_MAX == 0xffffffff
#define NATIVE_ELF_ARCH Elf32
#elif ULONG_MAX == 0xffffffffffffffff
#define NATIVE_ELF_ARCH Elf64
#else
#error "Invalid word size"
#endif
template <typename ElfArch>
static bool IsElfFile(const int fd, const string &path) {
if (fd < 0)
return false;
if (!ElfReaderImpl<ElfArch>::IsArchElfFile(fd, NULL)) {
// No error message here. IsElfFile gets called many times.
return false;
}
return true;
}
bool ElfReader::IsNativeElfFile() const {
return IsElfFile<NATIVE_ELF_ARCH>(fd_, path_);
}
bool ElfReader::IsElf32File() const {
return IsElfFile<Elf32>(fd_, path_);
}
bool ElfReader::IsElf64File() const {
return IsElfFile<Elf64>(fd_, path_);
}
/*
void ElfReader::AddSymbols(SymbolMap *symbols,
uint64 mem_offset, uint64 file_offset,
uint64 length) {
if (fd_ < 0)
return;
// TODO(chatham): Actually use the information about file offset and
// the length of the mapped section. On some machines the data
// section gets mapped as executable, and we'll end up reading the
// file twice and getting some of the offsets wrong.
if (IsElf32File()) {
GetImpl32()->GetSymbolPositions(symbols, SHT_SYMTAB,
mem_offset, file_offset);
GetImpl32()->GetSymbolPositions(symbols, SHT_DYNSYM,
mem_offset, file_offset);
} else if (IsElf64File()) {
GetImpl64()->GetSymbolPositions(symbols, SHT_SYMTAB,
mem_offset, file_offset);
GetImpl64()->GetSymbolPositions(symbols, SHT_DYNSYM,
mem_offset, file_offset);
}
}
*/
void ElfReader::VisitSymbols(ElfReader::SymbolSink *sink) {
VisitSymbols(sink, -1, -1);
}
void ElfReader::VisitSymbols(ElfReader::SymbolSink *sink,
int symbol_binding,
int symbol_type) {
VisitSymbols(sink, symbol_binding, symbol_type, false);
}
void ElfReader::VisitSymbols(ElfReader::SymbolSink *sink,
int symbol_binding,
int symbol_type,
bool get_raw_symbol_values) {
if (IsElf32File()) {
GetImpl32()->VisitRelocationEntries();
GetImpl32()->VisitSymbols(SHT_SYMTAB, sink, symbol_binding, symbol_type,
get_raw_symbol_values);
GetImpl32()->VisitSymbols(SHT_DYNSYM, sink, symbol_binding, symbol_type,
get_raw_symbol_values);
} else if (IsElf64File()) {
GetImpl64()->VisitRelocationEntries();
GetImpl64()->VisitSymbols(SHT_SYMTAB, sink, symbol_binding, symbol_type,
get_raw_symbol_values);
GetImpl64()->VisitSymbols(SHT_DYNSYM, sink, symbol_binding, symbol_type,
get_raw_symbol_values);
}
}
uint64 ElfReader::VaddrOfFirstLoadSegment() {
if (IsElf32File()) {
return GetImpl32()->VaddrOfFirstLoadSegment();
} else if (IsElf64File()) {
return GetImpl64()->VaddrOfFirstLoadSegment();
} else {
return 0;
}
}
const char *ElfReader::GetSectionName(int shndx) {
if (shndx < 0 || static_cast<unsigned int>(shndx) >= GetNumSections()) return NULL;
if (IsElf32File()) {
return GetImpl32()->GetSectionNameByIndex(shndx);
} else if (IsElf64File()) {
return GetImpl64()->GetSectionNameByIndex(shndx);
} else {
return NULL;
}
}
uint64 ElfReader::GetNumSections() {
if (IsElf32File()) {
return GetImpl32()->GetNumSections();
} else if (IsElf64File()) {
return GetImpl64()->GetNumSections();
} else {
return 0;
}
}
const char *ElfReader::GetSectionByIndex(int shndx, size_t *size) {
if (IsElf32File()) {
return GetImpl32()->GetSectionContentsByIndex(shndx, size);
} else if (IsElf64File()) {
return GetImpl64()->GetSectionContentsByIndex(shndx, size);
} else {
return NULL;
}
}
const char *ElfReader::GetSectionByName(const string &section_name,
size_t *size) {
if (IsElf32File()) {
return GetImpl32()->GetSectionContentsByName(section_name, size);
} else if (IsElf64File()) {
return GetImpl64()->GetSectionContentsByName(section_name, size);
} else {
return NULL;
}
}
const char *ElfReader::GetSectionInfoByName(const string &section_name,
SectionInfo *info) {
if (IsElf32File()) {
return GetImpl32()->GetSectionInfoByName(section_name, info);
} else if (IsElf64File()) {
return GetImpl64()->GetSectionInfoByName(section_name, info);
} else {
return NULL;
}
}
bool ElfReader::SectionNamesMatch(const string &name, const string &sh_name) {
if ((name.find(".debug_", 0) == 0) && (sh_name.find(".zdebug_", 0) == 0)) {
const string name_suffix(name, strlen(".debug_"));
const string sh_name_suffix(sh_name, strlen(".zdebug_"));
return name_suffix == sh_name_suffix;
}
return name == sh_name;
}
bool ElfReader::IsDynamicSharedObject() {
if (IsElf32File()) {
return GetImpl32()->IsDynamicSharedObject();
} else if (IsElf64File()) {
return GetImpl64()->IsDynamicSharedObject();
} else {
return false;
}
}
ElfReaderImpl<Elf32> *ElfReader::GetImpl32() {
if (impl32_ == NULL) {
impl32_ = new ElfReaderImpl<Elf32>(path_, fd_);
}
return impl32_;
}
ElfReaderImpl<Elf64> *ElfReader::GetImpl64() {
if (impl64_ == NULL) {
impl64_ = new ElfReaderImpl<Elf64>(path_, fd_);
}
return impl64_;
}
// Return true if file is an ELF binary of ElfArch, with unstripped
// debug info (debug_only=true) or symbol table (debug_only=false).
// Otherwise, return false.
template <typename ElfArch>
static bool IsNonStrippedELFBinaryImpl(const string &path, const int fd,
bool debug_only) {
if (!ElfReaderImpl<ElfArch>::IsArchElfFile(fd, NULL)) return false;
ElfReaderImpl<ElfArch> elf_reader(path, fd);
return debug_only ?
elf_reader.HasDebugSections()
: (elf_reader.GetSectionByType(SHT_SYMTAB) != NULL);
}
// Helper for the IsNon[Debug]StrippedELFBinary functions.
static bool IsNonStrippedELFBinaryHelper(const string &path,
bool debug_only) {
const int fd = open(path.c_str(), O_RDONLY);
if (fd == -1) {
return false;
}
if (IsNonStrippedELFBinaryImpl<Elf32>(path, fd, debug_only) ||
IsNonStrippedELFBinaryImpl<Elf64>(path, fd, debug_only)) {
close(fd);
return true;
}
close(fd);
return false;
}
bool ElfReader::IsNonStrippedELFBinary(const string &path) {
return IsNonStrippedELFBinaryHelper(path, false);
}
bool ElfReader::IsNonDebugStrippedELFBinary(const string &path) {
return IsNonStrippedELFBinaryHelper(path, true);
}
} // namespace dwarf2reader