1181834Sroberto//===- lib/MC/MCWin64EH.cpp - MCWin64EH implementation --------------------===// 2181834Sroberto// 3181834Sroberto// The LLVM Compiler Infrastructure 4181834Sroberto// 5181834Sroberto// This file is distributed under the University of Illinois Open Source 6181834Sroberto// License. See LICENSE.TXT for details. 7181834Sroberto// 8181834Sroberto//===----------------------------------------------------------------------===// 9181834Sroberto 10181834Sroberto#include "llvm/MC/MCWin64EH.h" 11181834Sroberto#include "llvm/ADT/Twine.h" 12181834Sroberto#include "llvm/MC/MCContext.h" 13181834Sroberto#include "llvm/MC/MCExpr.h" 14181834Sroberto#include "llvm/MC/MCObjectFileInfo.h" 15181834Sroberto#include "llvm/MC/MCSectionCOFF.h" 16181834Sroberto#include "llvm/MC/MCStreamer.h" 17181834Sroberto#include "llvm/MC/MCSymbol.h" 18181834Sroberto 19181834Srobertonamespace llvm { 20181834Sroberto 21181834Sroberto// NOTE: All relocations generated here are 4-byte image-relative. 22181834Sroberto 23181834Srobertostatic uint8_t CountOfUnwindCodes(std::vector<MCWin64EHInstruction> &instArray){ 24181834Sroberto uint8_t count = 0; 25181834Sroberto for (std::vector<MCWin64EHInstruction>::const_iterator I = instArray.begin(), 26181834Sroberto E = instArray.end(); I != E; ++I) { 27181834Sroberto switch (I->getOperation()) { 28181834Sroberto case Win64EH::UOP_PushNonVol: 29181834Sroberto case Win64EH::UOP_AllocSmall: 30181834Sroberto case Win64EH::UOP_SetFPReg: 31181834Sroberto case Win64EH::UOP_PushMachFrame: 32181834Sroberto count += 1; 33181834Sroberto break; 34181834Sroberto case Win64EH::UOP_SaveNonVol: 35181834Sroberto case Win64EH::UOP_SaveXMM128: 36181834Sroberto count += 2; 37181834Sroberto break; 38181834Sroberto case Win64EH::UOP_SaveNonVolBig: 39181834Sroberto case Win64EH::UOP_SaveXMM128Big: 40181834Sroberto count += 3; 41181834Sroberto break; 42181834Sroberto case Win64EH::UOP_AllocLarge: 43181834Sroberto if (I->getSize() > 512*1024-8) 44181834Sroberto count += 3; 45181834Sroberto else 46181834Sroberto count += 2; 47181834Sroberto break; 48181834Sroberto } 49181834Sroberto } 50181834Sroberto return count; 51181834Sroberto} 52181834Sroberto 53181834Srobertostatic void EmitAbsDifference(MCStreamer &streamer, MCSymbol *lhs, 54181834Sroberto MCSymbol *rhs) { 55181834Sroberto MCContext &context = streamer.getContext(); 56181834Sroberto const MCExpr *diff = MCBinaryExpr::CreateSub(MCSymbolRefExpr::Create( 57181834Sroberto lhs, context), 58181834Sroberto MCSymbolRefExpr::Create( 59181834Sroberto rhs, context), 60181834Sroberto context); 61181834Sroberto streamer.EmitAbsValue(diff, 1); 62181834Sroberto 63181834Sroberto} 64181834Sroberto 65181834Srobertostatic void EmitUnwindCode(MCStreamer &streamer, MCSymbol *begin, 66181834Sroberto MCWin64EHInstruction &inst) { 67181834Sroberto uint8_t b2; 68181834Sroberto uint16_t w; 69181834Sroberto b2 = (inst.getOperation() & 0x0F); 70181834Sroberto switch (inst.getOperation()) { 71181834Sroberto case Win64EH::UOP_PushNonVol: 72181834Sroberto EmitAbsDifference(streamer, inst.getLabel(), begin); 73181834Sroberto b2 |= (inst.getRegister() & 0x0F) << 4; 74181834Sroberto streamer.EmitIntValue(b2, 1); 75181834Sroberto break; 76181834Sroberto case Win64EH::UOP_AllocLarge: 77181834Sroberto EmitAbsDifference(streamer, inst.getLabel(), begin); 78181834Sroberto if (inst.getSize() > 512*1024-8) { 79181834Sroberto b2 |= 0x10; 80181834Sroberto streamer.EmitIntValue(b2, 1); 81181834Sroberto w = inst.getSize() & 0xFFF8; 82181834Sroberto streamer.EmitIntValue(w, 2); 83181834Sroberto w = inst.getSize() >> 16; 84181834Sroberto } else { 85181834Sroberto streamer.EmitIntValue(b2, 1); 86181834Sroberto w = inst.getSize() >> 3; 87181834Sroberto } 88181834Sroberto streamer.EmitIntValue(w, 2); 89181834Sroberto break; 90181834Sroberto case Win64EH::UOP_AllocSmall: 91181834Sroberto b2 |= (((inst.getSize()-8) >> 3) & 0x0F) << 4; 92181834Sroberto EmitAbsDifference(streamer, inst.getLabel(), begin); 93181834Sroberto streamer.EmitIntValue(b2, 1); 94181834Sroberto break; 95181834Sroberto case Win64EH::UOP_SetFPReg: 96181834Sroberto EmitAbsDifference(streamer, inst.getLabel(), begin); 97181834Sroberto streamer.EmitIntValue(b2, 1); 98181834Sroberto break; 99181834Sroberto case Win64EH::UOP_SaveNonVol: 100181834Sroberto case Win64EH::UOP_SaveXMM128: 101181834Sroberto b2 |= (inst.getRegister() & 0x0F) << 4; 102181834Sroberto EmitAbsDifference(streamer, inst.getLabel(), begin); 103181834Sroberto streamer.EmitIntValue(b2, 1); 104181834Sroberto w = inst.getOffset() >> 3; 105181834Sroberto if (inst.getOperation() == Win64EH::UOP_SaveXMM128) 106181834Sroberto w >>= 1; 107181834Sroberto streamer.EmitIntValue(w, 2); 108181834Sroberto break; 109181834Sroberto case Win64EH::UOP_SaveNonVolBig: 110181834Sroberto case Win64EH::UOP_SaveXMM128Big: 111181834Sroberto b2 |= (inst.getRegister() & 0x0F) << 4; 112181834Sroberto EmitAbsDifference(streamer, inst.getLabel(), begin); 113181834Sroberto streamer.EmitIntValue(b2, 1); 114181834Sroberto if (inst.getOperation() == Win64EH::UOP_SaveXMM128Big) 115181834Sroberto w = inst.getOffset() & 0xFFF0; 116181834Sroberto else 117181834Sroberto w = inst.getOffset() & 0xFFF8; 118181834Sroberto streamer.EmitIntValue(w, 2); 119181834Sroberto w = inst.getOffset() >> 16; 120181834Sroberto streamer.EmitIntValue(w, 2); 121181834Sroberto break; 122181834Sroberto case Win64EH::UOP_PushMachFrame: 123181834Sroberto if (inst.isPushCodeFrame()) 124181834Sroberto b2 |= 0x10; 125181834Sroberto EmitAbsDifference(streamer, inst.getLabel(), begin); 126181834Sroberto streamer.EmitIntValue(b2, 1); 127181834Sroberto break; 128181834Sroberto } 129181834Sroberto} 130181834Sroberto 131181834Srobertostatic void EmitSymbolRefWithOfs(MCStreamer &streamer, 132181834Sroberto const MCSymbol *Base, 133181834Sroberto const MCSymbol *Other) { 134181834Sroberto MCContext &Context = streamer.getContext(); 135181834Sroberto const MCSymbolRefExpr *BaseRef = MCSymbolRefExpr::Create(Base, Context); 136181834Sroberto const MCSymbolRefExpr *OtherRef = MCSymbolRefExpr::Create(Other, Context); 137181834Sroberto const MCExpr *Ofs = MCBinaryExpr::CreateSub(OtherRef, BaseRef, Context); 138181834Sroberto const MCSymbolRefExpr *BaseRefRel = MCSymbolRefExpr::Create(Base, 139181834Sroberto MCSymbolRefExpr::VK_COFF_IMGREL32, 140181834Sroberto Context); 141181834Sroberto streamer.EmitValue(MCBinaryExpr::CreateAdd(BaseRefRel, Ofs, Context), 4); 142181834Sroberto} 143181834Sroberto 144181834Srobertostatic void EmitRuntimeFunction(MCStreamer &streamer, 145181834Sroberto const MCWin64EHUnwindInfo *info) { 146181834Sroberto MCContext &context = streamer.getContext(); 147181834Sroberto 148181834Sroberto streamer.EmitValueToAlignment(4); 149181834Sroberto EmitSymbolRefWithOfs(streamer, info->Function, info->Begin); 150181834Sroberto EmitSymbolRefWithOfs(streamer, info->Function, info->End); 151181834Sroberto streamer.EmitValue(MCSymbolRefExpr::Create(info->Symbol, 152181834Sroberto MCSymbolRefExpr::VK_COFF_IMGREL32, 153181834Sroberto context), 4); 154181834Sroberto} 155181834Sroberto 156181834Srobertostatic void EmitUnwindInfo(MCStreamer &streamer, MCWin64EHUnwindInfo *info) { 157181834Sroberto // If this UNWIND_INFO already has a symbol, it's already been emitted. 158181834Sroberto if (info->Symbol) return; 159181834Sroberto 160181834Sroberto MCContext &context = streamer.getContext(); 161181834Sroberto streamer.EmitValueToAlignment(4); 162181834Sroberto info->Symbol = context.CreateTempSymbol(); 163181834Sroberto streamer.EmitLabel(info->Symbol); 164181834Sroberto 165181834Sroberto // Upper 3 bits are the version number (currently 1). 166181834Sroberto uint8_t flags = 0x01; 167181834Sroberto if (info->ChainedParent) 168181834Sroberto flags |= Win64EH::UNW_ChainInfo << 3; 169181834Sroberto else { 170181834Sroberto if (info->HandlesUnwind) 171181834Sroberto flags |= Win64EH::UNW_TerminateHandler << 3; 172181834Sroberto if (info->HandlesExceptions) 173181834Sroberto flags |= Win64EH::UNW_ExceptionHandler << 3; 174181834Sroberto } 175181834Sroberto streamer.EmitIntValue(flags, 1); 176181834Sroberto 177181834Sroberto if (info->PrologEnd) 178181834Sroberto EmitAbsDifference(streamer, info->PrologEnd, info->Begin); 179181834Sroberto else 180181834Sroberto streamer.EmitIntValue(0, 1); 181181834Sroberto 182181834Sroberto uint8_t numCodes = CountOfUnwindCodes(info->Instructions); 183181834Sroberto streamer.EmitIntValue(numCodes, 1); 184181834Sroberto 185181834Sroberto uint8_t frame = 0; 186181834Sroberto if (info->LastFrameInst >= 0) { 187181834Sroberto MCWin64EHInstruction &frameInst = info->Instructions[info->LastFrameInst]; 188181834Sroberto assert(frameInst.getOperation() == Win64EH::UOP_SetFPReg); 189181834Sroberto frame = (frameInst.getRegister() & 0x0F) | 190181834Sroberto (frameInst.getOffset() & 0xF0); 191181834Sroberto } 192181834Sroberto streamer.EmitIntValue(frame, 1); 193181834Sroberto 194181834Sroberto // Emit unwind instructions (in reverse order). 195181834Sroberto uint8_t numInst = info->Instructions.size(); 196181834Sroberto for (uint8_t c = 0; c < numInst; ++c) { 197181834Sroberto MCWin64EHInstruction inst = info->Instructions.back(); 198181834Sroberto info->Instructions.pop_back(); 199181834Sroberto EmitUnwindCode(streamer, info->Begin, inst); 200181834Sroberto } 201181834Sroberto 202181834Sroberto // For alignment purposes, the instruction array will always have an even 203181834Sroberto // number of entries, with the final entry potentially unused (in which case 204181834Sroberto // the array will be one longer than indicated by the count of unwind codes 205181834Sroberto // field). 206181834Sroberto if (numCodes & 1) { 207181834Sroberto streamer.EmitIntValue(0, 2); 208181834Sroberto } 209181834Sroberto 210181834Sroberto if (flags & (Win64EH::UNW_ChainInfo << 3)) 211181834Sroberto EmitRuntimeFunction(streamer, info->ChainedParent); 212181834Sroberto else if (flags & 213181834Sroberto ((Win64EH::UNW_TerminateHandler|Win64EH::UNW_ExceptionHandler) << 3)) 214181834Sroberto streamer.EmitValue(MCSymbolRefExpr::Create(info->ExceptionHandler, 215181834Sroberto MCSymbolRefExpr::VK_COFF_IMGREL32, 216181834Sroberto context), 4); 217181834Sroberto else if (numCodes == 0) { 218181834Sroberto // The minimum size of an UNWIND_INFO struct is 8 bytes. If we're not 219181834Sroberto // a chained unwind info, if there is no handler, and if there are fewer 220181834Sroberto // than 2 slots used in the unwind code array, we have to pad to 8 bytes. 221181834Sroberto streamer.EmitIntValue(0, 4); 222181834Sroberto } 223181834Sroberto} 224181834Sroberto 225181834SrobertoStringRef MCWin64EHUnwindEmitter::GetSectionSuffix(const MCSymbol *func) { 226181834Sroberto if (!func || !func->isInSection()) return ""; 227181834Sroberto const MCSection *section = &func->getSection(); 228181834Sroberto const MCSectionCOFF *COFFSection; 229181834Sroberto if ((COFFSection = dyn_cast<MCSectionCOFF>(section))) { 230181834Sroberto StringRef name = COFFSection->getSectionName(); 231181834Sroberto size_t dollar = name.find('$'); 232181834Sroberto size_t dot = name.find('.', 1); 233181834Sroberto if (dollar == StringRef::npos && dot == StringRef::npos) 234181834Sroberto return ""; 235181834Sroberto if (dot == StringRef::npos) 236181834Sroberto return name.substr(dollar); 237181834Sroberto if (dollar == StringRef::npos || dot < dollar) 238181834Sroberto return name.substr(dot); 239181834Sroberto return name.substr(dollar); 240181834Sroberto } 241181834Sroberto return ""; 242181834Sroberto} 243181834Sroberto 244181834Srobertostatic const MCSection *getWin64EHTableSection(StringRef suffix, 245181834Sroberto MCContext &context) { 246181834Sroberto if (suffix == "") 247181834Sroberto return context.getObjectFileInfo()->getXDataSection(); 248181834Sroberto 249181834Sroberto return context.getCOFFSection((".xdata"+suffix).str(), 250181834Sroberto COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | 251181834Sroberto COFF::IMAGE_SCN_MEM_READ, 252181834Sroberto SectionKind::getDataRel()); 253181834Sroberto} 254181834Sroberto 255181834Srobertostatic const MCSection *getWin64EHFuncTableSection(StringRef suffix, 256181834Sroberto MCContext &context) { 257181834Sroberto if (suffix == "") 258181834Sroberto return context.getObjectFileInfo()->getPDataSection(); 259181834Sroberto return context.getCOFFSection((".pdata"+suffix).str(), 260181834Sroberto COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | 261181834Sroberto COFF::IMAGE_SCN_MEM_READ, 262181834Sroberto SectionKind::getDataRel()); 263181834Sroberto} 264181834Sroberto 265181834Srobertovoid MCWin64EHUnwindEmitter::EmitUnwindInfo(MCStreamer &streamer, 266181834Sroberto MCWin64EHUnwindInfo *info) { 267181834Sroberto // Switch sections (the static function above is meant to be called from 268181834Sroberto // here and from Emit(). 269181834Sroberto MCContext &context = streamer.getContext(); 270181834Sroberto const MCSection *xdataSect = 271181834Sroberto getWin64EHTableSection(GetSectionSuffix(info->Function), context); 272181834Sroberto streamer.SwitchSection(xdataSect); 273181834Sroberto 274181834Sroberto llvm::EmitUnwindInfo(streamer, info); 275181834Sroberto} 276181834Sroberto 277181834Srobertovoid MCWin64EHUnwindEmitter::Emit(MCStreamer &streamer) { 278181834Sroberto MCContext &context = streamer.getContext(); 279181834Sroberto // Emit the unwind info structs first. 280181834Sroberto for (unsigned i = 0; i < streamer.getNumW64UnwindInfos(); ++i) { 281181834Sroberto MCWin64EHUnwindInfo &info = streamer.getW64UnwindInfo(i); 282181834Sroberto const MCSection *xdataSect = 283181834Sroberto getWin64EHTableSection(GetSectionSuffix(info.Function), context); 284181834Sroberto streamer.SwitchSection(xdataSect); 285181834Sroberto llvm::EmitUnwindInfo(streamer, &info); 286181834Sroberto } 287181834Sroberto // Now emit RUNTIME_FUNCTION entries. 288181834Sroberto for (unsigned i = 0; i < streamer.getNumW64UnwindInfos(); ++i) { 289181834Sroberto MCWin64EHUnwindInfo &info = streamer.getW64UnwindInfo(i); 290181834Sroberto const MCSection *pdataSect = 291181834Sroberto getWin64EHFuncTableSection(GetSectionSuffix(info.Function), context); 292181834Sroberto streamer.SwitchSection(pdataSect); 293181834Sroberto EmitRuntimeFunction(streamer, &info); 294181834Sroberto } 295181834Sroberto} 296181834Sroberto 297181834Sroberto} // End of namespace llvm 298181834Sroberto 299181834Sroberto