1223013Sdim//===- lib/MC/MCWin64EH.cpp - MCWin64EH implementation --------------------===// 2223013Sdim// 3223013Sdim// The LLVM Compiler Infrastructure 4223013Sdim// 5223013Sdim// This file is distributed under the University of Illinois Open Source 6223013Sdim// License. See LICENSE.TXT for details. 7223013Sdim// 8223013Sdim//===----------------------------------------------------------------------===// 9223013Sdim 10223013Sdim#include "llvm/MC/MCWin64EH.h" 11249423Sdim#include "llvm/ADT/Twine.h" 12223013Sdim#include "llvm/MC/MCContext.h" 13249423Sdim#include "llvm/MC/MCExpr.h" 14226633Sdim#include "llvm/MC/MCObjectFileInfo.h" 15249423Sdim#include "llvm/MC/MCSectionCOFF.h" 16249423Sdim#include "llvm/MC/MCStreamer.h" 17223013Sdim#include "llvm/MC/MCSymbol.h" 18223013Sdim 19223013Sdimnamespace llvm { 20223013Sdim 21223013Sdim// NOTE: All relocations generated here are 4-byte image-relative. 22223013Sdim 23223013Sdimstatic uint8_t CountOfUnwindCodes(std::vector<MCWin64EHInstruction> &instArray){ 24223013Sdim uint8_t count = 0; 25223013Sdim for (std::vector<MCWin64EHInstruction>::const_iterator I = instArray.begin(), 26223013Sdim E = instArray.end(); I != E; ++I) { 27223013Sdim switch (I->getOperation()) { 28223013Sdim case Win64EH::UOP_PushNonVol: 29223013Sdim case Win64EH::UOP_AllocSmall: 30223013Sdim case Win64EH::UOP_SetFPReg: 31223013Sdim case Win64EH::UOP_PushMachFrame: 32223013Sdim count += 1; 33223013Sdim break; 34223013Sdim case Win64EH::UOP_SaveNonVol: 35223013Sdim case Win64EH::UOP_SaveXMM128: 36223013Sdim count += 2; 37223013Sdim break; 38223013Sdim case Win64EH::UOP_SaveNonVolBig: 39223013Sdim case Win64EH::UOP_SaveXMM128Big: 40223013Sdim count += 3; 41223013Sdim break; 42223013Sdim case Win64EH::UOP_AllocLarge: 43223013Sdim if (I->getSize() > 512*1024-8) 44223013Sdim count += 3; 45223013Sdim else 46223013Sdim count += 2; 47223013Sdim break; 48223013Sdim } 49223013Sdim } 50223013Sdim return count; 51223013Sdim} 52223013Sdim 53223013Sdimstatic void EmitAbsDifference(MCStreamer &streamer, MCSymbol *lhs, 54223013Sdim MCSymbol *rhs) { 55223013Sdim MCContext &context = streamer.getContext(); 56223013Sdim const MCExpr *diff = MCBinaryExpr::CreateSub(MCSymbolRefExpr::Create( 57223013Sdim lhs, context), 58223013Sdim MCSymbolRefExpr::Create( 59223013Sdim rhs, context), 60223013Sdim context); 61223013Sdim streamer.EmitAbsValue(diff, 1); 62223013Sdim 63223013Sdim} 64223013Sdim 65223013Sdimstatic void EmitUnwindCode(MCStreamer &streamer, MCSymbol *begin, 66223013Sdim MCWin64EHInstruction &inst) { 67263508Sdim uint8_t b2; 68223013Sdim uint16_t w; 69223013Sdim b2 = (inst.getOperation() & 0x0F); 70223013Sdim switch (inst.getOperation()) { 71223013Sdim case Win64EH::UOP_PushNonVol: 72223013Sdim EmitAbsDifference(streamer, inst.getLabel(), begin); 73223013Sdim b2 |= (inst.getRegister() & 0x0F) << 4; 74223013Sdim streamer.EmitIntValue(b2, 1); 75223013Sdim break; 76223013Sdim case Win64EH::UOP_AllocLarge: 77223013Sdim EmitAbsDifference(streamer, inst.getLabel(), begin); 78223013Sdim if (inst.getSize() > 512*1024-8) { 79223013Sdim b2 |= 0x10; 80223013Sdim streamer.EmitIntValue(b2, 1); 81223013Sdim w = inst.getSize() & 0xFFF8; 82223013Sdim streamer.EmitIntValue(w, 2); 83223013Sdim w = inst.getSize() >> 16; 84223013Sdim } else { 85223013Sdim streamer.EmitIntValue(b2, 1); 86223013Sdim w = inst.getSize() >> 3; 87223013Sdim } 88223013Sdim streamer.EmitIntValue(w, 2); 89223013Sdim break; 90223013Sdim case Win64EH::UOP_AllocSmall: 91223013Sdim b2 |= (((inst.getSize()-8) >> 3) & 0x0F) << 4; 92223013Sdim EmitAbsDifference(streamer, inst.getLabel(), begin); 93223013Sdim streamer.EmitIntValue(b2, 1); 94223013Sdim break; 95223013Sdim case Win64EH::UOP_SetFPReg: 96263508Sdim EmitAbsDifference(streamer, inst.getLabel(), begin); 97223013Sdim streamer.EmitIntValue(b2, 1); 98223013Sdim break; 99223013Sdim case Win64EH::UOP_SaveNonVol: 100223013Sdim case Win64EH::UOP_SaveXMM128: 101223013Sdim b2 |= (inst.getRegister() & 0x0F) << 4; 102223013Sdim EmitAbsDifference(streamer, inst.getLabel(), begin); 103223013Sdim streamer.EmitIntValue(b2, 1); 104223013Sdim w = inst.getOffset() >> 3; 105223013Sdim if (inst.getOperation() == Win64EH::UOP_SaveXMM128) 106223013Sdim w >>= 1; 107223013Sdim streamer.EmitIntValue(w, 2); 108223013Sdim break; 109223013Sdim case Win64EH::UOP_SaveNonVolBig: 110223013Sdim case Win64EH::UOP_SaveXMM128Big: 111223013Sdim b2 |= (inst.getRegister() & 0x0F) << 4; 112223013Sdim EmitAbsDifference(streamer, inst.getLabel(), begin); 113223013Sdim streamer.EmitIntValue(b2, 1); 114223013Sdim if (inst.getOperation() == Win64EH::UOP_SaveXMM128Big) 115223013Sdim w = inst.getOffset() & 0xFFF0; 116223013Sdim else 117223013Sdim w = inst.getOffset() & 0xFFF8; 118223013Sdim streamer.EmitIntValue(w, 2); 119223013Sdim w = inst.getOffset() >> 16; 120223013Sdim streamer.EmitIntValue(w, 2); 121223013Sdim break; 122223013Sdim case Win64EH::UOP_PushMachFrame: 123223013Sdim if (inst.isPushCodeFrame()) 124223013Sdim b2 |= 0x10; 125223013Sdim EmitAbsDifference(streamer, inst.getLabel(), begin); 126223013Sdim streamer.EmitIntValue(b2, 1); 127223013Sdim break; 128223013Sdim } 129223013Sdim} 130223013Sdim 131263508Sdimstatic void EmitSymbolRefWithOfs(MCStreamer &streamer, 132263508Sdim const MCSymbol *Base, 133263508Sdim const MCSymbol *Other) { 134263508Sdim MCContext &Context = streamer.getContext(); 135263508Sdim const MCSymbolRefExpr *BaseRef = MCSymbolRefExpr::Create(Base, Context); 136263508Sdim const MCSymbolRefExpr *OtherRef = MCSymbolRefExpr::Create(Other, Context); 137263508Sdim const MCExpr *Ofs = MCBinaryExpr::CreateSub(OtherRef, BaseRef, Context); 138263508Sdim const MCSymbolRefExpr *BaseRefRel = MCSymbolRefExpr::Create(Base, 139263508Sdim MCSymbolRefExpr::VK_COFF_IMGREL32, 140263508Sdim Context); 141263508Sdim streamer.EmitValue(MCBinaryExpr::CreateAdd(BaseRefRel, Ofs, Context), 4); 142263508Sdim} 143263508Sdim 144223013Sdimstatic void EmitRuntimeFunction(MCStreamer &streamer, 145223013Sdim const MCWin64EHUnwindInfo *info) { 146223013Sdim MCContext &context = streamer.getContext(); 147223013Sdim 148223013Sdim streamer.EmitValueToAlignment(4); 149263508Sdim EmitSymbolRefWithOfs(streamer, info->Function, info->Begin); 150263508Sdim EmitSymbolRefWithOfs(streamer, info->Function, info->End); 151263508Sdim streamer.EmitValue(MCSymbolRefExpr::Create(info->Symbol, 152263508Sdim MCSymbolRefExpr::VK_COFF_IMGREL32, 153263508Sdim context), 4); 154223013Sdim} 155223013Sdim 156223013Sdimstatic void EmitUnwindInfo(MCStreamer &streamer, MCWin64EHUnwindInfo *info) { 157223013Sdim // If this UNWIND_INFO already has a symbol, it's already been emitted. 158223013Sdim if (info->Symbol) return; 159223013Sdim 160223013Sdim MCContext &context = streamer.getContext(); 161223013Sdim streamer.EmitValueToAlignment(4); 162223013Sdim info->Symbol = context.CreateTempSymbol(); 163223013Sdim streamer.EmitLabel(info->Symbol); 164223013Sdim 165263508Sdim // Upper 3 bits are the version number (currently 1). 166263508Sdim uint8_t flags = 0x01; 167223013Sdim if (info->ChainedParent) 168223013Sdim flags |= Win64EH::UNW_ChainInfo << 3; 169223013Sdim else { 170223013Sdim if (info->HandlesUnwind) 171223013Sdim flags |= Win64EH::UNW_TerminateHandler << 3; 172223013Sdim if (info->HandlesExceptions) 173223013Sdim flags |= Win64EH::UNW_ExceptionHandler << 3; 174223013Sdim } 175223013Sdim streamer.EmitIntValue(flags, 1); 176223013Sdim 177223013Sdim if (info->PrologEnd) 178223013Sdim EmitAbsDifference(streamer, info->PrologEnd, info->Begin); 179223013Sdim else 180223013Sdim streamer.EmitIntValue(0, 1); 181223013Sdim 182223013Sdim uint8_t numCodes = CountOfUnwindCodes(info->Instructions); 183223013Sdim streamer.EmitIntValue(numCodes, 1); 184223013Sdim 185223013Sdim uint8_t frame = 0; 186223013Sdim if (info->LastFrameInst >= 0) { 187223013Sdim MCWin64EHInstruction &frameInst = info->Instructions[info->LastFrameInst]; 188223013Sdim assert(frameInst.getOperation() == Win64EH::UOP_SetFPReg); 189223013Sdim frame = (frameInst.getRegister() & 0x0F) | 190223013Sdim (frameInst.getOffset() & 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) { 197223013Sdim MCWin64EHInstruction inst = info->Instructions.back(); 198223013Sdim info->Instructions.pop_back(); 199223013Sdim EmitUnwindCode(streamer, info->Begin, inst); 200223013Sdim } 201223013Sdim 202263508Sdim // For alignment purposes, the instruction array will always have an even 203263508Sdim // number of entries, with the final entry potentially unused (in which case 204263508Sdim // the array will be one longer than indicated by the count of unwind codes 205263508Sdim // field). 206263508Sdim if (numCodes & 1) { 207263508Sdim streamer.EmitIntValue(0, 2); 208263508Sdim } 209263508Sdim 210223013Sdim if (flags & (Win64EH::UNW_ChainInfo << 3)) 211223013Sdim EmitRuntimeFunction(streamer, info->ChainedParent); 212223013Sdim else if (flags & 213223013Sdim ((Win64EH::UNW_TerminateHandler|Win64EH::UNW_ExceptionHandler) << 3)) 214263508Sdim streamer.EmitValue(MCSymbolRefExpr::Create(info->ExceptionHandler, 215263508Sdim MCSymbolRefExpr::VK_COFF_IMGREL32, 216263508Sdim context), 4); 217263508Sdim 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. 221263508Sdim streamer.EmitIntValue(0, 4); 222223013Sdim } 223223013Sdim} 224223013Sdim 225223013SdimStringRef MCWin64EHUnwindEmitter::GetSectionSuffix(const MCSymbol *func) { 226223013Sdim if (!func || !func->isInSection()) return ""; 227223013Sdim const MCSection *section = &func->getSection(); 228223013Sdim const MCSectionCOFF *COFFSection; 229223013Sdim if ((COFFSection = dyn_cast<MCSectionCOFF>(section))) { 230223013Sdim StringRef name = COFFSection->getSectionName(); 231223013Sdim size_t dollar = name.find('$'); 232223013Sdim size_t dot = name.find('.', 1); 233223013Sdim if (dollar == StringRef::npos && dot == StringRef::npos) 234223013Sdim return ""; 235223013Sdim if (dot == StringRef::npos) 236223013Sdim return name.substr(dollar); 237223013Sdim if (dollar == StringRef::npos || dot < dollar) 238223013Sdim return name.substr(dot); 239223013Sdim return name.substr(dollar); 240223013Sdim } 241223013Sdim return ""; 242223013Sdim} 243223013Sdim 244226633Sdimstatic const MCSection *getWin64EHTableSection(StringRef suffix, 245226633Sdim MCContext &context) { 246226633Sdim if (suffix == "") 247226633Sdim return context.getObjectFileInfo()->getXDataSection(); 248226633Sdim 249226633Sdim return context.getCOFFSection((".xdata"+suffix).str(), 250226633Sdim COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | 251239462Sdim COFF::IMAGE_SCN_MEM_READ, 252226633Sdim SectionKind::getDataRel()); 253226633Sdim} 254226633Sdim 255226633Sdimstatic const MCSection *getWin64EHFuncTableSection(StringRef suffix, 256226633Sdim MCContext &context) { 257226633Sdim if (suffix == "") 258226633Sdim return context.getObjectFileInfo()->getPDataSection(); 259226633Sdim return context.getCOFFSection((".pdata"+suffix).str(), 260226633Sdim COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | 261239462Sdim COFF::IMAGE_SCN_MEM_READ, 262226633Sdim SectionKind::getDataRel()); 263226633Sdim} 264226633Sdim 265223013Sdimvoid MCWin64EHUnwindEmitter::EmitUnwindInfo(MCStreamer &streamer, 266223013Sdim MCWin64EHUnwindInfo *info) { 267223013Sdim // Switch sections (the static function above is meant to be called from 268223013Sdim // here and from Emit(). 269223013Sdim MCContext &context = streamer.getContext(); 270223013Sdim const MCSection *xdataSect = 271226633Sdim getWin64EHTableSection(GetSectionSuffix(info->Function), context); 272223013Sdim streamer.SwitchSection(xdataSect); 273223013Sdim 274223013Sdim llvm::EmitUnwindInfo(streamer, info); 275223013Sdim} 276223013Sdim 277223013Sdimvoid MCWin64EHUnwindEmitter::Emit(MCStreamer &streamer) { 278223013Sdim MCContext &context = streamer.getContext(); 279223013Sdim // Emit the unwind info structs first. 280223013Sdim for (unsigned i = 0; i < streamer.getNumW64UnwindInfos(); ++i) { 281223013Sdim MCWin64EHUnwindInfo &info = streamer.getW64UnwindInfo(i); 282223013Sdim const MCSection *xdataSect = 283226633Sdim getWin64EHTableSection(GetSectionSuffix(info.Function), context); 284223013Sdim streamer.SwitchSection(xdataSect); 285223013Sdim llvm::EmitUnwindInfo(streamer, &info); 286223013Sdim } 287223013Sdim // Now emit RUNTIME_FUNCTION entries. 288223013Sdim for (unsigned i = 0; i < streamer.getNumW64UnwindInfos(); ++i) { 289223013Sdim MCWin64EHUnwindInfo &info = streamer.getW64UnwindInfo(i); 290223013Sdim const MCSection *pdataSect = 291226633Sdim getWin64EHFuncTableSection(GetSectionSuffix(info.Function), context); 292223013Sdim streamer.SwitchSection(pdataSect); 293223013Sdim EmitRuntimeFunction(streamer, &info); 294223013Sdim } 295223013Sdim} 296223013Sdim 297223013Sdim} // End of namespace llvm 298223013Sdim 299