[lldb] Fix DW_TAG_GNU_call_site-DW_AT_low_pc as produced by GCC
authorJan Kratochvil <jan.kratochvil@redhat.com>
Tue, 9 Jun 2020 11:41:41 +0000 (13:41 +0200)
committerJan Kratochvil <jan.kratochvil@redhat.com>
Tue, 9 Jun 2020 11:41:41 +0000 (13:41 +0200)
D80519 <https://reviews.llvm.org/D80519>
added support for `DW_TAG_GNU_call_site` but
Bug 45886 <https://bugs.llvm.org/show_bug.cgi?id=45886>
found one case did not work.

There is:

  0x000000b1:     DW_TAG_GNU_call_site
                    DW_AT_low_pc  (0x000000000040111e)
                    DW_AT_abstract_origin (0x000000cc "a")
  ...
  0x000000cc:   DW_TAG_subprogram
                  DW_AT_name      ("a")
                  DW_AT_prototyped        (true)
                  DW_AT_low_pc    (0x0000000000401109)
                  ^^^^^^^^^^^^ - here it did overwrite the 'low_pc' variable containing value 0x40111e we wanted
                  DW_AT_high_pc   (0x0000000000401114)
                  DW_AT_frame_base        (DW_OP_call_frame_cfa)
                  DW_AT_GNU_all_call_sites        (true)

DW_TAG_GNU_call_site attributes order as produced by GCC:
0x000000b1:     DW_TAG_GNU_call_site
                  DW_AT_low_pc  (0x000000000040111e)
                  DW_AT_abstract_origin (0x000000cc "a")

clang produces the attributes in opposite order:
0x00000064:     DW_TAG_GNU_call_site
                  DW_AT_abstract_origin (0x0000002a "a")
                  DW_AT_low_pc  (0x0000000000401146)

Differential Revision: https://reviews.llvm.org/D81334

lldb/source/Plugins/SymbolFile/DWARF/DWARFBaseDIE.cpp
lldb/source/Plugins/SymbolFile/DWARF/DWARFBaseDIE.h
lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp
lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h
lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
lldb/test/Shell/SymbolFile/DWARF/DW_TAG_GNU_call_site-DW_AT_low_pc.s [new file with mode: 0644]

index c330eff..fcb4240 100644 (file)
@@ -104,9 +104,10 @@ bool DWARFBaseDIE::Supports_DW_AT_APPLE_objc_complete_type() const {
   return IsValid() && GetDWARF()->Supports_DW_AT_APPLE_objc_complete_type(m_cu);
 }
 
-size_t DWARFBaseDIE::GetAttributes(DWARFAttributes &attributes) const {
+size_t DWARFBaseDIE::GetAttributes(DWARFAttributes &attributes,
+                                   Recurse recurse) const {
   if (IsValid())
-    return m_die->GetAttributes(m_cu, attributes);
+    return m_die->GetAttributes(m_cu, attributes, recurse);
   attributes.Clear();
   return 0;
 }
index 0bad53f..059b848 100644 (file)
@@ -110,7 +110,9 @@ public:
   uint64_t GetAttributeValueAsAddress(const dw_attr_t attr,
                                       uint64_t fail_value) const;
 
-  size_t GetAttributes(DWARFAttributes &attributes) const;
+  enum class Recurse : bool { no, yes };
+  size_t GetAttributes(DWARFAttributes &attributes,
+                       Recurse recurse = Recurse::yes) const;
 
 protected:
   DWARFUnit *m_cu;
index a133ce1..f6425a8 100644 (file)
@@ -399,9 +399,10 @@ bool DWARFDebugInfoEntry::GetDIENamesAndRanges(
 // specification or abstract origin attributes and including those in the
 // results. Any duplicate attributes will have the first instance take
 // precedence (this can happen for declaration attributes).
-size_t DWARFDebugInfoEntry::GetAttributes(
-    const DWARFUnit *cu, DWARFAttributes &attributes,
-    uint32_t curr_depth) const {
+size_t DWARFDebugInfoEntry::GetAttributes(const DWARFUnit *cu,
+                                          DWARFAttributes &attributes,
+                                          Recurse recurse,
+                                          uint32_t curr_depth) const {
   const auto *abbrevDecl = GetAbbreviationDeclarationPtr(cu);
   if (abbrevDecl) {
     const DWARFDataExtractor &data = cu->GetData();
@@ -432,12 +433,13 @@ size_t DWARFDebugInfoEntry::GetAttributes(
         break;
       }
 
-      if ((attr == DW_AT_specification) || (attr == DW_AT_abstract_origin)) {
+      if (recurse == Recurse::yes &&
+          ((attr == DW_AT_specification) || (attr == DW_AT_abstract_origin))) {
         if (form_value.ExtractValue(data, &offset)) {
           DWARFDIE spec_die = form_value.Reference();
           if (spec_die)
             spec_die.GetDIE()->GetAttributes(spec_die.GetCU(), attributes,
-                                             curr_depth + 1);
+                                             recurse, curr_depth + 1);
         }
       } else {
         llvm::Optional<uint8_t> fixed_skip_size = DWARFFormValue::GetFixedSize(form, cu);
@@ -730,7 +732,7 @@ DWARFDeclContext DWARFDebugInfoEntry::GetDWARFDeclContext(DWARFUnit *cu) const {
 DWARFDIE
 DWARFDebugInfoEntry::GetParentDeclContextDIE(DWARFUnit *cu) const {
   DWARFAttributes attributes;
-  GetAttributes(cu, attributes);
+  GetAttributes(cu, attributes, Recurse::yes);
   return GetParentDeclContextDIE(cu, attributes);
 }
 
@@ -780,7 +782,7 @@ DWARFDebugInfoEntry::GetParentDeclContextDIE(
 const char *DWARFDebugInfoEntry::GetQualifiedName(DWARFUnit *cu,
                                                   std::string &storage) const {
   DWARFAttributes attributes;
-  GetAttributes(cu, attributes);
+  GetAttributes(cu, attributes, Recurse::yes);
   return GetQualifiedName(cu, attributes, storage);
 }
 
index 7bcff31..3019e17 100644 (file)
@@ -13,6 +13,7 @@
 #include "llvm/ADT/SmallVector.h"
 
 #include "DWARFAbbreviationDeclaration.h"
+#include "DWARFBaseDIE.h"
 #include "DWARFDebugAbbrev.h"
 #include "DWARFDebugRanges.h"
 #include <map>
@@ -47,8 +48,10 @@ public:
   bool Extract(const lldb_private::DWARFDataExtractor &data,
                const DWARFUnit *cu, lldb::offset_t *offset_ptr);
 
-  size_t GetAttributes(const DWARFUnit *cu, DWARFAttributes &attrs) const {
-    return GetAttributes(cu, attrs, 0 /* curr_depth */);
+  using Recurse = DWARFBaseDIE::Recurse;
+  size_t GetAttributes(const DWARFUnit *cu, DWARFAttributes &attrs,
+                       Recurse recurse = Recurse::yes) const {
+    return GetAttributes(cu, attrs, recurse, 0 /* curr_depth */);
   }
 
   dw_offset_t
@@ -178,7 +181,7 @@ protected:
 
 private:
   size_t GetAttributes(const DWARFUnit *cu, DWARFAttributes &attrs,
-                       uint32_t curr_depth) const;
+                       Recurse recurse, uint32_t curr_depth) const;
 };
 
 #endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFDEBUGINFOENTRY_H
index b6ae9e2..6f2444b 100644 (file)
@@ -3677,8 +3677,12 @@ SymbolFileDWARF::CollectCallEdges(ModuleSP module, DWARFDIE function_die) {
     addr_t low_pc = LLDB_INVALID_ADDRESS;
     bool tail_call = false;
 
+    // Second DW_AT_low_pc may come from DW_TAG_subprogram referenced by
+    // DW_TAG_GNU_call_site's DW_AT_abstract_origin overwriting our 'low_pc'.
+    // So do not inherit attributes from DW_AT_abstract_origin.
     DWARFAttributes attributes;
-    const size_t num_attributes = child.GetAttributes(attributes);
+    const size_t num_attributes =
+        child.GetAttributes(attributes, DWARFDIE::Recurse::no);
     for (size_t i = 0; i < num_attributes; ++i) {
       DWARFFormValue form_value;
       if (!attributes.ExtractFormValueAtIndex(i, form_value)) {
diff --git a/lldb/test/Shell/SymbolFile/DWARF/DW_TAG_GNU_call_site-DW_AT_low_pc.s b/lldb/test/Shell/SymbolFile/DWARF/DW_TAG_GNU_call_site-DW_AT_low_pc.s
new file mode 100644 (file)
index 0000000..29cafeb
--- /dev/null
@@ -0,0 +1,230 @@
+# This tests that lldb is compatible with DWARF-4 entry values GNU extension
+# with DW_TAG_GNU_call_site attributes order as produced by GCC:
+# 0x000000b1:     DW_TAG_GNU_call_site
+#                   DW_AT_low_pc  (0x000000000040111e)
+#                   DW_AT_abstract_origin (0x000000cc "a")
+# clang produces the attributes in opposite order:
+# 0x00000064:     DW_TAG_GNU_call_site
+#                   DW_AT_abstract_origin (0x0000002a "a")
+#                   DW_AT_low_pc  (0x0000000000401146)
+
+# REQUIRES: target-x86_64, system-linux, lld
+
+# RUN: %clang_host -o %t %s
+# RUN: %lldb %t -o r -o 'p p' -o exit | FileCheck %s
+
+# CHECK: (int) $0 = 1
+
+# The DWARF has been produced and modified from:
+# static __attribute__((noinline, noclone)) void b(int x) {
+#   asm("");
+# }
+# static __attribute__((noinline, noclone)) void a(int p) {
+#   b(2);
+# }
+# int main() {
+#   a(1);
+#   return 0;
+# }
+
+       .text
+.Ltext0:
+       .type   b, @function
+b:
+       .cfi_startproc
+       ret
+       .cfi_endproc
+       .size   b, .-b
+       .type   a, @function
+a:
+.LVL1:
+.LFB1:
+       .cfi_startproc
+       movl    $2, %edi
+.LVL2:
+       call    b
+       int3
+       ret
+       .cfi_endproc
+.LFE1:
+       .size   a, .-a
+       .globl  main
+       .type   main, @function
+main:
+.LFB2:
+       .cfi_startproc
+       movl    $1, %edi
+       call    a
+.LVL4:
+       movl    $0, %eax
+       ret
+       .cfi_endproc
+.LFE2:
+       .size   main, .-main
+.Letext0:
+       .section        .debug_info,"",@progbits
+.Ldebug_info0:
+       .long   .Ldebuginfo_end - .Ldebuginfo_start     # Length of Compilation Unit Info
+.Ldebuginfo_start:
+       .value  0x4     # DWARF version number
+       .long   .Ldebug_abbrev0 # Offset Into Abbrev. Section
+       .byte   0x8     # Pointer Size (in bytes)
+       .uleb128 0x1    # (DIE (0xb) DW_TAG_compile_unit)
+       .asciz "GNU C17 10.1.1 20200507 (Red Hat 10.1.1-1) -mtune=generic -march=x86-64 -g -Og" # DW_AT_producer: "GNU C17 10.1.1 20200507 (Red Hat 10.1.1-1) -mtune=generic -march=x86-64 -g -Og"
+       .byte   0xc     # DW_AT_language
+       .asciz "DW_TAG_GNU_call_site-DW_AT_low_pc.c"    # DW_AT_name
+       .asciz ""       # DW_AT_comp_dir: "/home/jkratoch/t"
+       .quad   .Ltext0 # DW_AT_low_pc
+       .quad   .Letext0-.Ltext0        # DW_AT_high_pc
+       .uleb128 0x2    # (DIE (0x2d) DW_TAG_subprogram)
+                       # DW_AT_external
+       .asciz "main"   # DW_AT_name: "main"
+       .long   .Ltype_int      # DW_AT_type
+       .quad   .LFB2   # DW_AT_low_pc
+       .quad   .LFE2-.LFB2     # DW_AT_high_pc
+       .uleb128 0x1    # DW_AT_frame_base
+       .byte   0x9c    # DW_OP_call_frame_cfa
+                       # DW_AT_GNU_all_call_sites
+       .uleb128 0x3    # (DIE (0x4f) DW_TAG_GNU_call_site)
+       .quad   .LVL4   # DW_AT_low_pc
+       .long   .Lfunc_a        # DW_AT_abstract_origin
+       .uleb128 0x4    # (DIE (0x5c) DW_TAG_GNU_call_site_parameter)
+       .uleb128 0x1    # DW_AT_location
+       .byte   0x55    # DW_OP_reg5
+       .uleb128 0x1    # DW_AT_GNU_call_site_value
+       .byte   0x31    # DW_OP_lit1
+       .byte   0       # end of children of DIE 0x4f
+       .byte   0       # end of children of DIE 0x2d
+.Ltype_int:
+       .uleb128 0x5    # (DIE (0x63) DW_TAG_base_type)
+       .byte   0x4     # DW_AT_byte_size
+       .byte   0x5     # DW_AT_encoding
+       .asciz "int"    # DW_AT_name
+.Lfunc_a:
+       .uleb128 0x6    # (DIE (0x6a) DW_TAG_subprogram)
+       .asciz "a"      # DW_AT_name
+                       # DW_AT_prototyped
+       .quad   .LFB1   # DW_AT_low_pc
+       .quad   .LFE1-.LFB1     # DW_AT_high_pc
+       .uleb128 0x1    # DW_AT_frame_base
+       .byte   0x9c    # DW_OP_call_frame_cfa
+                       # DW_AT_GNU_all_call_sites
+       .uleb128 0x7    # (DIE (0x86) DW_TAG_formal_parameter)
+       .asciz "p"      # DW_AT_name
+       .long   .Ltype_int      # DW_AT_type
+       .long   .LLST0  # DW_AT_location
+       .byte   0       # end of children of DIE 0x6a
+       .byte   0       # end of children of DIE 0xb
+.Ldebuginfo_end:
+       .section        .debug_abbrev,"",@progbits
+.Ldebug_abbrev0:
+       .uleb128 0x1    # (abbrev code)
+       .uleb128 0x11   # (TAG: DW_TAG_compile_unit)
+       .byte   0x1     # DW_children_yes
+       .uleb128 0x25   # (DW_AT_producer)
+       .uleb128 0x8    # (DW_FORM_string)
+       .uleb128 0x13   # (DW_AT_language)
+       .uleb128 0xb    # (DW_FORM_data1)
+       .uleb128 0x3    # (DW_AT_name)
+       .uleb128 0x8    # (DW_FORM_string)
+       .uleb128 0x1b   # (DW_AT_comp_dir)
+       .uleb128 0x8    # (DW_FORM_string)
+       .uleb128 0x11   # (DW_AT_low_pc)
+       .uleb128 0x1    # (DW_FORM_addr)
+       .uleb128 0x12   # (DW_AT_high_pc)
+       .uleb128 0x7    # (DW_FORM_data8)
+       .byte   0
+       .byte   0
+       .uleb128 0x2    # (abbrev code)
+       .uleb128 0x2e   # (TAG: DW_TAG_subprogram)
+       .byte   0x1     # DW_children_yes
+       .uleb128 0x3f   # (DW_AT_external)
+       .uleb128 0x19   # (DW_FORM_flag_present)
+       .uleb128 0x3    # (DW_AT_name)
+       .uleb128 0x8    # (DW_FORM_string)
+       .uleb128 0x49   # (DW_AT_type)
+       .uleb128 0x13   # (DW_FORM_ref4)
+       .uleb128 0x11   # (DW_AT_low_pc)
+       .uleb128 0x1    # (DW_FORM_addr)
+       .uleb128 0x12   # (DW_AT_high_pc)
+       .uleb128 0x7    # (DW_FORM_data8)
+       .uleb128 0x40   # (DW_AT_frame_base)
+       .uleb128 0x18   # (DW_FORM_exprloc)
+       .uleb128 0x2117 # (DW_AT_GNU_all_call_sites)
+       .uleb128 0x19   # (DW_FORM_flag_present)
+       .byte   0
+       .byte   0
+       .uleb128 0x3    # (abbrev code)
+       .uleb128 0x4109 # (TAG: DW_TAG_GNU_call_site)
+       .byte   0x1     # DW_children_yes
+       .uleb128 0x11   # (DW_AT_low_pc)
+       .uleb128 0x1    # (DW_FORM_addr)
+       .uleb128 0x31   # (DW_AT_abstract_origin)
+       .uleb128 0x13   # (DW_FORM_ref4)
+       .byte   0
+       .byte   0
+       .uleb128 0x4    # (abbrev code)
+       .uleb128 0x410a # (TAG: DW_TAG_GNU_call_site_parameter)
+       .byte   0       # DW_children_no
+       .uleb128 0x2    # (DW_AT_location)
+       .uleb128 0x18   # (DW_FORM_exprloc)
+       .uleb128 0x2111 # (DW_AT_GNU_call_site_value)
+       .uleb128 0x18   # (DW_FORM_exprloc)
+       .byte   0
+       .byte   0
+       .uleb128 0x5    # (abbrev code)
+       .uleb128 0x24   # (TAG: DW_TAG_base_type)
+       .byte   0       # DW_children_no
+       .uleb128 0xb    # (DW_AT_byte_size)
+       .uleb128 0xb    # (DW_FORM_data1)
+       .uleb128 0x3e   # (DW_AT_encoding)
+       .uleb128 0xb    # (DW_FORM_data1)
+       .uleb128 0x3    # (DW_AT_name)
+       .uleb128 0x8    # (DW_FORM_string)
+       .byte   0
+       .byte   0
+       .uleb128 0x6    # (abbrev code)
+       .uleb128 0x2e   # (TAG: DW_TAG_subprogram)
+       .byte   0x1     # DW_children_yes
+       .uleb128 0x3    # (DW_AT_name)
+       .uleb128 0x8    # (DW_FORM_string)
+       .uleb128 0x27   # (DW_AT_prototyped)
+       .uleb128 0x19   # (DW_FORM_flag_present)
+       .uleb128 0x11   # (DW_AT_low_pc)
+       .uleb128 0x1    # (DW_FORM_addr)
+       .uleb128 0x12   # (DW_AT_high_pc)
+       .uleb128 0x7    # (DW_FORM_data8)
+       .uleb128 0x40   # (DW_AT_frame_base)
+       .uleb128 0x18   # (DW_FORM_exprloc)
+       .uleb128 0x2117 # (DW_AT_GNU_all_call_sites)
+       .uleb128 0x19   # (DW_FORM_flag_present)
+       .byte   0
+       .byte   0
+       .uleb128 0x7    # (abbrev code)
+       .uleb128 0x5    # (TAG: DW_TAG_formal_parameter)
+       .byte   0       # DW_children_no
+       .uleb128 0x3    # (DW_AT_name)
+       .uleb128 0x8    # (DW_FORM_string)
+       .uleb128 0x49   # (DW_AT_type)
+       .uleb128 0x13   # (DW_FORM_ref4)
+       .uleb128 0x2    # (DW_AT_location)
+       .uleb128 0x17   # (DW_FORM_sec_offset)
+       .byte   0
+       .byte   0
+       .byte   0
+       .section        .debug_loc,"",@progbits
+.LLST0:
+       .quad   .LVL1-.Ltext0   # Location list begin address (*.LLST0)
+       .quad   .LVL2-.Ltext0   # Location list end address (*.LLST0)
+       .value  0x1     # Location expression size
+       .byte   0x55    # DW_OP_reg5
+       .quad   .LVL2-.Ltext0   # Location list begin address (*.LLST0)
+       .quad   .LFE1-.Ltext0   # Location list end address (*.LLST0)
+       .value  0x4     # Location expression size
+       .byte   0xf3    # DW_OP_GNU_entry_value
+       .uleb128 0x1
+       .byte   0x55    # DW_OP_reg5
+       .byte   0x9f    # DW_OP_stack_value
+       .quad   0       # Location list terminator begin (*.LLST0)
+       .quad   0       # Location list terminator end (*.LLST0)
+       .section        .note.GNU-stack,"",@progbits