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