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,17 @@ 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 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFExpression.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFExpression.h @@ -137,6 +137,12 @@ 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 --- a/llvm/include/llvm/Support/FormattedStream.h +++ b/llvm/include/llvm/Support/FormattedStream.h @@ -105,11 +105,19 @@ /// \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() { + // Calculate current position, taking buffer contents into account. + ComputePosition(getBufferStart(), GetNumBytesInBuffer()); + return Position.first; + } - /// getLine - Return the line number - unsigned getLine() { return Position.second; } + /// Return the line number. + 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 --- a/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp @@ -339,4 +339,74 @@ 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<20> 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 --- 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,451 @@ +## 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 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,28 @@ cl::cat(ObjdumpCat)); static cl::alias WideShort("w", cl::Grouping, cl::aliasopt(Wide)); +enum DebugVarsFormat { + DV_Disabled, + DV_Unicode, + DV_ASCII, +}; + +static cl::opt DbgVariables( + "debug-vars", cl::init(DV_Disabled), + cl::desc("Print the locations (in registers or memory) of " + "source-level variables alongside disassembly"), + cl::ValueOptional, + cl::values(clEnumValN(DV_Unicode, "", "unicode"), + clEnumValN(DV_Unicode, "unicode", "unicode"), + clEnumValN(DV_ASCII, "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,360 @@ } 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; + bool LiveIn; + bool LiveOut; + bool MustDrawLabel; + + Column() + : VarIdx(NullVarIdx), LiveIn(false), LiveOut(false), + 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 == DV_ASCII; + 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 +933,12 @@ 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 +952,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 = "; "); }; @@ -607,9 +989,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; @@ -634,14 +1017,15 @@ } 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 +1038,16 @@ } if (LineInfo.FileName != DILineInfo::BadString && LineInfo.Line != 0 && (OldLineInfo.Line != LineInfo.Line || - OldLineInfo.FileName != LineInfo.FileName || PrintFunctionName)) + OldLineInfo.FileName != LineInfo.FileName || PrintFunctionName)) { OS << Delimiter << LineInfo.FileName << ":" << LineInfo.Line << "\n"; + 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)) @@ -681,6 +1068,7 @@ } // Vector begins at 0, line numbers are non-zero OS << Delimiter << LineBuffer->second[LineInfo.Line - 1] << '\n'; + LVP.printBetweenInsts(OS, true); } } @@ -698,28 +1086,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) @@ -731,7 +1121,7 @@ // 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 +1136,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) @@ -758,12 +1148,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 << " "; @@ -789,7 +1179,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; @@ -800,7 +1190,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 = " "; @@ -828,12 +1218,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; @@ -880,12 +1270,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) { @@ -1071,33 +1461,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; } @@ -1242,6 +1633,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 != DV_Disabled) { + 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 +1807,7 @@ 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 +1816,7 @@ if (CheckARMELFData && getMappingSymbolKind(MappingSymbols, Index) == 'd') { Index = dumpARMELFData(SectionAddr, Index, End, Obj, Bytes, - MappingSymbols); + MappingSymbols, FOS); continue; } @@ -1428,7 +1831,7 @@ if (size_t N = countSkippableZeroBytes(Bytes.slice(Index, MaxOffset))) { - outs() << "\t\t..." << '\n'; + FOS << "\t\t..." << '\n'; Index += N; continue; } @@ -1452,17 +1855,20 @@ 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 +1921,17 @@ --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 +1957,9 @@ Offset += AdjustVMA; } - printRelocation(Obj->getFileName(), *RelCur, SectionAddr + Offset, - Is64Bits); + printRelocation(FOS, Obj->getFileName(), *RelCur, + SectionAddr + Offset, Is64Bits); + LVP.printAfterOtherLine(FOS, true); ++RelCur; } }