diff --git a/llvm/include/llvm/BinaryFormat/SFrame.h b/llvm/include/llvm/BinaryFormat/SFrame.h index 095db18b9c254..7b58043c60363 100644 --- a/llvm/include/llvm/BinaryFormat/SFrame.h +++ b/llvm/include/llvm/BinaryFormat/SFrame.h @@ -117,6 +117,7 @@ template struct FDEInfo { Info = ((PAuthKey & 1) << 5) | ((static_cast(FDE) & 1) << 4) | (static_cast(FRE) & 0xf); } + uint8_t getFuncInfo() const { return Info; } }; template struct FuncDescEntry { @@ -155,6 +156,7 @@ template struct FREInfo { Info = ((RA & 1) << 7) | ((static_cast(Sz) & 3) << 5) | ((N & 0xf) << 1) | (static_cast(Reg) & 1); } + uint8_t getFREInfo() const { return Info; } }; template struct FrameRowEntry { diff --git a/llvm/include/llvm/MC/MCAsmBackend.h b/llvm/include/llvm/MC/MCAsmBackend.h index 1625355323692..58363f0b671e2 100644 --- a/llvm/include/llvm/MC/MCAsmBackend.h +++ b/llvm/include/llvm/MC/MCAsmBackend.h @@ -168,6 +168,7 @@ class LLVM_ABI MCAsmBackend { virtual bool relaxAlign(MCFragment &F, unsigned &Size) { return false; } virtual bool relaxDwarfLineAddr(MCFragment &) const { return false; } virtual bool relaxDwarfCFA(MCFragment &) const { return false; } + virtual bool relaxSFrameCFA(MCFragment &) const { return false; } // Defined by linker relaxation targets to possibly emit LEB128 relocations // and set Value at the relocated location. diff --git a/llvm/include/llvm/MC/MCAssembler.h b/llvm/include/llvm/MC/MCAssembler.h index 1316d8669239d..6e1d6421b8d33 100644 --- a/llvm/include/llvm/MC/MCAssembler.h +++ b/llvm/include/llvm/MC/MCAssembler.h @@ -117,6 +117,7 @@ class MCAssembler { void relaxBoundaryAlign(MCBoundaryAlignFragment &BF); void relaxDwarfLineAddr(MCFragment &F); void relaxDwarfCallFrameFragment(MCFragment &F); + void relaxSFrameFragment(MCFragment &DF); public: /// Construct a new assembler instance. diff --git a/llvm/include/llvm/MC/MCObjectStreamer.h b/llvm/include/llvm/MC/MCObjectStreamer.h index b9e813b9b0d28..1899cb6331c6f 100644 --- a/llvm/include/llvm/MC/MCObjectStreamer.h +++ b/llvm/include/llvm/MC/MCObjectStreamer.h @@ -150,6 +150,9 @@ class MCObjectStreamer : public MCStreamer { MCSymbol *EndLabel = nullptr) override; void emitDwarfAdvanceFrameAddr(const MCSymbol *LastLabel, const MCSymbol *Label, SMLoc Loc); + void emitSFrameCalculateFuncOffset(const MCSymbol *FunCabsel, + const MCSymbol *FREBegin, + MCFragment *FDEFrag, SMLoc Loc); void emitCVLocDirective(unsigned FunctionId, unsigned FileNo, unsigned Line, unsigned Column, bool PrologueEnd, bool IsStmt, StringRef FileName, SMLoc Loc) override; diff --git a/llvm/include/llvm/MC/MCSFrame.h b/llvm/include/llvm/MC/MCSFrame.h index 8f182a86d1ab1..694aec55aefeb 100644 --- a/llvm/include/llvm/MC/MCSFrame.h +++ b/llvm/include/llvm/MC/MCSFrame.h @@ -16,9 +16,14 @@ #ifndef LLVM_MC_MCSFRAME_H #define LLVM_MC_MCSFRAME_H +#include "llvm/ADT/SmallVector.h" +#include + namespace llvm { +class MCContext; class MCObjectStreamer; +class MCFragment; class MCSFrameEmitter { public: @@ -26,6 +31,15 @@ class MCSFrameEmitter { // // \param Streamer - Emit into this stream. static void emit(MCObjectStreamer &Streamer); + + // Encode the FRE's function offset. + // + // \param C - Context. + // \param Offset - Offset to encode. + // \param Out - Destination of the encoding. + // \param FDEFrag - Frag that specifies the encoding format. + static void encodeFuncOffset(MCContext &C, uint64_t Offset, + SmallVectorImpl &Out, MCFragment *FDEFrag); }; } // end namespace llvm diff --git a/llvm/include/llvm/MC/MCSection.h b/llvm/include/llvm/MC/MCSection.h index 12389d623e588..a26e6cfb2158a 100644 --- a/llvm/include/llvm/MC/MCSection.h +++ b/llvm/include/llvm/MC/MCSection.h @@ -59,6 +59,7 @@ class MCFragment { FT_Org, FT_Dwarf, FT_DwarfFrame, + FT_SFrame, FT_BoundaryAlign, FT_SymbolId, FT_CVInlineLines, @@ -143,6 +144,12 @@ class MCFragment { // .loc dwarf directives. int64_t LineDelta; } dwarf; + struct { + // This FRE describes unwind info at AddrDelta from function start. + const MCExpr *AddrDelta; + // Fragment that records how many bytes of AddrDelta to emit. + MCFragment *FDEFragment; + } sframe; } u{}; public: @@ -296,6 +303,24 @@ class MCFragment { assert(Kind == FT_Dwarf); u.dwarf.LineDelta = LineDelta; } + + //== FT_SFrame functions + const MCExpr &getSFrameAddrDelta() const { + assert(Kind == FT_SFrame); + return *u.sframe.AddrDelta; + } + void setSFrameAddrDelta(const MCExpr *E) { + assert(Kind == FT_SFrame); + u.sframe.AddrDelta = E; + } + MCFragment *getSFrameFDE() const { + assert(Kind == FT_SFrame); + return u.sframe.FDEFragment; + } + void setSFrameFDE(MCFragment *F) { + assert(Kind == FT_SFrame); + u.sframe.FDEFragment = F; + } }; // MCFragment subclasses do not use the fixed-size part or variable-size tail of diff --git a/llvm/lib/MC/MCAssembler.cpp b/llvm/lib/MC/MCAssembler.cpp index b1031d7822604..cee281597cfed 100644 --- a/llvm/lib/MC/MCAssembler.cpp +++ b/llvm/lib/MC/MCAssembler.cpp @@ -22,6 +22,7 @@ #include "llvm/MC/MCFixup.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCObjectWriter.h" +#include "llvm/MC/MCSFrame.h" #include "llvm/MC/MCSection.h" #include "llvm/MC/MCSymbol.h" #include "llvm/MC/MCValue.h" @@ -199,6 +200,7 @@ uint64_t MCAssembler::computeFragmentSize(const MCFragment &F) const { case MCFragment::FT_LEB: case MCFragment::FT_Dwarf: case MCFragment::FT_DwarfFrame: + case MCFragment::FT_SFrame: case MCFragment::FT_CVInlineLines: case MCFragment::FT_CVDefRange: return F.getSize(); @@ -399,6 +401,7 @@ static void writeFragment(raw_ostream &OS, const MCAssembler &Asm, case MCFragment::FT_LEB: case MCFragment::FT_Dwarf: case MCFragment::FT_DwarfFrame: + case MCFragment::FT_SFrame: case MCFragment::FT_CVInlineLines: case MCFragment::FT_CVDefRange: { if (F.getKind() == MCFragment::FT_Data) @@ -914,6 +917,24 @@ void MCAssembler::relaxDwarfCallFrameFragment(MCFragment &F) { F.clearVarFixups(); } +void MCAssembler::relaxSFrameFragment(MCFragment &F) { + assert(F.getKind() == MCFragment::FT_SFrame); + MCContext &C = getContext(); + int64_t Value; + bool Abs = F.getSFrameAddrDelta().evaluateAsAbsolute(Value, *this); + if (!Abs) { + C.reportError(F.getSFrameAddrDelta().getLoc(), + "invalid CFI advance_loc expression in sframe"); + F.setSFrameAddrDelta(MCConstantExpr::create(0, C)); + return; + } + + SmallVector Data; + MCSFrameEmitter::encodeFuncOffset(Context, Value, Data, F.getSFrameFDE()); + F.setVarContents(Data); + F.clearVarFixups(); +} + bool MCAssembler::relaxFragment(MCFragment &F) { auto Size = computeFragmentSize(F); switch (F.getKind()) { @@ -932,6 +953,9 @@ bool MCAssembler::relaxFragment(MCFragment &F) { case MCFragment::FT_DwarfFrame: relaxDwarfCallFrameFragment(F); break; + case MCFragment::FT_SFrame: + relaxSFrameFragment(F); + break; case MCFragment::FT_BoundaryAlign: relaxBoundaryAlign(static_cast(F)); break; diff --git a/llvm/lib/MC/MCFragment.cpp b/llvm/lib/MC/MCFragment.cpp index 21da79bb0aa30..85d1c5888f1da 100644 --- a/llvm/lib/MC/MCFragment.cpp +++ b/llvm/lib/MC/MCFragment.cpp @@ -53,6 +53,7 @@ LLVM_DUMP_METHOD void MCFragment::dump() const { case MCFragment::FT_Org: OS << "Org"; break; case MCFragment::FT_Dwarf: OS << "Dwarf"; break; case MCFragment::FT_DwarfFrame: OS << "DwarfCallFrame"; break; + case MCFragment::FT_SFrame: OS << "SFrame"; break; case MCFragment::FT_LEB: OS << "LEB"; break; case MCFragment::FT_BoundaryAlign: OS<<"BoundaryAlign"; break; case MCFragment::FT_SymbolId: OS << "SymbolId"; break; @@ -79,7 +80,8 @@ LLVM_DUMP_METHOD void MCFragment::dump() const { case MCFragment::FT_Align: case MCFragment::FT_LEB: case MCFragment::FT_Dwarf: - case MCFragment::FT_DwarfFrame: { + case MCFragment::FT_DwarfFrame: + case MCFragment::FT_SFrame: { if (isLinkerRelaxable()) OS << " LinkerRelaxable"; auto Fixed = getContents(); @@ -129,6 +131,7 @@ LLVM_DUMP_METHOD void MCFragment::dump() const { OS << " LineDelta:" << getDwarfLineDelta(); break; case MCFragment::FT_DwarfFrame: + case MCFragment::FT_SFrame: OS << " AddrDelta:"; getDwarfAddrDelta().print(OS, nullptr); break; diff --git a/llvm/lib/MC/MCObjectStreamer.cpp b/llvm/lib/MC/MCObjectStreamer.cpp index 59265bc8595ba..701a0836d2c70 100644 --- a/llvm/lib/MC/MCObjectStreamer.cpp +++ b/llvm/lib/MC/MCObjectStreamer.cpp @@ -583,6 +583,19 @@ void MCObjectStreamer::emitDwarfAdvanceFrameAddr(const MCSymbol *LastLabel, newFragment(); } +void MCObjectStreamer::emitSFrameCalculateFuncOffset(const MCSymbol *FuncBase, + const MCSymbol *FREBegin, + MCFragment *FDEFrag, + SMLoc Loc) { + assert(FuncBase && "No function base address"); + assert(FREBegin && "FRE doesn't describe a location"); + auto *F = getCurrentFragment(); + F->Kind = MCFragment::FT_SFrame; + F->setSFrameAddrDelta(buildSymbolDiff(*this, FREBegin, FuncBase, Loc)); + F->setSFrameFDE(FDEFrag); + newFragment(); +} + void MCObjectStreamer::emitCVLocDirective(unsigned FunctionId, unsigned FileNo, unsigned Line, unsigned Column, bool PrologueEnd, bool IsStmt, diff --git a/llvm/lib/MC/MCSFrame.cpp b/llvm/lib/MC/MCSFrame.cpp index a0d6c80ab72ea..066d1a34e1548 100644 --- a/llvm/lib/MC/MCSFrame.cpp +++ b/llvm/lib/MC/MCSFrame.cpp @@ -14,6 +14,7 @@ #include "llvm/MC/MCObjectStreamer.h" #include "llvm/MC/MCSection.h" #include "llvm/MC/MCSymbol.h" +#include "llvm/Support/Endian.h" #include "llvm/Support/EndianStream.h" using namespace llvm; @@ -33,10 +34,70 @@ struct SFrameFRE { size_t CFAOffset = 0; size_t FPOffset = 0; size_t RAOffset = 0; - bool FromFP = false; + FREInfo Info; bool CFARegSet = false; - SFrameFRE(const MCSymbol *Start) : Label(Start) {} + SFrameFRE(const MCSymbol *Start) : Label(Start) { Info.Info = 0; } + + void emitOffset(MCObjectStreamer &S, FREOffset OffsetSize, size_t Offset) { + switch (OffsetSize) { + case (FREOffset::B1): + S.emitInt8(Offset); + return; + case (FREOffset::B2): + S.emitInt16(Offset); + return; + case (FREOffset::B4): + S.emitInt32(Offset); + return; + } + } + + void emit(MCObjectStreamer &S, const MCSymbol *FuncBegin, + MCFragment *FDEFrag) { + S.emitSFrameCalculateFuncOffset(FuncBegin, Label, FDEFrag, SMLoc()); + + // fre_cfa_base_reg_id already set during parsing + + // fre_offset_count + unsigned RegsTracked = 1; // always track the cfa. + if (FPOffset != 0) + ++RegsTracked; + if (RAOffset != 0) + ++RegsTracked; + Info.setOffsetCount(RegsTracked); + + // fre_offset_size + if (isInt<8>(CFAOffset) && isInt<8>(FPOffset) && isInt<8>(RAOffset)) + Info.setOffsetSize(FREOffset::B1); + else if (isInt<16>(CFAOffset) && isInt<16>(FPOffset) && isInt<16>(RAOffset)) + Info.setOffsetSize(FREOffset::B2); + else { + assert(isInt<32>(CFAOffset) && isInt<32>(FPOffset) && + isInt<32>(RAOffset) && "Offset too big for sframe"); + Info.setOffsetSize(FREOffset::B4); + } + + // No support for fre_mangled_ra_p yet. + Info.setReturnAddressSigned(false); + + // sframe_fre_info_word + S.emitInt8(Info.getFREInfo()); + + // FRE Offsets + [[maybe_unused]] unsigned OffsetsEmitted = 1; + emitOffset(S, Info.getOffsetSize(), CFAOffset); + if (FPOffset) { + ++OffsetsEmitted; + emitOffset(S, Info.getOffsetSize(), FPOffset); + } + if (RAOffset) { + ++OffsetsEmitted; + emitOffset(S, Info.getOffsetSize(), RAOffset); + } + assert(OffsetsEmitted == RegsTracked && + "Didn't emit the right number of offsets"); + } }; // High-level structure to track info needed to emit a sframe_func_desc_entry @@ -46,11 +107,13 @@ struct SFrameFDE { const MCDwarfFrameInfo &DFrame; // Label where this FDE's FREs start. MCSymbol *FREStart; + // Frag where this FDE is emitted. + MCFragment *Frag; // Unwinding fres SmallVector FREs; SFrameFDE(const MCDwarfFrameInfo &DF, MCSymbol *FRES) - : DFrame(DF), FREStart(FRES) {} + : DFrame(DF), FREStart(FRES), Frag(nullptr) {} void emit(MCObjectStreamer &S, const MCSymbol *FRESubSectionStart) { MCContext &C = S.getContext(); @@ -74,13 +137,21 @@ struct SFrameFDE { S.emitInt32(0); // sfde_func_num_fres - // TODO: When we actually emit fres, replace 0 with FREs.size() - S.emitInt32(0); + S.emitInt32(FREs.size()); // sfde_func_info word - FDEInfo I; - I.setFuncInfo(0 /* No pauth key */, FDEType::PCInc, FREType::Addr1); - S.emitInt8(I.Info); + + // All FREs within an FDE share the same sframe::FREType::AddrX. The value + // of 'X' is determined by the FRE with the largest offset, which is the + // last. This offset isn't known until relax time, so emit a frag which can + // calculate that now. + // + // At relax time, this FDE frag calculates the proper AddrX value (as well + // as the rest of the FDE FuncInfo word). Subsequent FRE frags will read it + // from this frag and emit the proper number of bytes. + Frag = S.getCurrentFragment(); + S.emitSFrameCalculateFuncOffset(DFrame.Begin, FREs.back().Label, nullptr, + SMLoc()); // sfde_func_rep_size. Not relevant in non-PCMASK fdes. S.emitInt8(0); @@ -96,13 +167,16 @@ struct SFrameFDE { class SFrameEmitterImpl { MCObjectStreamer &Streamer; SmallVector FDEs; + uint32_t TotalFREs; ABI SFrameABI; // Target-specific convenience variables to detect when a CFI instruction // references these registers. Unlike in dwarf frame descriptions, they never - // escape into the sframe section itself. + // escape into the sframe section itself. TODO: These should be retrieved from + // the target. unsigned SPReg; unsigned FPReg; unsigned RAReg; + int8_t FixedRAOffset; MCSymbol *FDESubSectionStart; MCSymbol *FRESubSectionStart; MCSymbol *FRESubSectionEnd; @@ -110,12 +184,12 @@ class SFrameEmitterImpl { bool setCFARegister(SFrameFRE &FRE, const MCCFIInstruction &I) { if (I.getRegister() == SPReg) { FRE.CFARegSet = true; - FRE.FromFP = false; + FRE.Info.setBaseRegister(BaseReg::SP); return true; } if (I.getRegister() == FPReg) { FRE.CFARegSet = true; - FRE.FromFP = true; + FRE.Info.setBaseRegister(BaseReg::FP); return true; } Streamer.getContext().reportWarning( @@ -182,7 +256,8 @@ class SFrameEmitterImpl { } public: - SFrameEmitterImpl(MCObjectStreamer &Streamer) : Streamer(Streamer) { + SFrameEmitterImpl(MCObjectStreamer &Streamer) + : Streamer(Streamer), TotalFREs(0) { assert(Streamer.getContext() .getObjectFileInfo() ->getSFrameABIArch() @@ -195,6 +270,7 @@ class SFrameEmitterImpl { SPReg = 31; RAReg = 29; FPReg = 30; + FixedRAOffset = 0; break; case ABI::AMD64EndianLittle: SPReg = 7; @@ -202,6 +278,7 @@ class SFrameEmitterImpl { // MCDwarfFrameInfo constructor. RAReg = static_cast(INT_MAX); FPReg = 6; + FixedRAOffset = -8; break; } @@ -219,10 +296,16 @@ class SFrameEmitterImpl { bool equalIgnoringLocation(const SFrameFRE &Left, const SFrameFRE &Right) { return Left.CFAOffset == Right.CFAOffset && Left.FPOffset == Right.FPOffset && Left.RAOffset == Right.RAOffset && - Left.FromFP == Right.FromFP && Left.CFARegSet == Right.CFARegSet; + Left.Info.getFREInfo() == Right.Info.getFREInfo() && + Left.CFARegSet == Right.CFARegSet; } void buildSFDE(const MCDwarfFrameInfo &DF) { + // Functions with zero size can happen with assembler macros and + // machine-generated code. They don't need unwind info at all, so + // no need to warn. + if (atSameLocation(DF.Begin, DF.End)) + return; bool Valid = true; SFrameFDE FDE(DF, Streamer.getContext().createTempSymbol()); // This would have been set via ".cfi_return_column", but @@ -277,8 +360,11 @@ class SFrameEmitterImpl { LastLabel = L; } } - if (Valid) + + if (Valid) { FDEs.push_back(FDE); + TotalFREs += FDE.FREs.size(); + } } void emitPreamble() { @@ -294,13 +380,12 @@ class SFrameEmitterImpl { // sfh_cfa_fixed_fp_offset Streamer.emitInt8(0); // sfh_cfa_fixed_ra_offset - Streamer.emitInt8(0); + Streamer.emitInt8(FixedRAOffset); // sfh_auxhdr_len Streamer.emitInt8(0); // shf_num_fdes Streamer.emitInt32(FDEs.size()); // shf_num_fres - uint32_t TotalFREs = 0; Streamer.emitInt32(TotalFREs); // shf_fre_len @@ -322,8 +407,11 @@ class SFrameEmitterImpl { void emitFREs() { Streamer.emitLabel(FRESubSectionStart); - for (auto &FDE : FDEs) + for (auto &FDE : FDEs) { Streamer.emitLabel(FDE.FREStart); + for (auto &FRE : FDE.FREs) + FRE.emit(Streamer, FDE.DFrame.Begin, FDE.Frag); + } Streamer.emitLabel(FRESubSectionEnd); } }; @@ -359,3 +447,55 @@ void MCSFrameEmitter::emit(MCObjectStreamer &Streamer) { Emitter.emitFDEs(); Emitter.emitFREs(); } + +void MCSFrameEmitter::encodeFuncOffset(MCContext &C, uint64_t Offset, + SmallVectorImpl &Out, + MCFragment *FDEFrag) { + // If encoding into the FDE Frag itself, generate the sfde_func_info. + if (FDEFrag == nullptr) { + // sfde_func_info + + // Offset is the difference between the function start label and the final + // FRE's offset, which is the max offset for this FDE. + FDEInfo I; + I.Info = 0; + if (isUInt<8>(Offset)) + I.setFREType(FREType::Addr1); + else if (isUInt<16>(Offset)) + I.setFREType(FREType::Addr2); + else { + assert(isUInt<32>(Offset)); + I.setFREType(FREType::Addr4); + } + I.setFDEType(FDEType::PCInc); + // TODO: When we support pauth keys, this will need to be retrieved + // from the frag itself. + I.setPAuthKey(0); + + Out.push_back(I.getFuncInfo()); + return; + } + + const auto &FDEData = FDEFrag->getVarContents(); + FDEInfo I; + I.Info = FDEData.back(); + FREType T = I.getFREType(); + llvm::endianness E = C.getAsmInfo()->isLittleEndian() + ? llvm::endianness::little + : llvm::endianness::big; + // sfre_start_address + switch (T) { + case FREType::Addr1: + assert(isUInt<8>(Offset) && "Miscalculated Sframe FREType"); + support::endian::write(Out, Offset, E); + break; + case FREType::Addr2: + assert(isUInt<16>(Offset) && "Miscalculated Sframe FREType"); + support::endian::write(Out, Offset, E); + break; + case FREType::Addr4: + assert(isUInt<32>(Offset) && "Miscalculated Sframe FREType"); + support::endian::write(Out, Offset, E); + break; + } +} diff --git a/llvm/test/MC/ELF/cfi-sframe-encoding.s b/llvm/test/MC/ELF/cfi-sframe-encoding.s new file mode 100644 index 0000000000000..1f293030c7dde --- /dev/null +++ b/llvm/test/MC/ELF/cfi-sframe-encoding.s @@ -0,0 +1,85 @@ +# RUN: llvm-mc --filetype=obj --gsframe -triple x86_64 %s -o %t.o +# RUN: llvm-readelf --sframe %t.o | FileCheck %s + +# Tests selection for the proper FDE AddrX encoding at the boundaries +# between uint8_t, uint16_t, and uint32_t. The first FRE always fits +# anywhere, because its address-offset is zero. The last FRE +# determines the smallest AddrX it is possible to use. Align +# functions to 1024 to make it easier to interpet offsets. + + .cfi_sections .sframe + + .align 1024 +fde0_uses_addr1: +# CHECK: FuncDescEntry [0] { +# CHECK: Start FRE Offset: 0x0 +# CHECK-NEXT: Num FREs: 2 +# CHECK: FRE Type: Addr1 (0x0) + .cfi_startproc +# CHECK: Frame Row Entry { +# CHECK-NEXT: Start Address: 0x0 +# CHECK-NEXT: Return Address Signed: No +# CHECK-NEXT: Offset Size: B1 (0x0) +# CHECK-NEXT: Base Register: SP (0x1) +# CHECK-NEXT: CFA Offset: 8 +# CHECK-NEXT: RA Offset: -8 +# CHECK-NEXT: } + .fill 0xFF + .cfi_def_cfa_offset 16 +# CHECK-NEXT: Frame Row Entry { +# CHECK-NEXT: Start Address: 0xFF +# CHECK-NEXT: Return Address Signed: No +# CHECK-NEXT: Offset Size: B1 (0x0) +# CHECK-NEXT: Base Register: SP (0x1) +# CHECK-NEXT: CFA Offset: 16 +# CHECK-NEXT: RA Offset: -8 + nop + .cfi_endproc + + .align 1024 +fde1_uses_addr2: +# CHECK: FuncDescEntry [1] { +# CHECK: Start FRE Offset: 0x6 +# CHECK-NEXT: Num FREs: 2 +# CHECK: FRE Type: Addr2 (0x1) + .cfi_startproc +# CHECK: Frame Row Entry { +# CHECK-NEXT: Start Address: 0x400 + .fill 0xFF + 1 + .cfi_def_cfa_offset 16 +# CHECK: Frame Row Entry { +# CHECK-NEXT: Start Address: 0x500 + .cfi_endproc + +.align 1024 +fde2_uses_addr2: +# CHECK: FuncDescEntry [2] { +# CHECK: Start FRE Offset: 0xE +# CHECK-NEXT: Num FREs: 2 +# CHECK: FRE Type: Addr2 (0x1) + .cfi_startproc +# CHECK: Frame Row Entry { +# CHECK-NEXT: Start Address: 0x800 + .fill 0xFFFF + .cfi_def_cfa_offset 16 +# CHECK: Frame Row Entry { +# CHECK-NEXT: Start Address: 0x107FF + nop + .cfi_endproc + + .align 1024 +fde3_uses_addr4: +# CHECK: FuncDescEntry [3] { +# CHECK: Start FRE Offset: 0x16 +# CHECK-NEXT: Num FREs: 2 +# CHECK: FRE Type: Addr4 (0x2) + .cfi_startproc +# CHECK: Frame Row Entry { +# CHECK-NEXT: Start Address: 0x10800 + .fill 0xFFFF + 1 +# CHECK: Frame Row Entry { +# CHECK-NEXT: Start Address: 0x20800 + .cfi_def_cfa_offset 16 + nop + .cfi_endproc + diff --git a/llvm/test/MC/ELF/cfi-sframe-fre-cases.s b/llvm/test/MC/ELF/cfi-sframe-fre-cases.s new file mode 100644 index 0000000000000..6d9e8c1b6480f --- /dev/null +++ b/llvm/test/MC/ELF/cfi-sframe-fre-cases.s @@ -0,0 +1,113 @@ +# RUN: llvm-mc --filetype=obj --gsframe -triple x86_64 %s -o %t.o +# RUN: llvm-readelf --sframe %t.o | FileCheck %s + +# Tests selection for the proper FRE::BX encoding at the boundaries +# between int8_t, int16_t, and int32_t. Ensures the largest offset +# between CFA, RA, and FP governs. Align functions to 1024 to make it +# easier to interpet offsets. Some directives require alignment, so +# it isn't always possible to test exact boundaries. + +# Also, check that irrelevant cfi directives don't create new fres, +# or affect the current ones. Checking the Start Address ensures that +# the proper FRE gets the proper checks. Using .long makes addresses +# architecture independent. + + .align 1024 +fde4_fre_offset_sizes: +# CHECK: FuncDescEntry [0] { +# CHECK: Start FRE Offset: 0 +# CHECK: FRE Type: Addr1 (0x0) + .cfi_startproc +# CHECK: Frame Row Entry { +# CHECK-NEXT: Start Address: 0x0 +# CHECK-NEXT: Return Address Signed: No +# CHECK-NEXT: Offset Size: B1 (0x0) +# CHECK-NEXT: Base Register: SP (0x1) +# CHECK-NEXT: CFA Offset: 8 +# CHECK-NEXT: RA Offset: -8 + .long 0 +# Uninteresting register no new fre, no effect on cfa + .cfi_offset 0, 8 + .long 0 + .cfi_def_cfa_offset 0x78 +# CHECK: Frame Row Entry { +# CHECK-NEXT: Start Address: 0x8 +# CHECK-NEXT: Return Address Signed: No +# CHECK-NEXT: Offset Size: B1 (0x0) +# CHECK-NEXT: Base Register: SP (0x1) +# CHECK-NEXT: CFA Offset: 120 +# CHECK-NEXT: RA Offset: -8 + .long 0 +# Uninteresting register no new fre, no effect on cfa + .cfi_rel_offset 1, 8 + .long 0 + .cfi_def_cfa_offset 0x80 +# CHECK: Frame Row Entry { +# CHECK-NEXT: Start Address: 0x10 +# CHECK-NEXT: Return Address Signed: No +# CHECK-NEXT: Offset Size: B2 (0x1) +# CHECK-NEXT: Base Register: SP (0x1) +# CHECK-NEXT: CFA Offset: 128 +# CHECK-NEXT: RA Offset: -8 + .long 0 +# Uninteresting register no new fre, no effect on cfa + .cfi_val_offset 1, 8 + .long 0 + .cfi_def_cfa_offset 0x7FFF +# CHECK: Frame Row Entry { +# CHECK-NEXT: Start Address: 0x18 +# CHECK-NEXT: Return Address Signed: No +# CHECK-NEXT: Offset Size: B2 (0x1) +# CHECK-NEXT: Base Register: SP (0x1) +# CHECK-NEXT: CFA Offset: 32767 +# CHECK-NEXT: RA Offset: -8 + .long 0 + .cfi_def_cfa_offset 0x8000 +# CHECK: Frame Row Entry { +# CHECK-NEXT: Start Address: 0x1C +# CHECK-NEXT: Return Address Signed: No +# CHECK-NEXT: Offset Size: B4 (0x2) +# CHECK-NEXT: Base Register: SP (0x1) +# CHECK-NEXT: CFA Offset: 32768 +# CHECK-NEXT: RA Offset: -8 + .long 0 + .cfi_def_cfa_offset 0x8 +# CHECK: Frame Row Entry { +# CHECK-NEXT: Start Address: 0x20 +# CHECK-NEXT: Return Address Signed: No +# CHECK-NEXT: Offset Size: B1 (0x0) +# CHECK-NEXT: Base Register: SP (0x1) +# CHECK-NEXT: CFA Offset: 8 +# CHECK-NEXT: RA Offset: -8 + .long 0 + .cfi_adjust_cfa_offset 0x8 +# CHECK: Frame Row Entry { +# CHECK-NEXT: Start Address: 0x24 +# CHECK-NEXT: Return Address Signed: No +# CHECK-NEXT: Offset Size: B1 (0x0) +# CHECK-NEXT: Base Register: SP (0x1) +# CHECK-NEXT: CFA Offset: 16 +# CHECK-NEXT: RA Offset: -8 + .long 0 + .cfi_def_cfa_register 6 # switch to fp +# CHECK: Frame Row Entry { +# CHECK-NEXT: Start Address: 0x28 +# CHECK-NEXT: Return Address Signed: No +# CHECK-NEXT: Offset Size: B1 (0x0) +# CHECK-NEXT: Base Register: FP (0x0) +# CHECK-NEXT: CFA Offset: 16 +# CHECK-NEXT: RA Offset: -8 + .long 0 + .cfi_offset 7, 32 + # sp not the cfa but with large offset still changes encoding. + .cfi_offset 6, 0x7FF8 +# CHECK: Frame Row Entry { +# CHECK-NEXT: Start Address: 0x2C +# CHECK-NEXT: Return Address Signed: No +# CHECK-NEXT: Offset Size: B2 (0x1) +# CHECK-NEXT: Base Register: FP (0x0) +# CHECK-NEXT: CFA Offset: 16 +# CHECK-NEXT: RA Offset: -8 +# CHECK-NEXT: FP Offset: 32760 + .long 0 + .cfi_endproc diff --git a/llvm/test/MC/ELF/cfi-sframe.s b/llvm/test/MC/ELF/cfi-sframe.s index ecf77bc3ea6b3..c7116bc874423 100644 --- a/llvm/test/MC/ELF/cfi-sframe.s +++ b/llvm/test/MC/ELF/cfi-sframe.s @@ -1,82 +1,85 @@ -// TODO: Add other architectures as they gain sframe support -// REQUIRES: x86-registered-target -// RUN: llvm-mc --assemble --filetype=obj --gsframe -triple x86_64 %s -o %t.o -// RUN: llvm-readelf --sframe %t.o | FileCheck %s +# RUN: llvm-mc --filetype=obj --gsframe -triple x86_64 %s -o %t.o +# RUN: llvm-readelf --sframe %t.o | FileCheck %s .cfi_sections .sframe -f1: - .cfi_startproc // FRE 0 +f0: + .cfi_startproc # FRE 0 nop - .cfi_def_cfa_offset 16 // FRE 1 - .cfi_def_cfa_offset 8 // location didn't change. No new FRE, but new offset. + .cfi_def_cfa_offset 16 # FRE 1 + .cfi_def_cfa_offset 8 # location didn't change. No new FRE, but new offset. nop - .cfi_def_cfa_offset 8 // offset didn't change. No new FRE. + .cfi_def_cfa_offset 8 # offset didn't change. No new FRE. nop - .cfi_def_cfa_offset 16 // FRE 2. new location, new offset. + .cfi_def_cfa_offset 16 # FRE 2. new location, new offset. nop - .cfi_register 0, 1 // Uninteresting register. No new FRE. + .cfi_register 0, 1 # Uninteresting register. No new FRE. nop .cfi_endproc -f2: +f1: .cfi_startproc nop nop .cfi_endproc -// CHECK: SFrame section '.sframe' { -// CHECK-NEXT: Header { -// CHECK-NEXT: Magic: 0xDEE2 -// CHECK-NEXT: Version: V2 (0x2) -// CHECK-NEXT: Flags [ (0x4) -// CHECK: ABI: AMD64EndianLittle (0x3) -// CHECK-NEXT: CFA fixed FP offset (unused): 0 -// CHECK-NEXT: CFA fixed RA offset: 0 -// CHECK-NEXT: Auxiliary header length: 0 -// CHECK-NEXT: Num FDEs: 2 -// CHECK-NEXT: Num FREs: 0 -// CHECK-NEXT: FRE subsection length: 0 -// CHECK-NEXT: FDE subsection offset: 0 -// CHECK-NEXT: FRE subsection offset: 40 -// CHECK: Function Index [ -// CHECK-NEXT: FuncDescEntry [0] { -// CHECK-NEXT: PC { -// CHECK-NEXT: Relocation: {{.*}}PC32{{.*}} -// CHECK-NEXT: Symbol Name: .text -// CHECK-NEXT: Start Address: {{.*}} -// CHECK-NEXT: } -// CHECK-NEXT: Size: 0x5 -// CHECK-NEXT: Start FRE Offset: 0x0 -// CHECK-NEXT: Num FREs: 0 -// CHECK-NEXT: Info { -// CHECK-NEXT: FRE Type: Addr1 (0x0) -// CHECK-NEXT: FDE Type: PCInc (0x0) -// CHECK-NEXT: Raw: 0x0 -// CHECK-NEXT: } -// CHECK-NEXT: Repetitive block size (unused): 0x0 -// CHECK-NEXT: Padding2: 0x0 -// CHECK-NEXT: FREs [ -// CHECK-NEXT: ] -// CHECK-NEXT: } -// CHECK-NEXT: FuncDescEntry [1] { -// CHECK-NEXT: PC { -// CHECK-NEXT: Relocation: {{.*}}PC32{{.*}} -// CHECK-NEXT: Symbol Name: .text -// CHECK-NEXT: Start Address: {{.*}} -// CHECK-NEXT: } -// CHECK-NEXT: Size: 0x2 -// CHECK-NEXT: Start FRE Offset: 0x0 -// CHECK-NEXT: Num FREs: 0 -// CHECK-NEXT: Info { -// CHECK-NEXT: FRE Type: Addr1 (0x0) -// CHECK-NEXT: FDE Type: PCInc (0x0) -// CHECK-NEXT: Raw: 0x0 -// CHECK-NEXT: } -// CHECK-NEXT: Repetitive block size (unused): 0x0 -// CHECK-NEXT: Padding2: 0x0 -// CHECK-NEXT: FREs [ -// CHECK-NEXT: ] -// CHECK-NEXT: } -// CHECK-NEXT: ] -// CHECK-NEXT: } +f2: + .cfi_startproc + .cfi_endproc + +f3: + .cfi_startproc simple + .cfi_endproc + +# CHECK: SFrame section '.sframe' { +# CHECK-NEXT: Header { +# CHECK-NEXT: Magic: 0xDEE2 +# CHECK-NEXT: Version: V2 (0x2) +# CHECK-NEXT: Flags [ (0x4) +# CHECK: ABI: AMD64EndianLittle (0x3) +# CHECK-NEXT: CFA fixed FP offset (unused): 0 +# CHECK-NEXT: CFA fixed RA offset: -8 +# CHECK-NEXT: Auxiliary header length: 0 +# CHECK-NEXT: Num FDEs: 2 +# CHECK-NEXT: Num FREs: 4 +# CHECK-NEXT: FRE subsection length: 12 +# CHECK-NEXT: FDE subsection offset: 0 +# CHECK-NEXT: FRE subsection offset: 40 +# CHECK: Function Index [ +# CHECK-NEXT: FuncDescEntry [0] { +# CHECK-NEXT: PC { +# CHECK-NEXT: Relocation: {{.*}}PC32{{.*}} +# CHECK-NEXT: Symbol Name: .text +# CHECK-NEXT: Start Address: {{.*}} +# CHECK-NEXT: } +# CHECK-NEXT: Size: 0x5 +# CHECK-NEXT: Start FRE Offset: 0x0 +# CHECK-NEXT: Num FREs: 3 +# CHECK-NEXT: Info { +# CHECK-NEXT: FRE Type: Addr1 (0x0) +# CHECK-NEXT: FDE Type: PCInc (0x0) +# CHECK-NEXT: Raw: 0x0 +# CHECK-NEXT: } +# CHECK-NEXT: Repetitive block size (unused): 0x0 +# CHECK-NEXT: Padding2: 0x0 + +# Contents of FREs are tested elsewhere + +# CHECK: FuncDescEntry [1] { +# CHECK-NEXT: PC { +# CHECK-NEXT: Relocation: {{.*}}PC32{{.*}} +# CHECK-NEXT: Symbol Name: .text +# CHECK-NEXT: Start Address: {{.*}} +# CHECK-NEXT: } +# CHECK-NEXT: Size: 0x2 +# CHECK-NEXT: Start FRE Offset: 0x9 +# CHECK-NEXT: Num FREs: 1 +# CHECK-NEXT: Info { +# CHECK-NEXT: FRE Type: Addr1 (0x0) +# CHECK-NEXT: FDE Type: PCInc (0x0) +# CHECK-NEXT: Raw: 0x0 +# CHECK-NEXT: } +# CHECK-NEXT: Repetitive block size (unused): 0x0 +# CHECK-NEXT: Padding2: 0x0 + +