Home | History | Annotate | Download | only in MC
      1 //===- lib/MC/MCWin64EH.cpp - MCWin64EH implementation --------------------===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 
     10 #include "llvm/MC/MCWin64EH.h"
     11 #include "llvm/ADT/Twine.h"
     12 #include "llvm/MC/MCContext.h"
     13 #include "llvm/MC/MCExpr.h"
     14 #include "llvm/MC/MCObjectFileInfo.h"
     15 #include "llvm/MC/MCSectionCOFF.h"
     16 #include "llvm/MC/MCStreamer.h"
     17 #include "llvm/MC/MCSymbol.h"
     18 
     19 namespace llvm {
     20 
     21 // NOTE: All relocations generated here are 4-byte image-relative.
     22 
     23 static uint8_t CountOfUnwindCodes(std::vector<MCWin64EHInstruction> &Insns) {
     24   uint8_t Count = 0;
     25   for (const auto &I : Insns) {
     26     switch (I.getOperation()) {
     27     case Win64EH::UOP_PushNonVol:
     28     case Win64EH::UOP_AllocSmall:
     29     case Win64EH::UOP_SetFPReg:
     30     case Win64EH::UOP_PushMachFrame:
     31       Count += 1;
     32       break;
     33     case Win64EH::UOP_SaveNonVol:
     34     case Win64EH::UOP_SaveXMM128:
     35       Count += 2;
     36       break;
     37     case Win64EH::UOP_SaveNonVolBig:
     38     case Win64EH::UOP_SaveXMM128Big:
     39       Count += 3;
     40       break;
     41     case Win64EH::UOP_AllocLarge:
     42       Count += (I.getSize() > 512 * 1024 - 8) ? 3 : 2;
     43       break;
     44     }
     45   }
     46   return Count;
     47 }
     48 
     49 static void EmitAbsDifference(MCStreamer &streamer, MCSymbol *lhs,
     50                               MCSymbol *rhs) {
     51   MCContext &context = streamer.getContext();
     52   const MCExpr *diff = MCBinaryExpr::CreateSub(MCSymbolRefExpr::Create(
     53                                                                   lhs, context),
     54                                                MCSymbolRefExpr::Create(
     55                                                                   rhs, context),
     56                                                context);
     57   streamer.EmitAbsValue(diff, 1);
     58 
     59 }
     60 
     61 static void EmitUnwindCode(MCStreamer &streamer, MCSymbol *begin,
     62                            MCWin64EHInstruction &inst) {
     63   uint8_t b2;
     64   uint16_t w;
     65   b2 = (inst.getOperation() & 0x0F);
     66   switch (inst.getOperation()) {
     67   case Win64EH::UOP_PushNonVol:
     68     EmitAbsDifference(streamer, inst.getLabel(), begin);
     69     b2 |= (inst.getRegister() & 0x0F) << 4;
     70     streamer.EmitIntValue(b2, 1);
     71     break;
     72   case Win64EH::UOP_AllocLarge:
     73     EmitAbsDifference(streamer, inst.getLabel(), begin);
     74     if (inst.getSize() > 512*1024-8) {
     75       b2 |= 0x10;
     76       streamer.EmitIntValue(b2, 1);
     77       w = inst.getSize() & 0xFFF8;
     78       streamer.EmitIntValue(w, 2);
     79       w = inst.getSize() >> 16;
     80     } else {
     81       streamer.EmitIntValue(b2, 1);
     82       w = inst.getSize() >> 3;
     83     }
     84     streamer.EmitIntValue(w, 2);
     85     break;
     86   case Win64EH::UOP_AllocSmall:
     87     b2 |= (((inst.getSize()-8) >> 3) & 0x0F) << 4;
     88     EmitAbsDifference(streamer, inst.getLabel(), begin);
     89     streamer.EmitIntValue(b2, 1);
     90     break;
     91   case Win64EH::UOP_SetFPReg:
     92     EmitAbsDifference(streamer, inst.getLabel(), begin);
     93     streamer.EmitIntValue(b2, 1);
     94     break;
     95   case Win64EH::UOP_SaveNonVol:
     96   case Win64EH::UOP_SaveXMM128:
     97     b2 |= (inst.getRegister() & 0x0F) << 4;
     98     EmitAbsDifference(streamer, inst.getLabel(), begin);
     99     streamer.EmitIntValue(b2, 1);
    100     w = inst.getOffset() >> 3;
    101     if (inst.getOperation() == Win64EH::UOP_SaveXMM128)
    102       w >>= 1;
    103     streamer.EmitIntValue(w, 2);
    104     break;
    105   case Win64EH::UOP_SaveNonVolBig:
    106   case Win64EH::UOP_SaveXMM128Big:
    107     b2 |= (inst.getRegister() & 0x0F) << 4;
    108     EmitAbsDifference(streamer, inst.getLabel(), begin);
    109     streamer.EmitIntValue(b2, 1);
    110     if (inst.getOperation() == Win64EH::UOP_SaveXMM128Big)
    111       w = inst.getOffset() & 0xFFF0;
    112     else
    113       w = inst.getOffset() & 0xFFF8;
    114     streamer.EmitIntValue(w, 2);
    115     w = inst.getOffset() >> 16;
    116     streamer.EmitIntValue(w, 2);
    117     break;
    118   case Win64EH::UOP_PushMachFrame:
    119     if (inst.isPushCodeFrame())
    120       b2 |= 0x10;
    121     EmitAbsDifference(streamer, inst.getLabel(), begin);
    122     streamer.EmitIntValue(b2, 1);
    123     break;
    124   }
    125 }
    126 
    127 static void EmitSymbolRefWithOfs(MCStreamer &streamer,
    128                                  const MCSymbol *Base,
    129                                  const MCSymbol *Other) {
    130   MCContext &Context = streamer.getContext();
    131   const MCSymbolRefExpr *BaseRef = MCSymbolRefExpr::Create(Base, Context);
    132   const MCSymbolRefExpr *OtherRef = MCSymbolRefExpr::Create(Other, Context);
    133   const MCExpr *Ofs = MCBinaryExpr::CreateSub(OtherRef, BaseRef, Context);
    134   const MCSymbolRefExpr *BaseRefRel = MCSymbolRefExpr::Create(Base,
    135                                               MCSymbolRefExpr::VK_COFF_IMGREL32,
    136                                               Context);
    137   streamer.EmitValue(MCBinaryExpr::CreateAdd(BaseRefRel, Ofs, Context), 4);
    138 }
    139 
    140 static void EmitRuntimeFunction(MCStreamer &streamer,
    141                                 const MCWin64EHUnwindInfo *info) {
    142   MCContext &context = streamer.getContext();
    143 
    144   streamer.EmitValueToAlignment(4);
    145   EmitSymbolRefWithOfs(streamer, info->Function, info->Begin);
    146   EmitSymbolRefWithOfs(streamer, info->Function, info->End);
    147   streamer.EmitValue(MCSymbolRefExpr::Create(info->Symbol,
    148                                              MCSymbolRefExpr::VK_COFF_IMGREL32,
    149                                              context), 4);
    150 }
    151 
    152 static void EmitUnwindInfo(MCStreamer &streamer, MCWin64EHUnwindInfo *info) {
    153   // If this UNWIND_INFO already has a symbol, it's already been emitted.
    154   if (info->Symbol) return;
    155 
    156   MCContext &context = streamer.getContext();
    157   streamer.EmitValueToAlignment(4);
    158   info->Symbol = context.CreateTempSymbol();
    159   streamer.EmitLabel(info->Symbol);
    160 
    161   // Upper 3 bits are the version number (currently 1).
    162   uint8_t flags = 0x01;
    163   if (info->ChainedParent)
    164     flags |= Win64EH::UNW_ChainInfo << 3;
    165   else {
    166     if (info->HandlesUnwind)
    167       flags |= Win64EH::UNW_TerminateHandler << 3;
    168     if (info->HandlesExceptions)
    169       flags |= Win64EH::UNW_ExceptionHandler << 3;
    170   }
    171   streamer.EmitIntValue(flags, 1);
    172 
    173   if (info->PrologEnd)
    174     EmitAbsDifference(streamer, info->PrologEnd, info->Begin);
    175   else
    176     streamer.EmitIntValue(0, 1);
    177 
    178   uint8_t numCodes = CountOfUnwindCodes(info->Instructions);
    179   streamer.EmitIntValue(numCodes, 1);
    180 
    181   uint8_t frame = 0;
    182   if (info->LastFrameInst >= 0) {
    183     MCWin64EHInstruction &frameInst = info->Instructions[info->LastFrameInst];
    184     assert(frameInst.getOperation() == Win64EH::UOP_SetFPReg);
    185     frame = (frameInst.getRegister() & 0x0F) |
    186             (frameInst.getOffset() & 0xF0);
    187   }
    188   streamer.EmitIntValue(frame, 1);
    189 
    190   // Emit unwind instructions (in reverse order).
    191   uint8_t numInst = info->Instructions.size();
    192   for (uint8_t c = 0; c < numInst; ++c) {
    193     MCWin64EHInstruction inst = info->Instructions.back();
    194     info->Instructions.pop_back();
    195     EmitUnwindCode(streamer, info->Begin, inst);
    196   }
    197 
    198   // For alignment purposes, the instruction array will always have an even
    199   // number of entries, with the final entry potentially unused (in which case
    200   // the array will be one longer than indicated by the count of unwind codes
    201   // field).
    202   if (numCodes & 1) {
    203     streamer.EmitIntValue(0, 2);
    204   }
    205 
    206   if (flags & (Win64EH::UNW_ChainInfo << 3))
    207     EmitRuntimeFunction(streamer, info->ChainedParent);
    208   else if (flags &
    209            ((Win64EH::UNW_TerminateHandler|Win64EH::UNW_ExceptionHandler) << 3))
    210     streamer.EmitValue(MCSymbolRefExpr::Create(info->ExceptionHandler,
    211                                               MCSymbolRefExpr::VK_COFF_IMGREL32,
    212                                               context), 4);
    213   else if (numCodes == 0) {
    214     // The minimum size of an UNWIND_INFO struct is 8 bytes. If we're not
    215     // a chained unwind info, if there is no handler, and if there are fewer
    216     // than 2 slots used in the unwind code array, we have to pad to 8 bytes.
    217     streamer.EmitIntValue(0, 4);
    218   }
    219 }
    220 
    221 StringRef MCWin64EHUnwindEmitter::GetSectionSuffix(const MCSymbol *func) {
    222   if (!func || !func->isInSection()) return "";
    223   const MCSection *section = &func->getSection();
    224   const MCSectionCOFF *COFFSection;
    225   if ((COFFSection = dyn_cast<MCSectionCOFF>(section))) {
    226     StringRef name = COFFSection->getSectionName();
    227     size_t dollar = name.find('$');
    228     size_t dot = name.find('.', 1);
    229     if (dollar == StringRef::npos && dot == StringRef::npos)
    230       return "";
    231     if (dot == StringRef::npos)
    232       return name.substr(dollar);
    233     if (dollar == StringRef::npos || dot < dollar)
    234       return name.substr(dot);
    235     return name.substr(dollar);
    236   }
    237   return "";
    238 }
    239 
    240 static const MCSection *getWin64EHTableSection(StringRef suffix,
    241                                                MCContext &context) {
    242   if (suffix == "")
    243     return context.getObjectFileInfo()->getXDataSection();
    244 
    245   return context.getCOFFSection((".xdata"+suffix).str(),
    246                                 COFF::IMAGE_SCN_CNT_INITIALIZED_DATA |
    247                                 COFF::IMAGE_SCN_MEM_READ,
    248                                 SectionKind::getDataRel());
    249 }
    250 
    251 static const MCSection *getWin64EHFuncTableSection(StringRef suffix,
    252                                                    MCContext &context) {
    253   if (suffix == "")
    254     return context.getObjectFileInfo()->getPDataSection();
    255   return context.getCOFFSection((".pdata"+suffix).str(),
    256                                 COFF::IMAGE_SCN_CNT_INITIALIZED_DATA |
    257                                 COFF::IMAGE_SCN_MEM_READ,
    258                                 SectionKind::getDataRel());
    259 }
    260 
    261 void MCWin64EHUnwindEmitter::EmitUnwindInfo(MCStreamer &streamer,
    262                                             MCWin64EHUnwindInfo *info) {
    263   // Switch sections (the static function above is meant to be called from
    264   // here and from Emit().
    265   MCContext &context = streamer.getContext();
    266   const MCSection *xdataSect =
    267     getWin64EHTableSection(GetSectionSuffix(info->Function), context);
    268   streamer.SwitchSection(xdataSect);
    269 
    270   llvm::EmitUnwindInfo(streamer, info);
    271 }
    272 
    273 void MCWin64EHUnwindEmitter::Emit(MCStreamer &Streamer) {
    274   MCContext &Context = Streamer.getContext();
    275 
    276   // Emit the unwind info structs first.
    277   for (const auto &CFI : Streamer.getW64UnwindInfos()) {
    278     const MCSection *XData =
    279         getWin64EHTableSection(GetSectionSuffix(CFI->Function), Context);
    280     Streamer.SwitchSection(XData);
    281     EmitUnwindInfo(Streamer, CFI);
    282   }
    283 
    284   // Now emit RUNTIME_FUNCTION entries.
    285   for (const auto &CFI : Streamer.getW64UnwindInfos()) {
    286     const MCSection *PData =
    287         getWin64EHFuncTableSection(GetSectionSuffix(CFI->Function), Context);
    288     Streamer.SwitchSection(PData);
    289     EmitRuntimeFunction(Streamer, CFI);
    290   }
    291 }
    292 
    293 } // End of namespace llvm
    294 
    295