diff --git a/test/tools/dsymutil/Inputs/frame-dw2.ll b/test/tools/dsymutil/Inputs/frame-dw2.ll new file mode 100644 index 00000000000..7ffc9339731 --- /dev/null +++ b/test/tools/dsymutil/Inputs/frame-dw2.ll @@ -0,0 +1,71 @@ +; Generated from frame.c on Darwin with '-arch i386 -g -emit-llvm' +; ModuleID = 'frame.c' +target datalayout = "e-m:o-p:32:32-f64:32:64-f80:128-n8:16:32-S128" +target triple = "i386-apple-macosx10.11.0" + +; Function Attrs: nounwind ssp +define i32 @bar(i32 %b) #0 { +entry: + %b.addr = alloca i32, align 4 + %var = alloca i32, align 4 + store i32 %b, i32* %b.addr, align 4 + call void @llvm.dbg.declare(metadata i32* %b.addr, metadata !13, metadata !14), !dbg !15 + call void @llvm.dbg.declare(metadata i32* %var, metadata !16, metadata !14), !dbg !17 + %0 = load i32, i32* %b.addr, align 4, !dbg !18 + %add = add nsw i32 %0, 1, !dbg !19 + store i32 %add, i32* %var, align 4, !dbg !17 + %call = call i32 @foo(i32* %var), !dbg !20 + ret i32 %call, !dbg !21 +} + +; Function Attrs: nounwind readnone +declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 + +declare i32 @foo(i32*) #2 + +; Function Attrs: nounwind ssp +define i32 @baz(i32 %b) #0 { +entry: + %b.addr = alloca i32, align 4 + store i32 %b, i32* %b.addr, align 4 + call void @llvm.dbg.declare(metadata i32* %b.addr, metadata !22, metadata !14), !dbg !23 + %0 = load i32, i32* %b.addr, align 4, !dbg !24 + %call = call i32 @bar(i32 %0), !dbg !25 + ret i32 %call, !dbg !26 +} + +attributes #0 = { nounwind ssp "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="yonah" "target-features"="+cx16,+sse,+sse2,+sse3" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { nounwind readnone } +attributes #2 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="yonah" "target-features"="+cx16,+sse,+sse2,+sse3" "unsafe-fp-math"="false" "use-soft-float"="false" } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!9, !10, !11} +!llvm.ident = !{!12} + +!0 = !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 3.7.0 (trunk 239176) (llvm/trunk 239190)", isOptimized: false, runtimeVersion: 0, emissionKind: 1, enums: !2, retainedTypes: !2, subprograms: !3, globals: !2, imports: !2) +!1 = !DIFile(filename: "frame.c", directory: "/tmp") +!2 = !{} +!3 = !{!4, !8} +!4 = !DISubprogram(name: "bar", scope: !1, file: !1, line: 3, type: !5, isLocal: false, isDefinition: true, scopeLine: 3, flags: DIFlagPrototyped, isOptimized: false, function: i32 (i32)* @bar, variables: !2) +!5 = !DISubroutineType(types: !6) +!6 = !{!7, !7} +!7 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed) +!8 = !DISubprogram(name: "baz", scope: !1, file: !1, line: 8, type: !5, isLocal: false, isDefinition: true, scopeLine: 8, flags: DIFlagPrototyped, isOptimized: false, function: i32 (i32)* @baz, variables: !2) +!9 = !{i32 2, !"Dwarf Version", i32 2} +!10 = !{i32 2, !"Debug Info Version", i32 3} +!11 = !{i32 1, !"PIC Level", i32 2} +!12 = !{!"clang version 3.7.0 (trunk 239176) (llvm/trunk 239190)"} +!13 = !DILocalVariable(tag: DW_TAG_arg_variable, name: "b", arg: 1, scope: !4, file: !1, line: 3, type: !7) +!14 = !DIExpression() +!15 = !DILocation(line: 3, column: 13, scope: !4) +!16 = !DILocalVariable(tag: DW_TAG_auto_variable, name: "var", scope: !4, file: !1, line: 4, type: !7) +!17 = !DILocation(line: 4, column: 6, scope: !4) +!18 = !DILocation(line: 4, column: 12, scope: !4) +!19 = !DILocation(line: 4, column: 14, scope: !4) +!20 = !DILocation(line: 5, column: 9, scope: !4) +!21 = !DILocation(line: 5, column: 2, scope: !4) +!22 = !DILocalVariable(tag: DW_TAG_arg_variable, name: "b", arg: 1, scope: !8, file: !1, line: 8, type: !7) +!23 = !DILocation(line: 8, column: 13, scope: !8) +!24 = !DILocation(line: 9, column: 13, scope: !8) +!25 = !DILocation(line: 9, column: 9, scope: !8) +!26 = !DILocation(line: 9, column: 2, scope: !8) diff --git a/test/tools/dsymutil/Inputs/frame-dw4.ll b/test/tools/dsymutil/Inputs/frame-dw4.ll new file mode 100644 index 00000000000..c8674b13e58 --- /dev/null +++ b/test/tools/dsymutil/Inputs/frame-dw4.ll @@ -0,0 +1,71 @@ +; Generated from frame.c on Darwin with '-arch i386 -gdwarf-4 -emit-llvm' +; ModuleID = 'frame.c' +target datalayout = "e-m:o-p:32:32-f64:32:64-f80:128-n8:16:32-S128" +target triple = "i386-apple-macosx10.11.0" + +; Function Attrs: nounwind ssp +define i32 @bar(i32 %b) #0 { +entry: + %b.addr = alloca i32, align 4 + %var = alloca i32, align 4 + store i32 %b, i32* %b.addr, align 4 + call void @llvm.dbg.declare(metadata i32* %b.addr, metadata !13, metadata !14), !dbg !15 + call void @llvm.dbg.declare(metadata i32* %var, metadata !16, metadata !14), !dbg !17 + %0 = load i32, i32* %b.addr, align 4, !dbg !18 + %add = add nsw i32 %0, 1, !dbg !19 + store i32 %add, i32* %var, align 4, !dbg !17 + %call = call i32 @foo(i32* %var), !dbg !20 + ret i32 %call, !dbg !21 +} + +; Function Attrs: nounwind readnone +declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 + +declare i32 @foo(i32*) #2 + +; Function Attrs: nounwind ssp +define i32 @baz(i32 %b) #0 { +entry: + %b.addr = alloca i32, align 4 + store i32 %b, i32* %b.addr, align 4 + call void @llvm.dbg.declare(metadata i32* %b.addr, metadata !22, metadata !14), !dbg !23 + %0 = load i32, i32* %b.addr, align 4, !dbg !24 + %call = call i32 @bar(i32 %0), !dbg !25 + ret i32 %call, !dbg !26 +} + +attributes #0 = { nounwind ssp "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="yonah" "target-features"="+cx16,+sse,+sse2,+sse3" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { nounwind readnone } +attributes #2 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="yonah" "target-features"="+cx16,+sse,+sse2,+sse3" "unsafe-fp-math"="false" "use-soft-float"="false" } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!9, !10, !11} +!llvm.ident = !{!12} + +!0 = !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 3.7.0 (trunk 239176) (llvm/trunk 239190)", isOptimized: false, runtimeVersion: 0, emissionKind: 1, enums: !2, retainedTypes: !2, subprograms: !3, globals: !2, imports: !2) +!1 = !DIFile(filename: "frame.c", directory: "/tmp") +!2 = !{} +!3 = !{!4, !8} +!4 = !DISubprogram(name: "bar", scope: !1, file: !1, line: 3, type: !5, isLocal: false, isDefinition: true, scopeLine: 3, flags: DIFlagPrototyped, isOptimized: false, function: i32 (i32)* @bar, variables: !2) +!5 = !DISubroutineType(types: !6) +!6 = !{!7, !7} +!7 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed) +!8 = !DISubprogram(name: "baz", scope: !1, file: !1, line: 8, type: !5, isLocal: false, isDefinition: true, scopeLine: 8, flags: DIFlagPrototyped, isOptimized: false, function: i32 (i32)* @baz, variables: !2) +!9 = !{i32 2, !"Dwarf Version", i32 4} +!10 = !{i32 2, !"Debug Info Version", i32 3} +!11 = !{i32 1, !"PIC Level", i32 2} +!12 = !{!"clang version 3.7.0 (trunk 239176) (llvm/trunk 239190)"} +!13 = !DILocalVariable(tag: DW_TAG_arg_variable, name: "b", arg: 1, scope: !4, file: !1, line: 3, type: !7) +!14 = !DIExpression() +!15 = !DILocation(line: 3, column: 13, scope: !4) +!16 = !DILocalVariable(tag: DW_TAG_auto_variable, name: "var", scope: !4, file: !1, line: 4, type: !7) +!17 = !DILocation(line: 4, column: 6, scope: !4) +!18 = !DILocation(line: 4, column: 12, scope: !4) +!19 = !DILocation(line: 4, column: 14, scope: !4) +!20 = !DILocation(line: 5, column: 9, scope: !4) +!21 = !DILocation(line: 5, column: 2, scope: !4) +!22 = !DILocalVariable(tag: DW_TAG_arg_variable, name: "b", arg: 1, scope: !8, file: !1, line: 8, type: !7) +!23 = !DILocation(line: 8, column: 13, scope: !8) +!24 = !DILocation(line: 9, column: 13, scope: !8) +!25 = !DILocation(line: 9, column: 9, scope: !8) +!26 = !DILocation(line: 9, column: 2, scope: !8) diff --git a/test/tools/dsymutil/Inputs/frame.c b/test/tools/dsymutil/Inputs/frame.c new file mode 100644 index 00000000000..9ca082d4ae5 --- /dev/null +++ b/test/tools/dsymutil/Inputs/frame.c @@ -0,0 +1,10 @@ +int foo(int *f); + +int bar(int b) { + int var = b + 1; + return foo(&var); +} + +int baz(int b) { + return bar(b); +} diff --git a/test/tools/dsymutil/X86/frame-1.test b/test/tools/dsymutil/X86/frame-1.test new file mode 100644 index 00000000000..7852e68a142 --- /dev/null +++ b/test/tools/dsymutil/X86/frame-1.test @@ -0,0 +1,32 @@ +# REQUIRES: object-emission +# RUN: rm -rf %t +# RUN: mkdir -p %t +# RUN: llc -filetype=obj %p/../Inputs/frame-dw2.ll -o %t/frame-dw2.o +# RUN: llvm-dsymutil -oso-prepend-path=%t -y %s -o - | llvm-dwarfdump -debug-dump=frames - | FileCheck %s + +# This test is meant to verify that identical CIEs will get reused +# in the same file but also inbetween files. For this to happen, we +# link twice the same file using this made-up debug map: + +--- +triple: 'i386-unknown-unknown-macho' +objects: + - filename: frame-dw2.o + symbols: + - { sym: _bar, objAddr: 0x0, binAddr: 0x1000, size: 0x12 } + - { sym: _baz, objAddr: 0x0, binAddr: 0x2000, size: 0x12 } + - filename: frame-dw2.o + symbols: + - { sym: _baz, objAddr: 0x0, binAddr: 0x3000, size: 0x12 } +... + +# CHECK: .debug_frame contents: +# CHECK: 00000000 {{[0-9a-f]*}} ffffffff CIE +# CHECK-NOT: FDE +# CHECK: FDE cie=00000000 pc=00001000...00001 +# CHECK-NOT: FDE +# CHECK: FDE cie=00000000 pc=00002000...00002 +# CHECK-NOT: FDE +# CHECK: FDE cie=00000000 pc=00003000...00003 +# CHECK-NOT: FDE + diff --git a/test/tools/dsymutil/X86/frame-2.test b/test/tools/dsymutil/X86/frame-2.test new file mode 100644 index 00000000000..168e342a4f7 --- /dev/null +++ b/test/tools/dsymutil/X86/frame-2.test @@ -0,0 +1,47 @@ +# REQUIRES: object-emission +# RUN: rm -rf %t +# RUN: mkdir -p %t +# RUN: llc -filetype=obj %p/../Inputs/frame-dw2.ll -o %t/frame-dw2.o +# RUN: llc -filetype=obj %p/../Inputs/frame-dw4.ll -o %t/frame-dw4.o +# RUN: llvm-dsymutil -oso-prepend-path=%t -y %s -o - | llvm-dwarfdump -debug-dump=frames - | FileCheck %s + +# Check the handling of multiple different CIEs. To have CIEs that +# appear to be different, use a dwarf2 version of the file along with +# a dwarf 4 version. The CIE header version (and layout) will be different. +# FIXME: this test also checks that we didn't reuse the first CIE when it +# appears again. This is a behavior we inherited from dsymutil-classic +# but this should be fixed (see comment in patchFrameInfoForObject()) +--- +triple: 'i386-unknown-unknown-macho' +objects: + - filename: frame-dw2.o + symbols: + - { sym: _bar, objAddr: 0x0, binAddr: 0x1000, size: 0x12 } + - { sym: _baz, objAddr: 0x0, binAddr: 0x2000, size: 0x12 } + - filename: frame-dw4.o + symbols: + - { sym: _baz, objAddr: 0x0, binAddr: 0x3000, size: 0x12 } + - filename: frame-dw2.o + symbols: + - { sym: _bar, objAddr: 0x0, binAddr: 0x4000, size: 0x12 } +... + +# CHECK: .debug_frame contents: +# CHECK: 00000000 {{[0-9a-f]*}} ffffffff CIE +# CHECK-NEXT: Version:{{.*}}1 +# CHECK-NOT: FDE +# CHECK: FDE cie=00000000 pc=00001000...00001 +# CHECK-NOT: FDE +# CHECK: FDE cie=00000000 pc=00002000...00002 +# CHECK-NOT: FDE +# CHECK: [[CIEDW4:[0-9a-f]*]] 00000010 ffffffff CIE +# CHECK-NEXT: Version:{{.*}}4 +# CHECK-NOT: FDE +# CHECK: FDE cie=[[CIEDW4]] pc=00003000...00003 +# CHECK-NOT: FDE +# CHECK: [[CIEDW2:[0-9a-f]*]] {{[0-9a-f]*}} ffffffff CIE +# CHECK-NEXT: Version:{{.*}}1 +# CHECK-NOT: FDE +# CHECK: FDE cie=[[CIEDW2]] pc=00004000...00004 +# CHECK-NOT: FDE + diff --git a/tools/dsymutil/DwarfLinker.cpp b/tools/dsymutil/DwarfLinker.cpp index c5a8b011461..7dc15b990ec 100644 --- a/tools/dsymutil/DwarfLinker.cpp +++ b/tools/dsymutil/DwarfLinker.cpp @@ -419,6 +419,7 @@ class DwarfStreamer { uint32_t RangesSectionSize; uint32_t LocSectionSize; uint32_t LineSectionSize; + uint32_t FrameSectionSize; /// \brief Emit the pubnames or pubtypes section contribution for \p /// Unit into \p Sec. The data is provided in \p Names. @@ -492,6 +493,15 @@ public: /// \brief Emit the .debug_pubtypes contribution for \p Unit. void emitPubTypesForUnit(const CompileUnit &Unit); + + /// \brief Emit a CIE. + void emitCIE(StringRef CIEBytes); + + /// \brief Emit an FDE with data \p Bytes. + void emitFDE(uint32_t CIEOffset, uint32_t AddreSize, uint32_t Address, + StringRef Bytes); + + uint32_t getFrameSectionSize() const { return FrameSectionSize; } }; bool DwarfStreamer::init(Triple TheTriple, StringRef OutputFilename) { @@ -561,6 +571,7 @@ bool DwarfStreamer::init(Triple TheTriple, StringRef OutputFilename) { RangesSectionSize = 0; LocSectionSize = 0; LineSectionSize = 0; + FrameSectionSize = 0; return true; } @@ -992,6 +1003,28 @@ void DwarfStreamer::emitPubTypesForUnit(const CompileUnit &Unit) { "types", Unit, Unit.getPubtypes()); } +/// \brief Emit a CIE into the debug_frame section. +void DwarfStreamer::emitCIE(StringRef CIEBytes) { + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfFrameSection()); + + MS->EmitBytes(CIEBytes); + FrameSectionSize += CIEBytes.size(); +} + +/// \brief Emit a FDE into the debug_frame section. \p FDEBytes +/// contains the FDE data without the length, CIE offset and address +/// which will be replaced with the paramter values. +void DwarfStreamer::emitFDE(uint32_t CIEOffset, uint32_t AddrSize, + uint32_t Address, StringRef FDEBytes) { + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfFrameSection()); + + MS->EmitIntValue(FDEBytes.size() + 4 + AddrSize, 4); + MS->EmitIntValue(CIEOffset, 4); + MS->EmitIntValue(Address, AddrSize); + MS->EmitBytes(FDEBytes); + FrameSectionSize += FDEBytes.size() + 8 + AddrSize; +} + /// \brief The core of the Dwarf linking logic. /// /// The link of the dwarf information from the object files will be @@ -1010,7 +1043,7 @@ class DwarfLinker { public: DwarfLinker(StringRef OutputFilename, const LinkOptions &Options) : OutputFilename(OutputFilename), Options(Options), - BinHolder(Options.Verbose) {} + BinHolder(Options.Verbose), LastCIEOffset(0) {} ~DwarfLinker() { for (auto *Abbrev : Abbreviations) @@ -1208,6 +1241,10 @@ private: /// \brief Emit the accelerator entries for \p Unit. void emitAcceleratorEntriesForUnit(CompileUnit &Unit); + /// \brief Patch the frame info for an object file and emit it. + void patchFrameInfoForObject(const DebugMapObject &, DWARFContext &, + unsigned AddressSize); + /// \brief DIELoc objects that need to be destructed (but not freed!). std::vector DIELocs; /// \brief DIEBlock objects that need to be destructed (but not freed!). @@ -1257,6 +1294,16 @@ private: /// /// See startDebugObject() for a more complete description of its use. std::map> Ranges; + + /// \brief The CIEs that have been emitted in the output + /// section. The actual CIE data serves a the key to this StringMap, + /// this takes care of comparing the semantics of CIEs defined in + /// different object files. + StringMap EmittedCIEs; + + /// Offset of the last CIE that has been emitted in the output + /// debug_frame section. + uint32_t LastCIEOffset; }; /// \brief Similar to DWARFUnitSection::getUnitForOffset(), but @@ -2458,6 +2505,91 @@ void DwarfLinker::emitAcceleratorEntriesForUnit(CompileUnit &Unit) { Streamer->emitPubTypesForUnit(Unit); } +/// \brief Read the frame info stored in the object, and emit the +/// patched frame descriptions for the linked binary. +/// +/// This is actually pretty easy as the data of the CIEs and FDEs can +/// be considered as black boxes and moved as is. The only thing to do +/// is to patch the addresses in the headers. +void DwarfLinker::patchFrameInfoForObject(const DebugMapObject &DMO, + DWARFContext &OrigDwarf, + unsigned AddrSize) { + StringRef FrameData = OrigDwarf.getDebugFrameSection(); + if (FrameData.empty()) + return; + + DataExtractor Data(FrameData, OrigDwarf.isLittleEndian(), 0); + uint32_t InputOffset = 0; + + // Store the data of the CIEs defined in this object, keyed by their + // offsets. + DenseMap LocalCIES; + + while (Data.isValidOffset(InputOffset)) { + uint32_t EntryOffset = InputOffset; + uint32_t InitialLength = Data.getU32(&InputOffset); + if (InitialLength == 0xFFFFFFFF) + return reportWarning("Dwarf64 bits no supported"); + + uint32_t CIEId = Data.getU32(&InputOffset); + if (CIEId == 0xFFFFFFFF) { + // This is a CIE, store it. + StringRef CIEData = FrameData.substr(EntryOffset, InitialLength + 4); + LocalCIES[EntryOffset] = CIEData; + // The -4 is to account for the CIEId we just read. + InputOffset += InitialLength - 4; + continue; + } + + uint32_t Loc = Data.getUnsigned(&InputOffset, AddrSize); + + // Some compilers seem to emit frame info that doesn't start at + // the function entry point, thus we can't just lookup the address + // in the debug map. Use the linker's range map to see if the FDE + // describes something that we can relocate. + auto Range = Ranges.upper_bound(Loc); + if (Range != Ranges.begin()) + --Range; + if (Range == Ranges.end() || Range->first > Loc || + Range->second.first <= Loc) { + // The +4 is to account for the size of the InitialLength field itself. + InputOffset = EntryOffset + InitialLength + 4; + continue; + } + + // This is an FDE, and we have a mapping. + // Have we already emitted a corresponding CIE? + StringRef CIEData = LocalCIES[CIEId]; + if (CIEData.empty()) + return reportWarning("Inconsistent debug_frame content. Dropping."); + + // Look if we already emitted a CIE that corresponds to the + // referenced one (the CIE data is the key of that lookup). + auto IteratorInserted = EmittedCIEs.insert( + std::make_pair(CIEData, Streamer->getFrameSectionSize())); + // If there is no CIE yet for this ID, emit it. + if (IteratorInserted.second || + // FIXME: dsymutil-classic only caches the last used CIE for + // reuse. Mimic that behavior for now. Just removing that + // second half of the condition and the LastCIEOffset variable + // makes the code DTRT. + LastCIEOffset != IteratorInserted.first->getValue()) { + LastCIEOffset = Streamer->getFrameSectionSize(); + IteratorInserted.first->getValue() = LastCIEOffset; + Streamer->emitCIE(CIEData); + } + + // Emit the FDE with updated address and CIE pointer. + // (4 + AddrSize) is the size of the CIEId + initial_location + // fields that will get reconstructed by emitFDE(). + unsigned FDERemainingBytes = InitialLength - (4 + AddrSize); + Streamer->emitFDE(IteratorInserted.first->getValue(), AddrSize, + Loc + Range->second.second, + FrameData.substr(InputOffset, FDERemainingBytes)); + InputOffset += FDERemainingBytes; + } +} + bool DwarfLinker::link(const DebugMap &Map) { if (Map.begin() == Map.end()) { @@ -2555,6 +2687,10 @@ bool DwarfLinker::link(const DebugMap &Map) { Streamer->emitDIE(*CurrentUnit.getOutputUnitDIE()); } + if (!ValidRelocs.empty() && !Options.NoOutput && !Units.empty()) + patchFrameInfoForObject(*Obj, DwarfContext, + Units[0].getOrigUnit().getAddressByteSize()); + // Clean-up before starting working on the next object. endDebugObject(); }