labath created this revision.
labath added reviewers: JDevlieghere, dblaikie, probinson.
Herald added a subscriber: aprantl.
Herald added a project: LLVM.

Interpreting a .debug_loclists entry is not completely trivial [citation
needed]. This patch creates a function which can be used by any
libDebugInfo user (thinking of LLDB mainly) to get the range of an
entry.

The debug_loclists parser already contained a partial implementation of
that in the dump function. This implementation is replaced by a call to
the new "getRange" function, and it falls back to printing of raw data
in case we fail to get the address range.

Because LLDB is not fully converted to llvm's debug info parser, I
provide two getRange signatures: one takes a DWARFUnit*, which is used
to resolve .debug_addr references; and one which delegates this job to a
user-supplied callback.

I add a more thorough test of debug_loclists dumping capabilities. In
writing this test, I discovered that we're not able to handle
relocations in the debug_loclists section. However, fixing this was not
completely straight-forward, so I left a TODO, and will address that in
a separate patch.


Repository:
  rL LLVM

https://reviews.llvm.org/D68270

Files:
  include/llvm/BinaryFormat/Dwarf.h
  include/llvm/DebugInfo/DWARF/DWARFDebugLoc.h
  lib/BinaryFormat/Dwarf.cpp
  lib/DebugInfo/DWARF/DWARFDebugLoc.cpp
  test/DebugInfo/X86/fission-ranges.ll
  test/DebugInfo/X86/loclists-dwp.ll
  test/tools/llvm-dwarfdump/X86/debug_loc_dwo.s
  test/tools/llvm-dwarfdump/X86/debug_loclists.s
  test/tools/llvm-dwarfdump/X86/debug_loclists_startx_length.s

Index: test/tools/llvm-dwarfdump/X86/debug_loclists_startx_length.s
===================================================================
--- test/tools/llvm-dwarfdump/X86/debug_loclists_startx_length.s
+++ test/tools/llvm-dwarfdump/X86/debug_loclists_startx_length.s
@@ -8,7 +8,7 @@
 # CHECK:         .debug_loclists contents:
 # CHECK-NEXT:    0x00000000: locations list header: length = 0x0000000e, version = 0x0005, addr_size = 0x08, seg_size = 0x00, offset_entry_count = 0x00000000
 # CHECK-NEXT:    0x00000000:
-# CHECK-NEXT:    Addr idx 1 (w/ length 16): DW_OP_reg5 RDI
+# CHECK-NEXT:    {DW_LLE_startx_length, 0x0000000000000001, 0x0000000000000010}: DW_OP_reg5 RDI
 
 .section .debug_loclists,"",@progbits
  .long  .Ldebug_loclist_table_end0-.Ldebug_loclist_table_start0
Index: test/tools/llvm-dwarfdump/X86/debug_loclists.s
===================================================================
--- /dev/null
+++ test/tools/llvm-dwarfdump/X86/debug_loclists.s
@@ -0,0 +1,122 @@
+# RUN: llvm-mc %s -filetype obj -triple x86_64-pc-linux -o %t
+# RUN: llvm-dwarfdump %t | FileCheck %s
+
+
+# CHECK:          DW_AT_location        (0x0000000c
+# CHECK-NEXT:        [0x0000000000000000, 0x0000000000000001): DW_OP_reg0 RAX
+# CHECK-NEXT:        [0x0000000000000001, 0x0000000000000002): DW_OP_reg1 RDX
+# CHECK-NEXT:        [0x0000000000000002, 0x0000000000000003): DW_OP_reg2 RCX
+# CHECK-NEXT:        [0x0000000000000003, 0x0000000000000004): DW_OP_reg3 RBX
+# CHECK-NEXT:        {DW_LLE_startx_length, 0x000000000000dead, 0x0000000000000001}: DW_OP_reg4 RSI)
+
+
+        .text
+f:                                      # @f
+.Lf0:
+        nop
+.Lf1:
+        nop
+.Lf2:
+        nop
+.Lf3:
+        nop
+.Lf4:
+.Lfend:
+                                        # -- End function
+        .section        .debug_loclists,"",@progbits
+        .long   .Ldebug_loclist_table_end0-.Ldebug_loclist_table_start0 # Length
+.Ldebug_loclist_table_start0:
+        .short  5                       # Version
+        .byte   8                       # Address size
+        .byte   0                       # Segment selector size
+        .long   0                       # Offset entry count
+.Lloclists_table_base0:
+.Ldebug_loc0:
+        .byte   3                       # DW_LLE_startx_length
+        .uleb128 0                      #   start idx
+        .uleb128 .Lf1-.Lf0              #   length
+        .byte   1                       # Loc expr size
+        .byte   80                      # super-register DW_OP_reg0
+        .byte   4                       # DW_LLE_offset_pair
+        .uleb128 .Lf1-.Lf0              #   starting offset
+        .uleb128 .Lf2-.Lf0              #   ending offset
+        .byte   1                       # Loc expr size
+        .byte   81                      # super-register DW_OP_reg1
+        .byte   8                       # DW_LLE_start_length
+        # TODO: Make it possible to use .Lf2 here.
+        .quad   2                       #   starting offset
+        .uleb128 .Lf3-.Lf2              #   length
+        .byte   1                       # Loc expr size
+        .byte   82                      # super-register DW_OP_reg2
+        .byte   6                       # DW_LLE_base_address
+        # TODO: Make it possible to use .Lf3 here.
+        .quad   3                       #   base address
+        .byte   4                       # DW_LLE_offset_pair
+        .uleb128 .Lf3-.Lf3              #   starting offset
+        .uleb128 .Lf4-.Lf3              #   ending offset
+        .byte   1                       # Loc expr size
+        .byte   83                      # super-register DW_OP_reg3
+        .byte   3                       # DW_LLE_startx_length
+        .uleb128 0xdead                 #   start idx
+        .uleb128 .Lf1-.Lf0              #   length
+        .byte   1                       # Loc expr size
+        .byte   84                      # super-register DW_OP_reg4
+        .byte   0                       # DW_LLE_end_of_list
+.Ldebug_loclist_table_end0:
+
+        .section        .debug_abbrev,"",@progbits
+        .byte   1                       # Abbreviation Code
+        .byte   17                      # DW_TAG_compile_unit
+        .byte   1                       # DW_CHILDREN_yes
+        .byte   115                     # DW_AT_addr_base
+        .byte   23                      # DW_FORM_sec_offset
+        .ascii  "\214\001"              # DW_AT_loclists_base
+        .byte   23                      # DW_FORM_sec_offset
+        .byte   0                       # EOM(1)
+        .byte   0                       # EOM(2)
+        .byte   2                       # Abbreviation Code
+        .byte   46                      # DW_TAG_subprogram
+        .byte   1                       # DW_CHILDREN_yes
+        .byte   17                      # DW_AT_low_pc
+        .byte   27                      # DW_FORM_addrx
+        .byte   18                      # DW_AT_high_pc
+        .byte   6                       # DW_FORM_data4
+        .byte   0                       # EOM(1)
+        .byte   0                       # EOM(2)
+        .byte   3                       # Abbreviation Code
+        .byte   5                       # DW_TAG_formal_parameter
+        .byte   0                       # DW_CHILDREN_no
+        .byte   2                       # DW_AT_location
+        .byte   23                      # DW_FORM_sec_offset
+        .byte   0                       # EOM(1)
+        .byte   0                       # EOM(2)
+        .byte   0                       # EOM(3)
+        .section        .debug_info,"",@progbits
+.Lcu_begin0:
+        .long   .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit
+.Ldebug_info_start0:
+        .short  5                       # DWARF version number
+        .byte   1                       # DWARF Unit Type
+        .byte   8                       # Address Size (in bytes)
+        .long   .debug_abbrev           # Offset Into Abbrev. Section
+        .byte   1                       # Abbrev [1] 0xc:0x3c DW_TAG_compile_unit
+        .long   .Laddr_table_base0      # DW_AT_addr_base
+        .long   .Lloclists_table_base0  # DW_AT_loclists_base
+        .byte   2                       # Abbrev [2] 0x27:0x1c DW_TAG_subprogram
+        .byte   0                       # DW_AT_low_pc
+        .long   .Lfend-.Lf0             # DW_AT_high_pc
+        .byte   3                       # Abbrev [3] 0x36:0xc DW_TAG_formal_parameter
+        .long   .Ldebug_loc0            # DW_AT_location
+        .byte   0                       # End Of Children Mark
+        .byte   0                       # End Of Children Mark
+.Ldebug_info_end0:
+
+        .section        .debug_addr,"",@progbits
+        .long   .Ldebug_addr_end0-.Ldebug_addr_start0 # Length of contribution
+.Ldebug_addr_start0:
+        .short  5                       # DWARF version number
+        .byte   8                       # Address size
+        .byte   0                       # Segment selector size
+.Laddr_table_base0:
+        .quad   .Lf0
+.Ldebug_addr_end0:
Index: test/tools/llvm-dwarfdump/X86/debug_loc_dwo.s
===================================================================
--- test/tools/llvm-dwarfdump/X86/debug_loc_dwo.s
+++ test/tools/llvm-dwarfdump/X86/debug_loc_dwo.s
@@ -6,7 +6,7 @@
 
 # CHECK:         .debug_loc.dwo contents:
 # CHECK-NEXT:    0x00000000:
-# CHECK-NEXT:    Addr idx 1 (w/ length 16): DW_OP_reg5 RDI
+# CHECK-NEXT:    {DW_LLE_startx_length, 0x00000001, 0x00000010}: DW_OP_reg5 RDI
 
 .section .debug_loc.dwo,"",@progbits
 # One location list. The pre-DWARF v5 implementation only recognizes
Index: test/DebugInfo/X86/loclists-dwp.ll
===================================================================
--- test/DebugInfo/X86/loclists-dwp.ll
+++ test/DebugInfo/X86/loclists-dwp.ll
@@ -19,10 +19,10 @@
 ; void b(int i) { asm("" : : : "rdi"); }
 
 ; CHECK:      DW_AT_location [DW_FORM_sec_offset]   (0x00000000
-; CHECK-NEXT: Addr idx 0 (w/ length 6): DW_OP_reg5 RDI)
+; CHECK-NEXT: {DW_LLE_startx_length, 0x0000000000000000, 0x0000000000000006}: DW_OP_reg5 RDI)
 
 ; CHECK:      DW_AT_location [DW_FORM_sec_offset]   (0x00000000
-; CHECK-NEXT: Addr idx 0 (w/ length 0): DW_OP_reg5 RDI)
+; CHECK-NEXT: {DW_LLE_startx_length, 0x0000000000000000, 0x0000000000000000}: DW_OP_reg5 RDI)
 
 target triple = "x86_64-unknown-linux-gnu"
 
Index: test/DebugInfo/X86/fission-ranges.ll
===================================================================
--- test/DebugInfo/X86/fission-ranges.ll
+++ test/DebugInfo/X86/fission-ranges.ll
@@ -45,18 +45,19 @@
 ; if they've changed due to a bugfix, change in register allocation, etc.
 
 ; CHECK:      [[A]]:
-; CHECK-NEXT:   Addr idx 2 (w/ length 15): DW_OP_consts +0, DW_OP_stack_value
-; CHECK-NEXT:   Addr idx 3 (w/ length 15): DW_OP_reg0 RAX
-; CHECK-NEXT:   Addr idx 4 (w/ length 18): DW_OP_breg7 RSP-8
+; CHECK-NEXT:   {DW_LLE_startx_length, 0x00000002, 0x0000000f}: DW_OP_consts +0, DW_OP_stack_value
+; CHECK-NEXT:   {DW_LLE_startx_length, 0x00000003, 0x0000000f}: DW_OP_reg0 RAX
+; CHECK-NEXT:   {DW_LLE_startx_length, 0x00000004, 0x00000012}: DW_OP_breg7 RSP-8
 ; CHECK:      [[E]]:
-; CHECK-NEXT:   Addr idx 5 (w/ length 9): DW_OP_reg0 RAX
-; CHECK-NEXT:   Addr idx 6 (w/ length 98): DW_OP_breg7 RSP-44
+; CHECK-NEXT:   {DW_LLE_startx_length, 0x00000005, 0x00000009}: DW_OP_reg0 RAX
+; CHECK-NEXT:   {DW_LLE_startx_length, 0x00000006, 0x00000062}: DW_OP_breg7 RSP-44
+
 ; CHECK:      [[B]]:
-; CHECK-NEXT:   Addr idx 7 (w/ length 15): DW_OP_reg0 RAX
-; CHECK-NEXT:   Addr idx 8 (w/ length 66): DW_OP_breg7 RSP-32
+; CHECK-NEXT:   {DW_LLE_startx_length, 0x00000007, 0x0000000f}: DW_OP_reg0 RAX
+; CHECK-NEXT:   {DW_LLE_startx_length, 0x00000008, 0x00000042}: DW_OP_breg7 RSP-32
 ; CHECK:      [[D]]:
-; CHECK-NEXT:   Addr idx 9 (w/ length 15): DW_OP_reg0 RAX
-; CHECK-NEXT:   Addr idx 10 (w/ length 42): DW_OP_breg7 RSP-20
+; CHECK-NEXT:   {DW_LLE_startx_length, 0x00000009, 0x0000000f}: DW_OP_reg0 RAX
+; CHECK-NEXT:   {DW_LLE_startx_length, 0x0000000a, 0x0000002a}: DW_OP_breg7 RSP-20
 
 ; Make sure we don't produce any relocations in any .dwo section (though in particular, debug_info.dwo)
 ; HDR-NOT: .rela.{{.*}}.dwo
Index: lib/DebugInfo/DWARF/DWARFDebugLoc.cpp
===================================================================
--- lib/DebugInfo/DWARF/DWARFDebugLoc.cpp
+++ lib/DebugInfo/DWARF/DWARFDebugLoc.cpp
@@ -23,6 +23,8 @@
 
 using namespace llvm;
 
+char IsBaseAddressError::ID;
+
 // When directly dumping the .debug_loc without a compile unit, we have to guess
 // at the DWARF version. This only affects DW_OP_call_ref, which is a rare
 // expression that LLVM doesn't produce. Guessing the wrong version means we
@@ -153,6 +155,7 @@
         E.Value1 = Data.getULEB128(C);
       break;
     case dwarf::DW_LLE_start_length:
+      // TODO: Read this as a relocated address.
       E.Value0 = Data.getAddress(C);
       E.Value1 = Data.getULEB128(C);
       break;
@@ -207,6 +210,43 @@
   return nullptr;
 }
 
+Expected<std::pair<uint64_t, uint64_t>> DWARFDebugLoclists::Entry::getRange(
+    function_ref<Optional<uint64_t>(uint32_t)> AddrOffsetResolver,
+    uint64_t &BaseAddr) const {
+  switch (Kind) {
+  case dwarf::DW_LLE_startx_length:
+    if (Optional<uint64_t> Start = AddrOffsetResolver(Value0))
+      return std::make_pair(*Start, *Start + Value1);
+    else
+      return createStringError(inconvertibleErrorCode(),
+                               "Reading debug_addr failed");
+
+  case dwarf::DW_LLE_start_length:
+    return std::make_pair(Value0, Value0 + Value1);
+  case dwarf::DW_LLE_offset_pair:
+    return std::make_pair(BaseAddr + Value0, BaseAddr + Value1);
+  case dwarf::DW_LLE_base_address:
+    BaseAddr = Value0;
+    return make_error<IsBaseAddressError>();
+  default:
+    llvm_unreachable("Unsupported locations list kind");
+  }
+}
+
+Expected<std::pair<uint64_t, uint64_t>>
+DWARFDebugLoclists::Entry::getRange(DWARFUnit *U, uint64_t &BaseAddr) const {
+  return getRange(
+      [U](uint32_t Index) -> Optional<uint64_t> {
+        if (!U)
+          return llvm::None;
+        if (Optional<object::SectionedAddress> Addr =
+                U->getAddrOffsetSectionItem(Index))
+          return Addr->Address;
+        return llvm::None;
+      },
+      BaseAddr);
+}
+
 void DWARFDebugLoclists::LocationList::dump(raw_ostream &OS, uint64_t BaseAddr,
                                             bool IsLittleEndian,
                                             unsigned AddressSize,
@@ -214,32 +254,24 @@
                                             DWARFUnit *U,
                                             unsigned Indent) const {
   for (const Entry &E : Entries) {
-    switch (E.Kind) {
-    case dwarf::DW_LLE_startx_length:
+    if (Expected<std::pair<uint64_t, uint64_t>> Range =
+            E.getRange(U, BaseAddr)) {
       OS << '\n';
       OS.indent(Indent);
-      OS << "Addr idx " << E.Value0 << " (w/ length " << E.Value1 << "): ";
-      break;
-    case dwarf::DW_LLE_start_length:
+      OS << format("[0x%.*" PRIx64 ", 0x%.*" PRIx64 ")", AddressSize * 2,
+                   Range->first, AddressSize * 2, Range->second);
+    } else {
+      bool IsBaseAddress = Range.errorIsA<IsBaseAddressError>();
+      consumeError(Range.takeError());
+      if (IsBaseAddress)
+        continue;
       OS << '\n';
       OS.indent(Indent);
-      OS << format("[0x%*.*" PRIx64 ", 0x%*.*" PRIx64 "): ", AddressSize * 2,
-                   AddressSize * 2, E.Value0, AddressSize * 2, AddressSize * 2,
-                   E.Value0 + E.Value1);
-      break;
-    case dwarf::DW_LLE_offset_pair:
-      OS << '\n';
-      OS.indent(Indent);
-      OS << format("[0x%*.*" PRIx64 ", 0x%*.*" PRIx64 "): ", AddressSize * 2,
-                   AddressSize * 2, BaseAddr + E.Value0, AddressSize * 2,
-                   AddressSize * 2, BaseAddr + E.Value1);
-      break;
-    case dwarf::DW_LLE_base_address:
-      BaseAddr = E.Value0;
-      break;
-    default:
-      llvm_unreachable("unreachable locations list kind");
+      OS << "{" << dwarf::LocationListEntryString(E.Kind)
+         << format(", 0x%.*" PRIx64 ", 0x%.*" PRIx64 "}", AddressSize * 2,
+                   E.Value0, AddressSize * 2, E.Value1);
     }
+    OS << ": ";
 
     dumpExpression(OS, E.Loc, IsLittleEndian, AddressSize, MRI, U);
   }
Index: lib/BinaryFormat/Dwarf.cpp
===================================================================
--- lib/BinaryFormat/Dwarf.cpp
+++ lib/BinaryFormat/Dwarf.cpp
@@ -617,6 +617,31 @@
   return StringRef();
 }
 
+StringRef llvm::dwarf::LocationListEntryString(unsigned Entry) {
+  switch(Entry) {
+  default:
+    return StringRef();
+  case DW_LLE_end_of_list:
+    return "DW_LLE_end_of_list";
+  case DW_LLE_base_addressx:
+    return "DW_LLE_base_addressx";
+  case DW_LLE_startx_endx:
+    return "DW_LLE_startx_endx";
+  case DW_LLE_startx_length:
+    return "DW_LLE_startx_length";
+  case DW_LLE_offset_pair:
+    return "DW_LLE_offset_pair";
+  case DW_LLE_default_location:
+    return "DW_LLE_default_location";
+  case DW_LLE_base_address:
+    return "DW_LLE_base_address";
+  case DW_LLE_start_end:
+    return "DW_LLE_start_end";
+  case DW_LLE_start_length:
+    return "DW_LLE_start_length";
+  }
+}
+
 StringRef llvm::dwarf::IndexString(unsigned Idx) {
   switch (Idx) {
   default:
Index: include/llvm/DebugInfo/DWARF/DWARFDebugLoc.h
===================================================================
--- include/llvm/DebugInfo/DWARF/DWARFDebugLoc.h
+++ include/llvm/DebugInfo/DWARF/DWARFDebugLoc.h
@@ -20,6 +20,17 @@
 class MCRegisterInfo;
 class raw_ostream;
 
+class IsBaseAddressError : public ErrorInfo<IsBaseAddressError> {
+public:
+  static char ID;
+
+  void log(raw_ostream &OS) const override { OS << "Is base address"; }
+
+  std::error_code convertToErrorCode() const override {
+    return inconvertibleErrorCode();
+  }
+};
+
 class DWARFDebugLoc {
 public:
   /// A single location within a location list.
@@ -79,6 +90,22 @@
     uint64_t Value0;
     uint64_t Value1;
     SmallVector<uint8_t, 4> Loc;
+
+    /// Return the half-open range of addresses covered by this entry.
+    /// DW_LLE_offset_pair entries are resolved using the given base address,
+    /// and the supplied DWARFUnit is used to look up any pooled (debug_addr)
+    /// addresses. In case of failure, an error is returned. If we could not get
+    /// an address range because this is a base address selection entry
+    /// (DW_LLE_base_address), then the error is of type IsBaseAddressError, and
+    /// the BaseAddr argument is updated accordingly.
+    Expected<std::pair<uint64_t, uint64_t>> getRange(DWARFUnit *U,
+                                                     uint64_t &BaseAddr) const;
+
+    /// Same as above, but uses a callback to resolve any address pool
+    /// references.
+    Expected<std::pair<uint64_t, uint64_t>>
+    getRange(function_ref<Optional<uint64_t>(uint32_t)> AddrOffsetResolver,
+             uint64_t &BaseAddr) const;
   };
 
   struct LocationList {
Index: include/llvm/BinaryFormat/Dwarf.h
===================================================================
--- include/llvm/BinaryFormat/Dwarf.h
+++ include/llvm/BinaryFormat/Dwarf.h
@@ -428,6 +428,7 @@
 StringRef AtomTypeString(unsigned Atom);
 StringRef GDBIndexEntryKindString(GDBIndexEntryKind Kind);
 StringRef GDBIndexEntryLinkageString(GDBIndexEntryLinkage Linkage);
+StringRef LocationListEntryString(unsigned Entry);
 StringRef IndexString(unsigned Idx);
 /// @}
 
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to