--- /dev/null
+; 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)
--- /dev/null
+; 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)
--- /dev/null
+int foo(int *f);
+
+int bar(int b) {
+ int var = b + 1;
+ return foo(&var);
+}
+
+int baz(int b) {
+ return bar(b);
+}
--- /dev/null
+# 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
+
--- /dev/null
+# 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
+
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.
/// \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) {
RangesSectionSize = 0;
LocSectionSize = 0;
LineSectionSize = 0;
+ FrameSectionSize = 0;
return true;
}
"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
public:
DwarfLinker(StringRef OutputFilename, const LinkOptions &Options)
: OutputFilename(OutputFilename), Options(Options),
- BinHolder(Options.Verbose) {}
+ BinHolder(Options.Verbose), LastCIEOffset(0) {}
~DwarfLinker() {
for (auto *Abbrev : Abbreviations)
/// \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<DIELoc *> DIELocs;
/// \brief DIEBlock objects that need to be destructed (but not freed!).
///
/// See startDebugObject() for a more complete description of its use.
std::map<uint64_t, std::pair<uint64_t, int64_t>> 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<uint32_t> 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
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<uint32_t, StringRef> 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()) {
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();
}