From 3a5ddedadb671e485ce5c638142817879ac14a8c Mon Sep 17 00:00:00 2001 From: Oliver Stannard Date: Mon, 9 Dec 2019 15:16:47 +0000 Subject: [PATCH] [llvm-objdump] Display locations of variables alongside disassembly This adds the --debug-vars option to llvm-objdump, which prints locations (registers/memory) of source-level variables alongside the disassembly based on DWARF info. A vertical line is printed for each live-range, with a label at the top giving the variable name and location, and the position and length of the line indicating the program counter range in which it is valid. Currently, this only works for object files, not executables or shared libraries. Differential revision: https://reviews.llvm.org/D70720 --- llvm/docs/CommandGuide/llvm-objdump.rst | 11 + .../llvm/DebugInfo/DWARF/DWARFExpression.h | 6 + llvm/include/llvm/Support/FormattedStream.h | 14 +- llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp | 70 +++ llvm/lib/Support/FormattedStream.cpp | 29 +- .../tools/llvm-objdump/ARM/Inputs/debug.c | 10 + .../tools/llvm-objdump/ARM/Inputs/wide-char.c | 3 + .../ARM/debug-vars-dwarf4-sections.s | 351 ++++++++++++ .../llvm-objdump/ARM/debug-vars-dwarf4.s | 453 +++++++++++++++ .../ARM/debug-vars-dwarf5-sections.s | 411 ++++++++++++++ .../llvm-objdump/ARM/debug-vars-dwarf5.s | 382 +++++++++++++ .../llvm-objdump/ARM/debug-vars-wide-chars.s | 232 ++++++++ .../tools/llvm-objdump/PowerPC/debug-vars.s | 372 ++++++++++++ llvm/tools/llvm-objdump/llvm-objdump.cpp | 537 +++++++++++++++--- 14 files changed, 2806 insertions(+), 75 deletions(-) create mode 100644 llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c create mode 100644 llvm/test/tools/llvm-objdump/ARM/Inputs/wide-char.c create mode 100644 llvm/test/tools/llvm-objdump/ARM/debug-vars-dwarf4-sections.s create mode 100644 llvm/test/tools/llvm-objdump/ARM/debug-vars-dwarf4.s create mode 100644 llvm/test/tools/llvm-objdump/ARM/debug-vars-dwarf5-sections.s create mode 100644 llvm/test/tools/llvm-objdump/ARM/debug-vars-dwarf5.s create mode 100644 llvm/test/tools/llvm-objdump/ARM/debug-vars-wide-chars.s create mode 100644 llvm/test/tools/llvm-objdump/PowerPC/debug-vars.s diff --git a/llvm/docs/CommandGuide/llvm-objdump.rst b/llvm/docs/CommandGuide/llvm-objdump.rst index 2730374d58103..bb330a0174e34 100644 --- a/llvm/docs/CommandGuide/llvm-objdump.rst +++ b/llvm/docs/CommandGuide/llvm-objdump.rst @@ -119,6 +119,17 @@ OPTIONS Demangle symbol names in the output. +.. option:: --debug-vars= + + Print the locations (in registers or memory) of source-level variables + alongside disassembly. ``format`` may be ``unicode`` or ``ascii``, defaulting + to ``unicode`` if omitted. + +.. option:: --debug-vars-indent= + + Distance to indent the source-level variable display, relative to the start + of the disassembly. Defaults to 40 characters. + .. 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 index c4dc53337c07c..08cfa3b1daaea 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFExpression.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFExpression.h @@ -137,6 +137,12 @@ class DWARFExpression { void print(raw_ostream &OS, const MCRegisterInfo *RegInfo, DWARFUnit *U, bool IsEH = false) const; + /// Print the expression in a format intended to be compact and useful to a + /// user, but not perfectly unambiguous, or capable of representing every + /// valid DWARF expression. Returns true if the expression was sucessfully + /// printed. + bool printCompact(raw_ostream &OS, const MCRegisterInfo &RegInfo); + bool verify(DWARFUnit *U); private: diff --git a/llvm/include/llvm/Support/FormattedStream.h b/llvm/include/llvm/Support/FormattedStream.h index b49c8d86531db..88e00647473e9 100644 --- a/llvm/include/llvm/Support/FormattedStream.h +++ b/llvm/include/llvm/Support/FormattedStream.h @@ -105,11 +105,17 @@ class formatted_raw_ostream : public raw_ostream { /// \param NewCol - The column to move to. formatted_raw_ostream &PadToColumn(unsigned NewCol); - /// getColumn - Return the column number - unsigned getColumn() { return Position.first; } + unsigned getColumn() { + // Calculate current position, taking buffer contents into account. + ComputePosition(getBufferStart(), GetNumBytesInBuffer()); + return Position.first; + } - /// getLine - Return the line number - unsigned getLine() { return Position.second; } + unsigned getLine() { + // Calculate current position, taking buffer contents into account. + ComputePosition(getBufferStart(), GetNumBytesInBuffer()); + return Position.second; + } raw_ostream &resetColor() override { TheStream->resetColor(); diff --git a/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp b/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp index 0a658034b67b8..d180076e73fda 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp @@ -351,4 +351,74 @@ bool DWARFExpression::verify(DWARFUnit *U) { return true; } +/// A user-facing string representation of a DWARF expression. This might be an +/// Address expression, in which case it will be implicitly dereferenced, or a +/// Value expression. +struct PrintedExpr { + enum ExprKind { + Address, + Value, + }; + ExprKind Kind; + SmallString<16> String; + + PrintedExpr(ExprKind K = Address) : Kind(K) {} +}; + +static bool printCompactDWARFExpr(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: { + // DW_OP_regx: A register, with the register num given as an operand. + // Printed as the plain register name. + uint64_t DwarfRegNum = Op.getRawOperand(0); + Optional LLVMRegNum = MRI.getLLVMRegNum(DwarfRegNum, false); + if (!LLVMRegNum) { + OS << ""; + return false; + } + 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) { + // DW_OP_reg: A register, with the register num implied by the + // opcode. Printed as the plain register name. + uint64_t DwarfRegNum = Opcode - dwarf::DW_OP_reg0; + Optional LLVMRegNum = MRI.getLLVMRegNum(DwarfRegNum, false); + if (!LLVMRegNum) { + OS << ""; + return false; + } + 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 false; + } + break; + } + ++I; + } + + assert(Stack.size() == 1 && "expected one value on stack"); + OS << Stack.front().String; + + return true; +} + +bool DWARFExpression::printCompact(raw_ostream &OS, const MCRegisterInfo &MRI) { + return printCompactDWARFExpr(OS, begin(), end(), MRI); +} + } // namespace llvm diff --git a/llvm/lib/Support/FormattedStream.cpp b/llvm/lib/Support/FormattedStream.cpp index 4eb747038bb9e..f30a9678029f7 100644 --- a/llvm/lib/Support/FormattedStream.cpp +++ b/llvm/lib/Support/FormattedStream.cpp @@ -11,7 +11,9 @@ //===----------------------------------------------------------------------===// #include "llvm/Support/FormattedStream.h" +#include "llvm/Support/ConvertUTF.h" #include "llvm/Support/Debug.h" +#include "llvm/Support/Locale.h" #include "llvm/Support/raw_ostream.h" #include @@ -19,15 +21,32 @@ using namespace llvm; /// UpdatePosition - Examine the given char sequence and figure out which /// column we end up in after output, and how many line breaks are contained. -/// -static void UpdatePosition(std::pair &Position, const char *Ptr, size_t Size) { +/// This assumes that the input string is well-formed UTF-8, and takes into +/// account unicode characters which render as multiple columns wide. +static void UpdatePosition(std::pair &Position, + const char *Ptr, size_t Size) { unsigned &Column = Position.first; unsigned &Line = Position.second; // Keep track of the current column and line by scanning the string for - // special characters - for (const char *End = Ptr + Size; Ptr != End; ++Ptr) { - ++Column; + // special characters. + unsigned NumBytes; + for (const char *End = Ptr + Size; Ptr < End; Ptr += NumBytes) { + NumBytes = getNumBytesForUTF8(*Ptr); + + // The string should never end part way through a multi-byte sequence. + assert((Ptr + NumBytes) <= End && "Malformed multi-byte sequence"); + + int Width = sys::locale::columnWidth(StringRef(Ptr, NumBytes)); + // columnWidth returns -1 for non-printing characters. + if (Width != -1) + Column += Width; + + // If this is the final byte of a multi-byte sequence, it can't be any of + // the special whitespace characters below. + if (NumBytes > 1) + 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 index 0000000000000..20c17f7d9762d --- /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/Inputs/wide-char.c b/llvm/test/tools/llvm-objdump/ARM/Inputs/wide-char.c new file mode 100644 index 0000000000000..8d923be013283 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/ARM/Inputs/wide-char.c @@ -0,0 +1,3 @@ +int foo(int *喵) { + return *喵; +} 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 index 0000000000000..9bbb36f14e3aa --- /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 : +# 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 : +# 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 index 0000000000000..9455726c693c4 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/ARM/debug-vars-dwarf4.s @@ -0,0 +1,453 @@ +## 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. These do not affect the DWARF parsing +## used by --debug-vars, but do add extra lines or columns to the output, so we +## test to make sure the live ranges are still displayed correctly. + +## 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 (40) 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=40 | \ +# 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 + +## An optional argument to the --debug-vars= option can be used to switch +## between unicode and ascii output (with unicode being the default). +# RUN: llvm-mc -triple armv8a--none-eabi < %s -filetype=obj | \ +# RUN: llvm-objdump - -d --debug-vars=unicode | \ +# RUN: FileCheck %s --check-prefix=RAW --strict-whitespace +# RUN: llvm-mc -triple armv8a--none-eabi < %s -filetype=obj | \ +# RUN: llvm-objdump - -d --debug-vars=ascii | \ +# 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 : +# 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 : +# 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 : +# 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 : +# 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 : +# 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 : +# NO-RAW-NEXT: ┠─ a = R0 +# NO-RAW-NEXT: c: add r0, r0, #1 ┃ +# NO-RAW-NEXT: 10: bx lr ┻ + +# LINE-NUMS: 00000000 : +# LINE-NUMS-NEXT: ; 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 : +# LINE-NUMS-NEXT: ; 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 : +# 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 : +# SOURCE-NEXT: ; a++; ┠─ a = R0 +# SOURCE-NEXT: c: add r0, r0, #1 ┃ +# SOURCE-NEXT: ; return a; ┃ +# SOURCE-NEXT: 10: bx lr ┻ + +# ASCII: 00000000 : +# 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 : +# 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 index 0000000000000..a9e08ff955561 --- /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 : +# 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 : +# 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 index 0000000000000..8a63a4ee9ec09 --- /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 : +# 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 : +# 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/ARM/debug-vars-wide-chars.s b/llvm/test/tools/llvm-objdump/ARM/debug-vars-wide-chars.s new file mode 100644 index 0000000000000..4085bd3cbd73a --- /dev/null +++ b/llvm/test/tools/llvm-objdump/ARM/debug-vars-wide-chars.s @@ -0,0 +1,232 @@ +# RUN: mkdir -p %t/a +# RUN: cp %p/Inputs/wide-char.c %t/a/wide-char.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 --source | \ +# RUN: FileCheck %s --strict-whitespace + +## The Chinese character in the source does not print correctly on Windows. +# UNSUPPORTED: system-windows + +## Check that the --debug-vars option correctly aligns the variable display when +## the source code (printed by the -S option) includes East Asian wide +## characters. + +# CHECK: 00000000 : +# CHECK-NEXT: ; return *喵; ┠─ 喵 = R0 +# CHECK-NEXT: 0: 00 00 90 e5 ldr r0, [r0] ┻ +# CHECK-NEXT: 4: 1e ff 2f e1 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 vfpv3 + .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 "wide.c" + .globl foo + .p2align 2 + .type foo,%function + .code 32 +foo: +.Lfunc_begin0: + .file 1 "SRC_COMPDIR/wide-char.c" + .loc 1 1 0 + .fnstart + .cfi_sections .debug_frame + .cfi_startproc + .loc 1 2 10 prologue_end + ldr r0, [r0] +.Ltmp0: + .loc 1 2 3 is_stmt 0 + bx lr +.Ltmp1: +.Lfunc_end0: + .size foo, .Lfunc_end0-foo + .cfi_endproc + .cantunwind + .fnend + + .section .debug_str,"MS",%progbits,1 +.Linfo_string0: + .asciz "clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final)" +.Linfo_string1: + .asciz "wide-char.c" +.Linfo_string2: + .asciz "SRC_COMPDIR" +.Linfo_string3: + .asciz "foo" +.Linfo_string4: + .asciz "int" +.Linfo_string5: + .asciz "\345\226\265" + .section .debug_loc,"",%progbits +.Ldebug_loc0: + .long .Lfunc_begin0-.Lfunc_begin0 + .long .Ltmp0-.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 + .ascii "\264B" + .byte 25 + .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 + .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 36 + .byte 0 + .byte 3 + .byte 14 + .byte 62 + .byte 11 + .byte 11 + .byte 11 + .byte 0 + .byte 0 + .byte 5 + .byte 15 + .byte 0 + .byte 73 + .byte 19 + .byte 0 + .byte 0 + .byte 0 + .section .debug_info,"",%progbits +.Lcu_begin0: + .long 84 + .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_end0-.Lfunc_begin0 + .byte 2 + .long .Lfunc_begin0 + .long .Lfunc_end0-.Lfunc_begin0 + .byte 1 + .byte 91 + .long .Linfo_string3 + .byte 1 + .byte 1 + + .long 75 + + .byte 3 + .long .Ldebug_loc0 + .long .Linfo_string5 + .byte 1 + .byte 1 + .long 82 + .byte 0 + .byte 4 + .long .Linfo_string4 + .byte 5 + .byte 4 + .byte 5 + .long 75 + .byte 0 + .section .debug_ranges,"",%progbits + .section .debug_macinfo,"",%progbits +.Lcu_macro_begin0: + .byte 0 + .section .debug_pubnames,"",%progbits + .long .LpubNames_end0-.LpubNames_begin0 +.LpubNames_begin0: + .short 2 + .long .Lcu_begin0 + .long 88 + .long 38 + .asciz "foo" + .long 0 +.LpubNames_end0: + .section .debug_pubtypes,"",%progbits + .long .LpubTypes_end0-.LpubTypes_begin0 +.LpubTypes_begin0: + .short 2 + .long .Lcu_begin0 + .long 88 + .long 75 + .asciz "int" + .long 0 +.LpubTypes_end0: + + .ident "clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final)" + .section ".note.GNU-stack","",%progbits + .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 index 0000000000000..6c3d326843c73 --- /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 index 1cfd06d2cef5e..4e2c6eaac7c4f 100644 --- 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,28 @@ static cl::opt cl::cat(ObjdumpCat)); static cl::alias WideShort("w", cl::Grouping, cl::aliasopt(Wide)); +enum DebugVarsFormat { + DVDisabled, + DVUnicode, + DVASCII, +}; + +static cl::opt DbgVariables( + "debug-vars", cl::init(DVDisabled), + cl::desc("Print the locations (in registers or memory) of " + "source-level variables alongside disassembly"), + cl::ValueOptional, + cl::values(clEnumValN(DVUnicode, "", "unicode"), + clEnumValN(DVUnicode, "unicode", "unicode"), + clEnumValN(DVASCII, "ascii", "unicode")), + cl::cat(ObjdumpCat)); + +static cl::opt + DbgIndent("debug-vars-indent", cl::init(40), + 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"); @@ -536,6 +562,356 @@ static bool getHidden(RelocationRef RelRef) { } namespace { + +/// Get the column at which we want to start printing the instruction +/// disassembly, taking into account anything which appears to the left of it. +unsigned getInstStartColumn() { + return NoShowRawInsn ? 16 : 40; +} + +/// 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->getAddressByteSize()); + Expression.printCompact(OS, 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 = NullVarIdx; + bool LiveIn = false; + bool LiveOut = false; + bool MustDrawLabel = false; + + 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 + getInstStartColumn(); + } + + // 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()), 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 IncludeDefinedVars is false, then live + /// ranges starting at NextAddr will be ignored. + void update(object::SectionedAddress ThisAddr, + object::SectionedAddress NextAddr, bool IncludeDefinedVars) { + // 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 (IncludeDefinedVars) { + 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 = VarIdx; + ActiveCols[ColIdx].LiveIn = LiveIn; + ActiveCols[ColIdx].LiveOut = LiveOut; + ActiveCols[ColIdx].MustDrawLabel = true; + } + } + } + + enum class LineChar { + RangeStart, + RangeMid, + RangeEnd, + LabelVert, + LabelCornerNew, + LabelCornerActive, + LabelHoriz, + }; + const char *getLineChar(LineChar C) const { + bool IsASCII = DbgVariables == DVASCII; + switch (C) { + case LineChar::RangeStart: + return IsASCII ? "^" : "╈"; + case LineChar::RangeMid: + return IsASCII ? "|" : "┃"; + case LineChar::RangeEnd: + return IsASCII ? "v" : "┻"; + case LineChar::LabelVert: + return IsASCII ? "|" : "│"; + case LineChar::LabelCornerNew: + return IsASCII ? "/" : "┌"; + case LineChar::LabelCornerActive: + return IsASCII ? "|" : "┠"; + case LineChar::LabelHoriz: + return IsASCII ? "-" : "─"; + } + } + + /// 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 << " "; + } + 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; @@ -553,11 +929,12 @@ class SourcePrinter { private: bool cacheSource(const DILineInfo& LineInfoFile); - void printLines(raw_ostream &OS, const DILineInfo &LineInfo, - StringRef Delimiter); + void printLines(formatted_raw_ostream &OS, const DILineInfo &LineInfo, + StringRef Delimiter, LiveVariablePrinter &LVP); - void printSources(raw_ostream &OS, const DILineInfo &LineInfo, - StringRef ObjectFilename, StringRef Delimiter); + void printSources(formatted_raw_ostream &OS, const DILineInfo &LineInfo, + StringRef ObjectFilename, StringRef Delimiter, + LiveVariablePrinter &LVP); public: SourcePrinter() = default; @@ -571,9 +948,10 @@ class SourcePrinter { 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 = "; "); }; @@ -607,9 +985,10 @@ bool SourcePrinter::cacheSource(const DILineInfo &LineInfo) { 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; @@ -634,14 +1013,15 @@ void SourcePrinter::printSourceLine(raw_ostream &OS, } if (PrintLines) - printLines(OS, LineInfo, Delimiter); + printLines(OS, LineInfo, Delimiter, LVP); if (PrintSource) - printSources(OS, LineInfo, ObjectFilename, Delimiter); + printSources(OS, LineInfo, ObjectFilename, Delimiter, LVP); OldLineInfo = LineInfo; } -void SourcePrinter::printLines(raw_ostream &OS, const DILineInfo &LineInfo, - StringRef Delimiter) { +void SourcePrinter::printLines(formatted_raw_ostream &OS, + const DILineInfo &LineInfo, StringRef Delimiter, + LiveVariablePrinter &LVP) { bool PrintFunctionName = LineInfo.FunctionName != DILineInfo::BadString && LineInfo.FunctionName != OldLineInfo.FunctionName; if (PrintFunctionName) { @@ -654,13 +1034,16 @@ void SourcePrinter::printLines(raw_ostream &OS, const DILineInfo &LineInfo, } if (LineInfo.FileName != DILineInfo::BadString && LineInfo.Line != 0 && (OldLineInfo.Line != LineInfo.Line || - OldLineInfo.FileName != LineInfo.FileName || PrintFunctionName)) - OS << Delimiter << LineInfo.FileName << ":" << LineInfo.Line << "\n"; + OldLineInfo.FileName != LineInfo.FileName || PrintFunctionName)) { + OS << Delimiter << LineInfo.FileName << ":" << LineInfo.Line; + LVP.printBetweenInsts(OS, true); + } } -void SourcePrinter::printSources(raw_ostream &OS, const DILineInfo &LineInfo, - StringRef ObjectFilename, - StringRef Delimiter) { +void SourcePrinter::printSources(formatted_raw_ostream &OS, + const DILineInfo &LineInfo, + StringRef ObjectFilename, StringRef Delimiter, + LiveVariablePrinter &LVP) { if (LineInfo.FileName == DILineInfo::BadString || LineInfo.Line == 0 || (OldLineInfo.Line == LineInfo.Line && OldLineInfo.FileName == LineInfo.FileName)) @@ -680,7 +1063,8 @@ void SourcePrinter::printSources(raw_ostream &OS, const DILineInfo &LineInfo, 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); } } @@ -698,28 +1082,30 @@ static bool hasMappingSymbols(const ObjectFile *Obj) { 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) @@ -731,7 +1117,7 @@ class PrettyPrinter { // The output of printInst starts with a tab. Print some spaces so that // the tab has 1 column and advances to the target tab stop. - unsigned TabStop = NoShowRawInsn ? 16 : 40; + unsigned TabStop = getInstStartColumn(); unsigned Column = OS.tell() - Start; OS.indent(Column < TabStop - 1 ? TabStop - 1 - Column : 7 - Column % 8); @@ -746,7 +1132,7 @@ PrettyPrinter PrettyPrinterInst; 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) @@ -758,12 +1144,12 @@ class HexagonPrettyPrinter : public PrettyPrinter { } } 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 << " "; @@ -789,7 +1175,7 @@ class HexagonPrettyPrinter : public PrettyPrinter { 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; @@ -800,7 +1186,7 @@ class HexagonPrettyPrinter : public PrettyPrinter { 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 = " "; @@ -828,12 +1214,12 @@ HexagonPrettyPrinter HexagonPrettyPrinterInst; 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; @@ -880,12 +1266,12 @@ AMDGCNPrettyPrinter AMDGCNPrettyPrinterInst; 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) { @@ -1071,33 +1457,34 @@ static char getMappingSymbolKind(ArrayRef MappingSymbols, 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; } @@ -1242,6 +1629,17 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj, 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 != DVDisabled) { + 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())) @@ -1405,6 +1803,7 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj, bool CheckARMELFData = hasMappingSymbols(Obj) && Symbols[SI].Type != 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 @@ -1413,7 +1812,7 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj, if (CheckARMELFData && getMappingSymbolKind(MappingSymbols, Index) == 'd') { Index = dumpARMELFData(SectionAddr, Index, End, Obj, Bytes, - MappingSymbols); + MappingSymbols, FOS); continue; } @@ -1428,7 +1827,7 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj, if (size_t N = countSkippableZeroBytes(Bytes.slice(Index, MaxOffset))) { - outs() << "\t\t..." << '\n'; + FOS << "\t\t..." << '\n'; Index += N; continue; } @@ -1452,17 +1851,20 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj, 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(); // If disassembly has failed, continue with the next instruction, to // avoid analysing invalid/incomplete instruction information. if (!Disassembled) { - outs() << "\n"; + FOS << "\n"; Index += Size; continue; } @@ -1515,15 +1917,17 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj, --TargetSym; uint64_t TargetAddress = TargetSym->Addr; StringRef TargetName = TargetSym->Name; - 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) { @@ -1549,8 +1953,9 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj, Offset += AdjustVMA; } - printRelocation(Obj->getFileName(), *RelCur, SectionAddr + Offset, - Is64Bits); + printRelocation(FOS, Obj->getFileName(), *RelCur, + SectionAddr + Offset, Is64Bits); + LVP.printAfterOtherLine(FOS, true); ++RelCur; } }