[dwarf] Ignore DIEs where DW_AT_declaration is set.
diff --git a/src/dwarf.cc b/src/dwarf.cc
index 3fc8446..7823e0b 100644
--- a/src/dwarf.cc
+++ b/src/dwarf.cc
@@ -1552,6 +1552,7 @@
struct GeneralDIE {
absl::optional<dwarf::AttrValue> name;
absl::optional<dwarf::AttrValue> linkage_name;
+ absl::optional<dwarf::AttrValue> declaration;
absl::optional<dwarf::AttrValue> location;
absl::optional<dwarf::AttrValue> low_pc;
absl::optional<dwarf::AttrValue> high_pc;
@@ -1568,6 +1569,9 @@
case DW_AT_linkage_name:
die->linkage_name = val;
break;
+ case DW_AT_declaration:
+ die->declaration = val;
+ break;
case DW_AT_location:
die->location = val;
break;
@@ -1605,34 +1609,57 @@
uint64_t stmt_list_ = 0;
};
+uint64_t TryReadPcPair(const std::string& name, const GeneralDIE& die,
+ const dwarf::DIEReader& die_reader, RangeSink* sink) {
+ if (!die.low_pc || !die.high_pc || !die.low_pc->IsUint()) return 0;
+
+ uint64_t low_pc = die.low_pc->GetUint(die_reader);
+ uint8_t address_size = die_reader.unit_sizes().address_size();
+ if (!dwarf::IsValidDwarfAddress(low_pc, address_size)) return 0;
+
+ uint64_t size;
+
+ switch (die.high_pc->form()) {
+ case DW_FORM_addr:
+ case DW_FORM_addrx:
+ case DW_FORM_addrx1:
+ case DW_FORM_addrx2:
+ case DW_FORM_addrx3:
+ case DW_FORM_addrx4:
+ // high_pc is absolute.
+ size = die.high_pc->GetUint(die_reader) - low_pc;
+ break;
+ case DW_FORM_data1:
+ case DW_FORM_data2:
+ case DW_FORM_data4:
+ case DW_FORM_data8:
+ // high_pc is a size.
+ size = *die.high_pc->ToUint(die_reader);
+ break;
+ default:
+ if (verbose_level > 0) {
+ fprintf(stderr, "Unexpected form for high_pc: %d\n", die.high_pc->form());
+ }
+ return 0;
+ }
+
+ sink->AddVMRangeIgnoreDuplicate("dwarf_pcpair", low_pc, size, name);
+ return low_pc;
+}
+
// To view DIEs for a given file, try:
// readelf --debug-dump=info foo.bin
void AddDIE(const dwarf::File& file, const std::string& name,
const GeneralDIE& die, const SymbolTable& symtab,
const DualMap& symbol_map, const dwarf::DIEReader& die_reader,
RangeSink* sink) {
- uint64_t low_pc = 0;
- // Some DIEs mark address ranges with high_pc/low_pc pairs (especially
- // functions).
- if (die.low_pc && die.low_pc->IsUint() && die.high_pc &&
- die.high_pc->IsUint() &&
- dwarf::IsValidDwarfAddress(die.low_pc->GetUint(die_reader),
- die_reader.unit_sizes().address_size())) {
- low_pc = die.low_pc->GetUint(die_reader);
- uint64_t high_pc = die.high_pc->GetUint(die_reader);
-
- // It appears that some compilers make high_pc a size, and others make it an
- // address.
- if (high_pc >= low_pc) {
- high_pc -= low_pc;
- }
- sink->AddVMRangeIgnoreDuplicate("dwarf_pcpair", low_pc, high_pc, name);
- }
+ uint64_t low_pc = TryReadPcPair(name, die, die_reader, sink);
// Sometimes a DIE has a linkage_name, which we can look up in the symbol
// table.
if (die.linkage_name && die.linkage_name->IsString()) {
- auto it = symtab.find(die.linkage_name->GetString(die_reader));
+ auto linkage_name = die.linkage_name->GetString(die_reader);
+ auto it = symtab.find(linkage_name);
if (it != symtab.end()) {
sink->AddVMRangeIgnoreDuplicate("dwarf_linkagename", it->second.first,
it->second.second, name);
@@ -2136,9 +2163,15 @@
});
// low_pc == 0 is a signal that this routine was stripped out of the
- // final binary. Skip this DIE and all of its children.
- if (die.low_pc && die.low_pc->IsUint() &&
- die.low_pc->GetUint(die_reader) == 0) {
+ // final binary.
+ bool is_stripped = die.low_pc && die.low_pc->IsUint() &&
+ die.low_pc->GetUint(die_reader) == 0;
+ // A declaration is not a definition and should not be attributed to this
+ // compileunit.
+ bool is_decl = die.declaration && die.declaration->IsUint() &&
+ die.declaration->GetUint(die_reader);
+
+ if (is_stripped || is_decl) {
die_reader.SkipChildren();
} else {
AddDIE(file, compileunit_name, die, symtab, symbol_map, die_reader,
diff --git a/tests/dwarf/debug_info/ignore-declarations.test b/tests/dwarf/debug_info/ignore-declarations.test
new file mode 100644
index 0000000..a08b5ee
--- /dev/null
+++ b/tests/dwarf/debug_info/ignore-declarations.test
@@ -0,0 +1,137 @@
+# Test that we properly ignore DIEs that have DW_AT_declaration=true.
+# This indicates a function that was declared, but not defined.
+# We don't want to register declarations, only definitions.
+
+# RUN: %yaml2obj %s --docnum=1 -o %t.obj
+# RUN: %yaml2obj %s --docnum=2 -o %t.dwo
+# RUN: %bloaty %t.obj --debug-file %t.dwo -d compileunits --raw-map --domain=vm | %FileCheck %s
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_DYN
+ Machine: EM_X86_64
+ Entry: 0x1040
+ProgramHeaders:
+ - Type: PT_LOAD
+ Flags: [ PF_X, PF_R ]
+ FirstSec: .text
+ LastSec: .text
+ VAddr: 0x1000
+ Align: 0x1000
+Sections:
+ - Name: .note.gnu.build-id
+ Type: SHT_NOTE
+ Notes:
+ - Name: GNU
+ Desc: 6CF422D909772A0FB5400518A689D9F15F14BF57
+ Type: 0x3 # NT_GNU_BUILD_ID
+ - Name: .text
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ Address: 0x1000
+ AddressAlign: 0x10
+ Content: 31ED4989D15E4889E24883E4F050544C8D053A010000488D0DD300000040
+...
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_DYN
+ Machine: EM_X86_64
+ Entry: 0x1040
+Sections:
+ - Name: .note.gnu.build-id
+ Type: SHT_NOTE
+ Notes:
+ - Name: GNU
+ Desc: 6CF422D909772A0FB5400518A689D9F15F14BF57
+ Type: 0x3 # NT_GNU_BUILD_ID
+DWARF:
+ debug_str:
+ - foo.c
+ - bar.c
+ debug_abbrev:
+ - ID: 0
+ Table:
+ - Code: 0x1
+ Tag: DW_TAG_compile_unit
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_strp
+ - Code: 0x2
+ Tag: DW_TAG_subprogram
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_low_pc
+ Form: DW_FORM_addr
+ - Attribute: DW_AT_high_pc
+ Form: DW_FORM_data4
+ - Attribute: DW_AT_declaration
+ Form: DW_FORM_flag_present
+ - Code: 0x3
+ Tag: DW_TAG_subprogram
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_low_pc
+ Form: DW_FORM_addr
+ - Attribute: DW_AT_high_pc
+ Form: DW_FORM_data4
+ debug_info:
+ # 0x0000000b: DW_TAG_compile_unit
+ # DW_AT_name ("foo.c")
+ #
+ # 0x00000010: DW_TAG_subprogram
+ # DW_AT_low_pc (0x0000000000001000)
+ # DW_AT_high_pc (0x0000000000001010)
+ # # This entry should be ignored because declaration=true.
+ # DW_AT_declaration (true)
+ #
+ # 0x0000001d: DW_TAG_subprogram
+ # DW_AT_low_pc (0x0000000000001010)
+ # DW_AT_high_pc (0x0000000000001020)
+ - Version: 4
+ AbbrevTableID: 0
+ AbbrOffset: 0x0
+ AddrSize: 8
+ Entries:
+ - AbbrCode: 0x1
+ Values:
+ - Value: 0x0
+ - AbbrCode: 0x2
+ Values:
+ - Value: 0x1000
+ - Value: 0x10
+ - AbbrCode: 0x3
+ Values:
+ - Value: 0x1010
+ - Value: 0x10
+ - AbbrCode: 0x0
+ # 0x00000036: DW_TAG_compile_unit
+ # DW_AT_name ("bar.c")
+ #
+ # 0x0000003b: DW_TAG_subprogram
+ # DW_AT_low_pc (0x0000000000001000)
+ # DW_AT_high_pc (0x0000000000001010)
+ - Version: 4
+ AbbrevTableID: 0
+ AbbrOffset: 0x0
+ AddrSize: 8
+ Entries:
+ - AbbrCode: 0x1
+ Values:
+ - Value: 0x6
+ - AbbrCode: 0x3
+ Values:
+ - Value: 0x1000
+ - Value: 0x10
+ - AbbrCode: 0x0
+...
+
+# CHECK: VM MAP:
+# CHECK: 0000-1000 4096 [-- Nothing mapped --]
+# CHECK: 1000-1010 16 bar.c
+# CHECK: 1010-101e 14 foo.c