1223013Sdim//===- lib/MC/MCWin64EH.cpp - MCWin64EH implementation --------------------===// 2223013Sdim// 3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4353358Sdim// See https://llvm.org/LICENSE.txt for license information. 5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6223013Sdim// 7223013Sdim//===----------------------------------------------------------------------===// 8223013Sdim 9223013Sdim#include "llvm/MC/MCWin64EH.h" 10249423Sdim#include "llvm/ADT/Twine.h" 11223013Sdim#include "llvm/MC/MCContext.h" 12249423Sdim#include "llvm/MC/MCExpr.h" 13344779Sdim#include "llvm/MC/MCObjectFileInfo.h" 14344779Sdim#include "llvm/MC/MCObjectStreamer.h" 15344779Sdim#include "llvm/MC/MCSectionCOFF.h" 16249423Sdim#include "llvm/MC/MCStreamer.h" 17223013Sdim#include "llvm/MC/MCSymbol.h" 18276479Sdim#include "llvm/Support/Win64EH.h" 19223013Sdim 20309124Sdimusing namespace llvm; 21223013Sdim 22223013Sdim// NOTE: All relocations generated here are 4-byte image-relative. 23223013Sdim 24276479Sdimstatic uint8_t CountOfUnwindCodes(std::vector<WinEH::Instruction> &Insns) { 25276479Sdim uint8_t Count = 0; 26276479Sdim for (const auto &I : Insns) { 27276479Sdim switch (static_cast<Win64EH::UnwindOpcodes>(I.Operation)) { 28344779Sdim default: 29344779Sdim llvm_unreachable("Unsupported unwind code"); 30223013Sdim case Win64EH::UOP_PushNonVol: 31223013Sdim case Win64EH::UOP_AllocSmall: 32223013Sdim case Win64EH::UOP_SetFPReg: 33223013Sdim case Win64EH::UOP_PushMachFrame: 34276479Sdim Count += 1; 35223013Sdim break; 36223013Sdim case Win64EH::UOP_SaveNonVol: 37223013Sdim case Win64EH::UOP_SaveXMM128: 38276479Sdim Count += 2; 39223013Sdim break; 40223013Sdim case Win64EH::UOP_SaveNonVolBig: 41223013Sdim case Win64EH::UOP_SaveXMM128Big: 42276479Sdim Count += 3; 43223013Sdim break; 44223013Sdim case Win64EH::UOP_AllocLarge: 45276479Sdim Count += (I.Offset > 512 * 1024 - 8) ? 3 : 2; 46223013Sdim break; 47223013Sdim } 48223013Sdim } 49276479Sdim return Count; 50223013Sdim} 51223013Sdim 52276479Sdimstatic void EmitAbsDifference(MCStreamer &Streamer, const MCSymbol *LHS, 53276479Sdim const MCSymbol *RHS) { 54276479Sdim MCContext &Context = Streamer.getContext(); 55276479Sdim const MCExpr *Diff = 56288943Sdim MCBinaryExpr::createSub(MCSymbolRefExpr::create(LHS, Context), 57288943Sdim MCSymbolRefExpr::create(RHS, Context), Context); 58280031Sdim Streamer.EmitValue(Diff, 1); 59223013Sdim} 60223013Sdim 61280031Sdimstatic void EmitUnwindCode(MCStreamer &streamer, const MCSymbol *begin, 62276479Sdim WinEH::Instruction &inst) { 63261991Sdim uint8_t b2; 64223013Sdim uint16_t w; 65276479Sdim b2 = (inst.Operation & 0x0F); 66276479Sdim switch (static_cast<Win64EH::UnwindOpcodes>(inst.Operation)) { 67344779Sdim default: 68344779Sdim llvm_unreachable("Unsupported unwind code"); 69223013Sdim case Win64EH::UOP_PushNonVol: 70276479Sdim EmitAbsDifference(streamer, inst.Label, begin); 71276479Sdim b2 |= (inst.Register & 0x0F) << 4; 72223013Sdim streamer.EmitIntValue(b2, 1); 73223013Sdim break; 74223013Sdim case Win64EH::UOP_AllocLarge: 75276479Sdim EmitAbsDifference(streamer, inst.Label, begin); 76276479Sdim if (inst.Offset > 512 * 1024 - 8) { 77223013Sdim b2 |= 0x10; 78223013Sdim streamer.EmitIntValue(b2, 1); 79276479Sdim w = inst.Offset & 0xFFF8; 80223013Sdim streamer.EmitIntValue(w, 2); 81276479Sdim w = inst.Offset >> 16; 82223013Sdim } else { 83223013Sdim streamer.EmitIntValue(b2, 1); 84276479Sdim w = inst.Offset >> 3; 85223013Sdim } 86223013Sdim streamer.EmitIntValue(w, 2); 87223013Sdim break; 88223013Sdim case Win64EH::UOP_AllocSmall: 89276479Sdim b2 |= (((inst.Offset - 8) >> 3) & 0x0F) << 4; 90276479Sdim EmitAbsDifference(streamer, inst.Label, begin); 91223013Sdim streamer.EmitIntValue(b2, 1); 92223013Sdim break; 93223013Sdim case Win64EH::UOP_SetFPReg: 94276479Sdim EmitAbsDifference(streamer, inst.Label, begin); 95223013Sdim streamer.EmitIntValue(b2, 1); 96223013Sdim break; 97223013Sdim case Win64EH::UOP_SaveNonVol: 98223013Sdim case Win64EH::UOP_SaveXMM128: 99276479Sdim b2 |= (inst.Register & 0x0F) << 4; 100276479Sdim EmitAbsDifference(streamer, inst.Label, begin); 101223013Sdim streamer.EmitIntValue(b2, 1); 102276479Sdim w = inst.Offset >> 3; 103276479Sdim if (inst.Operation == Win64EH::UOP_SaveXMM128) 104223013Sdim w >>= 1; 105223013Sdim streamer.EmitIntValue(w, 2); 106223013Sdim break; 107223013Sdim case Win64EH::UOP_SaveNonVolBig: 108223013Sdim case Win64EH::UOP_SaveXMM128Big: 109276479Sdim b2 |= (inst.Register & 0x0F) << 4; 110276479Sdim EmitAbsDifference(streamer, inst.Label, begin); 111223013Sdim streamer.EmitIntValue(b2, 1); 112276479Sdim if (inst.Operation == Win64EH::UOP_SaveXMM128Big) 113276479Sdim w = inst.Offset & 0xFFF0; 114223013Sdim else 115276479Sdim w = inst.Offset & 0xFFF8; 116223013Sdim streamer.EmitIntValue(w, 2); 117276479Sdim w = inst.Offset >> 16; 118223013Sdim streamer.EmitIntValue(w, 2); 119223013Sdim break; 120223013Sdim case Win64EH::UOP_PushMachFrame: 121276479Sdim if (inst.Offset == 1) 122223013Sdim b2 |= 0x10; 123276479Sdim EmitAbsDifference(streamer, inst.Label, begin); 124223013Sdim streamer.EmitIntValue(b2, 1); 125223013Sdim break; 126223013Sdim } 127223013Sdim} 128223013Sdim 129261991Sdimstatic void EmitSymbolRefWithOfs(MCStreamer &streamer, 130261991Sdim const MCSymbol *Base, 131261991Sdim const MCSymbol *Other) { 132261991Sdim MCContext &Context = streamer.getContext(); 133288943Sdim const MCSymbolRefExpr *BaseRef = MCSymbolRefExpr::create(Base, Context); 134288943Sdim const MCSymbolRefExpr *OtherRef = MCSymbolRefExpr::create(Other, Context); 135288943Sdim const MCExpr *Ofs = MCBinaryExpr::createSub(OtherRef, BaseRef, Context); 136288943Sdim const MCSymbolRefExpr *BaseRefRel = MCSymbolRefExpr::create(Base, 137261991Sdim MCSymbolRefExpr::VK_COFF_IMGREL32, 138261991Sdim Context); 139288943Sdim streamer.EmitValue(MCBinaryExpr::createAdd(BaseRefRel, Ofs, Context), 4); 140261991Sdim} 141261991Sdim 142223013Sdimstatic void EmitRuntimeFunction(MCStreamer &streamer, 143280031Sdim const WinEH::FrameInfo *info) { 144223013Sdim MCContext &context = streamer.getContext(); 145223013Sdim 146223013Sdim streamer.EmitValueToAlignment(4); 147261991Sdim EmitSymbolRefWithOfs(streamer, info->Function, info->Begin); 148261991Sdim EmitSymbolRefWithOfs(streamer, info->Function, info->End); 149288943Sdim streamer.EmitValue(MCSymbolRefExpr::create(info->Symbol, 150261991Sdim MCSymbolRefExpr::VK_COFF_IMGREL32, 151261991Sdim context), 4); 152223013Sdim} 153223013Sdim 154280031Sdimstatic void EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info) { 155223013Sdim // If this UNWIND_INFO already has a symbol, it's already been emitted. 156280031Sdim if (info->Symbol) 157280031Sdim return; 158223013Sdim 159223013Sdim MCContext &context = streamer.getContext(); 160288943Sdim MCSymbol *Label = context.createTempSymbol(); 161280031Sdim 162223013Sdim streamer.EmitValueToAlignment(4); 163280031Sdim streamer.EmitLabel(Label); 164280031Sdim info->Symbol = Label; 165223013Sdim 166261991Sdim // Upper 3 bits are the version number (currently 1). 167261991Sdim uint8_t flags = 0x01; 168223013Sdim if (info->ChainedParent) 169223013Sdim flags |= Win64EH::UNW_ChainInfo << 3; 170223013Sdim else { 171223013Sdim if (info->HandlesUnwind) 172223013Sdim flags |= Win64EH::UNW_TerminateHandler << 3; 173223013Sdim if (info->HandlesExceptions) 174223013Sdim flags |= Win64EH::UNW_ExceptionHandler << 3; 175223013Sdim } 176223013Sdim streamer.EmitIntValue(flags, 1); 177223013Sdim 178223013Sdim if (info->PrologEnd) 179223013Sdim EmitAbsDifference(streamer, info->PrologEnd, info->Begin); 180223013Sdim else 181223013Sdim streamer.EmitIntValue(0, 1); 182223013Sdim 183223013Sdim uint8_t numCodes = CountOfUnwindCodes(info->Instructions); 184223013Sdim streamer.EmitIntValue(numCodes, 1); 185223013Sdim 186223013Sdim uint8_t frame = 0; 187223013Sdim if (info->LastFrameInst >= 0) { 188276479Sdim WinEH::Instruction &frameInst = info->Instructions[info->LastFrameInst]; 189276479Sdim assert(frameInst.Operation == Win64EH::UOP_SetFPReg); 190276479Sdim frame = (frameInst.Register & 0x0F) | (frameInst.Offset & 0xF0); 191223013Sdim } 192223013Sdim streamer.EmitIntValue(frame, 1); 193223013Sdim 194223013Sdim // Emit unwind instructions (in reverse order). 195223013Sdim uint8_t numInst = info->Instructions.size(); 196223013Sdim for (uint8_t c = 0; c < numInst; ++c) { 197276479Sdim WinEH::Instruction inst = info->Instructions.back(); 198223013Sdim info->Instructions.pop_back(); 199223013Sdim EmitUnwindCode(streamer, info->Begin, inst); 200223013Sdim } 201223013Sdim 202261991Sdim // For alignment purposes, the instruction array will always have an even 203261991Sdim // number of entries, with the final entry potentially unused (in which case 204261991Sdim // the array will be one longer than indicated by the count of unwind codes 205261991Sdim // field). 206261991Sdim if (numCodes & 1) { 207261991Sdim streamer.EmitIntValue(0, 2); 208261991Sdim } 209261991Sdim 210223013Sdim if (flags & (Win64EH::UNW_ChainInfo << 3)) 211223013Sdim EmitRuntimeFunction(streamer, info->ChainedParent); 212223013Sdim else if (flags & 213223013Sdim ((Win64EH::UNW_TerminateHandler|Win64EH::UNW_ExceptionHandler) << 3)) 214288943Sdim streamer.EmitValue(MCSymbolRefExpr::create(info->ExceptionHandler, 215261991Sdim MCSymbolRefExpr::VK_COFF_IMGREL32, 216261991Sdim context), 4); 217261991Sdim else if (numCodes == 0) { 218223013Sdim // The minimum size of an UNWIND_INFO struct is 8 bytes. If we're not 219223013Sdim // a chained unwind info, if there is no handler, and if there are fewer 220223013Sdim // than 2 slots used in the unwind code array, we have to pad to 8 bytes. 221261991Sdim streamer.EmitIntValue(0, 4); 222223013Sdim } 223223013Sdim} 224223013Sdim 225309124Sdimvoid llvm::Win64EH::UnwindEmitter::Emit(MCStreamer &Streamer) const { 226223013Sdim // Emit the unwind info structs first. 227327952Sdim for (const auto &CFI : Streamer.getWinFrameInfos()) { 228309124Sdim MCSection *XData = Streamer.getAssociatedXDataSection(CFI->TextSection); 229276479Sdim Streamer.SwitchSection(XData); 230327952Sdim ::EmitUnwindInfo(Streamer, CFI.get()); 231223013Sdim } 232276479Sdim 233223013Sdim // Now emit RUNTIME_FUNCTION entries. 234327952Sdim for (const auto &CFI : Streamer.getWinFrameInfos()) { 235309124Sdim MCSection *PData = Streamer.getAssociatedPDataSection(CFI->TextSection); 236276479Sdim Streamer.SwitchSection(PData); 237327952Sdim EmitRuntimeFunction(Streamer, CFI.get()); 238223013Sdim } 239223013Sdim} 240223013Sdim 241309124Sdimvoid llvm::Win64EH::UnwindEmitter::EmitUnwindInfo( 242309124Sdim MCStreamer &Streamer, WinEH::FrameInfo *info) const { 243280031Sdim // Switch sections (the static function above is meant to be called from 244280031Sdim // here and from Emit(). 245309124Sdim MCSection *XData = Streamer.getAssociatedXDataSection(info->TextSection); 246309124Sdim Streamer.SwitchSection(XData); 247280031Sdim 248309124Sdim ::EmitUnwindInfo(Streamer, info); 249280031Sdim} 250223013Sdim 251344779Sdimstatic int64_t GetAbsDifference(MCStreamer &Streamer, const MCSymbol *LHS, 252344779Sdim const MCSymbol *RHS) { 253344779Sdim MCContext &Context = Streamer.getContext(); 254344779Sdim const MCExpr *Diff = 255344779Sdim MCBinaryExpr::createSub(MCSymbolRefExpr::create(LHS, Context), 256344779Sdim MCSymbolRefExpr::create(RHS, Context), Context); 257344779Sdim MCObjectStreamer *OS = (MCObjectStreamer *)(&Streamer); 258353358Sdim // It should normally be possible to calculate the length of a function 259353358Sdim // at this point, but it might not be possible in the presence of certain 260353358Sdim // unusual constructs, like an inline asm with an alignment directive. 261344779Sdim int64_t value; 262353358Sdim if (!Diff->evaluateAsAbsolute(value, OS->getAssembler())) 263353358Sdim report_fatal_error("Failed to evaluate function length in SEH unwind info"); 264344779Sdim return value; 265344779Sdim} 266344779Sdim 267344779Sdimstatic uint32_t 268344779SdimARM64CountOfUnwindCodes(const std::vector<WinEH::Instruction> &Insns) { 269344779Sdim uint32_t Count = 0; 270344779Sdim for (const auto &I : Insns) { 271344779Sdim switch (static_cast<Win64EH::UnwindOpcodes>(I.Operation)) { 272344779Sdim default: 273344779Sdim llvm_unreachable("Unsupported ARM64 unwind code"); 274344779Sdim case Win64EH::UOP_AllocSmall: 275344779Sdim Count += 1; 276344779Sdim break; 277344779Sdim case Win64EH::UOP_AllocMedium: 278344779Sdim Count += 2; 279344779Sdim break; 280344779Sdim case Win64EH::UOP_AllocLarge: 281344779Sdim Count += 4; 282344779Sdim break; 283344779Sdim case Win64EH::UOP_SaveFPLRX: 284344779Sdim Count += 1; 285344779Sdim break; 286344779Sdim case Win64EH::UOP_SaveFPLR: 287344779Sdim Count += 1; 288344779Sdim break; 289344779Sdim case Win64EH::UOP_SaveReg: 290344779Sdim Count += 2; 291344779Sdim break; 292344779Sdim case Win64EH::UOP_SaveRegP: 293344779Sdim Count += 2; 294344779Sdim break; 295344779Sdim case Win64EH::UOP_SaveRegPX: 296344779Sdim Count += 2; 297344779Sdim break; 298344779Sdim case Win64EH::UOP_SaveRegX: 299344779Sdim Count += 2; 300344779Sdim break; 301344779Sdim case Win64EH::UOP_SaveFReg: 302344779Sdim Count += 2; 303344779Sdim break; 304344779Sdim case Win64EH::UOP_SaveFRegP: 305344779Sdim Count += 2; 306344779Sdim break; 307344779Sdim case Win64EH::UOP_SaveFRegX: 308344779Sdim Count += 2; 309344779Sdim break; 310344779Sdim case Win64EH::UOP_SaveFRegPX: 311344779Sdim Count += 2; 312344779Sdim break; 313344779Sdim case Win64EH::UOP_SetFP: 314344779Sdim Count += 1; 315344779Sdim break; 316344779Sdim case Win64EH::UOP_AddFP: 317344779Sdim Count += 2; 318344779Sdim break; 319344779Sdim case Win64EH::UOP_Nop: 320344779Sdim Count += 1; 321344779Sdim break; 322344779Sdim case Win64EH::UOP_End: 323344779Sdim Count += 1; 324344779Sdim break; 325344779Sdim } 326344779Sdim } 327344779Sdim return Count; 328344779Sdim} 329344779Sdim 330344779Sdim// Unwind opcode encodings and restrictions are documented at 331344779Sdim// https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling 332344779Sdimstatic void ARM64EmitUnwindCode(MCStreamer &streamer, const MCSymbol *begin, 333344779Sdim WinEH::Instruction &inst) { 334344779Sdim uint8_t b, reg; 335344779Sdim switch (static_cast<Win64EH::UnwindOpcodes>(inst.Operation)) { 336344779Sdim default: 337344779Sdim llvm_unreachable("Unsupported ARM64 unwind code"); 338344779Sdim case Win64EH::UOP_AllocSmall: 339344779Sdim b = (inst.Offset >> 4) & 0x1F; 340344779Sdim streamer.EmitIntValue(b, 1); 341344779Sdim break; 342344779Sdim case Win64EH::UOP_AllocMedium: { 343344779Sdim uint16_t hw = (inst.Offset >> 4) & 0x7FF; 344344779Sdim b = 0xC0; 345344779Sdim b |= (hw >> 8); 346344779Sdim streamer.EmitIntValue(b, 1); 347344779Sdim b = hw & 0xFF; 348344779Sdim streamer.EmitIntValue(b, 1); 349344779Sdim break; 350344779Sdim } 351344779Sdim case Win64EH::UOP_AllocLarge: { 352344779Sdim uint32_t w; 353344779Sdim b = 0xE0; 354344779Sdim streamer.EmitIntValue(b, 1); 355344779Sdim w = inst.Offset >> 4; 356344779Sdim b = (w & 0x00FF0000) >> 16; 357344779Sdim streamer.EmitIntValue(b, 1); 358344779Sdim b = (w & 0x0000FF00) >> 8; 359344779Sdim streamer.EmitIntValue(b, 1); 360344779Sdim b = w & 0x000000FF; 361344779Sdim streamer.EmitIntValue(b, 1); 362344779Sdim break; 363344779Sdim } 364344779Sdim case Win64EH::UOP_SetFP: 365344779Sdim b = 0xE1; 366344779Sdim streamer.EmitIntValue(b, 1); 367344779Sdim break; 368344779Sdim case Win64EH::UOP_AddFP: 369344779Sdim b = 0xE2; 370344779Sdim streamer.EmitIntValue(b, 1); 371344779Sdim b = (inst.Offset >> 3); 372344779Sdim streamer.EmitIntValue(b, 1); 373344779Sdim break; 374344779Sdim case Win64EH::UOP_Nop: 375344779Sdim b = 0xE3; 376344779Sdim streamer.EmitIntValue(b, 1); 377344779Sdim break; 378344779Sdim case Win64EH::UOP_SaveFPLRX: 379344779Sdim b = 0x80; 380344779Sdim b |= ((inst.Offset - 1) >> 3) & 0x3F; 381344779Sdim streamer.EmitIntValue(b, 1); 382344779Sdim break; 383344779Sdim case Win64EH::UOP_SaveFPLR: 384344779Sdim b = 0x40; 385344779Sdim b |= (inst.Offset >> 3) & 0x3F; 386344779Sdim streamer.EmitIntValue(b, 1); 387344779Sdim break; 388344779Sdim case Win64EH::UOP_SaveReg: 389344779Sdim assert(inst.Register >= 19 && "Saved reg must be >= 19"); 390344779Sdim reg = inst.Register - 19; 391344779Sdim b = 0xD0 | ((reg & 0xC) >> 2); 392344779Sdim streamer.EmitIntValue(b, 1); 393344779Sdim b = ((reg & 0x3) << 6) | (inst.Offset >> 3); 394344779Sdim streamer.EmitIntValue(b, 1); 395344779Sdim break; 396344779Sdim case Win64EH::UOP_SaveRegX: 397344779Sdim assert(inst.Register >= 19 && "Saved reg must be >= 19"); 398344779Sdim reg = inst.Register - 19; 399344779Sdim b = 0xD4 | ((reg & 0x8) >> 3); 400344779Sdim streamer.EmitIntValue(b, 1); 401344779Sdim b = ((reg & 0x7) << 5) | ((inst.Offset >> 3) - 1); 402344779Sdim streamer.EmitIntValue(b, 1); 403344779Sdim break; 404344779Sdim case Win64EH::UOP_SaveRegP: 405344779Sdim assert(inst.Register >= 19 && "Saved registers must be >= 19"); 406344779Sdim reg = inst.Register - 19; 407344779Sdim b = 0xC8 | ((reg & 0xC) >> 2); 408344779Sdim streamer.EmitIntValue(b, 1); 409344779Sdim b = ((reg & 0x3) << 6) | (inst.Offset >> 3); 410344779Sdim streamer.EmitIntValue(b, 1); 411344779Sdim break; 412344779Sdim case Win64EH::UOP_SaveRegPX: 413344779Sdim assert(inst.Register >= 19 && "Saved registers must be >= 19"); 414344779Sdim reg = inst.Register - 19; 415344779Sdim b = 0xCC | ((reg & 0xC) >> 2); 416344779Sdim streamer.EmitIntValue(b, 1); 417344779Sdim b = ((reg & 0x3) << 6) | ((inst.Offset >> 3) - 1); 418344779Sdim streamer.EmitIntValue(b, 1); 419344779Sdim break; 420344779Sdim case Win64EH::UOP_SaveFReg: 421344779Sdim assert(inst.Register >= 8 && "Saved dreg must be >= 8"); 422344779Sdim reg = inst.Register - 8; 423344779Sdim b = 0xDC | ((reg & 0x4) >> 2); 424344779Sdim streamer.EmitIntValue(b, 1); 425344779Sdim b = ((reg & 0x3) << 6) | (inst.Offset >> 3); 426344779Sdim streamer.EmitIntValue(b, 1); 427344779Sdim break; 428344779Sdim case Win64EH::UOP_SaveFRegX: 429344779Sdim assert(inst.Register >= 8 && "Saved dreg must be >= 8"); 430344779Sdim reg = inst.Register - 8; 431344779Sdim b = 0xDE; 432344779Sdim streamer.EmitIntValue(b, 1); 433344779Sdim b = ((reg & 0x7) << 5) | ((inst.Offset >> 3) - 1); 434344779Sdim streamer.EmitIntValue(b, 1); 435344779Sdim break; 436344779Sdim case Win64EH::UOP_SaveFRegP: 437344779Sdim assert(inst.Register >= 8 && "Saved dregs must be >= 8"); 438344779Sdim reg = inst.Register - 8; 439344779Sdim b = 0xD8 | ((reg & 0x4) >> 2); 440344779Sdim streamer.EmitIntValue(b, 1); 441344779Sdim b = ((reg & 0x3) << 6) | (inst.Offset >> 3); 442344779Sdim streamer.EmitIntValue(b, 1); 443344779Sdim break; 444344779Sdim case Win64EH::UOP_SaveFRegPX: 445344779Sdim assert(inst.Register >= 8 && "Saved dregs must be >= 8"); 446344779Sdim reg = inst.Register - 8; 447344779Sdim b = 0xDA | ((reg & 0x4) >> 2); 448344779Sdim streamer.EmitIntValue(b, 1); 449344779Sdim b = ((reg & 0x3) << 6) | ((inst.Offset >> 3) - 1); 450344779Sdim streamer.EmitIntValue(b, 1); 451344779Sdim break; 452344779Sdim case Win64EH::UOP_End: 453344779Sdim b = 0xE4; 454344779Sdim streamer.EmitIntValue(b, 1); 455344779Sdim break; 456344779Sdim } 457344779Sdim} 458344779Sdim 459344779Sdim// Returns the epilog symbol of an epilog with the exact same unwind code 460344779Sdim// sequence, if it exists. Otherwise, returns nulltpr. 461344779Sdim// EpilogInstrs - Unwind codes for the current epilog. 462344779Sdim// Epilogs - Epilogs that potentialy match the current epilog. 463344779Sdimstatic MCSymbol* 464344779SdimFindMatchingEpilog(const std::vector<WinEH::Instruction>& EpilogInstrs, 465344779Sdim const std::vector<MCSymbol *>& Epilogs, 466344779Sdim const WinEH::FrameInfo *info) { 467344779Sdim for (auto *EpilogStart : Epilogs) { 468344779Sdim auto InstrsIter = info->EpilogMap.find(EpilogStart); 469344779Sdim assert(InstrsIter != info->EpilogMap.end() && 470344779Sdim "Epilog not found in EpilogMap"); 471344779Sdim const auto &Instrs = InstrsIter->second; 472344779Sdim 473344779Sdim if (Instrs.size() != EpilogInstrs.size()) 474344779Sdim continue; 475344779Sdim 476344779Sdim bool Match = true; 477344779Sdim for (unsigned i = 0; i < Instrs.size(); ++i) 478344779Sdim if (Instrs[i].Operation != EpilogInstrs[i].Operation || 479344779Sdim Instrs[i].Offset != EpilogInstrs[i].Offset || 480344779Sdim Instrs[i].Register != EpilogInstrs[i].Register) { 481344779Sdim Match = false; 482344779Sdim break; 483344779Sdim } 484344779Sdim 485344779Sdim if (Match) 486344779Sdim return EpilogStart; 487344779Sdim } 488344779Sdim return nullptr; 489344779Sdim} 490344779Sdim 491344779Sdim// Populate the .xdata section. The format of .xdata on ARM64 is documented at 492344779Sdim// https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling 493344779Sdimstatic void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info) { 494344779Sdim // If this UNWIND_INFO already has a symbol, it's already been emitted. 495344779Sdim if (info->Symbol) 496344779Sdim return; 497344779Sdim 498344779Sdim MCContext &context = streamer.getContext(); 499344779Sdim MCSymbol *Label = context.createTempSymbol(); 500344779Sdim 501344779Sdim streamer.EmitValueToAlignment(4); 502344779Sdim streamer.EmitLabel(Label); 503344779Sdim info->Symbol = Label; 504344779Sdim 505353358Sdim int64_t RawFuncLength; 506353358Sdim if (!info->FuncletOrFuncEnd) { 507353358Sdim // FIXME: This is very wrong; we emit SEH data which covers zero bytes 508353358Sdim // of code. But otherwise test/MC/AArch64/seh.s crashes. 509353358Sdim RawFuncLength = 0; 510353358Sdim } else { 511353358Sdim // FIXME: GetAbsDifference tries to compute the length of the function 512353358Sdim // immediately, before the whole file is emitted, but in general 513353358Sdim // that's impossible: the size in bytes of certain assembler directives 514353358Sdim // like .align and .fill is not known until the whole file is parsed and 515353358Sdim // relaxations are applied. Currently, GetAbsDifference fails with a fatal 516353358Sdim // error in that case. (We mostly don't hit this because inline assembly 517353358Sdim // specifying those directives is rare, and we don't normally try to 518353358Sdim // align loops on AArch64.) 519353358Sdim // 520353358Sdim // There are two potential approaches to delaying the computation. One, 521353358Sdim // we could emit something like ".word (endfunc-beginfunc)/4+0x10800000", 522353358Sdim // as long as we have some conservative estimate we could use to prove 523353358Sdim // that we don't need to split the unwind data. Emitting the constant 524353358Sdim // is straightforward, but there's no existing code for estimating the 525353358Sdim // size of the function. 526353358Sdim // 527353358Sdim // The other approach would be to use a dedicated, relaxable fragment, 528353358Sdim // which could grow to accommodate splitting the unwind data if 529353358Sdim // necessary. This is more straightforward, since it automatically works 530353358Sdim // without any new infrastructure, and it's consistent with how we handle 531353358Sdim // relaxation in other contexts. But it would require some refactoring 532353358Sdim // to move parts of the pdata/xdata emission into the implementation of 533353358Sdim // a fragment. We could probably continue to encode the unwind codes 534353358Sdim // here, but we'd have to emit the pdata, the xdata header, and the 535353358Sdim // epilogue scopes later, since they depend on whether the we need to 536353358Sdim // split the unwind data. 537353358Sdim RawFuncLength = GetAbsDifference(streamer, info->FuncletOrFuncEnd, 538353358Sdim info->Begin); 539353358Sdim } 540353358Sdim if (RawFuncLength > 0xFFFFF) 541353358Sdim report_fatal_error("SEH unwind data splitting not yet implemented"); 542353358Sdim uint32_t FuncLength = (uint32_t)RawFuncLength / 4; 543344779Sdim uint32_t PrologCodeBytes = ARM64CountOfUnwindCodes(info->Instructions); 544344779Sdim uint32_t TotalCodeBytes = PrologCodeBytes; 545344779Sdim 546344779Sdim // Process epilogs. 547344779Sdim MapVector<MCSymbol *, uint32_t> EpilogInfo; 548344779Sdim // Epilogs processed so far. 549344779Sdim std::vector<MCSymbol *> AddedEpilogs; 550344779Sdim 551344779Sdim for (auto &I : info->EpilogMap) { 552344779Sdim MCSymbol *EpilogStart = I.first; 553344779Sdim auto &EpilogInstrs = I.second; 554344779Sdim uint32_t CodeBytes = ARM64CountOfUnwindCodes(EpilogInstrs); 555344779Sdim 556344779Sdim MCSymbol* MatchingEpilog = 557344779Sdim FindMatchingEpilog(EpilogInstrs, AddedEpilogs, info); 558344779Sdim if (MatchingEpilog) { 559344779Sdim assert(EpilogInfo.find(MatchingEpilog) != EpilogInfo.end() && 560344779Sdim "Duplicate epilog not found"); 561349004Sdim EpilogInfo[EpilogStart] = EpilogInfo.lookup(MatchingEpilog); 562344779Sdim // Clear the unwind codes in the EpilogMap, so that they don't get output 563344779Sdim // in the logic below. 564344779Sdim EpilogInstrs.clear(); 565344779Sdim } else { 566344779Sdim EpilogInfo[EpilogStart] = TotalCodeBytes; 567344779Sdim TotalCodeBytes += CodeBytes; 568344779Sdim AddedEpilogs.push_back(EpilogStart); 569344779Sdim } 570344779Sdim } 571344779Sdim 572344779Sdim // Code Words, Epilog count, E, X, Vers, Function Length 573344779Sdim uint32_t row1 = 0x0; 574344779Sdim uint32_t CodeWords = TotalCodeBytes / 4; 575344779Sdim uint32_t CodeWordsMod = TotalCodeBytes % 4; 576344779Sdim if (CodeWordsMod) 577344779Sdim CodeWords++; 578344779Sdim uint32_t EpilogCount = info->EpilogMap.size(); 579344779Sdim bool ExtensionWord = EpilogCount > 31 || TotalCodeBytes > 124; 580344779Sdim if (!ExtensionWord) { 581344779Sdim row1 |= (EpilogCount & 0x1F) << 22; 582344779Sdim row1 |= (CodeWords & 0x1F) << 27; 583344779Sdim } 584344779Sdim // E is always 0 right now, TODO: packed epilog setup 585344779Sdim if (info->HandlesExceptions) // X 586344779Sdim row1 |= 1 << 20; 587344779Sdim row1 |= FuncLength & 0x3FFFF; 588344779Sdim streamer.EmitIntValue(row1, 4); 589344779Sdim 590344779Sdim // Extended Code Words, Extended Epilog Count 591344779Sdim if (ExtensionWord) { 592344779Sdim // FIXME: We should be able to split unwind info into multiple sections. 593344779Sdim // FIXME: We should share epilog codes across epilogs, where possible, 594344779Sdim // which would make this issue show up less frequently. 595344779Sdim if (CodeWords > 0xFF || EpilogCount > 0xFFFF) 596344779Sdim report_fatal_error("SEH unwind data splitting not yet implemented"); 597344779Sdim uint32_t row2 = 0x0; 598344779Sdim row2 |= (CodeWords & 0xFF) << 16; 599344779Sdim row2 |= (EpilogCount & 0xFFFF); 600344779Sdim streamer.EmitIntValue(row2, 4); 601344779Sdim } 602344779Sdim 603344779Sdim // Epilog Start Index, Epilog Start Offset 604344779Sdim for (auto &I : EpilogInfo) { 605344779Sdim MCSymbol *EpilogStart = I.first; 606344779Sdim uint32_t EpilogIndex = I.second; 607344779Sdim uint32_t EpilogOffset = 608344779Sdim (uint32_t)GetAbsDifference(streamer, EpilogStart, info->Begin); 609344779Sdim if (EpilogOffset) 610344779Sdim EpilogOffset /= 4; 611344779Sdim uint32_t row3 = EpilogOffset; 612344779Sdim row3 |= (EpilogIndex & 0x3FF) << 22; 613344779Sdim streamer.EmitIntValue(row3, 4); 614344779Sdim } 615344779Sdim 616344779Sdim // Emit prolog unwind instructions (in reverse order). 617344779Sdim uint8_t numInst = info->Instructions.size(); 618344779Sdim for (uint8_t c = 0; c < numInst; ++c) { 619344779Sdim WinEH::Instruction inst = info->Instructions.back(); 620344779Sdim info->Instructions.pop_back(); 621344779Sdim ARM64EmitUnwindCode(streamer, info->Begin, inst); 622344779Sdim } 623344779Sdim 624344779Sdim // Emit epilog unwind instructions 625344779Sdim for (auto &I : info->EpilogMap) { 626344779Sdim auto &EpilogInstrs = I.second; 627344779Sdim for (uint32_t i = 0; i < EpilogInstrs.size(); i++) { 628344779Sdim WinEH::Instruction inst = EpilogInstrs[i]; 629344779Sdim ARM64EmitUnwindCode(streamer, info->Begin, inst); 630344779Sdim } 631344779Sdim } 632344779Sdim 633344779Sdim int32_t BytesMod = CodeWords * 4 - TotalCodeBytes; 634344779Sdim assert(BytesMod >= 0); 635344779Sdim for (int i = 0; i < BytesMod; i++) 636344779Sdim streamer.EmitIntValue(0xE3, 1); 637344779Sdim 638344779Sdim if (info->HandlesExceptions) 639344779Sdim streamer.EmitValue( 640344779Sdim MCSymbolRefExpr::create(info->ExceptionHandler, 641344779Sdim MCSymbolRefExpr::VK_COFF_IMGREL32, context), 642344779Sdim 4); 643344779Sdim} 644344779Sdim 645344779Sdimstatic void ARM64EmitRuntimeFunction(MCStreamer &streamer, 646344779Sdim const WinEH::FrameInfo *info) { 647344779Sdim MCContext &context = streamer.getContext(); 648344779Sdim 649344779Sdim streamer.EmitValueToAlignment(4); 650344779Sdim EmitSymbolRefWithOfs(streamer, info->Function, info->Begin); 651344779Sdim streamer.EmitValue(MCSymbolRefExpr::create(info->Symbol, 652344779Sdim MCSymbolRefExpr::VK_COFF_IMGREL32, 653344779Sdim context), 654344779Sdim 4); 655344779Sdim} 656344779Sdim 657344779Sdimvoid llvm::Win64EH::ARM64UnwindEmitter::Emit(MCStreamer &Streamer) const { 658344779Sdim // Emit the unwind info structs first. 659344779Sdim for (const auto &CFI : Streamer.getWinFrameInfos()) { 660344779Sdim MCSection *XData = Streamer.getAssociatedXDataSection(CFI->TextSection); 661344779Sdim Streamer.SwitchSection(XData); 662344779Sdim ARM64EmitUnwindInfo(Streamer, CFI.get()); 663344779Sdim } 664344779Sdim 665344779Sdim // Now emit RUNTIME_FUNCTION entries. 666344779Sdim for (const auto &CFI : Streamer.getWinFrameInfos()) { 667344779Sdim MCSection *PData = Streamer.getAssociatedPDataSection(CFI->TextSection); 668344779Sdim Streamer.SwitchSection(PData); 669344779Sdim ARM64EmitRuntimeFunction(Streamer, CFI.get()); 670344779Sdim } 671344779Sdim} 672344779Sdim 673344779Sdimvoid llvm::Win64EH::ARM64UnwindEmitter::EmitUnwindInfo( 674344779Sdim MCStreamer &Streamer, WinEH::FrameInfo *info) const { 675344779Sdim // Switch sections (the static function above is meant to be called from 676344779Sdim // here and from Emit(). 677344779Sdim MCSection *XData = Streamer.getAssociatedXDataSection(info->TextSection); 678344779Sdim Streamer.SwitchSection(XData); 679344779Sdim ARM64EmitUnwindInfo(Streamer, info); 680344779Sdim} 681