| //===- lib/MC/MCSFrame.cpp - MCSFrame implementation ----------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/MC/MCSFrame.h" |
| #include "llvm/BinaryFormat/SFrame.h" |
| #include "llvm/DebugInfo/DWARF/LowLevel/DWARFCFIProgram.h" |
| #include "llvm/DebugInfo/DWARF/LowLevel/DWARFDataExtractorSimple.h" |
| #include "llvm/MC/MCAsmInfo.h" |
| #include "llvm/MC/MCContext.h" |
| #include "llvm/MC/MCObjectFileInfo.h" |
| #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; |
| using namespace sframe; |
| |
| namespace { |
| |
| // High-level structure to track info needed to emit a |
| // sframe_frame_row_entry_addrX. On disk these have both a fixed portion of type |
| // sframe_frame_row_entry_addrX and trailing data of X * S bytes, where X is the |
| // datum size, and S is 1, 2, or 3 depending on which of CFA, SP, and FP are |
| // being tracked. |
| struct SFrameFRE { |
| // An FRE describes how to find the registers when the PC is at this |
| // Label from function start. |
| const MCSymbol *Label = nullptr; |
| size_t CFAOffset = 0; |
| size_t FPOffset = 0; |
| size_t RAOffset = 0; |
| FREInfo<endianness::native> Info; |
| bool CFARegSet = false; |
| |
| 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 |
| // and its associated FREs. |
| struct SFrameFDE { |
| // Reference to the original dwarf frame to avoid copying. |
| const MCDwarfFrameInfo &DFrame; |
| // Label where this FDE's FREs start. |
| MCSymbol *FREStart; |
| // Frag where this FDE is emitted. |
| MCFragment *Frag; |
| // Unwinding fres |
| SmallVector<SFrameFRE> FREs; |
| // .cfi_remember_state stack |
| SmallVector<SFrameFRE> SaveState; |
| |
| SFrameFDE(const MCDwarfFrameInfo &DF, MCSymbol *FRES) |
| : DFrame(DF), FREStart(FRES), Frag(nullptr) {} |
| |
| void emit(MCObjectStreamer &S, const MCSymbol *FRESubSectionStart) { |
| MCContext &C = S.getContext(); |
| |
| // sfde_func_start_address |
| const MCExpr *V = C.getAsmInfo()->getExprForFDESymbol( |
| &(*DFrame.Begin), C.getObjectFileInfo()->getFDEEncoding(), S); |
| S.emitValue(V, sizeof(int32_t)); |
| |
| // sfde_func_size |
| S.emitAbsoluteSymbolDiff(DFrame.End, DFrame.Begin, sizeof(uint32_t)); |
| |
| // sfde_func_start_fre_off |
| auto *F = S.getCurrentFragment(); |
| const MCExpr *Diff = MCBinaryExpr::createSub( |
| MCSymbolRefExpr::create(FREStart, C), |
| MCSymbolRefExpr::create(FRESubSectionStart, C), C); |
| |
| F->addFixup(MCFixup::create(F->getContents().size(), Diff, |
| MCFixup::getDataKindForSize(4))); |
| S.emitInt32(0); |
| |
| // sfde_func_num_fres |
| S.emitInt32(FREs.size()); |
| |
| // sfde_func_info word |
| |
| // 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); |
| |
| // sfde_func_padding2 |
| S.emitInt16(0); |
| } |
| }; |
| |
| // Emitting these field-by-field, instead of constructing the actual structures |
| // lets Streamer do target endian-fixups for free. |
| |
| class SFrameEmitterImpl { |
| MCObjectStreamer &Streamer; |
| SmallVector<SFrameFDE> 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. TODO: These should be retrieved from |
| // the target. |
| unsigned SPReg; |
| unsigned FPReg; |
| unsigned RAReg; |
| int8_t FixedRAOffset; |
| MCSymbol *FDESubSectionStart; |
| MCSymbol *FRESubSectionStart; |
| MCSymbol *FRESubSectionEnd; |
| |
| bool setCFARegister(SFrameFRE &FRE, const MCCFIInstruction &I) { |
| if (I.getRegister() == SPReg) { |
| FRE.CFARegSet = true; |
| FRE.Info.setBaseRegister(BaseReg::SP); |
| return true; |
| } |
| if (I.getRegister() == FPReg) { |
| FRE.CFARegSet = true; |
| FRE.Info.setBaseRegister(BaseReg::FP); |
| return true; |
| } |
| Streamer.getContext().reportWarning( |
| I.getLoc(), "canonical Frame Address not in stack- or frame-pointer. " |
| "Omitting SFrame unwind info for this function"); |
| return false; |
| } |
| |
| bool setCFAOffset(SFrameFRE &FRE, SMLoc Loc, size_t Offset) { |
| if (!FRE.CFARegSet) { |
| Streamer.getContext().reportWarning( |
| Loc, "adjusting CFA offset without a base register. " |
| "Omitting SFrame unwind info for this function"); |
| return false; |
| } |
| FRE.CFAOffset = Offset; |
| return true; |
| } |
| |
| // Technically, the escape data could be anything, but it is commonly a dwarf |
| // CFI program. Even then, it could contain an arbitrarily complicated Dwarf |
| // expression. Following gnu-gas, look for certain common cases that could |
| // invalidate an FDE, emit a warning for those sequences, and don't generate |
| // an FDE in those cases. Allow any that are known safe. It is likely that |
| // more thorough test cases could refine this code, but it handles the most |
| // important ones compatibly with gas. |
| // Returns true if the CFI escape sequence is safe for sframes. |
| bool isCFIEscapeSafe(SFrameFDE &FDE, const SFrameFRE &FRE, |
| const MCCFIInstruction &CFI) { |
| const MCAsmInfo *AI = Streamer.getContext().getAsmInfo(); |
| DWARFDataExtractorSimple data(CFI.getValues(), AI->isLittleEndian(), |
| AI->getCodePointerSize()); |
| |
| // Normally, both alignment factors are extracted from the enclosing Dwarf |
| // FDE or CIE. We don't have one here. Alignments are used for scaling |
| // factors for ops like CFA_def_cfa_offset_sf. But this particular function |
| // is only interested in registers. |
| dwarf::CFIProgram P(/*CodeAlignmentFactor=*/1, |
| /*DataAlignmentFactor=*/1, |
| Streamer.getContext().getTargetTriple().getArch()); |
| uint64_t Offset = 0; |
| if (P.parse(data, &Offset, CFI.getValues().size())) { |
| // Not a parsable dwarf expression. Assume the worst. |
| Streamer.getContext().reportWarning( |
| CFI.getLoc(), |
| "skipping SFrame FDE; .cfi_escape with unknown effects"); |
| return false; |
| } |
| |
| // This loop deals with dwarf::CFIProgram::Instructions. Everywhere else |
| // this file deals with MCCFIInstructions. |
| for (const dwarf::CFIProgram::Instruction &I : P) { |
| switch (I.Opcode) { |
| case dwarf::DW_CFA_nop: |
| break; |
| case dwarf::DW_CFA_val_offset: { |
| // First argument is a register. Anything that touches CFA, FP, or RA is |
| // a problem, but allow others through. As an even more special case, |
| // allow SP + 0. |
| auto Reg = I.getOperandAsUnsigned(P, 0); |
| // The parser should have failed in this case. |
| assert(Reg && "DW_CFA_val_offset with no register."); |
| bool SPOk = true; |
| if (*Reg == SPReg) { |
| auto Opnd = I.getOperandAsSigned(P, 1); |
| if (!Opnd || *Opnd != 0) |
| SPOk = false; |
| } |
| if (!SPOk || *Reg == RAReg || *Reg == FPReg) { |
| StringRef RN = *Reg == SPReg |
| ? "SP reg " |
| : (*Reg == FPReg ? "FP reg " : "RA reg "); |
| Streamer.getContext().reportWarning( |
| CFI.getLoc(), |
| Twine( |
| "skipping SFrame FDE; .cfi_escape DW_CFA_val_offset with ") + |
| RN + Twine(*Reg)); |
| return false; |
| } |
| } break; |
| case dwarf::DW_CFA_expression: { |
| // First argument is a register. Anything that touches CFA, FP, or RA is |
| // a problem, but allow others through. |
| auto Reg = I.getOperandAsUnsigned(P, 0); |
| if (!Reg) { |
| Streamer.getContext().reportWarning( |
| CFI.getLoc(), |
| "skipping SFrame FDE; .cfi_escape with unknown effects"); |
| return false; |
| } |
| if (*Reg == SPReg || *Reg == RAReg || *Reg == FPReg) { |
| StringRef RN = *Reg == SPReg |
| ? "SP reg " |
| : (*Reg == FPReg ? "FP reg " : "RA reg "); |
| Streamer.getContext().reportWarning( |
| CFI.getLoc(), |
| Twine( |
| "skipping SFrame FDE; .cfi_escape DW_CFA_expression with ") + |
| RN + Twine(*Reg)); |
| return false; |
| } |
| } break; |
| case dwarf::DW_CFA_GNU_args_size: { |
| auto Size = I.getOperandAsSigned(P, 0); |
| // Zero size doesn't affect the cfa. |
| if (Size && *Size == 0) |
| break; |
| if (FRE.Info.getBaseRegister() != BaseReg::FP) { |
| Streamer.getContext().reportWarning( |
| CFI.getLoc(), |
| Twine("skipping SFrame FDE; .cfi_escape DW_CFA_GNU_args_size " |
| "with non frame-pointer CFA")); |
| return false; |
| } |
| } break; |
| // Cases that gas doesn't specially handle. TODO: Some of these could be |
| // analyzed and handled instead of just punting. But these are uncommon, |
| // or should be written as normal cfi directives. Some will need fixes to |
| // the scaling factor. |
| case dwarf::DW_CFA_advance_loc: |
| case dwarf::DW_CFA_offset: |
| case dwarf::DW_CFA_restore: |
| case dwarf::DW_CFA_set_loc: |
| case dwarf::DW_CFA_advance_loc1: |
| case dwarf::DW_CFA_advance_loc2: |
| case dwarf::DW_CFA_advance_loc4: |
| case dwarf::DW_CFA_offset_extended: |
| case dwarf::DW_CFA_restore_extended: |
| case dwarf::DW_CFA_undefined: |
| case dwarf::DW_CFA_same_value: |
| case dwarf::DW_CFA_register: |
| case dwarf::DW_CFA_remember_state: |
| case dwarf::DW_CFA_restore_state: |
| case dwarf::DW_CFA_def_cfa: |
| case dwarf::DW_CFA_def_cfa_register: |
| case dwarf::DW_CFA_def_cfa_offset: |
| case dwarf::DW_CFA_def_cfa_expression: |
| case dwarf::DW_CFA_offset_extended_sf: |
| case dwarf::DW_CFA_def_cfa_sf: |
| case dwarf::DW_CFA_def_cfa_offset_sf: |
| case dwarf::DW_CFA_val_offset_sf: |
| case dwarf::DW_CFA_val_expression: |
| case dwarf::DW_CFA_MIPS_advance_loc8: |
| case dwarf::DW_CFA_AARCH64_negate_ra_state_with_pc: |
| case dwarf::DW_CFA_AARCH64_negate_ra_state: |
| case dwarf::DW_CFA_LLVM_def_aspace_cfa: |
| case dwarf::DW_CFA_LLVM_def_aspace_cfa_sf: |
| Streamer.getContext().reportWarning( |
| CFI.getLoc(), "skipping SFrame FDE; .cfi_escape " |
| "CFA expression with unknown side effects"); |
| return false; |
| default: |
| // Dwarf expression was only partially valid, and user could have |
| // written anything. |
| Streamer.getContext().reportWarning( |
| CFI.getLoc(), |
| "skipping SFrame FDE; .cfi_escape with unknown effects"); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| // Add the effects of CFI to the current FDE, creating a new FRE when |
| // necessary. Return true if the CFI is representable in the sframe format. |
| bool handleCFI(SFrameFDE &FDE, SFrameFRE &FRE, const MCCFIInstruction &CFI) { |
| switch (CFI.getOperation()) { |
| case MCCFIInstruction::OpDefCfaRegister: |
| return setCFARegister(FRE, CFI); |
| case MCCFIInstruction::OpDefCfa: |
| case MCCFIInstruction::OpLLVMDefAspaceCfa: |
| if (!setCFARegister(FRE, CFI)) |
| return false; |
| return setCFAOffset(FRE, CFI.getLoc(), CFI.getOffset()); |
| case MCCFIInstruction::OpOffset: |
| if (CFI.getRegister() == FPReg) |
| FRE.FPOffset = CFI.getOffset(); |
| else if (CFI.getRegister() == RAReg) |
| FRE.RAOffset = CFI.getOffset(); |
| return true; |
| case MCCFIInstruction::OpRelOffset: |
| if (CFI.getRegister() == FPReg) |
| FRE.FPOffset += CFI.getOffset(); |
| else if (CFI.getRegister() == RAReg) |
| FRE.RAOffset += CFI.getOffset(); |
| return true; |
| case MCCFIInstruction::OpDefCfaOffset: |
| return setCFAOffset(FRE, CFI.getLoc(), CFI.getOffset()); |
| case MCCFIInstruction::OpAdjustCfaOffset: |
| return setCFAOffset(FRE, CFI.getLoc(), FRE.CFAOffset + CFI.getOffset()); |
| case MCCFIInstruction::OpRememberState: |
| if (FDE.FREs.size() == 1) { |
| // Error for gas compatibility: If the initial FRE isn't complete, |
| // then any state is incomplete. FIXME: Dwarf doesn't error here. |
| // Why should sframe? |
| Streamer.getContext().reportWarning( |
| CFI.getLoc(), "skipping SFrame FDE; .cfi_remember_state without " |
| "prior SFrame FRE state"); |
| return false; |
| } |
| FDE.SaveState.push_back(FRE); |
| return true; |
| case MCCFIInstruction::OpRestore: |
| // The first FRE generated has the original state. |
| if (CFI.getRegister() == FPReg) |
| FRE.FPOffset = FDE.FREs.front().FPOffset; |
| else if (CFI.getRegister() == RAReg) |
| FRE.RAOffset = FDE.FREs.front().RAOffset; |
| return true; |
| case MCCFIInstruction::OpRestoreState: |
| // The cfi parser will have caught unbalanced directives earlier, so a |
| // mismatch here is an implementation error. |
| assert(!FDE.SaveState.empty() && |
| "cfi_restore_state without cfi_save_state"); |
| FRE = FDE.SaveState.pop_back_val(); |
| return true; |
| case MCCFIInstruction::OpEscape: |
| // This is a string of bytes that contains an arbitrary dwarf-expression |
| // that may or may not affect unwind info. |
| return isCFIEscapeSafe(FDE, FRE, CFI); |
| default: |
| // Instructions that don't affect the CFA, RA, and FP can be safely |
| // ignored. |
| return true; |
| } |
| } |
| |
| public: |
| SFrameEmitterImpl(MCObjectStreamer &Streamer) |
| : Streamer(Streamer), TotalFREs(0) { |
| assert(Streamer.getContext() |
| .getObjectFileInfo() |
| ->getSFrameABIArch() |
| .has_value()); |
| FDEs.reserve(Streamer.getDwarfFrameInfos().size()); |
| SFrameABI = *Streamer.getContext().getObjectFileInfo()->getSFrameABIArch(); |
| switch (SFrameABI) { |
| case ABI::AArch64EndianBig: |
| case ABI::AArch64EndianLittle: |
| SPReg = 31; |
| RAReg = 29; |
| FPReg = 30; |
| FixedRAOffset = 0; |
| break; |
| case ABI::AMD64EndianLittle: |
| SPReg = 7; |
| // RARegister untracked in this abi. Value chosen to match |
| // MCDwarfFrameInfo constructor. |
| RAReg = static_cast<unsigned>(INT_MAX); |
| FPReg = 6; |
| FixedRAOffset = -8; |
| break; |
| } |
| |
| FDESubSectionStart = Streamer.getContext().createTempSymbol(); |
| FRESubSectionStart = Streamer.getContext().createTempSymbol(); |
| FRESubSectionEnd = Streamer.getContext().createTempSymbol(); |
| } |
| |
| bool atSameLocation(const MCSymbol *Left, const MCSymbol *Right) { |
| return Left != nullptr && Right != nullptr && |
| Left->getFragment() == Right->getFragment() && |
| Left->getOffset() == Right->getOffset(); |
| } |
| |
| bool equalIgnoringLocation(const SFrameFRE &Left, const SFrameFRE &Right) { |
| return Left.CFAOffset == Right.CFAOffset && |
| Left.FPOffset == Right.FPOffset && Left.RAOffset == Right.RAOffset && |
| 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 |
| // MCObjectStreamer doesn't emit an MCCFIInstruction for that. It just |
| // sets the DF.RAReg. |
| // FIXME: This also prevents providing a proper location for the error. |
| // LLVM doesn't change the return column itself, so this was |
| // hand-written assembly. |
| if (DF.RAReg != RAReg) { |
| Streamer.getContext().reportWarning( |
| SMLoc(), "non-default RA register in .cfi_return_column " + |
| Twine(DF.RAReg) + |
| ". Omitting SFrame unwind info for this function"); |
| Valid = false; |
| } |
| MCSymbol *LastLabel = DF.Begin; |
| SFrameFRE BaseFRE(LastLabel); |
| if (!DF.IsSimple) { |
| for (const auto &CFI : |
| Streamer.getContext().getAsmInfo()->getInitialFrameState()) |
| if (!handleCFI(FDE, BaseFRE, CFI)) |
| Valid = false; |
| } |
| FDE.FREs.push_back(BaseFRE); |
| |
| for (const auto &CFI : DF.Instructions) { |
| // Instructions from InitialFrameState may not have a label, but if these |
| // instructions don't, then they are in dead code or otherwise unused. |
| // TODO: This check follows MCDwarf.cpp |
| // FrameEmitterImplementation::emitCFIInstructions, but nothing in the |
| // testsuite triggers it. We should see if it can be removed in both |
| // places, or alternately, add a test to exercise it. |
| auto *L = CFI.getLabel(); |
| if (L && !L->isDefined()) |
| continue; |
| |
| SFrameFRE FRE = FDE.FREs.back(); |
| if (!handleCFI(FDE, FRE, CFI)) |
| Valid = false; |
| |
| // If nothing relevant but the location changed, don't add the FRE. |
| if (equalIgnoringLocation(FRE, FDE.FREs.back())) |
| continue; |
| |
| // If the location stayed the same, then update the current |
| // row. Otherwise, add a new one. |
| if (atSameLocation(LastLabel, L)) |
| FDE.FREs.back() = FRE; |
| else { |
| FDE.FREs.push_back(FRE); |
| FDE.FREs.back().Label = L; |
| LastLabel = L; |
| } |
| } |
| |
| if (Valid) { |
| FDEs.push_back(FDE); |
| TotalFREs += FDE.FREs.size(); |
| } |
| } |
| |
| void emitPreamble() { |
| Streamer.emitInt16(Magic); |
| Streamer.emitInt8(static_cast<uint8_t>(Version::V2)); |
| Streamer.emitInt8(static_cast<uint8_t>(Flags::FDEFuncStartPCRel)); |
| } |
| |
| void emitHeader() { |
| emitPreamble(); |
| // sfh_abi_arch |
| Streamer.emitInt8(static_cast<uint8_t>(SFrameABI)); |
| // sfh_cfa_fixed_fp_offset |
| Streamer.emitInt8(0); |
| // sfh_cfa_fixed_ra_offset |
| Streamer.emitInt8(FixedRAOffset); |
| // sfh_auxhdr_len |
| Streamer.emitInt8(0); |
| // shf_num_fdes |
| Streamer.emitInt32(FDEs.size()); |
| // shf_num_fres |
| Streamer.emitInt32(TotalFREs); |
| |
| // shf_fre_len |
| Streamer.emitAbsoluteSymbolDiff(FRESubSectionEnd, FRESubSectionStart, |
| sizeof(int32_t)); |
| // shf_fdeoff. With no sfh_auxhdr, these immediately follow this header. |
| Streamer.emitInt32(0); |
| // shf_freoff |
| Streamer.emitInt32(FDEs.size() * |
| sizeof(sframe::FuncDescEntry<endianness::native>)); |
| } |
| |
| void emitFDEs() { |
| Streamer.emitLabel(FDESubSectionStart); |
| for (auto &FDE : FDEs) { |
| FDE.emit(Streamer, FRESubSectionStart); |
| } |
| } |
| |
| void emitFREs() { |
| Streamer.emitLabel(FRESubSectionStart); |
| for (auto &FDE : FDEs) { |
| Streamer.emitLabel(FDE.FREStart); |
| for (auto &FRE : FDE.FREs) |
| FRE.emit(Streamer, FDE.DFrame.Begin, FDE.Frag); |
| } |
| Streamer.emitLabel(FRESubSectionEnd); |
| } |
| }; |
| |
| } // end anonymous namespace |
| |
| void MCSFrameEmitter::emit(MCObjectStreamer &Streamer) { |
| MCContext &Context = Streamer.getContext(); |
| // If this target doesn't support sframes, return now. Gas doesn't warn in |
| // this case, but if we want to, it should be done at option-parsing time, |
| // rather than here. |
| if (!Streamer.getContext() |
| .getObjectFileInfo() |
| ->getSFrameABIArch() |
| .has_value()) |
| return; |
| |
| SFrameEmitterImpl Emitter(Streamer); |
| ArrayRef<MCDwarfFrameInfo> FrameArray = Streamer.getDwarfFrameInfos(); |
| |
| // Both the header itself and the FDEs include various offsets and counts. |
| // Therefore, all of this must be precomputed. |
| for (const auto &DFrame : FrameArray) |
| Emitter.buildSFDE(DFrame); |
| |
| MCSection *Section = Context.getObjectFileInfo()->getSFrameSection(); |
| // Not strictly necessary, but gas always aligns to 8, so match that. |
| Section->ensureMinAlignment(Align(8)); |
| Streamer.switchSection(Section); |
| MCSymbol *SectionStart = Context.createTempSymbol(); |
| Streamer.emitLabel(SectionStart); |
| Emitter.emitHeader(); |
| Emitter.emitFDEs(); |
| Emitter.emitFREs(); |
| } |
| |
| void MCSFrameEmitter::encodeFuncOffset(MCContext &C, uint64_t Offset, |
| SmallVectorImpl<char> &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<endianness::native> 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<endianness::native> 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<uint8_t>(Out, Offset, E); |
| break; |
| case FREType::Addr2: |
| assert(isUInt<16>(Offset) && "Miscalculated Sframe FREType"); |
| support::endian::write<uint16_t>(Out, Offset, E); |
| break; |
| case FREType::Addr4: |
| assert(isUInt<32>(Offset) && "Miscalculated Sframe FREType"); |
| support::endian::write<uint32_t>(Out, Offset, E); |
| break; |
| } |
| } |