diff --git a/llvm/docs/CommandGuide/llvm-objdump.rst b/llvm/docs/CommandGuide/llvm-objdump.rst --- a/llvm/docs/CommandGuide/llvm-objdump.rst +++ b/llvm/docs/CommandGuide/llvm-objdump.rst @@ -119,6 +119,21 @@ Demangle symbol names in the output. +.. option:: --debug-vars + + Print the locations (in registers or memory) of source-level variables + alongside disassembly. + +.. option:: --debug-vars-indent= + + Distance to indent the source-level variable display, relative to the start + of the disassembly. Defaults to 50 characters. + +.. option:: --debug-vars-unicode= + + If true, use unicode characters when displaying source-level variables. If + false, use only ASCII characters. Defaults to true. + .. option:: -j, --section= Perform commands on the specified sections only. For Mach-O use diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFExpression.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFExpression.h --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFExpression.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFExpression.h @@ -119,6 +119,10 @@ return Op; } + iterator skip_bytes(uint32_t Add) { + return iterator(Expr, Op.EndOffset + Add); + } + class Operation &operator*() { return Op; } diff --git a/llvm/include/llvm/Support/FormattedStream.h b/llvm/include/llvm/Support/FormattedStream.h --- a/llvm/include/llvm/Support/FormattedStream.h +++ b/llvm/include/llvm/Support/FormattedStream.h @@ -105,11 +105,17 @@ /// \param NewCol - The column to move to. formatted_raw_ostream &PadToColumn(unsigned NewCol); - /// getColumn - Return the column number - unsigned getColumn() { return Position.first; } + /// Return the column number. + unsigned getColumn() { + ComputePosition(getBufferStart(), GetNumBytesInBuffer()); + return Position.first; + } - /// getLine - Return the line number - unsigned getLine() { return Position.second; } + /// Return the line number. + unsigned getLine() { + ComputePosition(getBufferStart(), GetNumBytesInBuffer()); + return Position.second; + } raw_ostream &resetColor() override { TheStream->resetColor(); diff --git a/llvm/lib/Support/FormattedStream.cpp b/llvm/lib/Support/FormattedStream.cpp --- a/llvm/lib/Support/FormattedStream.cpp +++ b/llvm/lib/Support/FormattedStream.cpp @@ -27,7 +27,28 @@ // Keep track of the current column and line by scanning the string for // special characters for (const char *End = Ptr + Size; Ptr != End; ++Ptr) { + // If this is a multi-byte UTF-8 character, skip the extra bytes, and don't + // check for special whitespace characters. + bool MultiByte = true; + if ((*Ptr & 0b11100000) == 0b11000000) + Ptr += 1; + else if ((*Ptr & 0b11110000) == 0b11100000) + Ptr += 2; + else if ((*Ptr & 0b11111000) == 0b11110000) + Ptr += 3; + else + MultiByte = false; + + // The string should never end part way through a multi-byte sequence. + assert(Ptr < End && "Malformed multi-byte sequence"); + ++Column; + + // If this is the final byte of a multi-byte sequence, it can't be any of + // the special whitespace characters below. + if (MultiByte) + continue; + switch (*Ptr) { case '\n': Line += 1; diff --git a/llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c b/llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c @@ -0,0 +1,10 @@ +int foo(int a, int b, int c) { + int x = a + b; + int y = x + c; + return y; +} + +int bar(int a) { + a++; + return a; +} diff --git a/llvm/test/tools/llvm-objdump/ARM/debug-vars-dwarf4-sections.s b/llvm/test/tools/llvm-objdump/ARM/debug-vars-dwarf4-sections.s new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/ARM/debug-vars-dwarf4-sections.s @@ -0,0 +1,351 @@ +## Check that the --debug-vars option works for simple register locations, when +## using DWARF4 debug info, with functions in multiple sections. + +## Generated with this compile command, with the source code in Inputs/debug.c: +## clang --target=arm--none-eabi -march=armv7-a -c debug.c -O1 -gdwarf-4 -S -o - -ffunction-sections + +# RUN: llvm-mc -triple armv8a--none-eabi < %s -filetype=obj | \ +# RUN: llvm-objdump - -d --debug-vars --no-show-raw-insn | \ +# RUN: FileCheck %s + +# CHECK: Disassembly of section .text.foo: +# CHECK-EMPTY: +# CHECK-NEXT: 00000000 foo: +# CHECK-NEXT: ┠─ a = R0 +# CHECK-NEXT: ┃ ┠─ b = R1 +# CHECK-NEXT: ┃ ┃ ┠─ c = R2 +# CHECK-NEXT: ┃ ┃ ┃ ┌─ x = R0 +# CHECK-NEXT: 0: add r0, r1, r0 ┻ ┃ ┃ ╈ +# CHECK-NEXT: ┌─ y = R0 +# CHECK-NEXT: 4: add r0, r0, r2 ╈ ┃ ┃ ┻ +# CHECK-NEXT: 8: bx lr ┻ ┻ ┻ +# CHECK-EMPTY: +# CHECK-NEXT: Disassembly of section .text.bar: +# CHECK-EMPTY: +# CHECK-NEXT: 00000000 bar: +# CHECK-NEXT: ┠─ a = R0 +# CHECK-NEXT: 0: add r0, r0, #1 ┃ +# CHECK-NEXT: 4: bx lr ┻ + + .text + .syntax unified + .eabi_attribute 67, "2.09" + .eabi_attribute 6, 10 + .eabi_attribute 7, 65 + .eabi_attribute 8, 1 + .eabi_attribute 9, 2 + .fpu neon + .eabi_attribute 34, 0 + .eabi_attribute 17, 1 + .eabi_attribute 20, 1 + .eabi_attribute 21, 1 + .eabi_attribute 23, 3 + .eabi_attribute 24, 1 + .eabi_attribute 25, 1 + .eabi_attribute 38, 1 + .eabi_attribute 18, 4 + .eabi_attribute 26, 2 + .eabi_attribute 14, 0 + .file "debug.c" + .section .text.foo,"ax",%progbits + .globl foo + .p2align 2 + .type foo,%function + .code 32 +foo: +.Lfunc_begin0: + .file 1 "/work" "llvm/src/llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c" + .loc 1 1 0 + .fnstart + .cfi_sections .debug_frame + .cfi_startproc + .loc 1 2 13 prologue_end + add r0, r1, r0 +.Ltmp0: + .loc 1 3 13 + add r0, r0, r2 +.Ltmp1: + .loc 1 4 3 + bx lr +.Ltmp2: +.Lfunc_end0: + .size foo, .Lfunc_end0-foo + .cfi_endproc + .cantunwind + .fnend + + .section .text.bar,"ax",%progbits + .globl bar + .p2align 2 + .type bar,%function + .code 32 +bar: +.Lfunc_begin1: + .loc 1 7 0 + .fnstart + .cfi_startproc + .loc 1 8 4 prologue_end + add r0, r0, #1 +.Ltmp3: + .loc 1 9 3 + bx lr +.Ltmp4: +.Lfunc_end1: + .size bar, .Lfunc_end1-bar + .cfi_endproc + .cantunwind + .fnend + + .section .debug_str,"MS",%progbits,1 +.Linfo_string0: + .asciz "clang version 10.0.0 (git@github.com:llvm/llvm-project.git e73f78acd34360f7450b81167d9dc858ccddc262)" +.Linfo_string1: + .asciz "/work/llvm/src/llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c" +.Linfo_string2: + .asciz "/work/scratch" +.Linfo_string3: + .asciz "foo" +.Linfo_string4: + .asciz "int" +.Linfo_string5: + .asciz "bar" +.Linfo_string6: + .asciz "a" +.Linfo_string7: + .asciz "b" +.Linfo_string8: + .asciz "c" +.Linfo_string9: + .asciz "x" +.Linfo_string10: + .asciz "y" + .section .debug_loc,"",%progbits +.Ldebug_loc0: + .long -1 + .long .Lfunc_begin0 + .long .Lfunc_begin0-.Lfunc_begin0 + .long .Ltmp0-.Lfunc_begin0 + .short 1 + .byte 80 + .long 0 + .long 0 +.Ldebug_loc1: + .long -1 + .long .Lfunc_begin0 + .long .Ltmp0-.Lfunc_begin0 + .long .Ltmp1-.Lfunc_begin0 + .short 1 + .byte 80 + .long 0 + .long 0 +.Ldebug_loc2: + .long -1 + .long .Lfunc_begin0 + .long .Ltmp1-.Lfunc_begin0 + .long .Lfunc_end0-.Lfunc_begin0 + .short 1 + .byte 80 + .long 0 + .long 0 + .section .debug_abbrev,"",%progbits + .byte 1 + .byte 17 + .byte 1 + .byte 37 + .byte 14 + .byte 19 + .byte 5 + .byte 3 + .byte 14 + .byte 16 + .byte 23 + .byte 27 + .byte 14 + .byte 17 + .byte 1 + .byte 85 + .byte 23 + .byte 0 + .byte 0 + .byte 2 + .byte 46 + .byte 1 + .byte 17 + .byte 1 + .byte 18 + .byte 6 + .byte 64 + .byte 24 + .ascii "\227B" + .byte 25 + .byte 3 + .byte 14 + .byte 58 + .byte 11 + .byte 59 + .byte 11 + .byte 39 + .byte 25 + .byte 73 + .byte 19 + .byte 63 + .byte 25 + .byte 0 + .byte 0 + .byte 3 + .byte 5 + .byte 0 + .byte 2 + .byte 23 + .byte 3 + .byte 14 + .byte 58 + .byte 11 + .byte 59 + .byte 11 + .byte 73 + .byte 19 + .byte 0 + .byte 0 + .byte 4 + .byte 5 + .byte 0 + .byte 2 + .byte 24 + .byte 3 + .byte 14 + .byte 58 + .byte 11 + .byte 59 + .byte 11 + .byte 73 + .byte 19 + .byte 0 + .byte 0 + .byte 5 + .byte 52 + .byte 0 + .byte 2 + .byte 23 + .byte 3 + .byte 14 + .byte 58 + .byte 11 + .byte 59 + .byte 11 + .byte 73 + .byte 19 + .byte 0 + .byte 0 + .byte 6 + .byte 36 + .byte 0 + .byte 3 + .byte 14 + .byte 62 + .byte 11 + .byte 11 + .byte 11 + .byte 0 + .byte 0 + .byte 0 + .section .debug_info,"",%progbits +.Lcu_begin0: + .long .Ldebug_info_end0-.Ldebug_info_start0 +.Ldebug_info_start0: + .short 4 + .long .debug_abbrev + .byte 4 + .byte 1 + .long .Linfo_string0 + .short 12 + .long .Linfo_string1 + .long .Lline_table_start0 + .long .Linfo_string2 + .long 0 + .long .Ldebug_ranges0 + .byte 2 + .long .Lfunc_begin0 + .long .Lfunc_end0-.Lfunc_begin0 + .byte 1 + .byte 91 + + .long .Linfo_string3 + .byte 1 + .byte 1 + + .long 166 + + .byte 3 + .long .Ldebug_loc0 + .long .Linfo_string6 + .byte 1 + .byte 1 + .long 166 + .byte 4 + .byte 1 + .byte 81 + .long .Linfo_string7 + .byte 1 + .byte 1 + .long 166 + .byte 4 + .byte 1 + .byte 82 + .long .Linfo_string8 + .byte 1 + .byte 1 + .long 166 + .byte 5 + .long .Ldebug_loc1 + .long .Linfo_string9 + .byte 1 + .byte 2 + .long 166 + .byte 5 + .long .Ldebug_loc2 + .long .Linfo_string10 + .byte 1 + .byte 3 + .long 166 + .byte 0 + .byte 2 + .long .Lfunc_begin1 + .long .Lfunc_end1-.Lfunc_begin1 + .byte 1 + .byte 91 + + .long .Linfo_string5 + .byte 1 + .byte 7 + + .long 166 + + .byte 4 + .byte 1 + .byte 80 + .long .Linfo_string6 + .byte 1 + .byte 7 + .long 166 + .byte 0 + .byte 6 + .long .Linfo_string4 + .byte 5 + .byte 4 + .byte 0 +.Ldebug_info_end0: + .section .debug_ranges,"",%progbits +.Ldebug_ranges0: + .long .Lfunc_begin0 + .long .Lfunc_end0 + .long .Lfunc_begin1 + .long .Lfunc_end1 + .long 0 + .long 0 + .ident "clang version 10.0.0 (git@github.com:llvm/llvm-project.git e73f78acd34360f7450b81167d9dc858ccddc262)" + .section ".note.GNU-stack","",%progbits + .addrsig + .eabi_attribute 30, 1 + .section .debug_line,"",%progbits +.Lline_table_start0: diff --git a/llvm/test/tools/llvm-objdump/ARM/debug-vars-dwarf4.s b/llvm/test/tools/llvm-objdump/ARM/debug-vars-dwarf4.s new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/ARM/debug-vars-dwarf4.s @@ -0,0 +1,444 @@ +## Check that the --debug-vars option works for simple register locations, when +## using DWARF4 debug info, with multiple functions in one section. Check that +## the live-range lines are rendered correctly when using the --no-show-raw-insn, +## --line-numbers and --source options. + +## Generated with this compile command, with the source code in Inputs/debug.c: +## clang --target=arm--none-eabi -march=armv7-a -c debug.c -O1 -gdwarf-4 -S -o - + +# RUN: llvm-mc -triple armv8a--none-eabi < %s -filetype=obj | \ +# RUN: llvm-objdump - -d --debug-vars | \ +# RUN: FileCheck %s --check-prefix=RAW --strict-whitespace + +## Check that passing the default value for --debug-vars-indent (50) makes no +## change to the output. +# RUN: llvm-mc -triple armv8a--none-eabi < %s -filetype=obj | \ +# RUN: llvm-objdump - -d --debug-vars --debug-vars-indent=50 | \ +# RUN: FileCheck %s --check-prefix=RAW --strict-whitespace + +# RUN: llvm-mc -triple armv8a--none-eabi < %s -filetype=obj | \ +# RUN: llvm-objdump - -d --debug-vars --debug-vars-indent=30 | \ +# RUN: FileCheck %s --check-prefix=INDENT --strict-whitespace + +# RUN: llvm-mc -triple armv8a--none-eabi < %s -filetype=obj | \ +# RUN: llvm-objdump - -d --debug-vars --no-show-raw-insn | \ +# RUN: FileCheck %s --check-prefix=NO-RAW --strict-whitespace + +# RUN: llvm-mc -triple armv8a--none-eabi < %s -filetype=obj | \ +# RUN: llvm-objdump - -d --debug-vars --no-show-raw-insn --line-numbers | \ +# RUN: FileCheck %s --check-prefix=LINE-NUMS --strict-whitespace + +# RUN: mkdir -p %t/a +# RUN: cp %p/Inputs/debug.c %t/a/debug.c +# RUN: sed -e "s,SRC_COMPDIR,%/t/a,g" %s > %t.s +# RUN: llvm-mc -triple armv8a--none-eabi < %t.s -filetype=obj | \ +# RUN: llvm-objdump - -d --debug-vars --no-show-raw-insn --source | \ +# RUN: FileCheck %s --check-prefix=SOURCE --strict-whitespace + +# RUN: llvm-mc -triple armv8a--none-eabi < %s -filetype=obj | \ +# RUN: llvm-objdump - -d --debug-vars --debug-vars-unicode=false | \ +# RUN: FileCheck %s --check-prefix=ASCII --strict-whitespace + +## Note that llvm-objdump emits tab characters in the disassembly, assuming an +## 8-byte tab stop, so these might not look aligned in a text editor. + +# RAW: 00000000 foo: +# RAW-NEXT: ┠─ a = R0 +# RAW-NEXT: ┃ ┠─ b = R1 +# RAW-NEXT: ┃ ┃ ┠─ c = R2 +# RAW-NEXT: ┃ ┃ ┃ ┌─ x = R0 +# RAW-NEXT: 0: 00 00 81 e0 add r0, r1, r0 ┻ ┃ ┃ ╈ +# RAW-NEXT: ┌─ y = R0 +# RAW-NEXT: 4: 02 00 80 e0 add r0, r0, r2 ╈ ┃ ┃ ┻ +# RAW-NEXT: 8: 1e ff 2f e1 bx lr ┻ ┻ ┻ +# RAW-EMPTY: +# RAW-NEXT: 0000000c bar: +# RAW-NEXT: ┠─ a = R0 +# RAW-NEXT: c: 01 00 80 e2 add r0, r0, #1 ┃ +# RAW-NEXT: 10: 1e ff 2f e1 bx lr ┻ + +# INDENT: 00000000 foo: +# INDENT-NEXT: ┠─ a = R0 +# INDENT-NEXT: ┃ ┠─ b = R1 +# INDENT-NEXT: ┃ ┃ ┠─ c = R2 +# INDENT-NEXT: ┃ ┃ ┃ ┌─ x = R0 +# INDENT-NEXT: 0: 00 00 81 e0 add r0, r1, r0 ┻ ┃ ┃ ╈ +# INDENT-NEXT: ┌─ y = R0 +# INDENT-NEXT: 4: 02 00 80 e0 add r0, r0, r2 ╈ ┃ ┃ ┻ +# INDENT-NEXT: 8: 1e ff 2f e1 bx lr ┻ ┻ ┻ +# INDENT-EMPTY: +# INDENT-NEXT: 0000000c bar: +# INDENT-NEXT: ┠─ a = R0 +# INDENT-NEXT: c: 01 00 80 e2 add r0, r0, #1 ┃ +# INDENT-NEXT: 10: 1e ff 2f e1 bx lr ┻ + +# NO-RAW: 00000000 foo: +# NO-RAW-NEXT: ┠─ a = R0 +# NO-RAW-NEXT: ┃ ┠─ b = R1 +# NO-RAW-NEXT: ┃ ┃ ┠─ c = R2 +# NO-RAW-NEXT: ┃ ┃ ┃ ┌─ x = R0 +# NO-RAW-NEXT: 0: add r0, r1, r0 ┻ ┃ ┃ ╈ +# NO-RAW-NEXT: ┌─ y = R0 +# NO-RAW-NEXT: 4: add r0, r0, r2 ╈ ┃ ┃ ┻ +# NO-RAW-NEXT: 8: bx lr ┻ ┻ ┻ +# NO-RAW-EMPTY: +# NO-RAW-NEXT: 0000000c bar: +# NO-RAW-NEXT: ┠─ a = R0 +# NO-RAW-NEXT: c: add r0, r0, #1 ┃ +# NO-RAW-NEXT: 10: bx lr ┻ + +# LINE-NUMS: 00000000 foo: +# LINE-NUMS-NEXT: ; SRC_COMPDIR{{[/\\]}}debug.c:2 ┠─ a = R0 +# LINE-NUMS-NEXT: ┃ ┠─ b = R1 +# LINE-NUMS-NEXT: ┃ ┃ ┠─ c = R2 +# LINE-NUMS-NEXT: ┃ ┃ ┃ ┌─ x = R0 +# LINE-NUMS-NEXT: 0: add r0, r1, r0 ┻ ┃ ┃ ╈ +# LINE-NUMS-NEXT: ; SRC_COMPDIR{{[/\\]}}debug.c:3 ┌─ y = R0 +# LINE-NUMS-NEXT: 4: add r0, r0, r2 ╈ ┃ ┃ ┻ +# LINE-NUMS-NEXT: ; SRC_COMPDIR{{[/\\]}}debug.c:4 ┃ ┃ ┃ +# LINE-NUMS-NEXT: 8: bx lr ┻ ┻ ┻ +# LINE-NUMS-EMPTY: +# LINE-NUMS-NEXT: 0000000c bar: +# LINE-NUMS-NEXT: ; SRC_COMPDIR{{[/\\]}}debug.c:8 ┠─ a = R0 +# LINE-NUMS-NEXT: c: add r0, r0, #1 ┃ +# LINE-NUMS-NEXT: ; SRC_COMPDIR{{[/\\]}}debug.c:9 ┃ +# LINE-NUMS-NEXT: 10: bx lr ┻ + +# SOURCE: 00000000 foo: +# SOURCE-NEXT: ; int x = a + b; ┠─ a = R0 +# SOURCE-NEXT: ┃ ┠─ b = R1 +# SOURCE-NEXT: ┃ ┃ ┠─ c = R2 +# SOURCE-NEXT: ┃ ┃ ┃ ┌─ x = R0 +# SOURCE-NEXT: 0: add r0, r1, r0 ┻ ┃ ┃ ╈ +# SOURCE-NEXT: ; int y = x + c; ┌─ y = R0 +# SOURCE-NEXT: 4: add r0, r0, r2 ╈ ┃ ┃ ┻ +# SOURCE-NEXT: ; return y; ┃ ┃ ┃ +# SOURCE-NEXT: 8: bx lr ┻ ┻ ┻ +# SOURCE-EMPTY: +# SOURCE-NEXT: 0000000c bar: +# SOURCE-NEXT: ; a++; ┠─ a = R0 +# SOURCE-NEXT: c: add r0, r0, #1 ┃ +# SOURCE-NEXT: ; return a; ┃ +# SOURCE-NEXT: 10: bx lr ┻ + +# ASCII: 00000000 foo: +# ASCII-NEXT: |- a = R0 +# ASCII-NEXT: | |- b = R1 +# ASCII-NEXT: | | |- c = R2 +# ASCII-NEXT: | | | /- x = R0 +# ASCII-NEXT: 0: 00 00 81 e0 add r0, r1, r0 v | | ^ +# ASCII-NEXT: /- y = R0 +# ASCII-NEXT: 4: 02 00 80 e0 add r0, r0, r2 ^ | | v +# ASCII-NEXT: 8: 1e ff 2f e1 bx lr v v v +# ASCII-EMPTY: +# ASCII-NEXT: 0000000c bar: +# ASCII-NEXT: |- a = R0 +# ASCII-NEXT: c: 01 00 80 e2 add r0, r0, #1 | +# ASCII-NEXT: 10: 1e ff 2f e1 bx lr v + + .text + .syntax unified + .eabi_attribute 67, "2.09" + .eabi_attribute 6, 10 + .eabi_attribute 7, 65 + .eabi_attribute 8, 1 + .eabi_attribute 9, 2 + .fpu neon + .eabi_attribute 34, 0 + .eabi_attribute 17, 1 + .eabi_attribute 20, 1 + .eabi_attribute 21, 1 + .eabi_attribute 23, 3 + .eabi_attribute 24, 1 + .eabi_attribute 25, 1 + .eabi_attribute 38, 1 + .eabi_attribute 18, 4 + .eabi_attribute 26, 2 + .eabi_attribute 14, 0 + .file "debug.c" + .globl foo + .p2align 2 + .type foo,%function + .code 32 +foo: +.Lfunc_begin0: + .file 1 "" "SRC_COMPDIR/debug.c" + .loc 1 1 0 + .fnstart + .cfi_sections .debug_frame + .cfi_startproc + .loc 1 2 13 prologue_end + add r0, r1, r0 +.Ltmp0: + .loc 1 3 13 + add r0, r0, r2 +.Ltmp1: + .loc 1 4 3 + bx lr +.Ltmp2: +.Lfunc_end0: + .size foo, .Lfunc_end0-foo + .cfi_endproc + .cantunwind + .fnend + + .globl bar + .p2align 2 + .type bar,%function + .code 32 +bar: +.Lfunc_begin1: + .loc 1 7 0 + .fnstart + .cfi_startproc + .loc 1 8 4 prologue_end + add r0, r0, #1 +.Ltmp3: + .loc 1 9 3 + bx lr +.Ltmp4: +.Lfunc_end1: + .size bar, .Lfunc_end1-bar + .cfi_endproc + .cantunwind + .fnend + + .section .debug_str,"MS",%progbits,1 +.Linfo_string0: + .asciz "clang version 10.0.0 (git@github.com:llvm/llvm-project.git e73f78acd34360f7450b81167d9dc858ccddc262)" +.Linfo_string1: + .asciz "SRC_COMPDIR/debug.c" +.Linfo_string2: + .asciz "" +.Linfo_string3: + .asciz "foo" +.Linfo_string4: + .asciz "int" +.Linfo_string5: + .asciz "bar" +.Linfo_string6: + .asciz "a" +.Linfo_string7: + .asciz "b" +.Linfo_string8: + .asciz "c" +.Linfo_string9: + .asciz "x" +.Linfo_string10: + .asciz "y" + .section .debug_loc,"",%progbits +.Ldebug_loc0: + .long .Lfunc_begin0-.Lfunc_begin0 + .long .Ltmp0-.Lfunc_begin0 + .short 1 + .byte 80 + .long 0 + .long 0 +.Ldebug_loc1: + .long .Ltmp0-.Lfunc_begin0 + .long .Ltmp1-.Lfunc_begin0 + .short 1 + .byte 80 + .long 0 + .long 0 +.Ldebug_loc2: + .long .Ltmp1-.Lfunc_begin0 + .long .Lfunc_end0-.Lfunc_begin0 + .short 1 + .byte 80 + .long 0 + .long 0 + .section .debug_abbrev,"",%progbits + .byte 1 + .byte 17 + .byte 1 + .byte 37 + .byte 14 + .byte 19 + .byte 5 + .byte 3 + .byte 14 + .byte 16 + .byte 23 + .byte 27 + .byte 14 + .byte 17 + .byte 1 + .byte 18 + .byte 6 + .byte 0 + .byte 0 + .byte 2 + .byte 46 + .byte 1 + .byte 17 + .byte 1 + .byte 18 + .byte 6 + .byte 64 + .byte 24 + .ascii "\227B" + .byte 25 + .byte 3 + .byte 14 + .byte 58 + .byte 11 + .byte 59 + .byte 11 + .byte 39 + .byte 25 + .byte 73 + .byte 19 + .byte 63 + .byte 25 + .byte 0 + .byte 0 + .byte 3 + .byte 5 + .byte 0 + .byte 2 + .byte 23 + .byte 3 + .byte 14 + .byte 58 + .byte 11 + .byte 59 + .byte 11 + .byte 73 + .byte 19 + .byte 0 + .byte 0 + .byte 4 + .byte 5 + .byte 0 + .byte 2 + .byte 24 + .byte 3 + .byte 14 + .byte 58 + .byte 11 + .byte 59 + .byte 11 + .byte 73 + .byte 19 + .byte 0 + .byte 0 + .byte 5 + .byte 52 + .byte 0 + .byte 2 + .byte 23 + .byte 3 + .byte 14 + .byte 58 + .byte 11 + .byte 59 + .byte 11 + .byte 73 + .byte 19 + .byte 0 + .byte 0 + .byte 6 + .byte 36 + .byte 0 + .byte 3 + .byte 14 + .byte 62 + .byte 11 + .byte 11 + .byte 11 + .byte 0 + .byte 0 + .byte 0 + .section .debug_info,"",%progbits +.Lcu_begin0: + .long .Ldebug_info_end0-.Ldebug_info_start0 +.Ldebug_info_start0: + .short 4 + .long .debug_abbrev + .byte 4 + .byte 1 + .long .Linfo_string0 + .short 12 + .long .Linfo_string1 + .long .Lline_table_start0 + .long .Linfo_string2 + .long .Lfunc_begin0 + .long .Lfunc_end1-.Lfunc_begin0 + .byte 2 + .long .Lfunc_begin0 + .long .Lfunc_end0-.Lfunc_begin0 + .byte 1 + .byte 91 + + .long .Linfo_string3 + .byte 1 + .byte 1 + + .long 166 + + .byte 3 + .long .Ldebug_loc0 + .long .Linfo_string6 + .byte 1 + .byte 1 + .long 166 + .byte 4 + .byte 1 + .byte 81 + .long .Linfo_string7 + .byte 1 + .byte 1 + .long 166 + .byte 4 + .byte 1 + .byte 82 + .long .Linfo_string8 + .byte 1 + .byte 1 + .long 166 + .byte 5 + .long .Ldebug_loc1 + .long .Linfo_string9 + .byte 1 + .byte 2 + .long 166 + .byte 5 + .long .Ldebug_loc2 + .long .Linfo_string10 + .byte 1 + .byte 3 + .long 166 + .byte 0 + .byte 2 + .long .Lfunc_begin1 + .long .Lfunc_end1-.Lfunc_begin1 + .byte 1 + .byte 91 + + .long .Linfo_string5 + .byte 1 + .byte 7 + + .long 166 + + .byte 4 + .byte 1 + .byte 80 + .long .Linfo_string6 + .byte 1 + .byte 7 + .long 166 + .byte 0 + .byte 6 + .long .Linfo_string4 + .byte 5 + .byte 4 + .byte 0 +.Ldebug_info_end0: + .ident "clang version 10.0.0 (git@github.com:llvm/llvm-project.git e73f78acd34360f7450b81167d9dc858ccddc262)" + .section ".note.GNU-stack","",%progbits + .addrsig + .eabi_attribute 30, 1 + .section .debug_line,"",%progbits +.Lline_table_start0: diff --git a/llvm/test/tools/llvm-objdump/ARM/debug-vars-dwarf5-sections.s b/llvm/test/tools/llvm-objdump/ARM/debug-vars-dwarf5-sections.s new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/ARM/debug-vars-dwarf5-sections.s @@ -0,0 +1,411 @@ +## Check that the --debug-vars option works for simple register locations, when +## using DWARF4 debug info, with functions in multiple sections. + +## Generated with this compile command, with the source code in Inputs/debug.c: +## clang --target=arm--none-eabi -march=armv7-a -c debug.c -O1 -gdwarf-5 -S -o - -ffunction-sections + +# RUN: llvm-mc -triple armv8a--none-eabi < %s -filetype=obj --dwarf-version=5 | \ +# RUN: llvm-objdump - -d --debug-vars --no-show-raw-insn | \ +# RUN: FileCheck %s + +# CHECK: Disassembly of section .text.foo: +# CHECK-EMPTY: +# CHECK-NEXT: 00000000 foo: +# CHECK-NEXT: ┠─ a = R0 +# CHECK-NEXT: ┃ ┠─ b = R1 +# CHECK-NEXT: ┃ ┃ ┠─ c = R2 +# CHECK-NEXT: ┃ ┃ ┃ ┌─ x = R0 +# CHECK-NEXT: 0: add r0, r1, r0 ┻ ┃ ┃ ╈ +# CHECK-NEXT: ┌─ y = R0 +# CHECK-NEXT: 4: add r0, r0, r2 ╈ ┃ ┃ ┻ +# CHECK-NEXT: 8: bx lr ┻ ┻ ┻ +# CHECK-EMPTY: +# CHECK-NEXT: Disassembly of section .text.bar: +# CHECK-EMPTY: +# CHECK-NEXT: 00000000 bar: +# CHECK-NEXT: ┠─ a = R0 +# CHECK-NEXT: 0: add r0, r0, #1 ┃ +# CHECK-NEXT: 4: bx lr ┻ + + .text + .syntax unified + .eabi_attribute 67, "2.09" + .eabi_attribute 6, 10 + .eabi_attribute 7, 65 + .eabi_attribute 8, 1 + .eabi_attribute 9, 2 + .fpu neon + .eabi_attribute 34, 0 + .eabi_attribute 17, 1 + .eabi_attribute 20, 1 + .eabi_attribute 21, 1 + .eabi_attribute 23, 3 + .eabi_attribute 24, 1 + .eabi_attribute 25, 1 + .eabi_attribute 38, 1 + .eabi_attribute 18, 4 + .eabi_attribute 26, 2 + .eabi_attribute 14, 0 + .file "debug.c" + .section .text.foo,"ax",%progbits + .globl foo + .p2align 2 + .type foo,%function + .code 32 +foo: +.Lfunc_begin0: + .file 0 "/work/scratch" "/work/llvm/src/llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c" md5 0x07374f01ab24ec7c07db73bc13bd778e + .file 1 "/work" "llvm/src/llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c" md5 0x07374f01ab24ec7c07db73bc13bd778e + .loc 1 1 0 + .fnstart + .cfi_sections .debug_frame + .cfi_startproc + .loc 1 2 13 prologue_end + add r0, r1, r0 +.Ltmp0: + .loc 1 3 13 + add r0, r0, r2 +.Ltmp1: + .loc 1 4 3 + bx lr +.Ltmp2: +.Lfunc_end0: + .size foo, .Lfunc_end0-foo + .cfi_endproc + .cantunwind + .fnend + + .section .text.bar,"ax",%progbits + .globl bar + .p2align 2 + .type bar,%function + .code 32 +bar: +.Lfunc_begin1: + .loc 1 7 0 + .fnstart + .cfi_startproc + .loc 1 8 4 prologue_end + add r0, r0, #1 +.Ltmp3: + .loc 1 9 3 + bx lr +.Ltmp4: +.Lfunc_end1: + .size bar, .Lfunc_end1-bar + .cfi_endproc + .cantunwind + .fnend + + .section .debug_str_offsets,"",%progbits + .long 48 + .short 5 + .short 0 +.Lstr_offsets_base0: + .section .debug_str,"MS",%progbits,1 +.Linfo_string0: + .asciz "clang version 10.0.0 (git@github.com:llvm/llvm-project.git e73f78acd34360f7450b81167d9dc858ccddc262)" +.Linfo_string1: + .asciz "/work/llvm/src/llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c" +.Linfo_string2: + .asciz "/work/scratch" +.Linfo_string3: + .asciz "foo" +.Linfo_string4: + .asciz "int" +.Linfo_string5: + .asciz "bar" +.Linfo_string6: + .asciz "a" +.Linfo_string7: + .asciz "b" +.Linfo_string8: + .asciz "c" +.Linfo_string9: + .asciz "x" +.Linfo_string10: + .asciz "y" + .section .debug_str_offsets,"",%progbits + .long .Linfo_string0 + .long .Linfo_string1 + .long .Linfo_string2 + .long .Linfo_string3 + .long .Linfo_string4 + .long .Linfo_string5 + .long .Linfo_string6 + .long .Linfo_string7 + .long .Linfo_string8 + .long .Linfo_string9 + .long .Linfo_string10 + .section .debug_loclists,"",%progbits + .long .Ldebug_loclist_table_end0-.Ldebug_loclist_table_start0 +.Ldebug_loclist_table_start0: + .short 5 + .byte 4 + .byte 0 + .long 3 +.Lloclists_table_base0: + .long .Ldebug_loc0-.Lloclists_table_base0 + .long .Ldebug_loc1-.Lloclists_table_base0 + .long .Ldebug_loc2-.Lloclists_table_base0 +.Ldebug_loc0: + .byte 3 + .byte 0 + .uleb128 .Ltmp0-.Lfunc_begin0 + .byte 1 + .byte 80 + .byte 0 +.Ldebug_loc1: + .byte 1 + .byte 0 + .byte 4 + .uleb128 .Ltmp0-.Lfunc_begin0 + .uleb128 .Ltmp1-.Lfunc_begin0 + .byte 1 + .byte 80 + .byte 0 +.Ldebug_loc2: + .byte 1 + .byte 0 + .byte 4 + .uleb128 .Ltmp1-.Lfunc_begin0 + .uleb128 .Lfunc_end0-.Lfunc_begin0 + .byte 1 + .byte 80 + .byte 0 +.Ldebug_loclist_table_end0: + .section .debug_abbrev,"",%progbits + .byte 1 + .byte 17 + .byte 1 + .byte 37 + .byte 37 + .byte 19 + .byte 5 + .byte 3 + .byte 37 + .byte 114 + .byte 23 + .byte 16 + .byte 23 + .byte 27 + .byte 37 + .byte 17 + .byte 1 + .byte 85 + .byte 35 + .byte 115 + .byte 23 + .byte 116 + .byte 23 + .ascii "\214\001" + .byte 23 + .byte 0 + .byte 0 + .byte 2 + .byte 46 + .byte 1 + .byte 17 + .byte 27 + .byte 18 + .byte 6 + .byte 64 + .byte 24 + .byte 122 + .byte 25 + .byte 3 + .byte 37 + .byte 58 + .byte 11 + .byte 59 + .byte 11 + .byte 39 + .byte 25 + .byte 73 + .byte 19 + .byte 63 + .byte 25 + .byte 0 + .byte 0 + .byte 3 + .byte 5 + .byte 0 + .byte 2 + .byte 34 + .byte 3 + .byte 37 + .byte 58 + .byte 11 + .byte 59 + .byte 11 + .byte 73 + .byte 19 + .byte 0 + .byte 0 + .byte 4 + .byte 5 + .byte 0 + .byte 2 + .byte 24 + .byte 3 + .byte 37 + .byte 58 + .byte 11 + .byte 59 + .byte 11 + .byte 73 + .byte 19 + .byte 0 + .byte 0 + .byte 5 + .byte 52 + .byte 0 + .byte 2 + .byte 34 + .byte 3 + .byte 37 + .byte 58 + .byte 11 + .byte 59 + .byte 11 + .byte 73 + .byte 19 + .byte 0 + .byte 0 + .byte 6 + .byte 36 + .byte 0 + .byte 3 + .byte 37 + .byte 62 + .byte 11 + .byte 11 + .byte 11 + .byte 0 + .byte 0 + .byte 0 + .section .debug_info,"",%progbits +.Lcu_begin0: + .long .Ldebug_info_end0-.Ldebug_info_start0 +.Ldebug_info_start0: + .short 5 + .byte 1 + .byte 4 + .long .debug_abbrev + .byte 1 + .byte 0 + .short 12 + .byte 1 + .long .Lstr_offsets_base0 + .long .Lline_table_start0 + .byte 2 + .long 0 + .byte 0 + .long .Laddr_table_base0 + .long .Lrnglists_table_base0 + .long .Lloclists_table_base0 + .byte 2 + .byte 0 + .long .Lfunc_end0-.Lfunc_begin0 + .byte 1 + .byte 91 + + .byte 3 + .byte 1 + .byte 1 + + .long 132 + + .byte 3 + .byte 0 + .byte 6 + .byte 1 + .byte 1 + .long 132 + .byte 4 + .byte 1 + .byte 81 + .byte 7 + .byte 1 + .byte 1 + .long 132 + .byte 4 + .byte 1 + .byte 82 + .byte 8 + .byte 1 + .byte 1 + .long 132 + .byte 5 + .byte 1 + .byte 9 + .byte 1 + .byte 2 + .long 132 + .byte 5 + .byte 2 + .byte 10 + .byte 1 + .byte 3 + .long 132 + .byte 0 + .byte 2 + .byte 1 + .long .Lfunc_end1-.Lfunc_begin1 + .byte 1 + .byte 91 + + .byte 5 + .byte 1 + .byte 7 + + .long 132 + + .byte 4 + .byte 1 + .byte 80 + .byte 6 + .byte 1 + .byte 7 + .long 132 + .byte 0 + .byte 6 + .byte 4 + .byte 5 + .byte 4 + .byte 0 +.Ldebug_info_end0: + .section .debug_rnglists,"",%progbits + .long .Ldebug_rnglist_table_end0-.Ldebug_rnglist_table_start0 +.Ldebug_rnglist_table_start0: + .short 5 + .byte 4 + .byte 0 + .long 1 +.Lrnglists_table_base0: + .long .Ldebug_ranges0-.Lrnglists_table_base0 +.Ldebug_ranges0: + .byte 3 + .byte 0 + .uleb128 .Lfunc_end0-.Lfunc_begin0 + .byte 3 + .byte 1 + .uleb128 .Lfunc_end1-.Lfunc_begin1 + .byte 0 +.Ldebug_rnglist_table_end0: + .section .debug_addr,"",%progbits + .long .Ldebug_addr_end0-.Ldebug_addr_start0 +.Ldebug_addr_start0: + .short 5 + .byte 4 + .byte 0 +.Laddr_table_base0: + .long .Lfunc_begin0 + .long .Lfunc_begin1 +.Ldebug_addr_end0: + .ident "clang version 10.0.0 (git@github.com:llvm/llvm-project.git e73f78acd34360f7450b81167d9dc858ccddc262)" + .section ".note.GNU-stack","",%progbits + .addrsig + .eabi_attribute 30, 1 + .section .debug_line,"",%progbits +.Lline_table_start0: diff --git a/llvm/test/tools/llvm-objdump/ARM/debug-vars-dwarf5.s b/llvm/test/tools/llvm-objdump/ARM/debug-vars-dwarf5.s new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/ARM/debug-vars-dwarf5.s @@ -0,0 +1,382 @@ +## Check that the --debug-vars option works for simple register locations, when +## using DWARF5 debug info, with multiple functions in one section. + +## Generated with this compile command, with the source code in Inputs/debug.c: +## clang --target=arm--none-eabi -march=armv7-a -c debug.c -O1 -gdwarf-3 -S -o - + +# RUN: llvm-mc -triple armv8a--none-eabi < %s -filetype=obj --dwarf-version=5 | \ +# RUN: llvm-objdump - -d --debug-vars --no-show-raw-insn | \ +# RUN: FileCheck %s + +# CHECK: Disassembly of section .text: +# CHECK-EMPTY: +# CHECK-NEXT: 00000000 foo: +# CHECK-NEXT: ┠─ a = R0 +# CHECK-NEXT: ┃ ┠─ b = R1 +# CHECK-NEXT: ┃ ┃ ┠─ c = R2 +# CHECK-NEXT: ┃ ┃ ┃ ┌─ x = R0 +# CHECK-NEXT: 0: add r0, r1, r0 ┻ ┃ ┃ ╈ +# CHECK-NEXT: ┌─ y = R0 +# CHECK-NEXT: 4: add r0, r0, r2 ╈ ┃ ┃ ┻ +# CHECK-NEXT: 8: bx lr ┻ ┻ ┻ +# CHECK-EMPTY: +# CHECK-NEXT: 0000000c bar: +# CHECK-NEXT: ┠─ a = R0 +# CHECK-NEXT: c: add r0, r0, #1 ┃ +# CHECK-NEXT: 10: bx lr ┻ + + .text + .syntax unified + .eabi_attribute 67, "2.09" + .eabi_attribute 6, 10 + .eabi_attribute 7, 65 + .eabi_attribute 8, 1 + .eabi_attribute 9, 2 + .fpu neon + .eabi_attribute 34, 0 + .eabi_attribute 17, 1 + .eabi_attribute 20, 1 + .eabi_attribute 21, 1 + .eabi_attribute 23, 3 + .eabi_attribute 24, 1 + .eabi_attribute 25, 1 + .eabi_attribute 38, 1 + .eabi_attribute 18, 4 + .eabi_attribute 26, 2 + .eabi_attribute 14, 0 + .file "debug.c" + .globl foo + .p2align 2 + .type foo,%function + .code 32 +foo: +.Lfunc_begin0: + .file 0 "/work/scratch" "/work/llvm/src/llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c" md5 0x07374f01ab24ec7c07db73bc13bd778e + .file 1 "/work" "llvm/src/llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c" md5 0x07374f01ab24ec7c07db73bc13bd778e + .loc 1 1 0 + .fnstart + .cfi_sections .debug_frame + .cfi_startproc + .loc 1 2 13 prologue_end + add r0, r1, r0 +.Ltmp0: + .loc 1 3 13 + add r0, r0, r2 +.Ltmp1: + .loc 1 4 3 + bx lr +.Ltmp2: +.Lfunc_end0: + .size foo, .Lfunc_end0-foo + .cfi_endproc + .cantunwind + .fnend + + .globl bar + .p2align 2 + .type bar,%function + .code 32 +bar: +.Lfunc_begin1: + .loc 1 7 0 + .fnstart + .cfi_startproc + .loc 1 8 4 prologue_end + add r0, r0, #1 +.Ltmp3: + .loc 1 9 3 + bx lr +.Ltmp4: +.Lfunc_end1: + .size bar, .Lfunc_end1-bar + .cfi_endproc + .cantunwind + .fnend + + .section .debug_str_offsets,"",%progbits + .long 48 + .short 5 + .short 0 +.Lstr_offsets_base0: + .section .debug_str,"MS",%progbits,1 +.Linfo_string0: + .asciz "clang version 10.0.0 (git@github.com:llvm/llvm-project.git e73f78acd34360f7450b81167d9dc858ccddc262)" +.Linfo_string1: + .asciz "/work/llvm/src/llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c" +.Linfo_string2: + .asciz "/work/scratch" +.Linfo_string3: + .asciz "foo" +.Linfo_string4: + .asciz "int" +.Linfo_string5: + .asciz "bar" +.Linfo_string6: + .asciz "a" +.Linfo_string7: + .asciz "b" +.Linfo_string8: + .asciz "c" +.Linfo_string9: + .asciz "x" +.Linfo_string10: + .asciz "y" + .section .debug_str_offsets,"",%progbits + .long .Linfo_string0 + .long .Linfo_string1 + .long .Linfo_string2 + .long .Linfo_string3 + .long .Linfo_string4 + .long .Linfo_string5 + .long .Linfo_string6 + .long .Linfo_string7 + .long .Linfo_string8 + .long .Linfo_string9 + .long .Linfo_string10 + .section .debug_loclists,"",%progbits + .long .Ldebug_loclist_table_end0-.Ldebug_loclist_table_start0 +.Ldebug_loclist_table_start0: + .short 5 + .byte 4 + .byte 0 + .long 3 +.Lloclists_table_base0: + .long .Ldebug_loc0-.Lloclists_table_base0 + .long .Ldebug_loc1-.Lloclists_table_base0 + .long .Ldebug_loc2-.Lloclists_table_base0 +.Ldebug_loc0: + .byte 4 + .uleb128 .Lfunc_begin0-.Lfunc_begin0 + .uleb128 .Ltmp0-.Lfunc_begin0 + .byte 1 + .byte 80 + .byte 0 +.Ldebug_loc1: + .byte 4 + .uleb128 .Ltmp0-.Lfunc_begin0 + .uleb128 .Ltmp1-.Lfunc_begin0 + .byte 1 + .byte 80 + .byte 0 +.Ldebug_loc2: + .byte 4 + .uleb128 .Ltmp1-.Lfunc_begin0 + .uleb128 .Lfunc_end0-.Lfunc_begin0 + .byte 1 + .byte 80 + .byte 0 +.Ldebug_loclist_table_end0: + .section .debug_abbrev,"",%progbits + .byte 1 + .byte 17 + .byte 1 + .byte 37 + .byte 37 + .byte 19 + .byte 5 + .byte 3 + .byte 37 + .byte 114 + .byte 23 + .byte 16 + .byte 23 + .byte 27 + .byte 37 + .byte 17 + .byte 27 + .byte 18 + .byte 6 + .byte 115 + .byte 23 + .ascii "\214\001" + .byte 23 + .byte 0 + .byte 0 + .byte 2 + .byte 46 + .byte 1 + .byte 17 + .byte 27 + .byte 18 + .byte 6 + .byte 64 + .byte 24 + .byte 122 + .byte 25 + .byte 3 + .byte 37 + .byte 58 + .byte 11 + .byte 59 + .byte 11 + .byte 39 + .byte 25 + .byte 73 + .byte 19 + .byte 63 + .byte 25 + .byte 0 + .byte 0 + .byte 3 + .byte 5 + .byte 0 + .byte 2 + .byte 34 + .byte 3 + .byte 37 + .byte 58 + .byte 11 + .byte 59 + .byte 11 + .byte 73 + .byte 19 + .byte 0 + .byte 0 + .byte 4 + .byte 5 + .byte 0 + .byte 2 + .byte 24 + .byte 3 + .byte 37 + .byte 58 + .byte 11 + .byte 59 + .byte 11 + .byte 73 + .byte 19 + .byte 0 + .byte 0 + .byte 5 + .byte 52 + .byte 0 + .byte 2 + .byte 34 + .byte 3 + .byte 37 + .byte 58 + .byte 11 + .byte 59 + .byte 11 + .byte 73 + .byte 19 + .byte 0 + .byte 0 + .byte 6 + .byte 36 + .byte 0 + .byte 3 + .byte 37 + .byte 62 + .byte 11 + .byte 11 + .byte 11 + .byte 0 + .byte 0 + .byte 0 + .section .debug_info,"",%progbits +.Lcu_begin0: + .long .Ldebug_info_end0-.Ldebug_info_start0 +.Ldebug_info_start0: + .short 5 + .byte 1 + .byte 4 + .long .debug_abbrev + .byte 1 + .byte 0 + .short 12 + .byte 1 + .long .Lstr_offsets_base0 + .long .Lline_table_start0 + .byte 2 + .byte 0 + .long .Lfunc_end1-.Lfunc_begin0 + .long .Laddr_table_base0 + .long .Lloclists_table_base0 + .byte 2 + .byte 0 + .long .Lfunc_end0-.Lfunc_begin0 + .byte 1 + .byte 91 + + .byte 3 + .byte 1 + .byte 1 + + .long 128 + + .byte 3 + .byte 0 + .byte 6 + .byte 1 + .byte 1 + .long 128 + .byte 4 + .byte 1 + .byte 81 + .byte 7 + .byte 1 + .byte 1 + .long 128 + .byte 4 + .byte 1 + .byte 82 + .byte 8 + .byte 1 + .byte 1 + .long 128 + .byte 5 + .byte 1 + .byte 9 + .byte 1 + .byte 2 + .long 128 + .byte 5 + .byte 2 + .byte 10 + .byte 1 + .byte 3 + .long 128 + .byte 0 + .byte 2 + .byte 1 + .long .Lfunc_end1-.Lfunc_begin1 + .byte 1 + .byte 91 + + .byte 5 + .byte 1 + .byte 7 + + .long 128 + + .byte 4 + .byte 1 + .byte 80 + .byte 6 + .byte 1 + .byte 7 + .long 128 + .byte 0 + .byte 6 + .byte 4 + .byte 5 + .byte 4 + .byte 0 +.Ldebug_info_end0: + .section .debug_addr,"",%progbits + .long .Ldebug_addr_end0-.Ldebug_addr_start0 +.Ldebug_addr_start0: + .short 5 + .byte 4 + .byte 0 +.Laddr_table_base0: + .long .Lfunc_begin0 + .long .Lfunc_begin1 +.Ldebug_addr_end0: + .ident "clang version 10.0.0 (git@github.com:llvm/llvm-project.git e73f78acd34360f7450b81167d9dc858ccddc262)" + .section ".note.GNU-stack","",%progbits + .addrsig + .eabi_attribute 30, 1 + .section .debug_line,"",%progbits +.Lline_table_start0: diff --git a/llvm/test/tools/llvm-objdump/PowerPC/debug-vars.s b/llvm/test/tools/llvm-objdump/PowerPC/debug-vars.s new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/PowerPC/debug-vars.s @@ -0,0 +1,372 @@ +## Check that the --debug-vars option works for simple register locations, when +## using DWARF4 debug info, with multiple functions in one section. + +## Generated with this compile command and source code: +## clang --target=arm--none-eabi -march=armv7-a -c debug.c -O1 -gdwarf-3 -S -o - + +## clang --target=powerpc64-unknown-linux -c debug.c -O1 -S -o - + +## int foo(int a, int b, int c) { +## int x = a + b; +## int y = x + c; +## return y; +## } +## +## int bar(int a) { +## a++; +## return a; +## } + +# RUN: llvm-mc -triple powerpc64-unknown-linux < %s -filetype=obj | \ +# RUN: llvm-objdump - -d --debug-vars --no-show-raw-insn | \ +# RUN: FileCheck %s + +# CHECK: Disassembly of section .text: +# CHECK-EMPTY: +# CHECK-NEXT: 0000000000000000 .text: +# CHECK-NEXT: ┠─ a = S3 +# CHECK-NEXT: ┃ ┠─ b = S4 +# CHECK-NEXT: ┃ ┃ ┠─ c = S5 +# CHECK-NEXT: ┃ ┃ ┃ ┌─ x = S3 +# CHECK-NEXT: 0: add 3, 4, 3 ┻ ┃ ┃ ╈ +# CHECK-NEXT: ┌─ y = S3 +# CHECK-NEXT: 4: add 3, 3, 5 ╈ ┃ ┃ ┻ +# CHECK-NEXT: 8: extsw 3, 3 ┻ ┃ ┃ +# CHECK-NEXT: c: blr ┃ ┃ +# CHECK-NEXT: ... +# CHECK-NEXT: ┠─ a = S3 +# CHECK-NEXT: 1c: addi 3, 3, 1 ┃ +# CHECK-NEXT: 20: extsw 3, 3 ┻ +# CHECK-NEXT: 24: blr +# CHECK-NEXT: ... + + .text + .file "debug.c" + .globl foo # -- Begin function foo + .p2align 2 + .type foo,@function + .section .opd,"aw",@progbits +foo: # @foo + .p2align 3 + .quad .Lfunc_begin0 + .quad .TOC.@tocbase + .quad 0 + .text +.Lfunc_begin0: + .file 1 "/work" "llvm/src/llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c" + .loc 1 1 0 # llvm/src/llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c:1:0 + .cfi_sections .debug_frame + .cfi_startproc +# %bb.0: # %entry + #DEBUG_VALUE: foo:a <- $x3 + #DEBUG_VALUE: foo:a <- $r3 + #DEBUG_VALUE: foo:b <- $x4 + #DEBUG_VALUE: foo:b <- $x4 + #DEBUG_VALUE: foo:b <- $r4 + #DEBUG_VALUE: foo:c <- $x5 + #DEBUG_VALUE: foo:c <- $x5 + #DEBUG_VALUE: foo:c <- $r5 + .loc 1 2 13 prologue_end # llvm/src/llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c:2:13 + add 3, 4, 3 +.Ltmp0: + #DEBUG_VALUE: foo:x <- $r3 + .loc 1 3 13 # llvm/src/llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c:3:13 + add 3, 3, 5 +.Ltmp1: + #DEBUG_VALUE: foo:y <- $r3 + .loc 1 4 3 # llvm/src/llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c:4:3 + extsw 3, 3 +.Ltmp2: + blr +.Ltmp3: + .long 0 + .quad 0 +.Lfunc_end0: + .size foo, .Lfunc_end0-.Lfunc_begin0 + .cfi_endproc + # -- End function + .globl bar # -- Begin function bar + .p2align 2 + .type bar,@function + .section .opd,"aw",@progbits +bar: # @bar + .p2align 3 + .quad .Lfunc_begin1 + .quad .TOC.@tocbase + .quad 0 + .text +.Lfunc_begin1: + .loc 1 7 0 # llvm/src/llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c:7:0 + .cfi_startproc +# %bb.0: # %entry + #DEBUG_VALUE: bar:a <- $x3 + #DEBUG_VALUE: bar:a <- $r3 + .loc 1 8 4 prologue_end # llvm/src/llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c:8:4 + addi 3, 3, 1 +.Ltmp4: + #DEBUG_VALUE: bar:a <- $r3 + .loc 1 9 3 # llvm/src/llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c:9:3 + extsw 3, 3 +.Ltmp5: + blr +.Ltmp6: + .long 0 + .quad 0 +.Lfunc_end1: + .size bar, .Lfunc_end1-.Lfunc_begin1 + .cfi_endproc + # -- End function + .section .debug_str,"MS",@progbits,1 +.Linfo_string0: + .asciz "clang version 10.0.0 (git@github.com:llvm/llvm-project.git e73f78acd34360f7450b81167d9dc858ccddc262)" # string offset=0 +.Linfo_string1: + .asciz "/work/llvm/src/llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c" # string offset=101 +.Linfo_string2: + .asciz "/work/scratch" # string offset=164 +.Linfo_string3: + .asciz "foo" # string offset=178 +.Linfo_string4: + .asciz "int" # string offset=182 +.Linfo_string5: + .asciz "bar" # string offset=186 +.Linfo_string6: + .asciz "a" # string offset=190 +.Linfo_string7: + .asciz "b" # string offset=192 +.Linfo_string8: + .asciz "c" # string offset=194 +.Linfo_string9: + .asciz "x" # string offset=196 +.Linfo_string10: + .asciz "y" # string offset=198 + .section .debug_loc,"",@progbits +.Ldebug_loc0: + .quad .Lfunc_begin0-.Lfunc_begin0 + .quad .Ltmp0-.Lfunc_begin0 + .short 3 # Loc expr size + .byte 144 # super-register DW_OP_regx + .byte 179 # 1203 + .byte 9 # + .quad 0 + .quad 0 +.Ldebug_loc1: + .quad .Ltmp0-.Lfunc_begin0 + .quad .Ltmp1-.Lfunc_begin0 + .short 3 # Loc expr size + .byte 144 # super-register DW_OP_regx + .byte 179 # 1203 + .byte 9 # + .quad 0 + .quad 0 +.Ldebug_loc2: + .quad .Ltmp1-.Lfunc_begin0 + .quad .Ltmp2-.Lfunc_begin0 + .short 3 # Loc expr size + .byte 144 # super-register DW_OP_regx + .byte 179 # 1203 + .byte 9 # + .quad 0 + .quad 0 +.Ldebug_loc3: + .quad .Lfunc_begin1-.Lfunc_begin0 + .quad .Ltmp5-.Lfunc_begin0 + .short 3 # Loc expr size + .byte 144 # super-register DW_OP_regx + .byte 179 # 1203 + .byte 9 # + .quad 0 + .quad 0 + .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 17 # DW_AT_low_pc + .byte 1 # DW_FORM_addr + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 64 # DW_AT_frame_base + .byte 24 # DW_FORM_exprloc + .ascii "\227B" # DW_AT_GNU_all_call_sites + .byte 25 # DW_FORM_flag_present + .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 39 # DW_AT_prototyped + .byte 25 # DW_FORM_flag_present + .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 5 # DW_TAG_formal_parameter + .byte 0 # DW_CHILDREN_no + .byte 2 # DW_AT_location + .byte 23 # DW_FORM_sec_offset + .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 5 # DW_TAG_formal_parameter + .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 5 # Abbreviation Code + .byte 52 # DW_TAG_variable + .byte 0 # DW_CHILDREN_no + .byte 2 # DW_AT_location + .byte 23 # DW_FORM_sec_offset + .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 6 # 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:0xb5 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_end1-.Lfunc_begin0 # DW_AT_high_pc + .byte 2 # Abbrev [2] 0x2a:0x65 DW_TAG_subprogram + .quad .Lfunc_begin0 # DW_AT_low_pc + .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc + .byte 1 # DW_AT_frame_base + .byte 81 + # DW_AT_GNU_all_call_sites + .long .Linfo_string3 # DW_AT_name + .byte 1 # DW_AT_decl_file + .byte 1 # DW_AT_decl_line + # DW_AT_prototyped + .long 184 # DW_AT_type + # DW_AT_external + .byte 3 # Abbrev [3] 0x43:0xf DW_TAG_formal_parameter + .long .Ldebug_loc0 # DW_AT_location + .long .Linfo_string6 # DW_AT_name + .byte 1 # DW_AT_decl_file + .byte 1 # DW_AT_decl_line + .long 184 # DW_AT_type + .byte 4 # Abbrev [4] 0x52:0xf DW_TAG_formal_parameter + .byte 3 # DW_AT_location + .byte 144 + .ascii "\264\t" + .long .Linfo_string7 # DW_AT_name + .byte 1 # DW_AT_decl_file + .byte 1 # DW_AT_decl_line + .long 184 # DW_AT_type + .byte 4 # Abbrev [4] 0x61:0xf DW_TAG_formal_parameter + .byte 3 # DW_AT_location + .byte 144 + .ascii "\265\t" + .long .Linfo_string8 # DW_AT_name + .byte 1 # DW_AT_decl_file + .byte 1 # DW_AT_decl_line + .long 184 # DW_AT_type + .byte 5 # Abbrev [5] 0x70:0xf DW_TAG_variable + .long .Ldebug_loc1 # DW_AT_location + .long .Linfo_string9 # DW_AT_name + .byte 1 # DW_AT_decl_file + .byte 2 # DW_AT_decl_line + .long 184 # DW_AT_type + .byte 5 # Abbrev [5] 0x7f:0xf DW_TAG_variable + .long .Ldebug_loc2 # DW_AT_location + .long .Linfo_string10 # DW_AT_name + .byte 1 # DW_AT_decl_file + .byte 3 # DW_AT_decl_line + .long 184 # DW_AT_type + .byte 0 # End Of Children Mark + .byte 2 # Abbrev [2] 0x8f:0x29 DW_TAG_subprogram + .quad .Lfunc_begin1 # DW_AT_low_pc + .long .Lfunc_end1-.Lfunc_begin1 # DW_AT_high_pc + .byte 1 # DW_AT_frame_base + .byte 81 + # DW_AT_GNU_all_call_sites + .long .Linfo_string5 # DW_AT_name + .byte 1 # DW_AT_decl_file + .byte 7 # DW_AT_decl_line + # DW_AT_prototyped + .long 184 # DW_AT_type + # DW_AT_external + .byte 3 # Abbrev [3] 0xa8:0xf DW_TAG_formal_parameter + .long .Ldebug_loc3 # DW_AT_location + .long .Linfo_string6 # DW_AT_name + .byte 1 # DW_AT_decl_file + .byte 7 # DW_AT_decl_line + .long 184 # DW_AT_type + .byte 0 # End Of Children Mark + .byte 6 # Abbrev [6] 0xb8: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: + .ident "clang version 10.0.0 (git@github.com:llvm/llvm-project.git e73f78acd34360f7450b81167d9dc858ccddc262)" + .section ".note.GNU-stack","",@progbits + .addrsig + .section .debug_line,"",@progbits +.Lline_table_start0: diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp --- a/llvm/tools/llvm-objdump/llvm-objdump.cpp +++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp @@ -16,7 +16,9 @@ //===----------------------------------------------------------------------===// #include "llvm-objdump.h" +#include "llvm/ADT/IndexedMap.h" #include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SetOperations.h" #include "llvm/ADT/StringExtras.h" @@ -70,6 +72,8 @@ #include #include +#define DEBUG_TYPE "objdump" + using namespace llvm::object; namespace llvm { @@ -334,6 +338,23 @@ cl::cat(ObjdumpCat)); static cl::alias WideShort("w", cl::Grouping, cl::aliasopt(Wide)); +static cl::opt + DbgVariables("debug-vars", cl::init(false), + cl::desc("Print the locations (in registers or memory) of " + "source-level variables alongside disassembly"), + cl::cat(ObjdumpCat)); + +static cl::opt DbgVariablesUnicode( + "debug-vars-unicode", cl::init(true), + cl::desc("Use unicode characters when printing variable locations"), + cl::cat(ObjdumpCat)); + +static cl::opt + DbgIndent("debug-vars-indent", cl::init(50), + cl::desc("Distance to indent the source-level variable display, " + "relative to the start of the disassembly"), + cl::cat(ObjdumpCat)); + static cl::extrahelp HelpResponse("\nPass @FILE as argument to read options from FILE.\n"); @@ -538,6 +559,415 @@ } namespace { + +/// A user-facing string representation of a DWARF expression. This might be an +/// Address expression, in which case it will get an implicit dereference, or a +/// Value expression, which does not. +struct PrintedExpr { + enum ExprKind { + Address, + Value, + }; + ExprKind Kind; + SmallString<20> String; + + PrintedExpr(ExprKind K = Address) : Kind(K) {} +}; + +static void prettyPrintDWARFExpr(raw_ostream &OS, DWARFExpression::iterator I, + const DWARFExpression::iterator E, + const MCRegisterInfo *MRI) { + SmallVector Stack; + + while (I != E) { + DWARFExpression::Operation &Op = *I; + uint8_t Opcode = Op.getCode(); + switch (Opcode) { + case dwarf::DW_OP_regx: { + uint64_t DwarfRegNum = Op.getRawOperand(0); + Optional LLVMRegNum = MRI->getLLVMRegNum(DwarfRegNum, false); + if (!LLVMRegNum) { + OS << ""; + return; + } + raw_svector_ostream S(Stack.emplace_back(PrintedExpr::Value).String); + S << MRI->getName(*LLVMRegNum); + break; + } + default: + if (Opcode >= dwarf::DW_OP_reg0 && Opcode <= dwarf::DW_OP_reg31) { + uint64_t DwarfRegNum = Opcode - dwarf::DW_OP_reg0; + Optional LLVMRegNum = MRI->getLLVMRegNum(DwarfRegNum, false); + if (!LLVMRegNum) { + OS << ""; + return; + } + raw_svector_ostream S(Stack.emplace_back(PrintedExpr::Value).String); + S << MRI->getName(*LLVMRegNum); + } else { + // If we hit an unknown operand, we don't know its effect on the stack, + // so bail out on the whole expression. + OS << ""; + return; + } + break; + } + ++I; + } + + assert(Stack.size() == 1 && "expected one value on stack"); + OS << Stack.front().String; +} + +static void prettyPrintDWARFExpr(raw_ostream &OS, const DWARFExpression *Expr, + const MCRegisterInfo *MRI) { + prettyPrintDWARFExpr(OS, Expr->begin(), Expr->end(), MRI); +} + +/// Stores a single expression representing the location of a source-level +/// variable, along with the PC range for which that expression is valid. +struct LiveVariable { + DWARFLocationExpression LocExpr; + const char *VarName; + DWARFUnit *Unit; + const DWARFDie FuncDie; + + LiveVariable(const DWARFLocationExpression &LocExpr, const char *VarName, + DWARFUnit *Unit, const DWARFDie FuncDie) + : LocExpr(LocExpr), VarName(VarName), Unit(Unit), FuncDie(FuncDie) {} + + bool liveAtAddress(object::SectionedAddress Addr) { + if (LocExpr.Range == None) + return false; + return LocExpr.Range->SectionIndex == Addr.SectionIndex && + LocExpr.Range->LowPC <= Addr.Address && + LocExpr.Range->HighPC > Addr.Address; + } + + void print(raw_ostream &OS, const MCRegisterInfo *MRI) const { + DataExtractor Data({LocExpr.Expr.data(), LocExpr.Expr.size()}, + Unit->getContext().isLittleEndian(), 0); + DWARFExpression Expression(Data, Unit->getVersion(), + Unit->getAddressByteSize()); + prettyPrintDWARFExpr(OS, &Expression, MRI); + } +}; + +/// Helper class for printing source variable locations alongside disassembly. +class LiveVariablePrinter { + // Information we want to track about one column in which we are printing a + // variable live range. + struct Column { + unsigned VarIdx; + bool LiveIn; + bool LiveOut; + bool MustDrawLabel; + + bool isActive() const { return VarIdx != NullVarIdx; } + + static constexpr unsigned NullVarIdx = std::numeric_limits::max(); + }; + + // All live variables we know about in the object/image file. + std::vector LiveVariables; + + // The columns we are currently drawing. + IndexedMap ActiveCols; + + const MCRegisterInfo *MRI; + + void addVariable(DWARFDie FuncDie, DWARFDie VarDie) { + uint64_t FuncLowPC, FuncHighPC, SectionIndex; + FuncDie.getLowAndHighPC(FuncLowPC, FuncHighPC, SectionIndex); + const char *VarName = VarDie.getName(DINameKind::ShortName); + DWARFUnit *U = VarDie.getDwarfUnit(); + + Expected Locs = + VarDie.getLocations(dwarf::DW_AT_location); + if (!Locs) { + // If the variable doesn't have any locations, just ignore it. We don't + // report an error or warning here as that could be noisy on optimised + // code. + consumeError(Locs.takeError()); + return; + } + + for (const DWARFLocationExpression &LocExpr : *Locs) { + if (LocExpr.Range) { + LiveVariables.emplace_back(LocExpr, VarName, U, FuncDie); + } else { + // If the LocExpr does not have an associated range, it is valid for + // the whole of the function. + // TODO: technically it is not valid for any range covered by another + // LocExpr, does that happen in reality? + DWARFLocationExpression WholeFuncExpr{ + DWARFAddressRange(FuncLowPC, FuncHighPC, SectionIndex), + LocExpr.Expr}; + LiveVariables.emplace_back(WholeFuncExpr, VarName, U, FuncDie); + } + } + } + + void addFunction(DWARFDie D) { + for (const DWARFDie &Child : D.children()) { + if (Child.getTag() == dwarf::DW_TAG_variable || + Child.getTag() == dwarf::DW_TAG_formal_parameter) + addVariable(D, Child); + else + addFunction(Child); + } + } + + // Get the column number (in characters) at which the first live variable + // line should be printed. + unsigned getIndentLevel() const { + return DbgIndent + (NoShowRawInsn ? 16 : 40); + } + + // Indent to the first live-range column to the right of the currently + // printed line, and return the index of that column. + // TODO: formatted_raw_ostream uses "column" to mean a number of characters + // since the last \n, and we use it to mean the number of slots in which we + // put live variable lines. Pick a less overloaded word. + unsigned moveToFirstVarColumn(formatted_raw_ostream &OS) { + // Logical column number: column zero is the first column we print in, each + // logical column is 2 physical columns wide. + unsigned FirstUnprintedLogicalColumn = + std::max((int)(OS.getColumn() - getIndentLevel() + 1) / 2, 0); + // Physical column number: the actual column number in characters, with + // zero being the left-most side of the screen. + unsigned FirstUnprintedPhysicalColumn = + getIndentLevel() + FirstUnprintedLogicalColumn * 2; + + if (FirstUnprintedPhysicalColumn > OS.getColumn()) + OS.PadToColumn(FirstUnprintedPhysicalColumn); + + return FirstUnprintedLogicalColumn; + } + + unsigned findFreeColumn() { + for (unsigned ColIdx = 0; ColIdx < ActiveCols.size(); ++ColIdx) + if (!ActiveCols[ColIdx].isActive()) + return ColIdx; + + size_t OldSize = ActiveCols.size(); + ActiveCols.grow(std::max(OldSize * 2, 1)); + return OldSize; + } + +public: + LiveVariablePrinter(const MCRegisterInfo *MRI) + : LiveVariables(), ActiveCols({Column::NullVarIdx, false, false, false}), + MRI(MRI) {} + + void dump() const { + for (const LiveVariable &LV : LiveVariables) { + dbgs() << LV.VarName << " @ " << LV.LocExpr.Range << ": "; + LV.print(dbgs(), MRI); + dbgs() << "\n"; + } + } + + void AddCompileUnit(DWARFDie D) { + if (D.getTag() == dwarf::DW_TAG_subprogram) + addFunction(D); + else + for (const DWARFDie &Child : D.children()) + addFunction(Child); + } + + /// Update to match the state of the instruction between ThisAddr and + /// NextAddr. In the common case, any live range active at ThisAddr is + /// live-in to the instruction, and any live range active at NextAddr is + /// live-out of the instruction. If EndFunction is true, then ThisAddr and + /// NextAddr are in different functions, so live ranges starting at NextAddr + /// will be ignored, because they belong to the next function. + void update(object::SectionedAddress ThisAddr, + object::SectionedAddress NextAddr, bool EndFunction) { + // First, check variables which have already been assigned a column, so + // that we don't change their order. + SmallSet CheckedVarIdxs; + for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx) { + if (!ActiveCols[ColIdx].isActive()) + continue; + CheckedVarIdxs.insert(ActiveCols[ColIdx].VarIdx); + LiveVariable &LV = LiveVariables[ActiveCols[ColIdx].VarIdx]; + ActiveCols[ColIdx].LiveIn = LV.liveAtAddress(ThisAddr); + ActiveCols[ColIdx].LiveOut = LV.liveAtAddress(NextAddr); + LLVM_DEBUG(dbgs() << "pass 1, " << ThisAddr.Address << "-" + << NextAddr.Address << ", " << LV.VarName << ", Col " + << ColIdx << ": LiveIn=" << ActiveCols[ColIdx].LiveIn + << ", LiveOut=" << ActiveCols[ColIdx].LiveOut << "\n"); + + if (!ActiveCols[ColIdx].LiveIn && !ActiveCols[ColIdx].LiveOut) + ActiveCols[ColIdx].VarIdx = Column::NullVarIdx; + } + + // Next, look for variables which don't already have a column, but which + // are now live. + if (!EndFunction) { + for (unsigned VarIdx = 0, End = LiveVariables.size(); VarIdx < End; + ++VarIdx) { + if (CheckedVarIdxs.count(VarIdx)) + continue; + LiveVariable &LV = LiveVariables[VarIdx]; + bool LiveIn = LV.liveAtAddress(ThisAddr); + bool LiveOut = LV.liveAtAddress(NextAddr); + if (!LiveIn && !LiveOut) + continue; + + unsigned ColIdx = findFreeColumn(); + LLVM_DEBUG(dbgs() << "pass 2, " << ThisAddr.Address << "-" + << NextAddr.Address << ", " << LV.VarName << ", Col " + << ColIdx << ": LiveIn=" << LiveIn + << ", LiveOut=" << LiveOut << "\n"); + ActiveCols[ColIdx] = {VarIdx, LiveIn, LiveOut, true}; + } + } + } + + enum class LineChar { + RangeStart, + RangeMid, + RangeEnd, + LabelVert, + LabelCornerNew, + LabelCornerActive, + LabelHoriz, + }; + const char *getLineChar(LineChar C) const { + switch (C) { + case LineChar::RangeStart: + return DbgVariablesUnicode ? "╈" : "^"; + case LineChar::RangeMid: + return DbgVariablesUnicode ? "┃" : "|"; + case LineChar::RangeEnd: + return DbgVariablesUnicode ? "┻" : "v"; + case LineChar::LabelVert: + return DbgVariablesUnicode ? "│" : "|"; + case LineChar::LabelCornerNew: + return DbgVariablesUnicode ? "┌" : "/"; + case LineChar::LabelCornerActive: + return DbgVariablesUnicode ? "┠" : "|"; + case LineChar::LabelHoriz: + return DbgVariablesUnicode ? "─" : "-"; + } + } + + /// Print live ranges to the right of an existing line. This assumes the + /// line is not an instruction, so doesn't start or end any live ranges, so + /// we only need to print active ranges or empty columns. If AfterInst is + /// true, this is being printed after the last instruction fed to update(), + /// otherwise this is being printed before it. + void printAfterOtherLine(formatted_raw_ostream &OS, bool AfterInst) { + if (ActiveCols.size()) { + unsigned FirstUnprintedColumn = moveToFirstVarColumn(OS); + for (size_t ColIdx = FirstUnprintedColumn, End = ActiveCols.size(); + ColIdx < End; ++ColIdx) { + if (ActiveCols[ColIdx].isActive()) { + if ((AfterInst && ActiveCols[ColIdx].LiveOut) || + (!AfterInst && ActiveCols[ColIdx].LiveIn)) + OS << getLineChar(LineChar::RangeMid) << " "; + else if (!AfterInst && ActiveCols[ColIdx].LiveOut) + OS << getLineChar(LineChar::LabelVert) << " "; + else + OS << " "; + } else { + OS << " "; + } + } + } + OS << "\n"; + } + + /// Print any live variable range info needed to the right of a + /// non-instruction line of disassembly. This is where we print the variable + /// names and expressions, with thin line-drawing characters connecting them + /// to the live range which starts at the next instruction. If MustPrint is + /// true, we have to print at least one line (with the continuation of any + /// already-active live ranges) because something has already been printed + /// earlier on this line. + void printBetweenInsts(formatted_raw_ostream &OS, bool MustPrint) { + bool PrintedSomething = false; + for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx) { + if (ActiveCols[ColIdx].isActive() && ActiveCols[ColIdx].MustDrawLabel) { + // First we need to print the live range markers for any active + // columns to the left of this one. + OS.PadToColumn(getIndentLevel()); + for (unsigned ColIdx2 = 0; ColIdx2 < ColIdx; ++ColIdx2) { + if (ActiveCols[ColIdx2].isActive()) { + if (ActiveCols[ColIdx2].MustDrawLabel && + !ActiveCols[ColIdx2].LiveIn) + OS << getLineChar(LineChar::LabelVert) << " "; + else + OS << getLineChar(LineChar::RangeMid) << " "; + } else + OS << " "; + } + + // Then print the variable name and location of the new live range, + // with box drawing characters joining it to the live range line. + OS << getLineChar(ActiveCols[ColIdx].LiveIn + ? LineChar::LabelCornerActive + : LineChar::LabelCornerNew) + << getLineChar(LineChar::LabelHoriz) << " "; + WithColor(OS, raw_ostream::GREEN) + << LiveVariables[ActiveCols[ColIdx].VarIdx].VarName; + OS << " = "; + { + WithColor ExprColor(OS, raw_ostream::CYAN); + LiveVariables[ActiveCols[ColIdx].VarIdx].print(OS, MRI); + } + + // If there are any columns to the right of the expression we just + // printed, then continue their live range lines. + unsigned FirstUnprintedColumn = moveToFirstVarColumn(OS); + for (unsigned ColIdx2 = FirstUnprintedColumn, End = ActiveCols.size(); + ColIdx2 < End; ++ColIdx2) { + if (ActiveCols[ColIdx2].isActive() && ActiveCols[ColIdx2].LiveIn) + OS << getLineChar(LineChar::RangeMid) << " "; + else + OS << " "; + } + + OS << "\n"; + PrintedSomething = true; + } + } + + for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx) + if (ActiveCols[ColIdx].isActive()) + ActiveCols[ColIdx].MustDrawLabel = false; + + // If we must print something (because we printed a line/column number), + // but don't have any new variables to print, then print a line which + // just continues any existing live ranges. + if (MustPrint && !PrintedSomething) + printAfterOtherLine(OS, false); + } + + /// Print the live variable ranges to the right of a disassembled instruction. + void printAfterInst(formatted_raw_ostream &OS) { + if (!ActiveCols.size()) + return; + unsigned FirstUnprintedColumn = moveToFirstVarColumn(OS); + for (unsigned ColIdx = FirstUnprintedColumn, End = ActiveCols.size(); + ColIdx < End; ++ColIdx) { + if (!ActiveCols[ColIdx].isActive()) + OS << " "; + else if (ActiveCols[ColIdx].LiveIn && ActiveCols[ColIdx].LiveOut) + OS << getLineChar(LineChar::RangeMid) << " "; + else if (ActiveCols[ColIdx].LiveOut) + OS << getLineChar(LineChar::RangeStart) << " "; + else if (ActiveCols[ColIdx].LiveIn) + OS << getLineChar(LineChar::RangeEnd) << " "; + else + llvm_unreachable("var must be live in or out!"); + } + } +}; + class SourcePrinter { protected: DILineInfo OldLineInfo; @@ -566,9 +996,10 @@ Symbolizer.reset(new symbolize::LLVMSymbolizer(SymbolizerOpts)); } virtual ~SourcePrinter() = default; - virtual void printSourceLine(raw_ostream &OS, + virtual void printSourceLine(formatted_raw_ostream &OS, object::SectionedAddress Address, StringRef ObjectFilename, + LiveVariablePrinter &LVP, StringRef Delimiter = "; "); }; @@ -602,9 +1033,10 @@ return true; } -void SourcePrinter::printSourceLine(raw_ostream &OS, +void SourcePrinter::printSourceLine(formatted_raw_ostream &OS, object::SectionedAddress Address, StringRef ObjectFilename, + LiveVariablePrinter &LVP, StringRef Delimiter) { if (!Symbolizer) return; @@ -633,8 +1065,10 @@ (OldLineInfo.FileName == LineInfo.FileName))) return; - if (PrintLines) - OS << Delimiter << LineInfo.FileName << ":" << LineInfo.Line << "\n"; + if (PrintLines) { + OS << Delimiter << LineInfo.FileName << ":" << LineInfo.Line; + LVP.printBetweenInsts(OS, true); + } if (PrintSource) { if (SourceCache.find(LineInfo.FileName) == SourceCache.end()) if (!cacheSource(LineInfo)) @@ -650,7 +1084,8 @@ return; } // Vector begins at 0, line numbers are non-zero - OS << Delimiter << LineBuffer->second[LineInfo.Line - 1] << '\n'; + OS << Delimiter << LineBuffer->second[LineInfo.Line - 1]; + LVP.printBetweenInsts(OS, true); } } OldLineInfo = LineInfo; @@ -670,28 +1105,30 @@ return isArmElf(Obj) || isAArch64Elf(Obj); } -static void printRelocation(StringRef FileName, const RelocationRef &Rel, - uint64_t Address, bool Is64Bits) { +static void printRelocation(formatted_raw_ostream &OS, StringRef FileName, + const RelocationRef &Rel, uint64_t Address, + bool Is64Bits) { StringRef Fmt = Is64Bits ? "\t\t%016" PRIx64 ": " : "\t\t\t%08" PRIx64 ": "; SmallString<16> Name; SmallString<32> Val; Rel.getTypeName(Name); if (Error E = getRelocationValueString(Rel, Val)) reportError(std::move(E), FileName); - outs() << format(Fmt.data(), Address) << Name << "\t" << Val << "\n"; + OS << format(Fmt.data(), Address) << Name << "\t" << Val; } class PrettyPrinter { public: virtual ~PrettyPrinter() = default; - virtual void printInst(MCInstPrinter &IP, const MCInst *MI, - ArrayRef Bytes, - object::SectionedAddress Address, raw_ostream &OS, - StringRef Annot, MCSubtargetInfo const &STI, - SourcePrinter *SP, StringRef ObjectFilename, - std::vector *Rels = nullptr) { + virtual void + printInst(MCInstPrinter &IP, const MCInst *MI, ArrayRef Bytes, + object::SectionedAddress Address, formatted_raw_ostream &OS, + StringRef Annot, MCSubtargetInfo const &STI, SourcePrinter *SP, + StringRef ObjectFilename, std::vector *Rels, + LiveVariablePrinter &LVP) { if (SP && (PrintSource || PrintLines)) - SP->printSourceLine(OS, Address, ObjectFilename); + SP->printSourceLine(OS, Address, ObjectFilename, LVP); + LVP.printBetweenInsts(OS, false); size_t Start = OS.tell(); if (!NoLeadingAddr) @@ -718,7 +1155,7 @@ class HexagonPrettyPrinter : public PrettyPrinter { public: void printLead(ArrayRef Bytes, uint64_t Address, - raw_ostream &OS) { + formatted_raw_ostream &OS) { uint32_t opcode = (Bytes[3] << 24) | (Bytes[2] << 16) | (Bytes[1] << 8) | Bytes[0]; if (!NoLeadingAddr) @@ -730,12 +1167,12 @@ } } void printInst(MCInstPrinter &IP, const MCInst *MI, ArrayRef Bytes, - object::SectionedAddress Address, raw_ostream &OS, + object::SectionedAddress Address, formatted_raw_ostream &OS, StringRef Annot, MCSubtargetInfo const &STI, SourcePrinter *SP, - StringRef ObjectFilename, - std::vector *Rels) override { + StringRef ObjectFilename, std::vector *Rels, + LiveVariablePrinter &LVP) override { if (SP && (PrintSource || PrintLines)) - SP->printSourceLine(OS, Address, ObjectFilename, ""); + SP->printSourceLine(OS, Address, ObjectFilename, LVP, ""); if (!MI) { printLead(Bytes, Address.Address, OS); OS << " "; @@ -761,7 +1198,7 @@ auto PrintReloc = [&]() -> void { while ((RelCur != RelEnd) && (RelCur->getOffset() <= Address.Address)) { if (RelCur->getOffset() == Address.Address) { - printRelocation(ObjectFilename, *RelCur, Address.Address, false); + printRelocation(OS, ObjectFilename, *RelCur, Address.Address, false); return; } ++RelCur; @@ -772,7 +1209,7 @@ OS << Separator; Separator = "\n"; if (SP && (PrintSource || PrintLines)) - SP->printSourceLine(OS, Address, ObjectFilename, ""); + SP->printSourceLine(OS, Address, ObjectFilename, LVP, ""); printLead(Bytes, Address.Address, OS); OS << Preamble; Preamble = " "; @@ -800,12 +1237,12 @@ class AMDGCNPrettyPrinter : public PrettyPrinter { public: void printInst(MCInstPrinter &IP, const MCInst *MI, ArrayRef Bytes, - object::SectionedAddress Address, raw_ostream &OS, + object::SectionedAddress Address, formatted_raw_ostream &OS, StringRef Annot, MCSubtargetInfo const &STI, SourcePrinter *SP, - StringRef ObjectFilename, - std::vector *Rels) override { + StringRef ObjectFilename, std::vector *Rels, + LiveVariablePrinter &LVP) override { if (SP && (PrintSource || PrintLines)) - SP->printSourceLine(OS, Address, ObjectFilename); + SP->printSourceLine(OS, Address, ObjectFilename, LVP); if (MI) { SmallString<40> InstStr; @@ -852,12 +1289,12 @@ class BPFPrettyPrinter : public PrettyPrinter { public: void printInst(MCInstPrinter &IP, const MCInst *MI, ArrayRef Bytes, - object::SectionedAddress Address, raw_ostream &OS, + object::SectionedAddress Address, formatted_raw_ostream &OS, StringRef Annot, MCSubtargetInfo const &STI, SourcePrinter *SP, - StringRef ObjectFilename, - std::vector *Rels) override { + StringRef ObjectFilename, std::vector *Rels, + LiveVariablePrinter &LVP) override { if (SP && (PrintSource || PrintLines)) - SP->printSourceLine(OS, Address, ObjectFilename); + SP->printSourceLine(OS, Address, ObjectFilename, LVP); if (!NoLeadingAddr) OS << format("%8" PRId64 ":", Address.Address / 8); if (!NoShowRawInsn) { @@ -1043,33 +1480,34 @@ return (It - 1)->second; } -static uint64_t -dumpARMELFData(uint64_t SectionAddr, uint64_t Index, uint64_t End, - const ObjectFile *Obj, ArrayRef Bytes, - ArrayRef MappingSymbols) { +static uint64_t dumpARMELFData(uint64_t SectionAddr, uint64_t Index, + uint64_t End, const ObjectFile *Obj, + ArrayRef Bytes, + ArrayRef MappingSymbols, + raw_ostream &OS) { support::endianness Endian = Obj->isLittleEndian() ? support::little : support::big; while (Index < End) { - outs() << format("%8" PRIx64 ":", SectionAddr + Index); - outs() << "\t"; + OS << format("%8" PRIx64 ":", SectionAddr + Index); + OS << "\t"; if (Index + 4 <= End) { - dumpBytes(Bytes.slice(Index, 4), outs()); - outs() << "\t.word\t" + dumpBytes(Bytes.slice(Index, 4), OS); + OS << "\t.word\t" << format_hex( support::endian::read32(Bytes.data() + Index, Endian), 10); Index += 4; } else if (Index + 2 <= End) { - dumpBytes(Bytes.slice(Index, 2), outs()); - outs() << "\t\t.short\t" + dumpBytes(Bytes.slice(Index, 2), OS); + OS << "\t\t.short\t" << format_hex( support::endian::read16(Bytes.data() + Index, Endian), 6); Index += 2; } else { - dumpBytes(Bytes.slice(Index, 1), outs()); - outs() << "\t\t.byte\t" << format_hex(Bytes[0], 4); + dumpBytes(Bytes.slice(Index, 1), OS); + OS << "\t\t.byte\t" << format_hex(Bytes[0], 4); ++Index; } - outs() << "\n"; + OS << "\n"; if (getMappingSymbolKind(MappingSymbols, Index) != 'd') break; } @@ -1214,6 +1652,17 @@ array_pod_sort(SecSyms.second.begin(), SecSyms.second.end()); array_pod_sort(AbsoluteSymbols.begin(), AbsoluteSymbols.end()); + std::unique_ptr DICtx; + LiveVariablePrinter LVP(Ctx.getRegisterInfo()); + + if (DbgVariables) { + DICtx = DWARFContext::create(*Obj); + for (const std::unique_ptr &CU : DICtx->compile_units()) + LVP.AddCompileUnit(CU->getUnitDIE(false)); + } + + LLVM_DEBUG(LVP.dump()); + for (const SectionRef &Section : ToolSectionFilter(*Obj)) { if (FilterSections.empty() && !DisassembleAll && (!Section.isText() || Section.isVirtual())) @@ -1377,6 +1826,7 @@ bool CheckARMELFData = hasMappingSymbols(Obj) && std::get<2>(Symbols[SI]) != ELF::STT_OBJECT && !DisassembleAll; + formatted_raw_ostream FOS(outs()); while (Index < End) { // ARM and AArch64 ELF binaries can interleave data and text in the // same section. We rely on the markers introduced to understand what @@ -1385,7 +1835,7 @@ if (CheckARMELFData && getMappingSymbolKind(MappingSymbols, Index) == 'd') { Index = dumpARMELFData(SectionAddr, Index, End, Obj, Bytes, - MappingSymbols); + MappingSymbols, FOS); continue; } @@ -1400,7 +1850,7 @@ if (size_t N = countSkippableZeroBytes(Bytes.slice(Index, MaxOffset))) { - outs() << "\t\t..." << '\n'; + FOS << "\t\t..." << '\n'; Index += N; continue; } @@ -1424,11 +1874,14 @@ if (Size == 0) Size = 1; + LVP.update({Index, Section.getIndex()}, + {Index + Size, Section.getIndex()}, Index + Size == End); + PIP.printInst(*IP, Disassembled ? &Inst : nullptr, Bytes.slice(Index, Size), {SectionAddr + Index + VMAAdjustment, Section.getIndex()}, - outs(), "", *STI, &SP, Obj->getFileName(), &Rels); - outs() << CommentStream.str(); + FOS, "", *STI, &SP, Obj->getFileName(), &Rels, LVP); + FOS << CommentStream.str(); Comments.clear(); // Try to resolve the target of a call, tail call, etc. to a specific @@ -1479,15 +1932,17 @@ --TargetSym; uint64_t TargetAddress = std::get<0>(*TargetSym); StringRef TargetName = std::get<1>(*TargetSym); - outs() << " <" << TargetName; + FOS << " <" << TargetName; uint64_t Disp = Target - TargetAddress; if (Disp) - outs() << "+0x" << Twine::utohexstr(Disp); - outs() << '>'; + FOS << "+0x" << Twine::utohexstr(Disp); + FOS << '>'; } } } - outs() << "\n"; + + LVP.printAfterInst(FOS); + FOS << "\n"; // Hexagon does this in pretty printer if (Obj->getArch() != Triple::hexagon) { @@ -1513,8 +1968,9 @@ Offset += AdjustVMA; } - printRelocation(Obj->getFileName(), *RelCur, SectionAddr + Offset, - Is64Bits); + printRelocation(FOS, Obj->getFileName(), *RelCur, + SectionAddr + Offset, Is64Bits); + LVP.printAfterOtherLine(FOS, true); ++RelCur; } }