blob: 3a50dc44c9ae8b6de026612fcb1abb503aca95d9 [file] [log] [blame]
// Copyright 2016 Google Inc. All Rights Reserved.
//
// 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.
#include <string>
#include <iostream>
#include "re2/re2.h"
#include "third_party/freebsd_elf/elf.h"
#include "bloaty.h"
#include <assert.h>
#include <limits.h>
#include <stdlib.h>
#define CHECK_RETURN(call) if (!(call)) { return false; }
namespace bloaty {
namespace {
struct ByteSwapFunc {
template <class T>
T operator()(T val) {
return ByteSwap(val);
}
};
struct NullFunc {
template <class T>
T operator()(T val) { return val; }
};
bool StringPieceToSize(StringPiece str, size_t* out) {
char *end = nullptr;
*out = strtoul(str.data(), &end, 10);
return end != str.data() && *out != ULONG_MAX;
}
// ElfFile /////////////////////////////////////////////////////////////////////
// For parsing the pieces we need out of an ELF file (.o, .so, and binaries).
class ElfFile {
public:
ElfFile(StringPiece data) : data_(data) {
ok_ = Initialize();
}
bool IsOpen() { return ok_; }
// Regions of the file where different headers live.
StringPiece entire_file() const { return data_; }
StringPiece header_region() const { return header_region_; }
StringPiece section_headers() const { return section_headers_; }
StringPiece segment_headers() const { return segment_headers_; }
const Elf64_Ehdr& header() const { return header_; }
Elf64_Xword section_count() const { return section_count_; }
Elf64_Xword section_string_index() const { return section_string_index_; }
// Represents an ELF segment (data used by the loader / dynamic linker).
class Segment {
public:
const Elf64_Phdr& header() const { return header_; }
StringPiece contents() const { return contents_; }
private:
friend class ElfFile;
Elf64_Phdr header_;
StringPiece contents_;
};
// Represents an ELF section (.text, .data, .bss, etc.)
class Section {
public:
const Elf64_Shdr& header() const { return header_; }
StringPiece contents() const { return contents_; }
// If header().sh_type == SHT_STRTAB.
bool ReadName(Elf64_Word index, StringPiece* name) const;
// If header().sh_type == SHT_SYMTAB
Elf64_Word GetSymbolCount() const;
bool ReadSymbol(Elf64_Word index, Elf64_Sym* sym) const;
private:
friend class ElfFile;
const ElfFile* elf_;
Elf64_Shdr header_;
StringPiece contents_;
};
bool ReadSegment(Elf64_Word index, Segment* segment) const;
bool ReadSection(Elf64_Word index, Section* section) const;
bool is_64bit() const { return is_64bit_; }
bool is_native_endian() const { return is_native_endian_; }
private:
friend class Section;
bool Initialize();
bool SetRegion(size_t start, size_t n, StringPiece* out) const {
CHECK_RETURN(start + n <= data_.size());
*out = data_.substr(start, n);
return true;
}
// Shared code for reading various ELF structures. Handles endianness
// conversion and 32->64 bit conversion, when necessary.
class StructReader {
public:
StructReader(const ElfFile& elf, StringPiece data)
: elf_(elf), data_(data) {}
template <class T32, class T64, class Munger>
bool Read(size_t offset, Munger /*munger*/, T64* out) const {
if (elf_.is_64bit() && elf_.is_native_endian()) {
return Memcpy(offset, out);
} else {
return ReadFallback<T32, T64, Munger>(offset, out);
}
}
private:
const ElfFile& elf_;
StringPiece data_;
template <class T32, class T64, class Munger>
bool ReadFallback(size_t offset, T64* out) const;
template <class T>
bool Memcpy(size_t offset, T* out) const {
CHECK_RETURN(offset + sizeof(*out) <= data_.size());
memcpy(out, data_.data() + offset, sizeof(*out));
return true;
}
};
template <class T32, class T64, class Munger>
bool ReadStruct(size_t offset, Munger munger, T64* out) const {
return StructReader(*this, data_).Read<T32>(offset, munger, out);
}
bool ok_;
bool is_64bit_;
bool is_native_endian_;
StringPiece data_;
Elf64_Ehdr header_;
Elf64_Xword section_count_;
Elf64_Xword section_string_index_;
StringPiece header_region_;
StringPiece section_headers_;
StringPiece segment_headers_;
};
// ELF uses different structure definitions for 32/64 bit files. The sizes of
// members are different, and members are even in a different order!
//
// These mungers can convert 32 bit structures to 64-bit ones. They can also
// handle converting endianness. We use templates so a single template function
// can handle all three patterns:
//
// 32 native -> 64 native
// 32 swapped -> 64 native
// 64 swapped -> 64 native
struct EhdrMunger {
template <class From, class Func>
void operator()(const From& from, Elf64_Ehdr* to, Func func) {
memcpy(&to->e_ident[0], &from.e_ident[0], EI_NIDENT);
to->e_type = func(from.e_type);
to->e_machine = func(from.e_machine);
to->e_version = func(from.e_version);
to->e_entry = func(from.e_entry);
to->e_phoff = func(from.e_phoff);
to->e_shoff = func(from.e_shoff);
to->e_flags = func(from.e_flags);
to->e_ehsize = func(from.e_ehsize);
to->e_phentsize = func(from.e_phentsize);
to->e_phnum = func(from.e_phnum);
to->e_shentsize = func(from.e_shentsize);
to->e_shnum = func(from.e_shnum);
to->e_shstrndx = func(from.e_shstrndx);
}
};
struct ShdrMunger {
template <class From, class Func>
void operator()(const From& from, Elf64_Shdr* to, Func func) {
to->sh_name = func(from.sh_name);
to->sh_type = func(from.sh_type);
to->sh_flags = func(from.sh_flags);
to->sh_addr = func(from.sh_addr);
to->sh_offset = func(from.sh_offset);
to->sh_size = func(from.sh_size);
to->sh_link = func(from.sh_link);
to->sh_info = func(from.sh_info);
to->sh_addralign = func(from.sh_addralign);
to->sh_entsize = func(from.sh_entsize);
}
};
struct PhdrMunger {
template <class From, class Func>
void operator()(const From& from, Elf64_Phdr* to, Func func) {
to->p_type = func(from.p_type);
to->p_flags = func(from.p_flags);
to->p_offset = func(from.p_offset);
to->p_vaddr = func(from.p_vaddr);
to->p_paddr = func(from.p_paddr);
to->p_filesz = func(from.p_filesz);
to->p_memsz = func(from.p_memsz);
to->p_align = func(from.p_align);
}
};
struct SymMunger {
template <class From, class Func>
void operator()(const From& from, Elf64_Sym* to, Func func) {
to->st_name = func(from.st_name);
to->st_info = func(from.st_info);
to->st_other = func(from.st_other);
to->st_shndx = func(from.st_shndx);
to->st_value = func(from.st_value);
to->st_size = func(from.st_size);
}
};
template <class T32, class T64, class Munger>
bool ElfFile::StructReader::ReadFallback(size_t offset, T64* out) const {
if (elf_.is_64bit()) {
assert(!elf_.is_native_endian());
CHECK_RETURN(Memcpy(offset, out));
Munger()(*out, out, ByteSwapFunc());
} else {
T32 data32;
CHECK_RETURN(Memcpy(offset, &data32));
if (elf_.is_native_endian()) {
Munger()(data32, out, NullFunc());
} else {
Munger()(data32, out, ByteSwapFunc());
}
}
return true;
}
bool ElfFile::Section::ReadName(Elf64_Word index, StringPiece* name) const {
assert(header().sh_type == SHT_STRTAB);
if (index == SHN_UNDEF || index >= contents_.size()) {
return false;
}
name->set(contents_.data(), contents_.size());
name->remove_prefix(index);
const char* null_pos =
static_cast<const char*>(memchr(name->data(), '\0', name->size()));
if (null_pos == NULL) {
return false;
}
name->remove_suffix(name->data() + name->size() - null_pos);
return true;
}
Elf64_Word ElfFile::Section::GetSymbolCount() const {
assert(header().sh_type == SHT_SYMTAB);
return contents_.size() / header_.sh_entsize;
}
bool ElfFile::Section::ReadSymbol(Elf64_Word index, Elf64_Sym* sym) const {
assert(header().sh_type == SHT_SYMTAB);
ElfFile::StructReader reader(*elf_, contents());
return reader.Read<Elf32_Sym>(header_.sh_entsize * index, SymMunger(), sym);
}
bool ElfFile::Initialize() {
CHECK_RETURN(data_.size() >= EI_NIDENT);
unsigned char ident[EI_NIDENT];
memcpy(ident, data_.data(), EI_NIDENT);
if (memcmp(ident, "\177ELF", 4) != 0) {
// Not an ELF file.
return false;
}
switch (ident[EI_CLASS]) {
case ELFCLASS32:
is_64bit_ = false;
break;
case ELFCLASS64:
is_64bit_ = true;
break;
default:
fprintf(stderr, "unexpected ELF class: %d\n", ident[EI_CLASS]);
return false;
}
switch (ident[EI_DATA]) {
case ELFDATA2LSB:
is_native_endian_ = IsLittleEndian();
break;
case ELFDATA2MSB:
is_native_endian_ = !IsLittleEndian();
break;
default:
fprintf(stderr, "unexpected ELF data: %d\n", ident[EI_DATA]);
return false;
}
CHECK_RETURN(ReadStruct<Elf32_Ehdr>(0, EhdrMunger(), &header_));
Section section0;
bool has_section0 = 0;
// ELF extensions: if certain fields overflow, we have to find their true data
// from elsewhere. For more info see:
// https://docs.oracle.com/cd/E19683-01/817-3677/chapter6-94076/index.html
if (header_.e_shoff > 0 &&
data_.size() > (header_.e_shoff + header_.e_shentsize)) {
section_count_ = 1;
CHECK_RETURN(ReadSection(0, &section0));
has_section0 = true;
}
section_count_ = header_.e_shnum;
section_string_index_ = header_.e_shstrndx;
if (section_count_ == 0 && has_section0) {
section_count_ = section0.header().sh_size;
}
if (section_string_index_ == SHN_XINDEX && has_section0) {
section_string_index_ = section0.header().sh_link;
}
CHECK_RETURN(SetRegion(0, header_.e_ehsize, &header_region_));
CHECK_RETURN(SetRegion(header_.e_shoff, header_.e_shentsize * section_count_,
&section_headers_));
CHECK_RETURN(SetRegion(header_.e_phoff, header_.e_phentsize * header_.e_phnum,
&segment_headers_));
return true;
}
bool ElfFile::ReadSegment(Elf64_Word index, Segment* segment) const {
assert(index < header_.e_phnum);
Elf64_Phdr* header = &segment->header_;
CHECK_RETURN(ReadStruct<Elf32_Phdr>(
header_.e_phoff + header_.e_phentsize * index, PhdrMunger(), header));
CHECK_RETURN(
SetRegion(header->p_offset, header->p_filesz, &segment->contents_));
return true;
}
bool ElfFile::ReadSection(Elf64_Word index, Section* section) const {
assert(index < section_count_);
Elf64_Shdr* header = &section->header_;
CHECK_RETURN(ReadStruct<Elf32_Shdr>(
header_.e_shoff + header_.e_shentsize * index, ShdrMunger(), header));
if (header->sh_type == SHT_NOBITS) {
section->contents_ = StringPiece();
} else {
CHECK_RETURN(
SetRegion(header->sh_offset, header->sh_size, &section->contents_));
}
section->elf_ = this;
return true;
}
// ArFile //////////////////////////////////////////////////////////////////////
// For parsing .a files (static libraries).
//
// The best documentation I've been able to find for this file format is
// Wikipedia: https://en.wikipedia.org/wiki/Ar_(Unix)
//
// So far we only parse the System V / GNU variant.
class ArFile {
public:
ArFile(StringPiece data)
: magic_(data.substr(0, kMagicSize)),
contents_(data.substr(kMagicSize)) {}
bool IsOpen() const { return magic() == StringPiece(kMagic); }
StringPiece magic() const { return magic_; }
StringPiece contents() const { return contents_; }
struct MemberFile {
enum {
kSymbolTable, // Stores a symbol table.
kLongFilenameTable, // Stores long filenames, users should ignore.
kNormal, // Regular data file.
} file_type;
StringPiece filename; // Only when file_type == kNormal
size_t size;
StringPiece header;
StringPiece contents;
};
class MemberReader {
public:
MemberReader(const ArFile& ar) : remaining_(ar.contents()) {}
bool ReadMember(MemberFile* file);
bool IsEof() const { return remaining_.size() == 0; }
private:
bool Consume(size_t n, StringPiece* field) {
CHECK_RETURN(remaining_.size() >= n);
*field = remaining_.substr(0, n);
remaining_.remove_prefix(n);
return true;
}
StringPiece long_filenames_;
StringPiece remaining_;
};
private:
const StringPiece magic_;
const StringPiece contents_;
static constexpr const char* kMagic = "!<arch>\n";
static constexpr int kMagicSize = 8;
};
bool ArFile::MemberReader::ReadMember(MemberFile* file) {
struct Header {
char file_id[16];
char modified_timestamp[12];
char owner_id[6];
char group_id[6];
char mode[8];
char size[10];
char end[2];
};
if (remaining_.size() < sizeof(Header)) {
return false;
}
const Header* header = reinterpret_cast<const Header*>(remaining_.data());
CHECK_RETURN(Consume(sizeof(Header), &file->header));
StringPiece file_id(&header->file_id[0], sizeof(header->file_id));
StringPiece size_str(&header->size[0], sizeof(header->size));
CHECK_RETURN(StringPieceToSize(size_str, &file->size));
CHECK_RETURN(Consume(file->size, &file->contents));
file->file_type = MemberFile::kNormal;
if (file_id[0] == '/') {
// Special filename, internal to the format.
if (file_id[1] == ' ') {
file->file_type = MemberFile::kSymbolTable;
} else if (file_id[1] == '/') {
file->file_type = MemberFile::kLongFilenameTable;
long_filenames_ = file->contents;
} else if (isdigit(file_id[1])) {
size_t offset;
CHECK_RETURN(StringPieceToSize(file_id.substr(1), &offset));
size_t end = long_filenames_.find('/', offset);
if (end == std::string::npos) {
return false;
}
file->filename = long_filenames_.substr(offset, end - offset);
} else {
return false; // Unexpected special filename.
}
} else {
// Normal filename, slash-terminated.
size_t slash = file_id.find('/');
if (slash == std::string::npos) {
fprintf(stderr, "BSD-style AR not yet implemented.\n");
return false;
}
file->filename = file_id.substr(0, slash);
}
return true;
}
void MaybeAddFileRange(RangeSink* sink, StringPiece label, StringPiece range) {
if (sink) {
sink->AddFileRange(label, range);
}
}
template <class Func>
bool OnElfFile(const ElfFile& elf, StringPiece filename,
unsigned long index_base, RangeSink* sink, Func func) {
CHECK_RETURN(func(elf, filename, index_base));
// Add these *after* running the user callback. That way if there is
// overlap, the user's annotations will take precedence.
MaybeAddFileRange(sink, "[ELF Headers]", elf.header_region());
MaybeAddFileRange(sink, "[ELF Headers]", elf.section_headers());
MaybeAddFileRange(sink, "[ELF Headers]", elf.segment_headers());
// Any sections of the file not covered by any segments/sections/symbols/etc.
MaybeAddFileRange(sink, "[Unmapped]", elf.entire_file());
return true;
}
template <class Func>
bool ForEachElf(const InputFile& file, RangeSink* sink, Func func) {
ArFile ar_file(file.data());
unsigned long index_base = 0;
if (ar_file.IsOpen()) {
ArFile::MemberFile member;
ArFile::MemberReader reader(ar_file);
MaybeAddFileRange(sink, "[AR Headers]", ar_file.magic());
while (reader.ReadMember(&member)) {
MaybeAddFileRange(sink, "[AR Headers]", member.header);
switch (member.file_type) {
case ArFile::MemberFile::kNormal: {
ElfFile elf(member.contents);
if (elf.IsOpen()) {
CHECK_RETURN(OnElfFile(elf, member.filename, index_base, sink, func));
index_base += elf.section_count();
} else {
MaybeAddFileRange(sink, "[AR Non-ELF Member File]",
member.contents);
}
break;
}
case ArFile::MemberFile::kSymbolTable:
MaybeAddFileRange(sink, "[AR Symbol Table]", member.contents);
break;
case ArFile::MemberFile::kLongFilenameTable:
MaybeAddFileRange(sink, "[AR Headers]", member.contents);
break;
}
}
} else {
ElfFile elf(file.data());
if (!elf.IsOpen()) {
fprintf(stderr, "Not an ELF or Archive file: %s\n",
file.filename().c_str());
return false;
}
CHECK_RETURN(OnElfFile(elf, file.filename(), index_base, sink, func));
}
return true;
}
// There are several programs that offer useful information about
// binaries:
//
// - objdump: display object file headers and contents (including disassembly)
// - readelf: more ELF-specific objdump (no disassembly though)
// - nm: display symbols
// - size: display binary size
// For object files, addresses are relative to the section they live in, which
// is indicated by ndx. We split this into:
//
// - 24 bits for index (up to 16M symbols with -ffunction-sections)
// - 40 bits for address (up to 1TB section)
static uint64_t ToVMAddr(size_t addr, long ndx, bool is_object) {
if (is_object) {
return (ndx << 40) | addr;
} else {
return addr;
}
}
static bool IsArchiveFile(StringPiece data) {
ArFile ar(data);
return ar.IsOpen();
}
static bool IsObjectFile(StringPiece data) {
ElfFile elf(data);
return IsArchiveFile(data) || (elf.IsOpen() && elf.header().e_type == ET_REL);
}
static bool CheckNotObject(const char* source, RangeSink* sink) {
if (IsObjectFile(sink->input_file().data())) {
fprintf(stderr,
"bloaty: can't use data source '%s' on object files (only binaries "
"and shared libraries)\n",
source);
return false;
}
return true;
}
static bool ReadELFSymbols(const InputFile& file, RangeSink* sink,
SymbolTable* table) {
bool is_object = IsObjectFile(file.data());
return ForEachElf(file, sink, [=](const ElfFile& elf, StringPiece /*filename*/,
uint32_t index_base) {
for (Elf64_Xword i = 1; i < elf.section_count(); i++) {
ElfFile::Section section;
CHECK_RETURN(elf.ReadSection(i, &section));
if (section.header().sh_type != SHT_SYMTAB) {
continue;
}
Elf64_Word symbol_count = section.GetSymbolCount();
// Find the corresponding section where the strings for the symbol table
// can be found.
ElfFile::Section strtab_section;
CHECK_RETURN(elf.ReadSection(section.header().sh_link, &strtab_section));
CHECK_RETURN(strtab_section.header().sh_type == SHT_STRTAB);
for (Elf64_Word i = 1; i < symbol_count; i++) {
Elf64_Sym sym;
StringPiece name;
CHECK_RETURN(section.ReadSymbol(i, &sym));
int type = ELF64_ST_TYPE(sym.st_info);
if (type != STT_OBJECT && type != STT_FUNC) {
continue;
}
if (sym.st_size == 0) {
// Maybe try to refine? See ReadELFSectionsRefineSymbols below.
continue;
}
CHECK_RETURN(strtab_section.ReadName(sym.st_name, &name));
uint64_t full_addr =
ToVMAddr(sym.st_value, index_base + sym.st_shndx, is_object);
if (sink) {
sink->AddVMRangeAllowAlias(full_addr, sym.st_size, name.as_string());
}
if (table) {
table->insert(
std::make_pair(name, std::make_pair(full_addr, sym.st_size)));
}
}
}
return true;
});
}
enum ReportSectionsBy {
kReportBySectionName,
kReportByFlags,
kReportByFilename,
};
static bool DoReadELFSections(RangeSink* sink, enum ReportSectionsBy report_by) {
bool is_object = IsObjectFile(sink->input_file().data());
return ForEachElf(
sink->input_file(), sink,
[=](const ElfFile& elf, StringPiece filename, uint32_t index_base) {
if (elf.section_count() == 0) {
return true;
}
std::string name_from_flags;
ElfFile::Section section_names;
CHECK_RETURN(
elf.ReadSection(elf.section_string_index(), &section_names));
CHECK_RETURN(section_names.header().sh_type == SHT_STRTAB);
for (Elf64_Xword i = 1; i < elf.section_count(); i++) {
ElfFile::Section section;
CHECK_RETURN(elf.ReadSection(i, &section));
const auto& header = section.header();
if (header.sh_name == SHN_UNDEF) {
return true;
}
StringPiece name;
CHECK_RETURN(section_names.ReadName(header.sh_name, &name));
auto addr = header.sh_addr;
auto size = header.sh_size;
auto filesize = (header.sh_type == SHT_NOBITS) ? 0 : size;
auto vmsize = (header.sh_flags & SHF_ALLOC) ? size : 0;
StringPiece contents = section.contents().substr(0, filesize);
uint64_t full_addr = ToVMAddr(addr, index_base + i, is_object);
if (report_by == kReportByFlags) {
name_from_flags = name.as_string();
name_from_flags = "Section [";
if (header.sh_flags & SHF_ALLOC) {
name_from_flags += 'A';
}
if (header.sh_flags & SHF_WRITE) {
name_from_flags += 'W';
}
if (header.sh_flags & SHF_EXECINSTR) {
name_from_flags += 'X';
}
name_from_flags += ']';
sink->AddRange(name_from_flags, full_addr, vmsize, contents);
} else if (report_by == kReportBySectionName) {
sink->AddRange(name, full_addr, vmsize, contents);
} else if (report_by == kReportByFilename) {
sink->AddRange(filename, full_addr, vmsize, contents);
}
}
if (report_by == kReportByFilename) {
// Cover unmapped parts of the file.
sink->AddFileRange(filename, elf.entire_file());
}
return true;
});
}
static bool ReadELFSegments(RangeSink* sink) {
if (IsObjectFile(sink->input_file().data())) {
// Object files don't actually have segments. But we can cheat a little bit
// and make up "segments" based on section flags. This can be really useful
// when you are compiling with -ffunction-sections and -fdata-sections,
// because in those cases the actual "sections" report becomes pretty
// useless (since every function/data has its own section, it's like the
// "symbols" report except less readable).
CHECK_RETURN(DoReadELFSections(sink, kReportByFlags));
return true;
}
return ForEachElf(sink->input_file(), sink, [=](const ElfFile& elf,
StringPiece /*filename*/,
uint32_t /*index_base*/) {
for (Elf64_Xword i = 0; i < elf.header().e_phnum; i++) {
ElfFile::Segment segment;
CHECK_RETURN(elf.ReadSegment(i, &segment));
const auto& header = segment.header();
if (header.p_type != PT_LOAD) {
continue;
}
std::string name = "LOAD [";
if (header.p_flags & PF_R) {
name += 'R';
}
if (header.p_flags & PF_W) {
name += 'W';
}
if (header.p_flags & PF_X) {
name += 'X';
}
name += ']';
sink->AddRange(name, header.p_vaddr, header.p_memsz, segment.contents());
}
return true;
});
}
// ELF files put debug info directly into the binary, so we call the DWARF
// reader directly on them. At the moment we don't attempt to make these
// work with object files.
static bool ReadDWARFSections(const ElfFile& elf, dwarf::File* dwarf) {
ElfFile::Section section_names;
CHECK_RETURN(elf.ReadSection(elf.section_string_index(), &section_names));
CHECK_RETURN(section_names.header().sh_type == SHT_STRTAB);
for (Elf64_Xword i = 1; i < elf.section_count(); i++) {
ElfFile::Section section;
CHECK_RETURN(elf.ReadSection(i, &section));
const auto& header = section.header();
if (header.sh_name == SHN_UNDEF) {
return true;
}
StringPiece name;
CHECK_RETURN(section_names.ReadName(header.sh_name, &name));
if (name == ".debug_aranges") {
dwarf->debug_aranges = section.contents();
} else if (name == ".debug_str") {
dwarf->debug_str = section.contents();
} else if (name == ".debug_info") {
dwarf->debug_info = section.contents();
} else if (name == ".debug_abbrev") {
dwarf->debug_abbrev = section.contents();
} else if (name == ".debug_line") {
dwarf->debug_line = section.contents();
} else if (name == ".zdebug_aranges") {
dwarf->zdebug_aranges = section.contents();
} else if (name == ".zdebug_str") {
dwarf->zdebug_str = section.contents();
} else if (name == ".zdebug_info") {
dwarf->zdebug_info = section.contents();
} else if (name == ".zdebug_abbrev") {
dwarf->zdebug_abbrev = section.contents();
} else if (name == ".zdebug_line") {
dwarf->zdebug_line = section.contents();
}
}
return true;
}
} // namespace
class ElfFileHandler : public FileHandler {
bool ProcessBaseMap(RangeSink* sink) override {
if (IsObjectFile(sink->input_file().data())) {
return DoReadELFSections(sink, kReportBySectionName);
} else {
// Slightly more complete for executables, but not present in object
// files.
return ReadELFSegments(sink);
}
}
bool ProcessFile(const std::vector<RangeSink*>& sinks) override {
for (auto sink : sinks) {
switch (sink->data_source()) {
case DataSource::kSegments:
CHECK_RETURN(ReadELFSegments(sink));
break;
case DataSource::kSections:
CHECK_RETURN(DoReadELFSections(sink, kReportBySectionName));
break;
case DataSource::kSymbols:
CHECK_RETURN(ReadELFSymbols(sink->input_file(), sink, nullptr));
break;
case DataSource::kArchiveMembers:
CHECK_RETURN(DoReadELFSections(sink, kReportByFilename));
break;
case DataSource::kCompileUnits: {
CHECK_RETURN(CheckNotObject("compileunits", sink));
SymbolTable symtab;
ElfFile elf(sink->input_file().data());
CHECK_RETURN(ReadELFSymbols(sink->input_file(), nullptr, &symtab));
dwarf::File dwarf;
CHECK_RETURN(elf.IsOpen() && ReadDWARFSections(elf, &dwarf))
CHECK_RETURN(ReadDWARFCompileUnits(dwarf, symtab, sink));
break;
}
case DataSource::kInlines: {
CHECK_RETURN(CheckNotObject("lineinfo", sink));
ElfFile elf(sink->input_file().data());
dwarf::File dwarf;
CHECK_RETURN(elf.IsOpen() && ReadDWARFSections(elf, &dwarf));
CHECK_RETURN(ReadDWARFInlines(dwarf, sink, true));
break;
}
default:
fprintf(stderr, "ELF file: Unknown data source.\n");
return false;
}
}
return true;
}
};
std::unique_ptr<FileHandler> TryOpenELFFile(const InputFile& file) {
ElfFile elf(file.data());
ArFile ar(file.data());
if (elf.IsOpen() || ar.IsOpen()) {
return std::unique_ptr<FileHandler>(new ElfFileHandler());
} else {
return nullptr;
}
}
} // namespace bloaty