[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