lldb: Add support for DW_AT_ranges on DW_TAG_subprograms
authorDavid Blaikie <dblaikie@gmail.com>
Tue, 5 Jan 2021 06:44:59 +0000 (22:44 -0800)
committerDavid Blaikie <dblaikie@gmail.com>
Thu, 7 Jan 2021 22:28:03 +0000 (14:28 -0800)
gcc already produces debug info with this form
-freorder-block-and-partition
clang produces this sort of thing with -fbasic-block-sections and with a
coming-soon tweak to use ranges in DWARFv5 where they can allow greater
reuse of debug_addr than the low/high_pc forms.

This fixes the case of breaking on a function name, but leaves broken
printing a variable - a follow-up commit will add that and improve the
test case to match.

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

lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp
lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h
lldb/test/Shell/SymbolFile/DWARF/Inputs/subprogram_ranges.s [new file with mode: 0644]
lldb/test/Shell/SymbolFile/DWARF/subprogram_ranges.test [new file with mode: 0644]

index 3eca911..4212988 100644 (file)
@@ -687,13 +687,15 @@ const char *DWARFDebugInfoEntry::GetPubname(const DWARFUnit *cu) const {
 /// table, except that the actual DIE offset for the function is placed in the
 /// table instead of the compile unit offset.
 void DWARFDebugInfoEntry::BuildFunctionAddressRangeTable(
-    const DWARFUnit *cu, DWARFDebugAranges *debug_aranges) const {
+    DWARFUnit *cu, DWARFDebugAranges *debug_aranges) const {
   if (m_tag) {
     if (m_tag == DW_TAG_subprogram) {
-      dw_addr_t lo_pc = LLDB_INVALID_ADDRESS;
-      dw_addr_t hi_pc = LLDB_INVALID_ADDRESS;
-      if (GetAttributeAddressRange(cu, lo_pc, hi_pc, LLDB_INVALID_ADDRESS)) {
-        debug_aranges->AppendRange(GetOffset(), lo_pc, hi_pc);
+      DWARFRangeList ranges;
+      GetAttributeAddressRanges(cu, ranges,
+                                /*check_hi_lo_pc=*/true);
+      for (const auto &r : ranges) {
+        debug_aranges->AppendRange(GetOffset(), r.GetRangeBase(),
+                                   r.GetRangeEnd());
       }
     }
 
index 534d92e..0ba56a0 100644 (file)
@@ -42,7 +42,7 @@ public:
   bool operator==(const DWARFDebugInfoEntry &rhs) const;
   bool operator!=(const DWARFDebugInfoEntry &rhs) const;
 
-  void BuildFunctionAddressRangeTable(const DWARFUnit *cu,
+  void BuildFunctionAddressRangeTable(DWARFUnit *cu,
                                       DWARFDebugAranges *debug_aranges) const;
 
   bool Extract(const lldb_private::DWARFDataExtractor &data,
diff --git a/lldb/test/Shell/SymbolFile/DWARF/Inputs/subprogram_ranges.s b/lldb/test/Shell/SymbolFile/DWARF/Inputs/subprogram_ranges.s
new file mode 100644 (file)
index 0000000..1ff883c
--- /dev/null
@@ -0,0 +1,159 @@
+       .text
+       .file   "main.c"
+       .globl  main                            # -- Begin function main
+       .p2align        4, 0x90
+       .type   main,@function
+main:                                   # @main
+.Lfunc_begin0:
+       .file   1 "/usr/local/google/home/blaikie/dev/scratch" "main.c"
+       .loc    1 1 0                           # main.c:1:0
+       .cfi_startproc
+# %bb.0:                                # %entry
+       pushq   %rbp
+       .cfi_def_cfa_offset 16
+       .cfi_offset %rbp, -16
+       movq    %rsp, %rbp
+       .cfi_def_cfa_register %rbp
+       xorl    %eax, %eax
+.Ltmp0:
+       .loc    1 2 7 prologue_end              # main.c:2:7
+       movl    $3, -4(%rbp)
+       .loc    1 3 1                           # main.c:3:1
+       popq    %rbp
+       .cfi_def_cfa %rsp, 8
+       retq
+.Ltmp1:
+.Lfunc_end0:
+       .size   main, .Lfunc_end0-main
+       .cfi_endproc
+                                        # -- End function
+       .section        .debug_abbrev,"",@progbits
+       .byte   1                               # Abbreviation Code
+       .byte   17                              # DW_TAG_compile_unit
+       .byte   1                               # DW_CHILDREN_yes
+       .byte   37                              # DW_AT_producer
+       .byte   14                              # DW_FORM_strp
+       .byte   19                              # DW_AT_language
+       .byte   5                               # DW_FORM_data2
+       .byte   3                               # DW_AT_name
+       .byte   14                              # DW_FORM_strp
+       .byte   16                              # DW_AT_stmt_list
+       .byte   23                              # DW_FORM_sec_offset
+       .byte   27                              # DW_AT_comp_dir
+       .byte   14                              # DW_FORM_strp
+       .byte   17                              # DW_AT_low_pc
+       .byte   1                               # DW_FORM_addr
+       .byte   18                              # DW_AT_high_pc
+       .byte   6                               # DW_FORM_data4
+       .byte   0                               # EOM(1)
+       .byte   0                               # EOM(2)
+       .byte   2                               # Abbreviation Code
+       .byte   46                              # DW_TAG_subprogram
+       .byte   1                               # DW_CHILDREN_yes
+       .byte   85                              # DW_AT_ranges
+       .byte   23                              # DW_FORM_sec_offset
+       .byte   64                              # DW_AT_frame_base
+       .byte   24                              # DW_FORM_exprloc
+       .byte   3                               # DW_AT_name
+       .byte   14                              # DW_FORM_strp
+       .byte   58                              # DW_AT_decl_file
+       .byte   11                              # DW_FORM_data1
+       .byte   59                              # DW_AT_decl_line
+       .byte   11                              # DW_FORM_data1
+       .byte   73                              # DW_AT_type
+       .byte   19                              # DW_FORM_ref4
+       .byte   63                              # DW_AT_external
+       .byte   25                              # DW_FORM_flag_present
+       .byte   0                               # EOM(1)
+       .byte   0                               # EOM(2)
+       .byte   3                               # Abbreviation Code
+       .byte   52                              # DW_TAG_variable
+       .byte   0                               # DW_CHILDREN_no
+       .byte   2                               # DW_AT_location
+       .byte   24                              # DW_FORM_exprloc
+       .byte   3                               # DW_AT_name
+       .byte   14                              # DW_FORM_strp
+       .byte   58                              # DW_AT_decl_file
+       .byte   11                              # DW_FORM_data1
+       .byte   59                              # DW_AT_decl_line
+       .byte   11                              # DW_FORM_data1
+       .byte   73                              # DW_AT_type
+       .byte   19                              # DW_FORM_ref4
+       .byte   0                               # EOM(1)
+       .byte   0                               # EOM(2)
+       .byte   4                               # Abbreviation Code
+       .byte   36                              # DW_TAG_base_type
+       .byte   0                               # DW_CHILDREN_no
+       .byte   3                               # DW_AT_name
+       .byte   14                              # DW_FORM_strp
+       .byte   62                              # DW_AT_encoding
+       .byte   11                              # DW_FORM_data1
+       .byte   11                              # DW_AT_byte_size
+       .byte   11                              # DW_FORM_data1
+       .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  4                               # DWARF version number
+       .long   .debug_abbrev                   # Offset Into Abbrev. Section
+       .byte   8                               # Address Size (in bytes)
+       .byte   1                               # Abbrev [1] 0xb:0x47 DW_TAG_compile_unit
+       .long   .Linfo_string0                  # DW_AT_producer
+       .short  12                              # DW_AT_language
+       .long   .Linfo_string1                  # DW_AT_name
+       .long   .Lline_table_start0             # DW_AT_stmt_list
+       .long   .Linfo_string2                  # DW_AT_comp_dir
+       .quad   .Lfunc_begin0                   # DW_AT_low_pc
+       .long   .Lfunc_end0-.Lfunc_begin0       # DW_AT_high_pc
+       .byte   2                               # Abbrev [2] 0x2a:0x20 DW_TAG_subprogram
+       .long   .Ldebug_ranges0                 # DW_AT_ranges
+       .byte   1                               # DW_AT_frame_base
+       .byte   86
+       .long   .Linfo_string3                  # DW_AT_name
+       .byte   1                               # DW_AT_decl_file
+       .byte   1                               # DW_AT_decl_line
+       .long   74                              # DW_AT_type
+                                        # DW_AT_external
+       .byte   3                               # Abbrev [3] 0x3b:0xe DW_TAG_variable
+       .byte   2                               # DW_AT_location
+       .byte   145
+       .byte   124
+       .long   .Linfo_string5                  # DW_AT_name
+       .byte   1                               # DW_AT_decl_file
+       .byte   2                               # DW_AT_decl_line
+       .long   74                              # DW_AT_type
+       .byte   0                               # End Of Children Mark
+       .byte   4                               # Abbrev [4] 0x4a:0x7 DW_TAG_base_type
+       .long   .Linfo_string4                  # DW_AT_name
+       .byte   5                               # DW_AT_encoding
+       .byte   4                               # DW_AT_byte_size
+       .byte   0                               # End Of Children Mark
+.Ldebug_info_end0:
+       .section        .debug_ranges,"",@progbits
+.Ldebug_ranges0:
+       .quad   .Lfunc_begin0-.Lfunc_begin0
+       .quad   .Lfunc_end0-.Lfunc_begin0
+       .quad   0
+       .quad   0
+       .section        .debug_str,"MS",@progbits,1
+.Linfo_string0:
+       .asciz  "clang version 12.0.0 (git@github.com:llvm/llvm-project.git 1c15aa171b2f67d9198a8498945cbdb936c0cd3b)" # string offset=0
+.Linfo_string1:
+       .asciz  "main.c"                        # string offset=101
+.Linfo_string2:
+       .asciz  "/usr/local/google/home/blaikie/dev/scratch" # string offset=108
+.Linfo_string3:
+       .asciz  "main"                          # string offset=151
+.Linfo_string4:
+       .asciz  "int"                           # string offset=156
+.Linfo_string5:
+       .asciz  "var"                           # string offset=160
+       .ident  "clang version 12.0.0 (git@github.com:llvm/llvm-project.git 1c15aa171b2f67d9198a8498945cbdb936c0cd3b)"
+       .section        ".note.GNU-stack","",@progbits
+       .addrsig
+       .section        .debug_line,"",@progbits
+.Lline_table_start0:
+
diff --git a/lldb/test/Shell/SymbolFile/DWARF/subprogram_ranges.test b/lldb/test/Shell/SymbolFile/DWARF/subprogram_ranges.test
new file mode 100644 (file)
index 0000000..13186e3
--- /dev/null
@@ -0,0 +1,17 @@
+# REQUIRES: x86
+# RUN: %clang -target x86_64-pc-linux -g -O0 %S/Inputs/subprogram_ranges.s -o %t.out
+# RUN: %lldb -b -s %s %t.out 2>&1 | FileCheck %s
+
+# Test breaking on symbols and printing variables when a DW_TAG_subprogram uses
+# DW_AT_ranges instead of DW_AT_low_pc/DW_AT_high_pc.  While the assembly here
+# is a bit unrealistic - it's a single-entry range using DWARFv4 which isn't
+# useful for anything (a single-entry range with DWARFv5 can reduce address
+# relocations, and multi-entry ranges can be used for function sections), but
+# it's the simplest thing to test. If anyone's updating this test at some
+# point, feel free to replace it with another equivalent test if it's
+# especially useful, but don't dismiss it as pointless just because it's a bit
+# weird.
+
+b main
+# CHECK: (lldb) b main
+# CHECK-NEXT: Breakpoint 1: where = subprogram_ranges.test.tmp.out`main + 6 at main.c:2:7