1//===- lib/MC/MCSFrame.cpp - MCSFrame implementation ----------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "llvm/MC/MCSFrame.h"
10#include "llvm/BinaryFormat/SFrame.h"
11#include "llvm/DebugInfo/DWARF/LowLevel/DWARFCFIProgram.h"
12#include "llvm/DebugInfo/DWARF/LowLevel/DWARFDataExtractorSimple.h"
13#include "llvm/MC/MCAsmInfo.h"
14#include "llvm/MC/MCContext.h"
15#include "llvm/MC/MCObjectFileInfo.h"
16#include "llvm/MC/MCObjectStreamer.h"
17#include "llvm/MC/MCSection.h"
18#include "llvm/MC/MCSymbol.h"
19#include "llvm/Support/Endian.h"
20#include "llvm/Support/EndianStream.h"
21
22using namespace llvm;
23using namespace sframe;
24
25namespace {
26
27// High-level structure to track info needed to emit a
28// sframe_frame_row_entry_addrX. On disk these have both a fixed portion of type
29// sframe_frame_row_entry_addrX and trailing data of X * S bytes, where X is the
30// datum size, and S is 1, 2, or 3 depending on which of CFA, SP, and FP are
31// being tracked.
32struct SFrameFRE {
33 // An FRE describes how to find the registers when the PC is at this
34 // Label from function start.
35 const MCSymbol *Label = nullptr;
36 size_t CFAOffset = 0;
37 size_t FPOffset = 0;
38 size_t RAOffset = 0;
39 FREInfo<endianness::native> Info;
40 bool CFARegSet = false;
41
42 SFrameFRE(const MCSymbol *Start) : Label(Start) { Info.Info = 0; }
43
44 void emitOffset(MCObjectStreamer &S, FREOffset OffsetSize, size_t Offset) {
45 switch (OffsetSize) {
46 case (FREOffset::B1):
47 S.emitInt8(Value: Offset);
48 return;
49 case (FREOffset::B2):
50 S.emitInt16(Value: Offset);
51 return;
52 case (FREOffset::B4):
53 S.emitInt32(Value: Offset);
54 return;
55 }
56 }
57
58 void emit(MCObjectStreamer &S, const MCSymbol *FuncBegin,
59 MCFragment *FDEFrag) {
60 S.emitSFrameCalculateFuncOffset(FunCabsel: FuncBegin, FREBegin: Label, FDEFrag, Loc: SMLoc());
61
62 // fre_cfa_base_reg_id already set during parsing
63
64 // fre_offset_count
65 unsigned RegsTracked = 1; // always track the cfa.
66 if (FPOffset != 0)
67 ++RegsTracked;
68 if (RAOffset != 0)
69 ++RegsTracked;
70 Info.setOffsetCount(RegsTracked);
71
72 // fre_offset_size
73 if (isInt<8>(x: CFAOffset) && isInt<8>(x: FPOffset) && isInt<8>(x: RAOffset))
74 Info.setOffsetSize(FREOffset::B1);
75 else if (isInt<16>(x: CFAOffset) && isInt<16>(x: FPOffset) && isInt<16>(x: RAOffset))
76 Info.setOffsetSize(FREOffset::B2);
77 else {
78 assert(isInt<32>(CFAOffset) && isInt<32>(FPOffset) &&
79 isInt<32>(RAOffset) && "Offset too big for sframe");
80 Info.setOffsetSize(FREOffset::B4);
81 }
82
83 // No support for fre_mangled_ra_p yet.
84 Info.setReturnAddressSigned(false);
85
86 // sframe_fre_info_word
87 S.emitInt8(Value: Info.getFREInfo());
88
89 // FRE Offsets
90 [[maybe_unused]] unsigned OffsetsEmitted = 1;
91 emitOffset(S, OffsetSize: Info.getOffsetSize(), Offset: CFAOffset);
92 if (FPOffset) {
93 ++OffsetsEmitted;
94 emitOffset(S, OffsetSize: Info.getOffsetSize(), Offset: FPOffset);
95 }
96 if (RAOffset) {
97 ++OffsetsEmitted;
98 emitOffset(S, OffsetSize: Info.getOffsetSize(), Offset: RAOffset);
99 }
100 assert(OffsetsEmitted == RegsTracked &&
101 "Didn't emit the right number of offsets");
102 }
103};
104
105// High-level structure to track info needed to emit a sframe_func_desc_entry
106// and its associated FREs.
107struct SFrameFDE {
108 // Reference to the original dwarf frame to avoid copying.
109 const MCDwarfFrameInfo &DFrame;
110 // Label where this FDE's FREs start.
111 MCSymbol *FREStart;
112 // Frag where this FDE is emitted.
113 MCFragment *Frag;
114 // Unwinding fres
115 SmallVector<SFrameFRE> FREs;
116 // .cfi_remember_state stack
117 SmallVector<SFrameFRE> SaveState;
118
119 SFrameFDE(const MCDwarfFrameInfo &DF, MCSymbol *FRES)
120 : DFrame(DF), FREStart(FRES), Frag(nullptr) {}
121
122 void emit(MCObjectStreamer &S, const MCSymbol *FRESubSectionStart) {
123 MCContext &C = S.getContext();
124
125 // sfde_func_start_address
126 const MCExpr *V = C.getAsmInfo().getExprForFDESymbol(
127 Sym: &(*DFrame.Begin), Encoding: C.getObjectFileInfo()->getFDEEncoding(), Streamer&: S);
128 S.emitValue(Value: V, Size: sizeof(int32_t));
129
130 // sfde_func_size
131 S.emitAbsoluteSymbolDiff(Hi: DFrame.End, Lo: DFrame.Begin, Size: sizeof(uint32_t));
132
133 // sfde_func_start_fre_off
134 auto *F = S.getCurrentFragment();
135 const MCExpr *Diff = MCBinaryExpr::createSub(
136 LHS: MCSymbolRefExpr::create(Symbol: FREStart, Ctx&: C),
137 RHS: MCSymbolRefExpr::create(Symbol: FRESubSectionStart, Ctx&: C), Ctx&: C);
138
139 F->addFixup(Fixup: MCFixup::create(Offset: F->getContents().size(), Value: Diff,
140 Kind: MCFixup::getDataKindForSize(Size: 4)));
141 S.emitInt32(Value: 0);
142
143 // sfde_func_num_fres
144 S.emitInt32(Value: FREs.size());
145
146 // sfde_func_info word
147
148 // All FREs within an FDE share the same sframe::FREType::AddrX. The value
149 // of 'X' is determined by the FRE with the largest offset, which is the
150 // last. This offset isn't known until relax time, so emit a frag which can
151 // calculate that now.
152 //
153 // At relax time, this FDE frag calculates the proper AddrX value (as well
154 // as the rest of the FDE FuncInfo word). Subsequent FRE frags will read it
155 // from this frag and emit the proper number of bytes.
156 Frag = S.getCurrentFragment();
157 S.emitSFrameCalculateFuncOffset(FunCabsel: DFrame.Begin, FREBegin: FREs.back().Label, FDEFrag: nullptr,
158 Loc: SMLoc());
159
160 // sfde_func_rep_size. Not relevant in non-PCMASK fdes.
161 S.emitInt8(Value: 0);
162
163 // sfde_func_padding2
164 S.emitInt16(Value: 0);
165 }
166};
167
168// Emitting these field-by-field, instead of constructing the actual structures
169// lets Streamer do target endian-fixups for free.
170
171class SFrameEmitterImpl {
172 MCObjectStreamer &Streamer;
173 SmallVector<SFrameFDE> FDEs;
174 uint32_t TotalFREs;
175 ABI SFrameABI;
176 // Target-specific convenience variables to detect when a CFI instruction
177 // references these registers. Unlike in dwarf frame descriptions, they never
178 // escape into the sframe section itself. TODO: These should be retrieved from
179 // the target.
180 unsigned SPReg;
181 unsigned FPReg;
182 unsigned RAReg;
183 int8_t FixedRAOffset;
184 MCSymbol *FDESubSectionStart;
185 MCSymbol *FRESubSectionStart;
186 MCSymbol *FRESubSectionEnd;
187
188 bool setCFARegister(SFrameFRE &FRE, const MCCFIInstruction &I) {
189 if (I.getRegister() == SPReg) {
190 FRE.CFARegSet = true;
191 FRE.Info.setBaseRegister(BaseReg::SP);
192 return true;
193 }
194 if (I.getRegister() == FPReg) {
195 FRE.CFARegSet = true;
196 FRE.Info.setBaseRegister(BaseReg::FP);
197 return true;
198 }
199 Streamer.getContext().reportWarning(
200 L: I.getLoc(), Msg: "canonical Frame Address not in stack- or frame-pointer. "
201 "Omitting SFrame unwind info for this function");
202 return false;
203 }
204
205 bool setCFAOffset(SFrameFRE &FRE, SMLoc Loc, size_t Offset) {
206 if (!FRE.CFARegSet) {
207 Streamer.getContext().reportWarning(
208 L: Loc, Msg: "adjusting CFA offset without a base register. "
209 "Omitting SFrame unwind info for this function");
210 return false;
211 }
212 FRE.CFAOffset = Offset;
213 return true;
214 }
215
216 // Technically, the escape data could be anything, but it is commonly a dwarf
217 // CFI program. Even then, it could contain an arbitrarily complicated Dwarf
218 // expression. Following gnu-gas, look for certain common cases that could
219 // invalidate an FDE, emit a warning for those sequences, and don't generate
220 // an FDE in those cases. Allow any that are known safe. It is likely that
221 // more thorough test cases could refine this code, but it handles the most
222 // important ones compatibly with gas.
223 // Returns true if the CFI escape sequence is safe for sframes.
224 bool isCFIEscapeSafe(SFrameFDE &FDE, const SFrameFRE &FRE,
225 const MCCFIInstruction &CFI) {
226 const MCAsmInfo &AI = Streamer.getContext().getAsmInfo();
227 DWARFDataExtractorSimple data(CFI.getValues(), AI.isLittleEndian(),
228 AI.getCodePointerSize());
229
230 // Normally, both alignment factors are extracted from the enclosing Dwarf
231 // FDE or CIE. We don't have one here. Alignments are used for scaling
232 // factors for ops like CFA_def_cfa_offset_sf. But this particular function
233 // is only interested in registers.
234 dwarf::CFIProgram P(/*CodeAlignmentFactor=*/1,
235 /*DataAlignmentFactor=*/1,
236 Streamer.getContext().getTargetTriple().getArch());
237 uint64_t Offset = 0;
238 if (P.parse(Data&: data, Offset: &Offset, EndOffset: CFI.getValues().size())) {
239 // Not a parsable dwarf expression. Assume the worst.
240 Streamer.getContext().reportWarning(
241 L: CFI.getLoc(),
242 Msg: "skipping SFrame FDE; .cfi_escape with unknown effects");
243 return false;
244 }
245
246 // This loop deals with dwarf::CFIProgram::Instructions. Everywhere else
247 // this file deals with MCCFIInstructions.
248 for (const dwarf::CFIProgram::Instruction &I : P) {
249 switch (I.Opcode) {
250 case dwarf::DW_CFA_nop:
251 break;
252 case dwarf::DW_CFA_val_offset: {
253 // First argument is a register. Anything that touches CFA, FP, or RA is
254 // a problem, but allow others through. As an even more special case,
255 // allow SP + 0.
256 auto Reg = I.getOperandAsUnsigned(CFIP: P, OperandIdx: 0);
257 // The parser should have failed in this case.
258 assert(Reg && "DW_CFA_val_offset with no register.");
259 bool SPOk = true;
260 if (*Reg == SPReg) {
261 auto Opnd = I.getOperandAsSigned(CFIP: P, OperandIdx: 1);
262 if (!Opnd || *Opnd != 0)
263 SPOk = false;
264 }
265 if (!SPOk || *Reg == RAReg || *Reg == FPReg) {
266 StringRef RN = *Reg == SPReg
267 ? "SP reg "
268 : (*Reg == FPReg ? "FP reg " : "RA reg ");
269 Streamer.getContext().reportWarning(
270 L: CFI.getLoc(),
271 Msg: Twine(
272 "skipping SFrame FDE; .cfi_escape DW_CFA_val_offset with ") +
273 RN + Twine(*Reg));
274 return false;
275 }
276 } break;
277 case dwarf::DW_CFA_expression: {
278 // First argument is a register. Anything that touches CFA, FP, or RA is
279 // a problem, but allow others through.
280 auto Reg = I.getOperandAsUnsigned(CFIP: P, OperandIdx: 0);
281 if (!Reg) {
282 Streamer.getContext().reportWarning(
283 L: CFI.getLoc(),
284 Msg: "skipping SFrame FDE; .cfi_escape with unknown effects");
285 return false;
286 }
287 if (*Reg == SPReg || *Reg == RAReg || *Reg == FPReg) {
288 StringRef RN = *Reg == SPReg
289 ? "SP reg "
290 : (*Reg == FPReg ? "FP reg " : "RA reg ");
291 Streamer.getContext().reportWarning(
292 L: CFI.getLoc(),
293 Msg: Twine(
294 "skipping SFrame FDE; .cfi_escape DW_CFA_expression with ") +
295 RN + Twine(*Reg));
296 return false;
297 }
298 } break;
299 case dwarf::DW_CFA_GNU_args_size: {
300 auto Size = I.getOperandAsSigned(CFIP: P, OperandIdx: 0);
301 // Zero size doesn't affect the cfa.
302 if (Size && *Size == 0)
303 break;
304 if (FRE.Info.getBaseRegister() != BaseReg::FP) {
305 Streamer.getContext().reportWarning(
306 L: CFI.getLoc(),
307 Msg: Twine("skipping SFrame FDE; .cfi_escape DW_CFA_GNU_args_size "
308 "with non frame-pointer CFA"));
309 return false;
310 }
311 } break;
312 // Cases that gas doesn't specially handle. TODO: Some of these could be
313 // analyzed and handled instead of just punting. But these are uncommon,
314 // or should be written as normal cfi directives. Some will need fixes to
315 // the scaling factor.
316 case dwarf::DW_CFA_advance_loc:
317 case dwarf::DW_CFA_offset:
318 case dwarf::DW_CFA_restore:
319 case dwarf::DW_CFA_set_loc:
320 case dwarf::DW_CFA_advance_loc1:
321 case dwarf::DW_CFA_advance_loc2:
322 case dwarf::DW_CFA_advance_loc4:
323 case dwarf::DW_CFA_offset_extended:
324 case dwarf::DW_CFA_restore_extended:
325 case dwarf::DW_CFA_undefined:
326 case dwarf::DW_CFA_same_value:
327 case dwarf::DW_CFA_register:
328 case dwarf::DW_CFA_remember_state:
329 case dwarf::DW_CFA_restore_state:
330 case dwarf::DW_CFA_def_cfa:
331 case dwarf::DW_CFA_def_cfa_register:
332 case dwarf::DW_CFA_def_cfa_offset:
333 case dwarf::DW_CFA_def_cfa_expression:
334 case dwarf::DW_CFA_offset_extended_sf:
335 case dwarf::DW_CFA_def_cfa_sf:
336 case dwarf::DW_CFA_def_cfa_offset_sf:
337 case dwarf::DW_CFA_val_offset_sf:
338 case dwarf::DW_CFA_val_expression:
339 case dwarf::DW_CFA_MIPS_advance_loc8:
340 case dwarf::DW_CFA_AARCH64_negate_ra_state_with_pc:
341 case dwarf::DW_CFA_AARCH64_negate_ra_state:
342 case dwarf::DW_CFA_AARCH64_set_ra_state:
343 case dwarf::DW_CFA_LLVM_def_aspace_cfa:
344 case dwarf::DW_CFA_LLVM_def_aspace_cfa_sf:
345 Streamer.getContext().reportWarning(
346 L: CFI.getLoc(), Msg: "skipping SFrame FDE; .cfi_escape "
347 "CFA expression with unknown side effects");
348 return false;
349 default:
350 // Dwarf expression was only partially valid, and user could have
351 // written anything.
352 Streamer.getContext().reportWarning(
353 L: CFI.getLoc(),
354 Msg: "skipping SFrame FDE; .cfi_escape with unknown effects");
355 return false;
356 }
357 }
358 return true;
359 }
360
361 // Add the effects of CFI to the current FDE, creating a new FRE when
362 // necessary. Return true if the CFI is representable in the sframe format.
363 bool handleCFI(SFrameFDE &FDE, SFrameFRE &FRE, const MCCFIInstruction &CFI) {
364 switch (CFI.getOperation()) {
365 case MCCFIInstruction::OpDefCfaRegister:
366 return setCFARegister(FRE, I: CFI);
367 case MCCFIInstruction::OpDefCfa:
368 case MCCFIInstruction::OpLLVMDefAspaceCfa:
369 if (!setCFARegister(FRE, I: CFI))
370 return false;
371 return setCFAOffset(FRE, Loc: CFI.getLoc(), Offset: CFI.getOffset());
372 case MCCFIInstruction::OpOffset:
373 if (CFI.getRegister() == FPReg)
374 FRE.FPOffset = CFI.getOffset();
375 else if (CFI.getRegister() == RAReg)
376 FRE.RAOffset = CFI.getOffset();
377 return true;
378 case MCCFIInstruction::OpRelOffset:
379 if (CFI.getRegister() == FPReg)
380 FRE.FPOffset += CFI.getOffset();
381 else if (CFI.getRegister() == RAReg)
382 FRE.RAOffset += CFI.getOffset();
383 return true;
384 case MCCFIInstruction::OpDefCfaOffset:
385 return setCFAOffset(FRE, Loc: CFI.getLoc(), Offset: CFI.getOffset());
386 case MCCFIInstruction::OpAdjustCfaOffset:
387 return setCFAOffset(FRE, Loc: CFI.getLoc(), Offset: FRE.CFAOffset + CFI.getOffset());
388 case MCCFIInstruction::OpRememberState:
389 if (FDE.FREs.size() == 1) {
390 // Error for gas compatibility: If the initial FRE isn't complete,
391 // then any state is incomplete. FIXME: Dwarf doesn't error here.
392 // Why should sframe?
393 Streamer.getContext().reportWarning(
394 L: CFI.getLoc(), Msg: "skipping SFrame FDE; .cfi_remember_state without "
395 "prior SFrame FRE state");
396 return false;
397 }
398 FDE.SaveState.push_back(Elt: FRE);
399 return true;
400 case MCCFIInstruction::OpRestore:
401 // The first FRE generated has the original state.
402 if (CFI.getRegister() == FPReg)
403 FRE.FPOffset = FDE.FREs.front().FPOffset;
404 else if (CFI.getRegister() == RAReg)
405 FRE.RAOffset = FDE.FREs.front().RAOffset;
406 return true;
407 case MCCFIInstruction::OpRestoreState:
408 // The cfi parser will have caught unbalanced directives earlier, so a
409 // mismatch here is an implementation error.
410 assert(!FDE.SaveState.empty() &&
411 "cfi_restore_state without cfi_save_state");
412 FRE = FDE.SaveState.pop_back_val();
413 return true;
414 case MCCFIInstruction::OpEscape:
415 // This is a string of bytes that contains an arbitrary dwarf-expression
416 // that may or may not affect unwind info.
417 return isCFIEscapeSafe(FDE, FRE, CFI);
418 default:
419 // Instructions that don't affect the CFA, RA, and FP can be safely
420 // ignored.
421 return true;
422 }
423 }
424
425public:
426 SFrameEmitterImpl(MCObjectStreamer &Streamer)
427 : Streamer(Streamer), TotalFREs(0) {
428 assert(Streamer.getContext()
429 .getObjectFileInfo()
430 ->getSFrameABIArch()
431 .has_value());
432 FDEs.reserve(N: Streamer.getDwarfFrameInfos().size());
433 SFrameABI = *Streamer.getContext().getObjectFileInfo()->getSFrameABIArch();
434 switch (SFrameABI) {
435 case ABI::AArch64EndianBig:
436 case ABI::AArch64EndianLittle:
437 SPReg = 31;
438 RAReg = 29;
439 FPReg = 30;
440 FixedRAOffset = 0;
441 break;
442 case ABI::AMD64EndianLittle:
443 SPReg = 7;
444 // RARegister untracked in this abi. Value chosen to match
445 // MCDwarfFrameInfo constructor.
446 RAReg = static_cast<unsigned>(INT_MAX);
447 FPReg = 6;
448 FixedRAOffset = -8;
449 break;
450 }
451
452 FDESubSectionStart = Streamer.getContext().createTempSymbol();
453 FRESubSectionStart = Streamer.getContext().createTempSymbol();
454 FRESubSectionEnd = Streamer.getContext().createTempSymbol();
455 }
456
457 bool atSameLocation(const MCSymbol *Left, const MCSymbol *Right) {
458 return Left != nullptr && Right != nullptr &&
459 Left->getFragment() == Right->getFragment() &&
460 Left->getOffset() == Right->getOffset();
461 }
462
463 bool equalIgnoringLocation(const SFrameFRE &Left, const SFrameFRE &Right) {
464 return Left.CFAOffset == Right.CFAOffset &&
465 Left.FPOffset == Right.FPOffset && Left.RAOffset == Right.RAOffset &&
466 Left.Info.getFREInfo() == Right.Info.getFREInfo() &&
467 Left.CFARegSet == Right.CFARegSet;
468 }
469
470 void buildSFDE(const MCDwarfFrameInfo &DF) {
471 // Functions with zero size can happen with assembler macros and
472 // machine-generated code. They don't need unwind info at all, so
473 // no need to warn.
474 if (atSameLocation(Left: DF.Begin, Right: DF.End))
475 return;
476 bool Valid = true;
477 SFrameFDE FDE(DF, Streamer.getContext().createTempSymbol());
478 // This would have been set via ".cfi_return_column", but
479 // MCObjectStreamer doesn't emit an MCCFIInstruction for that. It just
480 // sets the DF.RAReg.
481 // FIXME: This also prevents providing a proper location for the error.
482 // LLVM doesn't change the return column itself, so this was
483 // hand-written assembly.
484 if (DF.RAReg != RAReg) {
485 Streamer.getContext().reportWarning(
486 L: SMLoc(), Msg: "non-default RA register in .cfi_return_column " +
487 Twine(DF.RAReg) +
488 ". Omitting SFrame unwind info for this function");
489 Valid = false;
490 }
491 MCSymbol *LastLabel = DF.Begin;
492 SFrameFRE BaseFRE(LastLabel);
493 if (!DF.IsSimple) {
494 for (const auto &CFI :
495 Streamer.getContext().getAsmInfo().getInitialFrameState())
496 if (!handleCFI(FDE, FRE&: BaseFRE, CFI))
497 Valid = false;
498 }
499 FDE.FREs.push_back(Elt: BaseFRE);
500
501 for (const auto &CFI : DF.Instructions) {
502 // Instructions from InitialFrameState may not have a label, but if these
503 // instructions don't, then they are in dead code or otherwise unused.
504 // TODO: This check follows MCDwarf.cpp
505 // FrameEmitterImplementation::emitCFIInstructions, but nothing in the
506 // testsuite triggers it. We should see if it can be removed in both
507 // places, or alternately, add a test to exercise it.
508 auto *L = CFI.getLabel();
509 if (L && !L->isDefined())
510 continue;
511
512 SFrameFRE FRE = FDE.FREs.back();
513 if (!handleCFI(FDE, FRE, CFI))
514 Valid = false;
515
516 // If nothing relevant but the location changed, don't add the FRE.
517 if (equalIgnoringLocation(Left: FRE, Right: FDE.FREs.back()))
518 continue;
519
520 // If the location stayed the same, then update the current
521 // row. Otherwise, add a new one.
522 if (atSameLocation(Left: LastLabel, Right: L))
523 FDE.FREs.back() = FRE;
524 else {
525 FDE.FREs.push_back(Elt: FRE);
526 FDE.FREs.back().Label = L;
527 LastLabel = L;
528 }
529 }
530
531 if (Valid) {
532 FDEs.push_back(Elt: FDE);
533 TotalFREs += FDE.FREs.size();
534 }
535 }
536
537 void emitPreamble() {
538 Streamer.emitInt16(Value: Magic);
539 Streamer.emitInt8(Value: static_cast<uint8_t>(Version::V2));
540 Streamer.emitInt8(Value: static_cast<uint8_t>(Flags::FDEFuncStartPCRel));
541 }
542
543 void emitHeader() {
544 emitPreamble();
545 // sfh_abi_arch
546 Streamer.emitInt8(Value: static_cast<uint8_t>(SFrameABI));
547 // sfh_cfa_fixed_fp_offset
548 Streamer.emitInt8(Value: 0);
549 // sfh_cfa_fixed_ra_offset
550 Streamer.emitInt8(Value: FixedRAOffset);
551 // sfh_auxhdr_len
552 Streamer.emitInt8(Value: 0);
553 // shf_num_fdes
554 Streamer.emitInt32(Value: FDEs.size());
555 // shf_num_fres
556 Streamer.emitInt32(Value: TotalFREs);
557
558 // shf_fre_len
559 Streamer.emitAbsoluteSymbolDiff(Hi: FRESubSectionEnd, Lo: FRESubSectionStart,
560 Size: sizeof(int32_t));
561 // shf_fdeoff. With no sfh_auxhdr, these immediately follow this header.
562 Streamer.emitInt32(Value: 0);
563 // shf_freoff
564 Streamer.emitInt32(Value: FDEs.size() *
565 sizeof(sframe::FuncDescEntry<endianness::native>));
566 }
567
568 void emitFDEs() {
569 Streamer.emitLabel(Symbol: FDESubSectionStart);
570 for (auto &FDE : FDEs) {
571 FDE.emit(S&: Streamer, FRESubSectionStart);
572 }
573 }
574
575 void emitFREs() {
576 Streamer.emitLabel(Symbol: FRESubSectionStart);
577 for (auto &FDE : FDEs) {
578 Streamer.emitLabel(Symbol: FDE.FREStart);
579 for (auto &FRE : FDE.FREs)
580 FRE.emit(S&: Streamer, FuncBegin: FDE.DFrame.Begin, FDEFrag: FDE.Frag);
581 }
582 Streamer.emitLabel(Symbol: FRESubSectionEnd);
583 }
584};
585
586} // end anonymous namespace
587
588void MCSFrameEmitter::emit(MCObjectStreamer &Streamer) {
589 MCContext &Context = Streamer.getContext();
590 // If this target doesn't support sframes, return now. Gas doesn't warn in
591 // this case, but if we want to, it should be done at option-parsing time,
592 // rather than here.
593 if (!Streamer.getContext()
594 .getObjectFileInfo()
595 ->getSFrameABIArch()
596 .has_value())
597 return;
598
599 SFrameEmitterImpl Emitter(Streamer);
600 ArrayRef<MCDwarfFrameInfo> FrameArray = Streamer.getDwarfFrameInfos();
601
602 // Both the header itself and the FDEs include various offsets and counts.
603 // Therefore, all of this must be precomputed.
604 for (const auto &DFrame : FrameArray)
605 Emitter.buildSFDE(DF: DFrame);
606
607 MCSection *Section = Context.getObjectFileInfo()->getSFrameSection();
608 // Not strictly necessary, but gas always aligns to 8, so match that.
609 Section->ensureMinAlignment(MinAlignment: Align(8));
610 Streamer.switchSection(Section);
611 MCSymbol *SectionStart = Context.createTempSymbol();
612 Streamer.emitLabel(Symbol: SectionStart);
613 Emitter.emitHeader();
614 Emitter.emitFDEs();
615 Emitter.emitFREs();
616}
617
618void MCSFrameEmitter::encodeFuncOffset(MCContext &C, uint64_t Offset,
619 SmallVectorImpl<char> &Out,
620 MCFragment *FDEFrag) {
621 // If encoding into the FDE Frag itself, generate the sfde_func_info.
622 if (FDEFrag == nullptr) {
623 // sfde_func_info
624
625 // Offset is the difference between the function start label and the final
626 // FRE's offset, which is the max offset for this FDE.
627 FDEInfo<endianness::native> I;
628 I.Info = 0;
629 if (isUInt<8>(x: Offset))
630 I.setFREType(FREType::Addr1);
631 else if (isUInt<16>(x: Offset))
632 I.setFREType(FREType::Addr2);
633 else {
634 assert(isUInt<32>(Offset));
635 I.setFREType(FREType::Addr4);
636 }
637 I.setFDEType(FDEType::PCInc);
638 // TODO: When we support pauth keys, this will need to be retrieved
639 // from the frag itself.
640 I.setPAuthKey(0);
641
642 Out.push_back(Elt: I.getFuncInfo());
643 return;
644 }
645
646 const auto &FDEData = FDEFrag->getVarContents();
647 FDEInfo<endianness::native> I;
648 I.Info = FDEData.back();
649 FREType T = I.getFREType();
650 llvm::endianness E = C.getAsmInfo().isLittleEndian()
651 ? llvm::endianness::little
652 : llvm::endianness::big;
653 // sfre_start_address
654 switch (T) {
655 case FREType::Addr1:
656 assert(isUInt<8>(Offset) && "Miscalculated Sframe FREType");
657 support::endian::write<uint8_t>(Out, V: Offset, E);
658 break;
659 case FREType::Addr2:
660 assert(isUInt<16>(Offset) && "Miscalculated Sframe FREType");
661 support::endian::write<uint16_t>(Out, V: Offset, E);
662 break;
663 case FREType::Addr4:
664 assert(isUInt<32>(Offset) && "Miscalculated Sframe FREType");
665 support::endian::write<uint32_t>(Out, V: Offset, E);
666 break;
667 }
668}
669