diff --git a/src/bloaty.h b/src/bloaty.h index 417b6342..a3cb57f1 100644 --- a/src/bloaty.h +++ b/src/bloaty.h @@ -168,12 +168,12 @@ struct File { } // namespace dwarf -typedef std::unordered_map> - SymbolTable; +typedef std::map> 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); diff --git a/src/dwarf.cc b/src/dwarf.cc index 8fbe1d98..8145905a 100644 --- a/src/dwarf.cc +++ b/src/dwarf.cc @@ -534,8 +534,6 @@ class DIEReader { bool ReadCompilationUnitHeader(StringPiece data); bool ReadCode(); - bool SkipDIEs(size_t levels); - bool SkipAttributes(); enum class State { kReadyToReadAttributes, @@ -619,7 +617,6 @@ bool DIEReader::NextCompilationUnit() { } bool DIEReader::NextDIE() { - do { if (remaining_.size() == 0) { state_ = State::kEof; @@ -884,14 +881,18 @@ class FormReader::value>::type> Func func) { switch (form) { case DW_FORM_data1: + case DW_FORM_ref1: return func(&Base::template ReadAttr<&ME::ReadFixed>); case DW_FORM_data2: + case DW_FORM_ref2: CHECK_RETURN(sizeof(T) >= 2); return func(&Base::template ReadAttr<&ME::ReadFixed>); case DW_FORM_data4: + case DW_FORM_ref4: CHECK_RETURN(sizeof(T) >= 4); return func(&Base::template ReadAttr<&ME::ReadFixed>); case DW_FORM_data8: + case DW_FORM_ref8: CHECK_RETURN(sizeof(T) >= 8); return func(&Base::template ReadAttr<&ME::ReadFixed>); case DW_FORM_addr: @@ -1014,8 +1015,11 @@ class FormReader : public FormReaderBase> { 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: @@ -1067,6 +1071,8 @@ class FormReader : public FormReaderBase> { } private: + bool DoNothing() { return true; } + template bool SkipFixed() { return SkipBytes(N, &data_); @@ -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; } } @@ -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 attr_reader( - &die_reader, {DW_AT_name, DW_AT_low_pc, DW_AT_high_pc}); + dwarf::FixedAttrReader + 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; @@ -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; } diff --git a/src/elf.cc b/src/elf.cc index d918ad2d..592d826f 100644 --- a/src/elf.cc +++ b/src/elf.cc @@ -507,6 +507,12 @@ bool ArFile::MemberReader::ReadMember(MemberFile* file) { return true; } +void MaybeAddFileRange(RangeSink* sink, StringPiece label, StringPiece range) { + if (sink) { + sink->AddFileRange(label, range); + } +} + template bool OnElfFile(const ElfFile& elf, StringPiece filename, unsigned long index_base, RangeSink* sink, Func func) { @@ -514,29 +520,29 @@ bool OnElfFile(const ElfFile& elf, StringPiece filename, // 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 -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); @@ -544,28 +550,28 @@ bool ForEachElf(RangeSink* sink, Func func) { 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; @@ -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: // @@ -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, §ion)); @@ -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))); + } } } @@ -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; } @@ -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)); @@ -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: { diff --git a/tests/bloaty_misc_test.cc b/tests/bloaty_misc_test.cc index 7d9ed2be..498e03ce 100644 --- a/tests/bloaty_misc_test.cc +++ b/tests/bloaty_misc_test.cc @@ -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"}); +} diff --git a/tests/testdata/misc/03-small-binary-that-crashed-inlines.bin b/tests/testdata/misc/03-small-binary-that-crashed-inlines.bin new file mode 100644 index 00000000..c4a8facb Binary files /dev/null and b/tests/testdata/misc/03-small-binary-that-crashed-inlines.bin differ