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