Index: test/tools/llvm-objdump/X86/Inputs/source-interleave-x86_64.c =================================================================== --- /dev/null +++ test/tools/llvm-objdump/X86/Inputs/source-interleave-x86_64.c @@ -0,0 +1,11 @@ +int a = 1; +int foo() +{ + return a; +} + + +int main() { + int *b = &a; + return *b + foo(); +} Index: test/tools/llvm-objdump/X86/source-interleave.s =================================================================== --- /dev/null +++ test/tools/llvm-objdump/X86/source-interleave.s @@ -0,0 +1,317 @@ +// RUN: sed -e "s,SRC_COMPDIR,%p/Inputs,g" %s > %t.s +// RUN: llvm-mc -o %t.o -filetype=obj -triple=x86_64-pc-linux %t.s +// RUN: llvm-objdump -d -l %t.o | FileCheck --check-prefix="LINES" %t.s +// RUN: llvm-objdump -d -S %t.o | FileCheck --check-prefix="SOURCE" %t.s + + .text + .file "source-interleave-x86_64.c" + .file 1 "source-interleave-x86_64.c" + .globl foo + .align 16, 0x90 + .type foo,@function +foo: # @foo +.Lfunc_begin0: + .loc 1 3 0 # source-interleave-x86_64.c:3:0 + .cfi_startproc +# BB#0: + pushq %rbp +.Ltmp0: + .cfi_def_cfa_offset 16 +.Ltmp1: + .cfi_offset %rbp, -16 + movq %rsp, %rbp +.Ltmp2: + .cfi_def_cfa_register %rbp + .loc 1 4 9 prologue_end # source-interleave-x86_64.c:4:9 +.Ltmp3: + movl a, %eax + .loc 1 4 2 is_stmt 0 # source-interleave-x86_64.c:4:2 + popq %rbp + retq +.Ltmp4: +.Lfunc_end0: + .size foo, .Lfunc_end0-foo + .cfi_endproc + + .globl main + .align 16, 0x90 + .type main,@function +main: # @main +.Lfunc_begin1: + .loc 1 8 0 is_stmt 1 # source-interleave-x86_64.c:8:0 + .cfi_startproc +# BB#0: + pushq %rbp +.Ltmp5: + .cfi_def_cfa_offset 16 +.Ltmp6: + .cfi_offset %rbp, -16 + movq %rsp, %rbp +.Ltmp7: + .cfi_def_cfa_register %rbp + subq $32, %rsp + movabsq $a, %rax + movl $0, -4(%rbp) + .loc 1 9 7 prologue_end # source-interleave-x86_64.c:9:7 +.Ltmp8: + movq %rax, -16(%rbp) + .loc 1 10 11 # source-interleave-x86_64.c:10:11 + movq -16(%rbp), %rax + .loc 1 10 10 is_stmt 0 # source-interleave-x86_64.c:10:10 + movl (%rax), %ecx + .loc 1 10 15 # source-interleave-x86_64.c:10:15 + movl %ecx, -20(%rbp) # 4-byte Spill + callq foo + .loc 1 10 13 # source-interleave-x86_64.c:10:13 + movl -20(%rbp), %ecx # 4-byte Reload + addl %eax, %ecx + .loc 1 10 3 # source-interleave-x86_64.c:10:3 + movl %ecx, %eax + addq $32, %rsp + popq %rbp + retq +.Ltmp9: +.Lfunc_end1: + .size main, .Lfunc_end1-main + .cfi_endproc + + .type a,@object # @a + .data + .globl a + .align 4 +a: + .long 1 # 0x1 + .size a, 4 + + .section .debug_str,"MS",@progbits,1 +.Linfo_string0: + .asciz "clang version 3.8.1-svn271772-1~exp1 (branches/release_38)" # string offset=0 +.Linfo_string1: + .asciz "source-interleave-x86_64.c" # string offset=59 +.Linfo_string2: + .asciz "SRC_COMPDIR" # string offset=86 +.Linfo_string3: + .asciz "a" # string offset=164 +.Linfo_string4: + .asciz "int" # string offset=166 +.Linfo_string5: + .asciz "foo" # string offset=170 +.Linfo_string6: + .asciz "main" # string offset=174 +.Linfo_string7: + .asciz "b" # string offset=179 + .section .debug_loc,"",@progbits + .section .debug_abbrev,"",@progbits +.Lsection_abbrev: + .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 52 # DW_TAG_variable + .byte 0 # DW_CHILDREN_no + .byte 3 # DW_AT_name + .byte 14 # DW_FORM_strp + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 63 # DW_AT_external + .byte 25 # DW_FORM_flag_present + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 2 # DW_AT_location + .byte 24 # DW_FORM_exprloc + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 3 # 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 4 # Abbreviation Code + .byte 46 # DW_TAG_subprogram + .byte 0 # DW_CHILDREN_no + .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 + .byte 3 # DW_AT_name + .byte 14 # DW_FORM_strp + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 63 # DW_AT_external + .byte 25 # DW_FORM_flag_present + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 5 # 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 + .byte 3 # DW_AT_name + .byte 14 # DW_FORM_strp + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 63 # DW_AT_external + .byte 25 # DW_FORM_flag_present + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 6 # Abbreviation Code + .byte 52 # DW_TAG_variable + .byte 0 # DW_CHILDREN_no + .byte 2 # DW_AT_location + .byte 24 # DW_FORM_exprloc + .byte 3 # DW_AT_name + .byte 14 # DW_FORM_strp + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 7 # Abbreviation Code + .byte 15 # DW_TAG_pointer_type + .byte 0 # DW_CHILDREN_no + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 0 # EOM(3) + .section .debug_info,"",@progbits +.Lsection_info: +.Lcu_begin0: + .long 137 # Length of Unit + .short 4 # DWARF version number + .long .Lsection_abbrev # Offset Into Abbrev. Section + .byte 8 # Address Size (in bytes) + .byte 1 # Abbrev [1] 0xb:0x82 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:0x15 DW_TAG_variable + .long .Linfo_string3 # DW_AT_name + .long 63 # DW_AT_type + # DW_AT_external + .byte 1 # DW_AT_decl_file + .byte 1 # DW_AT_decl_line + .byte 9 # DW_AT_location + .byte 3 + .quad a + .byte 3 # Abbrev [3] 0x3f:0x7 DW_TAG_base_type + .long .Linfo_string4 # DW_AT_name + .byte 5 # DW_AT_encoding + .byte 4 # DW_AT_byte_size + .byte 4 # Abbrev [4] 0x46:0x19 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 86 + .long .Linfo_string5 # DW_AT_name + .byte 1 # DW_AT_decl_file + .byte 2 # DW_AT_decl_line + .long 63 # DW_AT_type + # DW_AT_external + .byte 5 # Abbrev [5] 0x5f:0x28 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 86 + .long .Linfo_string6 # DW_AT_name + .byte 1 # DW_AT_decl_file + .byte 8 # DW_AT_decl_line + .long 63 # DW_AT_type + # DW_AT_external + .byte 6 # Abbrev [6] 0x78:0xe DW_TAG_variable + .byte 2 # DW_AT_location + .byte 145 + .byte 112 + .long .Linfo_string7 # DW_AT_name + .byte 1 # DW_AT_decl_file + .byte 9 # DW_AT_decl_line + .long 135 # DW_AT_type + .byte 0 # End Of Children Mark + .byte 7 # Abbrev [7] 0x87:0x5 DW_TAG_pointer_type + .long 63 # DW_AT_type + .byte 0 # End Of Children Mark + .section .debug_ranges,"",@progbits +.Ldebug_range: + .section .debug_macinfo,"",@progbits + .byte 0 # End Of Macro List Mark + .section .debug_pubnames,"",@progbits + .long .LpubNames_end0-.LpubNames_begin0 # Length of Public Names Info +.LpubNames_begin0: + .short 2 # DWARF Version + .long .Lcu_begin0 # Offset of Compilation Unit Info + .long 141 # Compilation Unit Length + .long 42 # DIE offset + .asciz "a" # External Name + .long 70 # DIE offset + .asciz "foo" # External Name + .long 95 # DIE offset + .asciz "main" # External Name + .long 0 # End Mark +.LpubNames_end0: + .section .debug_pubtypes,"",@progbits + .long .LpubTypes_end0-.LpubTypes_begin0 # Length of Public Types Info +.LpubTypes_begin0: + .short 2 # DWARF Version + .long .Lcu_begin0 # Offset of Compilation Unit Info + .long 141 # Compilation Unit Length + .long 63 # DIE offset + .asciz "int" # External Name + .long 0 # End Mark +.LpubTypes_end0: + + .ident "clang version 3.8.1-svn271772-1~exp1 (branches/release_38)" + .section ".note.GNU-stack","",@progbits + .section .debug_line,"",@progbits +.Lline_table_start0: + +//LINES: main: +//LINES-NEXT: ; SRC_COMPDIR/source-interleave-x86_64.c:8 + +//SOURCE: main: +//SOURCE-NEXT: ; int main() { + Index: tools/llvm-objdump/CMakeLists.txt =================================================================== --- tools/llvm-objdump/CMakeLists.txt +++ tools/llvm-objdump/CMakeLists.txt @@ -5,10 +5,12 @@ AllTargetsInfos CodeGen DebugInfoDWARF + DebugInfoPDB MC MCDisassembler Object Support + Symbolize ) add_llvm_tool(llvm-objdump Index: tools/llvm-objdump/llvm-objdump.cpp =================================================================== --- tools/llvm-objdump/llvm-objdump.cpp +++ tools/llvm-objdump/llvm-objdump.cpp @@ -23,6 +23,7 @@ #include "llvm/ADT/Triple.h" #include "llvm/CodeGen/FaultMaps.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/Symbolize/Symbolize.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCDisassembler/MCDisassembler.h" @@ -60,6 +61,7 @@ #include #include #include +#include using namespace llvm; using namespace object; @@ -188,6 +190,20 @@ cl::values(clEnumValN(DIDT_Frames, "frames", ".debug_frame"), clEnumValEnd)); +cl::opt PrintSource( + "source", + cl::desc( + "Display source inlined with disassembly. Implies disassmble object")); + +cl::alias PrintSourceShort("S", cl::desc("Alias for -source"), + cl::aliasopt(PrintSource)); + +cl::opt PrintLines("line-numbers", + cl::desc("Display source line numbers with " + "disassembly. Implies disassemble object")); + +cl::alias PrintLinesShort("l", cl::desc("Alias for -line-numbers"), + cl::aliasopt(PrintLines)); static StringRef ToolName; namespace { @@ -361,13 +377,95 @@ } namespace { +class SourcePrinter { +protected: + DILineInfo OldLineInfo; + const ObjectFile *Obj; + std::unique_ptr Symbolizer; + // File name to file contents of source + std::unordered_map> SourceCache; + // Mark the line endings of the cached source + std::unordered_map> LineCache; + +private: + bool cacheSource(std::string File); + +public: + virtual ~SourcePrinter() {} + SourcePrinter() : Obj(nullptr), Symbolizer(nullptr) {} + SourcePrinter(const ObjectFile *Obj, StringRef DefaultArch) : Obj(Obj) { + symbolize::LLVMSymbolizer::Options SymbolizerOpts( + DILineInfoSpecifier::FunctionNameKind::None, true, false, false, + DefaultArch); + Symbolizer.reset(new symbolize::LLVMSymbolizer(SymbolizerOpts)); + } + virtual void printSourceLine(raw_ostream &OS, uint64_t Address, + StringRef Delimiter = "; "); +}; + +bool SourcePrinter::cacheSource(std::string File) { + auto BufferOrError = MemoryBuffer::getFile(File); + if (!BufferOrError) + return false; + // Chomp the file to get lines + size_t BufferSize = (*BufferOrError)->getBufferSize(); + const char *BufferStart = (*BufferOrError)->getBufferStart(); + for (const char *Start = BufferStart, *End = BufferStart; + End < BufferStart + BufferSize; End++) + if (*End == '\n' || End == BufferStart + BufferSize - 1 || + (*End == '\r' && *(End + 1) == '\n')) { + LineCache[File].push_back(StringRef(Start, End - Start)); + if (*End == '\r') + End++; + Start = End + 1; + } + SourceCache[File] = std::move(*BufferOrError); + return true; +} + +void SourcePrinter::printSourceLine(raw_ostream &OS, uint64_t Address, + StringRef Delimiter) { + if (!Symbolizer) + return; + DILineInfo LineInfo = DILineInfo(); + auto ExpectecLineInfo = + Symbolizer->symbolizeCode(Obj->getFileName(), Address); + if (!ExpectecLineInfo) + consumeError(ExpectecLineInfo.takeError()); + else + LineInfo = *ExpectecLineInfo; + + if ((LineInfo.FileName == "") || OldLineInfo.Line == LineInfo.Line || + LineInfo.Line == 0) + return; + + if (PrintLines) + OS << Delimiter << LineInfo.FileName << ":" << LineInfo.Line << "\n"; + if (PrintSource) { + if (SourceCache.find(LineInfo.FileName) == SourceCache.end()) + if (!cacheSource(LineInfo.FileName)) + return; + auto FileBuffer = SourceCache.find(LineInfo.FileName); + if (FileBuffer != SourceCache.end()) { + auto LineBuffer = LineCache.find(LineInfo.FileName); + if (LineBuffer != LineCache.end()) + // Vector begins at 0, line numbers are non-zero + OS << Delimiter << LineBuffer->second[LineInfo.Line - 1].ltrim() + << "\n"; + } + } + OldLineInfo = LineInfo; +} + class PrettyPrinter { public: virtual ~PrettyPrinter(){} virtual void printInst(MCInstPrinter &IP, const MCInst *MI, ArrayRef Bytes, uint64_t Address, raw_ostream &OS, StringRef Annot, - MCSubtargetInfo const &STI) { + MCSubtargetInfo const &STI, SourcePrinter *SP) { + if (SP && (PrintSource || PrintLines)) + SP->printSourceLine(OS, Address); OS << format("%8" PRIx64 ":", Address); if (!NoShowRawInsn) { OS << "\t"; @@ -393,10 +491,9 @@ OS << format("%08" PRIx32, opcode); } } - void printInst(MCInstPrinter &IP, const MCInst *MI, - ArrayRef Bytes, uint64_t Address, - raw_ostream &OS, StringRef Annot, - MCSubtargetInfo const &STI) override { + void printInst(MCInstPrinter &IP, const MCInst *MI, ArrayRef Bytes, + uint64_t Address, raw_ostream &OS, StringRef Annot, + MCSubtargetInfo const &STI, SourcePrinter *SP) override { if (!MI) { printLead(Bytes, Address, OS); OS << " "; @@ -441,13 +538,9 @@ class AMDGCNPrettyPrinter : public PrettyPrinter { public: - void printInst(MCInstPrinter &IP, - const MCInst *MI, - ArrayRef Bytes, - uint64_t Address, - raw_ostream &OS, - StringRef Annot, - MCSubtargetInfo const &STI) override { + void printInst(MCInstPrinter &IP, const MCInst *MI, ArrayRef Bytes, + uint64_t Address, raw_ostream &OS, StringRef Annot, + MCSubtargetInfo const &STI, SourcePrinter *SP) override { if (!MI) { OS << " "; return; @@ -990,6 +1083,8 @@ StringRef Fmt = Obj->getBytesInAddress() > 4 ? "\t\t%016" PRIx64 ": " : "\t\t\t%08" PRIx64 ": "; + SourcePrinter SP(Obj, TheTarget->getName()); + // Create a mapping, RelocSecs = SectionRelocMap[S], where sections // in RelocSecs contain the relocations for section S. std::error_code EC; @@ -1212,9 +1307,10 @@ CommentStream); if (Size == 0) Size = 1; + PIP.printInst(*IP, Disassembled ? &Inst : nullptr, - Bytes.slice(Index, Size), - SectionAddr + Index, outs(), "", *STI); + Bytes.slice(Index, Size), SectionAddr + Index, outs(), "", + *STI, &SP); outs() << CommentStream.str(); Comments.clear(); @@ -1769,7 +1865,7 @@ if (InputFilenames.size() == 0) InputFilenames.push_back("a.out"); - if (DisassembleAll) + if (DisassembleAll || PrintSource || PrintLines) Disassemble = true; if (!Disassemble && !Relocations