Skip to content

Commit

Permalink
Fixed some bugs in DWARF parsing and expanded -d compileunits cover…
Browse files Browse the repository at this point in the history
…age.
  • Loading branch information
haberman committed Nov 11, 2016
1 parent 515bed5 commit 0c85f9f
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 55 deletions.
6 changes: 3 additions & 3 deletions src/bloaty.h
Original file line number Diff line number Diff line change
Expand Up @@ -168,12 +168,12 @@ struct File {

} // namespace dwarf

typedef std::unordered_map<StringPiece, std::pair<uint64_t, uint64_t>>
SymbolTable;
typedef std::map<StringPiece, std::pair<uint64_t, uint64_t>> SymbolTable;

// Provided by dwarf.cc. To use these, a module should fill in a dwarf::File
// and then call these functions.
bool ReadDWARFCompileUnits(const dwarf::File& file, RangeSink* sink);
bool ReadDWARFCompileUnits(const dwarf::File& file, const SymbolTable& symtab,
RangeSink* sink);
bool ReadDWARFInlines(const dwarf::File& file, RangeSink* sink,
bool include_line);

Expand Down
57 changes: 37 additions & 20 deletions src/dwarf.cc
Original file line number Diff line number Diff line change
Expand Up @@ -534,8 +534,6 @@ class DIEReader {

bool ReadCompilationUnitHeader(StringPiece data);
bool ReadCode();
bool SkipDIEs(size_t levels);
bool SkipAttributes();

enum class State {
kReadyToReadAttributes,
Expand Down Expand Up @@ -619,7 +617,6 @@ bool DIEReader::NextCompilationUnit() {
}

bool DIEReader::NextDIE() {

do {
if (remaining_.size() == 0) {
state_ = State::kEof;
Expand Down Expand Up @@ -884,14 +881,18 @@ class FormReader<T, typename std::enable_if<std::is_integral<T>::value>::type>
Func func) {
switch (form) {
case DW_FORM_data1:
case DW_FORM_ref1:
return func(&Base::template ReadAttr<&ME::ReadFixed<int8_t>>);
case DW_FORM_data2:
case DW_FORM_ref2:
CHECK_RETURN(sizeof(T) >= 2);
return func(&Base::template ReadAttr<&ME::ReadFixed<int16_t>>);
case DW_FORM_data4:
case DW_FORM_ref4:
CHECK_RETURN(sizeof(T) >= 4);
return func(&Base::template ReadAttr<&ME::ReadFixed<int32_t>>);
case DW_FORM_data8:
case DW_FORM_ref8:
CHECK_RETURN(sizeof(T) >= 8);
return func(&Base::template ReadAttr<&ME::ReadFixed<int64_t>>);
case DW_FORM_addr:
Expand Down Expand Up @@ -1014,8 +1015,11 @@ class FormReader<void> : public FormReaderBase<FormReader<void>> {
static bool GetFunctionForForm(CompilationUnitSizes sizes, uint8_t form,
Func func) {
switch (form) {
case DW_FORM_flag_present:
return func(&Base::template ReadAttr<&ME::DoNothing>);
case DW_FORM_data1:
case DW_FORM_ref1:
case DW_FORM_flag:
return func(&Base::template ReadAttr<&ME::SkipFixed<1>>);
case DW_FORM_data2:
case DW_FORM_ref2:
Expand Down Expand Up @@ -1067,6 +1071,8 @@ class FormReader<void> : public FormReaderBase<FormReader<void>> {
}

private:
bool DoNothing() { return true; }

template <size_t N>
bool SkipFixed() {
return SkipBytes(N, &data_);
Expand Down Expand Up @@ -1165,15 +1171,14 @@ ActionBuf::ActionBuf(const AbbrevTable::Abbrev& abbrev,

// Overwrite any entries for attributes we actually want to store somewhere.
for (const auto& action : indexed_actions) {
assert(action.index < action_list_.size());
if (action_list_[action.index].data) {
fprintf(stderr,
"bloaty: internal error, specified same DWARF attribute more "
"than once\n");
exit(1);
}

if (action.action.func) {
assert(action.index < action_list_.size());
if (action_list_[action.index].data) {
fprintf(stderr,
"bloaty: internal error, specified same DWARF attribute more "
"than once\n");
exit(1);
}
action_list_[action.index] = action.action;
}
}
Expand Down Expand Up @@ -1738,33 +1743,45 @@ static bool ReadDWARFAddressRanges(const dwarf::File& file, RangeSink* sink) {
// The DWARF debug info can help us get compileunits info. DIEs for compilation
// units, functions, and global variables often have attributes that will
// resolve to addresses.
static bool ReadDWARFDebugInfo(const dwarf::File& file, RangeSink* sink) {
static bool ReadDWARFDebugInfo(const dwarf::File& file,
const SymbolTable& symtab, RangeSink* sink) {
dwarf::DIEReader die_reader(file);
dwarf::FixedAttrReader<StringPiece, uint64_t, uint64_t> attr_reader(
&die_reader, {DW_AT_name, DW_AT_low_pc, DW_AT_high_pc});
dwarf::FixedAttrReader<StringPiece, StringPiece, uint64_t, uint64_t>
attr_reader(&die_reader, {DW_AT_name, DW_AT_linkage_name, DW_AT_low_pc,
DW_AT_high_pc});

CHECK_RETURN(die_reader.SeekToStart(dwarf::DIEReader::Section::kDebugInfo));

do {
CHECK_RETURN(attr_reader.ReadAttributes(&die_reader));
std::string name = attr_reader.GetAttribute<0>().as_string();
if (name.empty()) {
continue;
}

do {
uint64_t low_pc = attr_reader.GetAttribute<1>();
uint64_t high_pc = attr_reader.GetAttribute<2>();
uint64_t low_pc = attr_reader.GetAttribute<2>();
uint64_t high_pc = attr_reader.GetAttribute<3>();

if (attr_reader.HasAttribute<1>() && attr_reader.HasAttribute<2>()) {
if (attr_reader.HasAttribute<2>() && attr_reader.HasAttribute<3>()) {
sink->AddVMRangeIgnoreDuplicate(low_pc, high_pc - low_pc, name);
}
} while (die_reader.NextDIE());

if (attr_reader.HasAttribute<1>()) {
auto it = symtab.find(attr_reader.GetAttribute<1>());
if (it != symtab.end()) {
sink->AddVMRangeIgnoreDuplicate(it->second.first, it->second.second,
name);
}
}
} while (die_reader.NextDIE() && attr_reader.ReadAttributes(&die_reader));
} while (die_reader.NextCompilationUnit());

return die_reader.IsEof();
}

bool ReadDWARFCompileUnits(const dwarf::File& file, RangeSink* sink) {
bool ReadDWARFCompileUnits(const dwarf::File& file, const SymbolTable& symtab,
RangeSink* sink) {
if (!file.debug_info.size()) {
fprintf(stderr, "bloaty: missing debug info\n");
return false;
Expand All @@ -1774,7 +1791,7 @@ bool ReadDWARFCompileUnits(const dwarf::File& file, RangeSink* sink) {
CHECK_RETURN(ReadDWARFAddressRanges(file, sink));
}

CHECK_RETURN(ReadDWARFDebugInfo(file, sink));
CHECK_RETURN(ReadDWARFDebugInfo(file, symtab, sink));

return true;
}
Expand Down
74 changes: 42 additions & 32 deletions src/elf.cc
Original file line number Diff line number Diff line change
Expand Up @@ -507,65 +507,71 @@ bool ArFile::MemberReader::ReadMember(MemberFile* file) {
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.
sink->AddFileRange("[ELF Headers]", elf.header_region());
sink->AddFileRange("[ELF Headers]", elf.section_headers());
sink->AddFileRange("[ELF Headers]", elf.segment_headers());
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.
sink->AddFileRange("[Unmapped]", elf.entire_file());
MaybeAddFileRange(sink, "[Unmapped]", elf.entire_file());

return true;
}

template <class Func>
bool ForEachElf(RangeSink* sink, Func func) {
ArFile ar_file(sink->input_file().data());
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);

sink->AddFileRange("[AR Headers]", ar_file.magic());
MaybeAddFileRange(sink, "[AR Headers]", ar_file.magic());

while (reader.ReadMember(&member)) {
sink->AddFileRange("[AR Headers]", member.header);
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 {
sink->AddFileRange("[AR Non-ELF Member File]", member.contents);
MaybeAddFileRange(sink, "[AR Non-ELF Member File]",
member.contents);
}
break;
}
case ArFile::MemberFile::kSymbolTable:
sink->AddFileRange("[AR Symbol Table]", member.contents);
MaybeAddFileRange(sink, "[AR Symbol Table]", member.contents);
break;
case ArFile::MemberFile::kLongFilenameTable:
sink->AddFileRange("[AR Headers]", member.contents);
MaybeAddFileRange(sink, "[AR Headers]", member.contents);
break;
}
}
} else {
ElfFile elf(sink->input_file().data());
ElfFile elf(file.data());
if (!elf.IsOpen()) {
fprintf(stderr, "Not an ELF or Archive file: %s\n",
sink->input_file().filename().c_str());
file.filename().c_str());
return false;
}

CHECK_RETURN(
OnElfFile(elf, sink->input_file().filename(), index_base, sink, func));
CHECK_RETURN(OnElfFile(elf, file.filename(), index_base, sink, func));
}

return true;
Expand All @@ -580,12 +586,6 @@ bool ForEachElf(RangeSink* sink, Func func) {
// - nm: display symbols
// - size: display binary size

static uint64_t AlignUpTo(uint64_t offset, uint64_t granularity) {
return offset; // If we allow this we sometimes span mappings.
// Granularity must be a power of two.
return (offset + granularity - 1) & ~(granularity - 1);
}

// For object files, addresses are relative to the section they live in, which
// is indicated by ndx. We split this into:
//
Expand Down Expand Up @@ -620,10 +620,11 @@ static bool CheckNotObject(const char* source, RangeSink* sink) {
return true;
}

static bool ReadELFSymbols(RangeSink* sink) {
bool is_object = IsObjectFile(sink->input_file().data());
return ForEachElf(sink, [=](const ElfFile& elf, StringPiece filename,
uint32_t index_base) {
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));
Expand Down Expand Up @@ -659,8 +660,13 @@ static bool ReadELFSymbols(RangeSink* sink) {
CHECK_RETURN(strtab_section.ReadName(sym.st_name, &name));
uint64_t full_addr =
ToVMAddr(sym.st_value, index_base + sym.st_shndx, is_object);
sink->AddVMRangeAllowAlias(full_addr, AlignUpTo(sym.st_size, 16),
name.as_string());
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)));
}
}
}

Expand All @@ -677,7 +683,8 @@ enum ReportSectionsBy {
static bool DoReadELFSections(RangeSink* sink, enum ReportSectionsBy report_by) {
bool is_object = IsObjectFile(sink->input_file().data());
return ForEachElf(
sink, [=](const ElfFile& elf, StringPiece filename, uint32_t index_base) {
sink->input_file(), sink,
[=](const ElfFile& elf, StringPiece filename, uint32_t index_base) {
if (elf.section_count() == 0) {
return true;
}
Expand Down Expand Up @@ -756,8 +763,9 @@ static bool ReadELFSegments(RangeSink* sink) {
return true;
}

return ForEachElf(sink, [=](const ElfFile& elf, StringPiece filename,
uint32_t index_base) {
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));
Expand Down Expand Up @@ -850,17 +858,19 @@ class ElfFileHandler : public FileHandler {
CHECK_RETURN(DoReadELFSections(sink, kReportBySectionName));
break;
case DataSource::kSymbols:
CHECK_RETURN(ReadELFSymbols(sink));
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, sink));
CHECK_RETURN(ReadDWARFCompileUnits(dwarf, symtab, sink));
break;
}
case DataSource::kInlines: {
Expand Down
5 changes: 5 additions & 0 deletions tests/bloaty_misc_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,8 @@ TEST_F(BloatyTest, NoSections) {
TEST_F(BloatyTest, SectionCountOverflow) {
RunBloaty({"bloaty", "02-section-count-overflow.o"});
}

TEST_F(BloatyTest, InlinesOnSmallFile) {
RunBloaty(
{"bloaty", "-d", "inlines", "03-small-binary-that-crashed-inlines.bin"});
}
Binary file not shown.

0 comments on commit 0c85f9f

Please sign in to comment.