1 | // Licensed to the .NET Foundation under one or more agreements. |
2 | // The .NET Foundation licenses this file to you under the MIT license. |
3 | // See the LICENSE file in the project root for more information. |
4 | |
5 | /*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
6 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
7 | XX XX |
8 | XX UnwindInfo XX |
9 | XX XX |
10 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
11 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
12 | */ |
13 | |
14 | #include "jitpch.h" |
15 | #ifdef _MSC_VER |
16 | #pragma hdrstop |
17 | #endif |
18 | |
19 | #if defined(_TARGET_AMD64_) |
20 | #ifdef UNIX_AMD64_ABI |
21 | int Compiler::mapRegNumToDwarfReg(regNumber reg) |
22 | { |
23 | int dwarfReg = DWARF_REG_ILLEGAL; |
24 | |
25 | switch (reg) |
26 | { |
27 | case REG_RAX: |
28 | dwarfReg = 0; |
29 | break; |
30 | case REG_RCX: |
31 | dwarfReg = 2; |
32 | break; |
33 | case REG_RDX: |
34 | dwarfReg = 1; |
35 | break; |
36 | case REG_RBX: |
37 | dwarfReg = 3; |
38 | break; |
39 | case REG_RSP: |
40 | dwarfReg = 7; |
41 | break; |
42 | case REG_RBP: |
43 | dwarfReg = 6; |
44 | break; |
45 | case REG_RSI: |
46 | dwarfReg = 4; |
47 | break; |
48 | case REG_RDI: |
49 | dwarfReg = 5; |
50 | break; |
51 | case REG_R8: |
52 | dwarfReg = 8; |
53 | break; |
54 | case REG_R9: |
55 | dwarfReg = 9; |
56 | break; |
57 | case REG_R10: |
58 | dwarfReg = 10; |
59 | break; |
60 | case REG_R11: |
61 | dwarfReg = 11; |
62 | break; |
63 | case REG_R12: |
64 | dwarfReg = 12; |
65 | break; |
66 | case REG_R13: |
67 | dwarfReg = 13; |
68 | break; |
69 | case REG_R14: |
70 | dwarfReg = 14; |
71 | break; |
72 | case REG_R15: |
73 | dwarfReg = 15; |
74 | break; |
75 | case REG_XMM0: |
76 | dwarfReg = 17; |
77 | break; |
78 | case REG_XMM1: |
79 | dwarfReg = 18; |
80 | break; |
81 | case REG_XMM2: |
82 | dwarfReg = 19; |
83 | break; |
84 | case REG_XMM3: |
85 | dwarfReg = 20; |
86 | break; |
87 | case REG_XMM4: |
88 | dwarfReg = 21; |
89 | break; |
90 | case REG_XMM5: |
91 | dwarfReg = 22; |
92 | break; |
93 | case REG_XMM6: |
94 | dwarfReg = 23; |
95 | break; |
96 | case REG_XMM7: |
97 | dwarfReg = 24; |
98 | break; |
99 | case REG_XMM8: |
100 | dwarfReg = 25; |
101 | break; |
102 | case REG_XMM9: |
103 | dwarfReg = 26; |
104 | break; |
105 | case REG_XMM10: |
106 | dwarfReg = 27; |
107 | break; |
108 | case REG_XMM11: |
109 | dwarfReg = 28; |
110 | break; |
111 | case REG_XMM12: |
112 | dwarfReg = 29; |
113 | break; |
114 | case REG_XMM13: |
115 | dwarfReg = 30; |
116 | break; |
117 | case REG_XMM14: |
118 | dwarfReg = 31; |
119 | break; |
120 | case REG_XMM15: |
121 | dwarfReg = 32; |
122 | break; |
123 | default: |
124 | noway_assert(!"unexpected REG_NUM" ); |
125 | } |
126 | |
127 | return dwarfReg; |
128 | } |
129 | |
130 | #endif // UNIX_AMD64_ABI |
131 | |
132 | //------------------------------------------------------------------------ |
133 | // Compiler::unwindBegProlog: Initialize the unwind info data structures. |
134 | // Called at the beginning of main function or funclet prolog generation. |
135 | // |
136 | void Compiler::unwindBegProlog() |
137 | { |
138 | #ifdef UNIX_AMD64_ABI |
139 | if (generateCFIUnwindCodes()) |
140 | { |
141 | unwindBegPrologCFI(); |
142 | } |
143 | else |
144 | #endif // UNIX_AMD64_ABI |
145 | { |
146 | unwindBegPrologWindows(); |
147 | } |
148 | } |
149 | |
150 | void Compiler::unwindBegPrologWindows() |
151 | { |
152 | assert(compGeneratingProlog); |
153 | |
154 | FuncInfoDsc* func = funCurrentFunc(); |
155 | |
156 | // There is only one prolog for a function/funclet, and it comes first. So now is |
157 | // a good time to initialize all the unwind data structures. |
158 | |
159 | unwindGetFuncLocations(func, true, &func->startLoc, &func->endLoc); |
160 | |
161 | if (fgFirstColdBlock != nullptr) |
162 | { |
163 | unwindGetFuncLocations(func, false, &func->coldStartLoc, &func->coldEndLoc); |
164 | } |
165 | |
166 | func->unwindCodeSlot = sizeof(func->unwindCodes); |
167 | func->unwindHeader.Version = 1; |
168 | func->unwindHeader.Flags = 0; |
169 | func->unwindHeader.CountOfUnwindCodes = 0; |
170 | func->unwindHeader.FrameRegister = 0; |
171 | func->unwindHeader.FrameOffset = 0; |
172 | } |
173 | |
174 | //------------------------------------------------------------------------ |
175 | // Compiler::unwindEndProlog: Called at the end of main function or funclet |
176 | // prolog generation to indicate there is no more unwind information for this prolog. |
177 | // |
178 | void Compiler::unwindEndProlog() |
179 | { |
180 | assert(compGeneratingProlog); |
181 | } |
182 | |
183 | //------------------------------------------------------------------------ |
184 | // Compiler::unwindBegEpilog: Called at the beginning of main function or funclet |
185 | // epilog generation. |
186 | // |
187 | void Compiler::unwindBegEpilog() |
188 | { |
189 | assert(compGeneratingEpilog); |
190 | } |
191 | |
192 | //------------------------------------------------------------------------ |
193 | // Compiler::unwindEndEpilog: Called at the end of main function or funclet |
194 | // epilog generation. |
195 | // |
196 | void Compiler::unwindEndEpilog() |
197 | { |
198 | assert(compGeneratingEpilog); |
199 | } |
200 | |
201 | //------------------------------------------------------------------------ |
202 | // Compiler::unwindPush: Record a push/save of a register. |
203 | // |
204 | // Arguments: |
205 | // reg - The register being pushed/saved. |
206 | // |
207 | void Compiler::unwindPush(regNumber reg) |
208 | { |
209 | #ifdef UNIX_AMD64_ABI |
210 | if (generateCFIUnwindCodes()) |
211 | { |
212 | unwindPushPopCFI(reg); |
213 | } |
214 | else |
215 | #endif // UNIX_AMD64_ABI |
216 | { |
217 | unwindPushWindows(reg); |
218 | } |
219 | } |
220 | |
221 | void Compiler::unwindPushWindows(regNumber reg) |
222 | { |
223 | assert(compGeneratingProlog); |
224 | |
225 | FuncInfoDsc* func = funCurrentFunc(); |
226 | |
227 | assert(func->unwindHeader.Version == 1); // Can't call this before unwindBegProlog |
228 | assert(func->unwindHeader.CountOfUnwindCodes == 0); // Can't call this after unwindReserve |
229 | assert(func->unwindCodeSlot > sizeof(UNWIND_CODE)); |
230 | UNWIND_CODE* code = (UNWIND_CODE*)&func->unwindCodes[func->unwindCodeSlot -= sizeof(UNWIND_CODE)]; |
231 | unsigned int cbProlog = unwindGetCurrentOffset(func); |
232 | noway_assert((BYTE)cbProlog == cbProlog); |
233 | code->CodeOffset = (BYTE)cbProlog; |
234 | |
235 | if ((RBM_CALLEE_SAVED & genRegMask(reg)) |
236 | #if ETW_EBP_FRAMED |
237 | // In case of ETW_EBP_FRAMED defined the REG_FPBASE (RBP) |
238 | // is excluded from the callee-save register list. |
239 | // Make sure the register gets PUSH unwind info in this case, |
240 | // since it is pushed as a frame register. |
241 | || (reg == REG_FPBASE) |
242 | #endif // ETW_EBP_FRAMED |
243 | ) |
244 | { |
245 | code->UnwindOp = UWOP_PUSH_NONVOL; |
246 | code->OpInfo = (BYTE)reg; |
247 | } |
248 | else |
249 | { |
250 | // Push of a volatile register is just a small stack allocation |
251 | code->UnwindOp = UWOP_ALLOC_SMALL; |
252 | code->OpInfo = 0; |
253 | } |
254 | } |
255 | |
256 | #ifdef UNIX_AMD64_ABI |
257 | #endif // UNIX_AMD64_ABI |
258 | |
259 | //------------------------------------------------------------------------ |
260 | // Compiler::unwindAllocStack: Record a stack frame allocation (sub sp, X). |
261 | // |
262 | // Arguments: |
263 | // size - The size of the stack frame allocation (the amount subtracted from the stack pointer). |
264 | // |
265 | void Compiler::unwindAllocStack(unsigned size) |
266 | { |
267 | #ifdef UNIX_AMD64_ABI |
268 | if (generateCFIUnwindCodes()) |
269 | { |
270 | unwindAllocStackCFI(size); |
271 | } |
272 | else |
273 | #endif // UNIX_AMD64_ABI |
274 | { |
275 | unwindAllocStackWindows(size); |
276 | } |
277 | } |
278 | |
279 | void Compiler::unwindAllocStackWindows(unsigned size) |
280 | { |
281 | assert(compGeneratingProlog); |
282 | |
283 | FuncInfoDsc* func = funCurrentFunc(); |
284 | |
285 | assert(func->unwindHeader.Version == 1); // Can't call this before unwindBegProlog |
286 | assert(func->unwindHeader.CountOfUnwindCodes == 0); // Can't call this after unwindReserve |
287 | assert(size % 8 == 0); // Stack size is *always* 8 byte aligned |
288 | UNWIND_CODE* code; |
289 | if (size <= 128) |
290 | { |
291 | assert(func->unwindCodeSlot > sizeof(UNWIND_CODE)); |
292 | code = (UNWIND_CODE*)&func->unwindCodes[func->unwindCodeSlot -= sizeof(UNWIND_CODE)]; |
293 | code->UnwindOp = UWOP_ALLOC_SMALL; |
294 | code->OpInfo = (size - 8) / 8; |
295 | } |
296 | else if (size <= 0x7FFF8) |
297 | { |
298 | assert(func->unwindCodeSlot > (sizeof(UNWIND_CODE) + sizeof(USHORT))); |
299 | USHORT* codedSize = (USHORT*)&func->unwindCodes[func->unwindCodeSlot -= sizeof(USHORT)]; |
300 | *codedSize = (USHORT)(size / 8); |
301 | code = (UNWIND_CODE*)&func->unwindCodes[func->unwindCodeSlot -= sizeof(UNWIND_CODE)]; |
302 | code->UnwindOp = UWOP_ALLOC_LARGE; |
303 | code->OpInfo = 0; |
304 | } |
305 | else |
306 | { |
307 | assert(func->unwindCodeSlot > (sizeof(UNWIND_CODE) + sizeof(ULONG))); |
308 | ULONG* codedSize = (ULONG*)&func->unwindCodes[func->unwindCodeSlot -= sizeof(ULONG)]; |
309 | *codedSize = size; |
310 | code = (UNWIND_CODE*)&func->unwindCodes[func->unwindCodeSlot -= sizeof(UNWIND_CODE)]; |
311 | code->UnwindOp = UWOP_ALLOC_LARGE; |
312 | code->OpInfo = 1; |
313 | } |
314 | unsigned int cbProlog = unwindGetCurrentOffset(func); |
315 | noway_assert((BYTE)cbProlog == cbProlog); |
316 | code->CodeOffset = (BYTE)cbProlog; |
317 | } |
318 | |
319 | //------------------------------------------------------------------------ |
320 | // Compiler::unwindSetFrameReg: Record a frame register. |
321 | // |
322 | // Arguments: |
323 | // reg - The register being set as the frame register. |
324 | // offset - The offset from the current stack pointer that the frame pointer will point at. |
325 | // |
326 | void Compiler::unwindSetFrameReg(regNumber reg, unsigned offset) |
327 | { |
328 | #ifdef UNIX_AMD64_ABI |
329 | if (generateCFIUnwindCodes()) |
330 | { |
331 | unwindSetFrameRegCFI(reg, offset); |
332 | } |
333 | else |
334 | #endif // UNIX_AMD64_ABI |
335 | { |
336 | unwindSetFrameRegWindows(reg, offset); |
337 | } |
338 | } |
339 | |
340 | void Compiler::unwindSetFrameRegWindows(regNumber reg, unsigned offset) |
341 | { |
342 | assert(compGeneratingProlog); |
343 | |
344 | FuncInfoDsc* func = funCurrentFunc(); |
345 | |
346 | assert(func->unwindHeader.Version == 1); // Can't call this before unwindBegProlog |
347 | assert(func->unwindHeader.CountOfUnwindCodes == 0); // Can't call this after unwindReserve |
348 | unsigned int cbProlog = unwindGetCurrentOffset(func); |
349 | noway_assert((BYTE)cbProlog == cbProlog); |
350 | |
351 | func->unwindHeader.FrameRegister = (BYTE)reg; |
352 | |
353 | #ifdef UNIX_AMD64_ABI |
354 | if (offset > 240) |
355 | { |
356 | // On Unix only, we have a CLR-only extension to the AMD64 unwind codes: UWOP_SET_FPREG_LARGE. |
357 | // It has a 32-bit offset (scaled). You must set UNWIND_INFO.FrameOffset to 15. The 32-bit |
358 | // offset follows in 2 UNWIND_CODE fields. |
359 | |
360 | assert(func->unwindCodeSlot > (sizeof(UNWIND_CODE) + sizeof(ULONG))); |
361 | ULONG* codedSize = (ULONG*)&func->unwindCodes[func->unwindCodeSlot -= sizeof(ULONG)]; |
362 | assert(offset % 16 == 0); |
363 | *codedSize = offset / 16; |
364 | |
365 | UNWIND_CODE* code = (UNWIND_CODE*)&func->unwindCodes[func->unwindCodeSlot -= sizeof(UNWIND_CODE)]; |
366 | code->CodeOffset = (BYTE)cbProlog; |
367 | code->OpInfo = 0; |
368 | code->UnwindOp = UWOP_SET_FPREG_LARGE; |
369 | func->unwindHeader.FrameOffset = 15; |
370 | } |
371 | else |
372 | #endif // UNIX_AMD64_ABI |
373 | { |
374 | assert(func->unwindCodeSlot > sizeof(UNWIND_CODE)); |
375 | UNWIND_CODE* code = (UNWIND_CODE*)&func->unwindCodes[func->unwindCodeSlot -= sizeof(UNWIND_CODE)]; |
376 | code->CodeOffset = (BYTE)cbProlog; |
377 | code->OpInfo = 0; |
378 | code->UnwindOp = UWOP_SET_FPREG; |
379 | assert(offset <= 240); |
380 | assert(offset % 16 == 0); |
381 | func->unwindHeader.FrameOffset = offset / 16; |
382 | } |
383 | } |
384 | |
385 | //------------------------------------------------------------------------ |
386 | // Compiler::unwindSaveReg: Record a register save. |
387 | // |
388 | // Arguments: |
389 | // reg - The register being saved. |
390 | // offset - The offset from the current stack pointer where the register is being saved. |
391 | // |
392 | void Compiler::unwindSaveReg(regNumber reg, unsigned offset) |
393 | { |
394 | #ifdef UNIX_AMD64_ABI |
395 | if (generateCFIUnwindCodes()) |
396 | { |
397 | unwindSaveRegCFI(reg, offset); |
398 | } |
399 | else |
400 | #endif // UNIX_AMD64_ABI |
401 | { |
402 | unwindSaveRegWindows(reg, offset); |
403 | } |
404 | } |
405 | |
406 | void Compiler::unwindSaveRegWindows(regNumber reg, unsigned offset) |
407 | { |
408 | assert(compGeneratingProlog); |
409 | |
410 | FuncInfoDsc* func = funCurrentFunc(); |
411 | |
412 | assert(func->unwindHeader.Version == 1); // Can't call this before unwindBegProlog |
413 | assert(func->unwindHeader.CountOfUnwindCodes == 0); // Can't call this after unwindReserve |
414 | if (RBM_CALLEE_SAVED & genRegMask(reg)) |
415 | { |
416 | UNWIND_CODE* code; |
417 | if (offset < 0x80000) |
418 | { |
419 | assert(func->unwindCodeSlot > (sizeof(UNWIND_CODE) + sizeof(USHORT))); |
420 | USHORT* codedSize = (USHORT*)&func->unwindCodes[func->unwindCodeSlot -= sizeof(USHORT)]; |
421 | code = (UNWIND_CODE*)&func->unwindCodes[func->unwindCodeSlot -= sizeof(UNWIND_CODE)]; |
422 | |
423 | // As per AMD64 ABI, if saving entire xmm reg, then offset need to be scaled by 16. |
424 | if (genIsValidFloatReg(reg)) |
425 | { |
426 | *codedSize = (USHORT)(offset / 16); |
427 | code->UnwindOp = UWOP_SAVE_XMM128; |
428 | } |
429 | else |
430 | { |
431 | *codedSize = (USHORT)(offset / 8); |
432 | code->UnwindOp = UWOP_SAVE_NONVOL; |
433 | } |
434 | } |
435 | else |
436 | { |
437 | assert(func->unwindCodeSlot > (sizeof(UNWIND_CODE) + sizeof(ULONG))); |
438 | ULONG* codedSize = (ULONG*)&func->unwindCodes[func->unwindCodeSlot -= sizeof(ULONG)]; |
439 | *codedSize = offset; |
440 | code = (UNWIND_CODE*)&func->unwindCodes[func->unwindCodeSlot -= sizeof(UNWIND_CODE)]; |
441 | code->UnwindOp = (genIsValidFloatReg(reg)) ? UWOP_SAVE_XMM128_FAR : UWOP_SAVE_NONVOL_FAR; |
442 | } |
443 | code->OpInfo = (BYTE)reg; |
444 | unsigned int cbProlog = unwindGetCurrentOffset(func); |
445 | noway_assert((BYTE)cbProlog == cbProlog); |
446 | code->CodeOffset = (BYTE)cbProlog; |
447 | } |
448 | } |
449 | |
450 | #ifdef UNIX_AMD64_ABI |
451 | void Compiler::unwindSaveRegCFI(regNumber reg, unsigned offset) |
452 | { |
453 | assert(compGeneratingProlog); |
454 | |
455 | if (RBM_CALLEE_SAVED & genRegMask(reg)) |
456 | { |
457 | FuncInfoDsc* func = funCurrentFunc(); |
458 | |
459 | unsigned int cbProlog = unwindGetCurrentOffset(func); |
460 | noway_assert((BYTE)cbProlog == cbProlog); |
461 | createCfiCode(func, cbProlog, CFI_REL_OFFSET, mapRegNumToDwarfReg(reg), offset); |
462 | } |
463 | } |
464 | #endif // UNIX_AMD64_ABI |
465 | |
466 | #ifdef DEBUG |
467 | |
468 | //------------------------------------------------------------------------ |
469 | // DumpUnwindInfo: Dump the unwind data. |
470 | // |
471 | // Arguments: |
472 | // isHotCode - true if this unwind data is for the hot section, false otherwise. |
473 | // startOffset - byte offset of the code start that this unwind data represents. |
474 | // endOffset - byte offset of the code end that this unwind data represents. |
475 | // pHeader - pointer to the unwind data blob. |
476 | // |
477 | void DumpUnwindInfo(bool isHotCode, |
478 | UNATIVE_OFFSET startOffset, |
479 | UNATIVE_OFFSET endOffset, |
480 | const UNWIND_INFO* const ) |
481 | { |
482 | printf("Unwind Info%s:\n" , isHotCode ? "" : " COLD" ); |
483 | printf(" >> Start offset : 0x%06x (not in unwind data)\n" , dspOffset(startOffset)); |
484 | printf(" >> End offset : 0x%06x (not in unwind data)\n" , dspOffset(endOffset)); |
485 | |
486 | if (pHeader == nullptr) |
487 | { |
488 | // Cold AMD64 code doesn't have unwind info; the VM creates chained unwind info. |
489 | assert(!isHotCode); |
490 | return; |
491 | } |
492 | |
493 | printf(" Version : %u\n" , pHeader->Version); |
494 | printf(" Flags : 0x%02x" , pHeader->Flags); |
495 | if (pHeader->Flags) |
496 | { |
497 | const UCHAR flags = pHeader->Flags; |
498 | printf(" (" ); |
499 | if (flags & UNW_FLAG_EHANDLER) |
500 | { |
501 | printf(" UNW_FLAG_EHANDLER" ); |
502 | } |
503 | if (flags & UNW_FLAG_UHANDLER) |
504 | { |
505 | printf(" UNW_FLAG_UHANDLER" ); |
506 | } |
507 | if (flags & UNW_FLAG_CHAININFO) |
508 | { |
509 | printf(" UNW_FLAG_CHAININFO" ); |
510 | } |
511 | printf(")" ); |
512 | } |
513 | printf("\n" ); |
514 | printf(" SizeOfProlog : 0x%02X\n" , pHeader->SizeOfProlog); |
515 | printf(" CountOfUnwindCodes: %u\n" , pHeader->CountOfUnwindCodes); |
516 | printf(" FrameRegister : %s (%u)\n" , |
517 | (pHeader->FrameRegister == 0) ? "none" : getRegName(pHeader->FrameRegister), |
518 | pHeader->FrameRegister); // RAX (0) is not allowed as a frame register |
519 | if (pHeader->FrameRegister == 0) |
520 | { |
521 | printf(" FrameOffset : N/A (no FrameRegister) (Value=%u)\n" , pHeader->FrameOffset); |
522 | } |
523 | else |
524 | { |
525 | printf(" FrameOffset : %u * 16 = 0x%02X\n" , pHeader->FrameOffset, pHeader->FrameOffset * 16); |
526 | } |
527 | printf(" UnwindCodes :\n" ); |
528 | |
529 | for (unsigned i = 0; i < pHeader->CountOfUnwindCodes; i++) |
530 | { |
531 | const UNWIND_CODE* const pCode = &(pHeader->UnwindCode[i]); |
532 | switch (pCode->UnwindOp) |
533 | { |
534 | case UWOP_PUSH_NONVOL: |
535 | printf(" CodeOffset: 0x%02X UnwindOp: UWOP_PUSH_NONVOL (%u) OpInfo: %s (%u)\n" , |
536 | pCode->CodeOffset, pCode->UnwindOp, getRegName(pCode->OpInfo), pCode->OpInfo); |
537 | break; |
538 | |
539 | case UWOP_ALLOC_LARGE: |
540 | printf(" CodeOffset: 0x%02X UnwindOp: UWOP_ALLOC_LARGE (%u) OpInfo: %u - " , pCode->CodeOffset, |
541 | pCode->UnwindOp, pCode->OpInfo); |
542 | if (pCode->OpInfo == 0) |
543 | { |
544 | i++; |
545 | printf("Scaled small \n Size: %u * 8 = %u = 0x%05X\n" , pHeader->UnwindCode[i].FrameOffset, |
546 | pHeader->UnwindCode[i].FrameOffset * 8, pHeader->UnwindCode[i].FrameOffset * 8); |
547 | } |
548 | else if (pCode->OpInfo == 1) |
549 | { |
550 | i++; |
551 | printf("Unscaled large\n Size: %u = 0x%08X\n\n" , *(ULONG*)&(pHeader->UnwindCode[i]), |
552 | *(ULONG*)&(pHeader->UnwindCode[i])); |
553 | i++; |
554 | } |
555 | else |
556 | { |
557 | printf("Unknown\n" ); |
558 | } |
559 | break; |
560 | |
561 | case UWOP_ALLOC_SMALL: |
562 | printf(" CodeOffset: 0x%02X UnwindOp: UWOP_ALLOC_SMALL (%u) OpInfo: %u * 8 + 8 = %u = 0x%02X\n" , |
563 | pCode->CodeOffset, pCode->UnwindOp, pCode->OpInfo, pCode->OpInfo * 8 + 8, pCode->OpInfo * 8 + 8); |
564 | break; |
565 | |
566 | case UWOP_SET_FPREG: |
567 | printf(" CodeOffset: 0x%02X UnwindOp: UWOP_SET_FPREG (%u) OpInfo: Unused (%u)\n" , |
568 | pCode->CodeOffset, pCode->UnwindOp, pCode->OpInfo); // This should be zero |
569 | break; |
570 | |
571 | #ifdef UNIX_AMD64_ABI |
572 | |
573 | case UWOP_SET_FPREG_LARGE: |
574 | { |
575 | printf(" CodeOffset: 0x%02X UnwindOp: UWOP_SET_FPREG_LARGE (%u) OpInfo: Unused (%u)\n" , |
576 | pCode->CodeOffset, pCode->UnwindOp, pCode->OpInfo); // This should be zero |
577 | i++; |
578 | unsigned offset = *(ULONG*)&(pHeader->UnwindCode[i]); |
579 | i++; |
580 | printf(" Scaled Offset: %u * 16 = %u = 0x%08X\n" , offset, offset * 16, offset * 16); |
581 | if ((offset & 0xF0000000) != 0) |
582 | { |
583 | printf(" Illegal unscaled offset: too large\n" ); |
584 | } |
585 | } |
586 | break; |
587 | |
588 | #endif // UNIX_AMD64_ABI |
589 | |
590 | case UWOP_SAVE_NONVOL: |
591 | printf(" CodeOffset: 0x%02X UnwindOp: UWOP_SAVE_NONVOL (%u) OpInfo: %s (%u)\n" , |
592 | pCode->CodeOffset, pCode->UnwindOp, getRegName(pCode->OpInfo), pCode->OpInfo); |
593 | i++; |
594 | printf(" Scaled Small Offset: %u * 8 = %u = 0x%05X\n" , pHeader->UnwindCode[i].FrameOffset, |
595 | pHeader->UnwindCode[i].FrameOffset * 8, pHeader->UnwindCode[i].FrameOffset * 8); |
596 | break; |
597 | |
598 | case UWOP_SAVE_NONVOL_FAR: |
599 | printf(" CodeOffset: 0x%02X UnwindOp: UWOP_SAVE_NONVOL_FAR (%u) OpInfo: %s (%u)\n" , |
600 | pCode->CodeOffset, pCode->UnwindOp, getRegName(pCode->OpInfo), pCode->OpInfo); |
601 | i++; |
602 | printf(" Unscaled Large Offset: 0x%08X\n\n" , *(ULONG*)&(pHeader->UnwindCode[i])); |
603 | i++; |
604 | break; |
605 | |
606 | case UWOP_SAVE_XMM128: |
607 | printf(" CodeOffset: 0x%02X UnwindOp: UWOP_SAVE_XMM128 (%u) OpInfo: XMM%u (%u)\n" , |
608 | pCode->CodeOffset, pCode->UnwindOp, pCode->OpInfo, pCode->OpInfo); |
609 | i++; |
610 | printf(" Scaled Small Offset: %u * 16 = %u = 0x%05X\n" , pHeader->UnwindCode[i].FrameOffset, |
611 | pHeader->UnwindCode[i].FrameOffset * 16, pHeader->UnwindCode[i].FrameOffset * 16); |
612 | break; |
613 | |
614 | case UWOP_SAVE_XMM128_FAR: |
615 | printf(" CodeOffset: 0x%02X UnwindOp: UWOP_SAVE_XMM128_FAR (%u) OpInfo: XMM%u (%u)\n" , |
616 | pCode->CodeOffset, pCode->UnwindOp, pCode->OpInfo, pCode->OpInfo); |
617 | i++; |
618 | printf(" Unscaled Large Offset: 0x%08X\n\n" , *(ULONG*)&(pHeader->UnwindCode[i])); |
619 | i++; |
620 | break; |
621 | |
622 | case UWOP_EPILOG: |
623 | case UWOP_SPARE_CODE: |
624 | case UWOP_PUSH_MACHFRAME: |
625 | default: |
626 | printf(" Unrecognized UNWIND_CODE: 0x%04X\n" , *(USHORT*)pCode); |
627 | break; |
628 | } |
629 | } |
630 | } |
631 | |
632 | #endif // DEBUG |
633 | |
634 | //------------------------------------------------------------------------ |
635 | // Compiler::unwindReserve: Ask the VM to reserve space for the unwind information |
636 | // for the function and all its funclets. Called once, just before asking the VM |
637 | // for memory and emitting the generated code. Calls unwindReserveFunc() to handle |
638 | // the main function and each of the funclets, in turn. |
639 | // |
640 | void Compiler::unwindReserve() |
641 | { |
642 | assert(!compGeneratingProlog); |
643 | assert(!compGeneratingEpilog); |
644 | |
645 | assert(compFuncInfoCount > 0); |
646 | for (unsigned funcIdx = 0; funcIdx < compFuncInfoCount; funcIdx++) |
647 | { |
648 | unwindReserveFunc(funGetFunc(funcIdx)); |
649 | } |
650 | } |
651 | |
652 | //------------------------------------------------------------------------ |
653 | // Compiler::unwindReserveFunc: Reserve the unwind information from the VM for a |
654 | // given main function or funclet. |
655 | // |
656 | // Arguments: |
657 | // func - The main function or funclet to reserve unwind info for. |
658 | // |
659 | void Compiler::unwindReserveFunc(FuncInfoDsc* func) |
660 | { |
661 | unwindReserveFuncHelper(func, true); |
662 | |
663 | if (fgFirstColdBlock != nullptr) |
664 | { |
665 | unwindReserveFuncHelper(func, false); |
666 | } |
667 | } |
668 | |
669 | //------------------------------------------------------------------------ |
670 | // Compiler::unwindReserveFuncHelper: Reserve the unwind information from the VM for a |
671 | // given main function or funclet, for either the hot or the cold section. |
672 | // |
673 | // Arguments: |
674 | // func - The main function or funclet to reserve unwind info for. |
675 | // isHotCode - 'true' to reserve the hot section, 'false' to reserve the cold section. |
676 | // |
677 | void Compiler::unwindReserveFuncHelper(FuncInfoDsc* func, bool isHotCode) |
678 | { |
679 | DWORD unwindCodeBytes = 0; |
680 | if (isHotCode) |
681 | { |
682 | #ifdef UNIX_AMD64_ABI |
683 | if (generateCFIUnwindCodes()) |
684 | { |
685 | unwindCodeBytes = (DWORD)(func->cfiCodes->size() * sizeof(CFI_CODE)); |
686 | } |
687 | else |
688 | #endif // UNIX_AMD64_ABI |
689 | { |
690 | assert(func->unwindHeader.Version == 1); // Can't call this before unwindBegProlog |
691 | assert(func->unwindHeader.CountOfUnwindCodes == 0); // Only call this once per prolog |
692 | |
693 | // Set the size of the prolog to be the last encoded action |
694 | if (func->unwindCodeSlot < sizeof(func->unwindCodes)) |
695 | { |
696 | UNWIND_CODE* code = (UNWIND_CODE*)&func->unwindCodes[func->unwindCodeSlot]; |
697 | func->unwindHeader.SizeOfProlog = code->CodeOffset; |
698 | } |
699 | else |
700 | { |
701 | func->unwindHeader.SizeOfProlog = 0; |
702 | } |
703 | func->unwindHeader.CountOfUnwindCodes = |
704 | (BYTE)((sizeof(func->unwindCodes) - func->unwindCodeSlot) / sizeof(UNWIND_CODE)); |
705 | |
706 | // Prepend the unwindHeader onto the unwind codes |
707 | assert(func->unwindCodeSlot >= offsetof(UNWIND_INFO, UnwindCode)); |
708 | |
709 | func->unwindCodeSlot -= offsetof(UNWIND_INFO, UnwindCode); |
710 | UNWIND_INFO* = (UNWIND_INFO*)&func->unwindCodes[func->unwindCodeSlot]; |
711 | memcpy(pHeader, &func->unwindHeader, offsetof(UNWIND_INFO, UnwindCode)); |
712 | |
713 | unwindCodeBytes = sizeof(func->unwindCodes) - func->unwindCodeSlot; |
714 | } |
715 | } |
716 | |
717 | BOOL isFunclet = (func->funKind != FUNC_ROOT); |
718 | BOOL isColdCode = isHotCode ? FALSE : TRUE; |
719 | |
720 | eeReserveUnwindInfo(isFunclet, isColdCode, unwindCodeBytes); |
721 | } |
722 | |
723 | //------------------------------------------------------------------------ |
724 | // Compiler::unwindEmit: Report all the unwind information to the VM. |
725 | // |
726 | // Arguments: |
727 | // pHotCode - Pointer to the beginning of the memory with the function and funclet hot code. |
728 | // pColdCode - Pointer to the beginning of the memory with the function and funclet cold code. |
729 | // |
730 | void Compiler::unwindEmit(void* pHotCode, void* pColdCode) |
731 | { |
732 | assert(!compGeneratingProlog); |
733 | assert(!compGeneratingEpilog); |
734 | |
735 | assert(compFuncInfoCount > 0); |
736 | for (unsigned funcIdx = 0; funcIdx < compFuncInfoCount; funcIdx++) |
737 | { |
738 | unwindEmitFunc(funGetFunc(funcIdx), pHotCode, pColdCode); |
739 | } |
740 | } |
741 | |
742 | //------------------------------------------------------------------------ |
743 | // Compiler::unwindEmitFuncHelper: Report the unwind information to the VM for a |
744 | // given main function or funclet, for either the hot or cold section. |
745 | // |
746 | // Arguments: |
747 | // func - The main function or funclet to reserve unwind info for. |
748 | // pHotCode - Pointer to the beginning of the memory with the function and funclet hot code. |
749 | // pColdCode - Pointer to the beginning of the memory with the function and funclet cold code. |
750 | // Ignored if 'isHotCode' is true. |
751 | // isHotCode - 'true' to report the hot section, 'false' to report the cold section. |
752 | // |
753 | void Compiler::unwindEmitFuncHelper(FuncInfoDsc* func, void* pHotCode, void* pColdCode, bool isHotCode) |
754 | { |
755 | UNATIVE_OFFSET startOffset; |
756 | UNATIVE_OFFSET endOffset; |
757 | DWORD unwindCodeBytes = 0; |
758 | BYTE* pUnwindBlock = nullptr; |
759 | |
760 | if (isHotCode) |
761 | { |
762 | if (func->startLoc == nullptr) |
763 | { |
764 | startOffset = 0; |
765 | } |
766 | else |
767 | { |
768 | startOffset = func->startLoc->CodeOffset(genEmitter); |
769 | } |
770 | |
771 | if (func->endLoc == nullptr) |
772 | { |
773 | endOffset = info.compNativeCodeSize; |
774 | } |
775 | else |
776 | { |
777 | endOffset = func->endLoc->CodeOffset(genEmitter); |
778 | } |
779 | |
780 | #ifdef UNIX_AMD64_ABI |
781 | if (generateCFIUnwindCodes()) |
782 | { |
783 | DWORD size = (DWORD)func->cfiCodes->size(); |
784 | if (size > 0) |
785 | { |
786 | unwindCodeBytes = size * sizeof(CFI_CODE); |
787 | pUnwindBlock = (BYTE*)&(*func->cfiCodes)[0]; |
788 | } |
789 | } |
790 | else |
791 | #endif // UNIX_AMD64_ABI |
792 | { |
793 | unwindCodeBytes = sizeof(func->unwindCodes) - func->unwindCodeSlot; |
794 | |
795 | #ifdef DEBUG |
796 | UNWIND_INFO* pUnwindInfo = (UNWIND_INFO*)(&func->unwindCodes[func->unwindCodeSlot]); |
797 | DWORD unwindCodeBytesSpecified = |
798 | offsetof(UNWIND_INFO, UnwindCode) + |
799 | pUnwindInfo->CountOfUnwindCodes * sizeof(UNWIND_CODE); // This is what the unwind codes themselves say; |
800 | // it better match what we tell the VM. |
801 | assert(unwindCodeBytes == unwindCodeBytesSpecified); |
802 | #endif // DEBUG |
803 | |
804 | pUnwindBlock = &func->unwindCodes[func->unwindCodeSlot]; |
805 | } |
806 | } |
807 | else |
808 | { |
809 | assert(fgFirstColdBlock != nullptr); |
810 | assert(func->funKind == FUNC_ROOT); // No splitting of funclets. |
811 | |
812 | if (func->coldStartLoc == nullptr) |
813 | { |
814 | startOffset = 0; |
815 | } |
816 | else |
817 | { |
818 | startOffset = func->coldStartLoc->CodeOffset(genEmitter); |
819 | } |
820 | |
821 | if (func->coldEndLoc == nullptr) |
822 | { |
823 | endOffset = info.compNativeCodeSize; |
824 | } |
825 | else |
826 | { |
827 | endOffset = func->coldEndLoc->CodeOffset(genEmitter); |
828 | } |
829 | } |
830 | |
831 | #ifdef DEBUG |
832 | if (opts.dspUnwind) |
833 | { |
834 | #ifdef UNIX_AMD64_ABI |
835 | if (generateCFIUnwindCodes()) |
836 | { |
837 | DumpCfiInfo(isHotCode, startOffset, endOffset, unwindCodeBytes, (const CFI_CODE* const)pUnwindBlock); |
838 | } |
839 | else |
840 | #endif // UNIX_AMD64_ABI |
841 | { |
842 | DumpUnwindInfo(isHotCode, startOffset, endOffset, (const UNWIND_INFO* const)pUnwindBlock); |
843 | } |
844 | } |
845 | #endif // DEBUG |
846 | |
847 | // Adjust for cold or hot code: |
848 | // 1. The VM doesn't want the cold code pointer unless this is cold code. |
849 | // 2. The startOffset and endOffset need to be from the base of the hot section for hot code |
850 | // and from the base of the cold section for cold code |
851 | |
852 | if (isHotCode) |
853 | { |
854 | assert(endOffset <= info.compTotalHotCodeSize); |
855 | pColdCode = nullptr; |
856 | } |
857 | else |
858 | { |
859 | assert(startOffset >= info.compTotalHotCodeSize); |
860 | startOffset -= info.compTotalHotCodeSize; |
861 | endOffset -= info.compTotalHotCodeSize; |
862 | } |
863 | |
864 | eeAllocUnwindInfo((BYTE*)pHotCode, (BYTE*)pColdCode, startOffset, endOffset, unwindCodeBytes, pUnwindBlock, |
865 | (CorJitFuncKind)func->funKind); |
866 | } |
867 | |
868 | //------------------------------------------------------------------------ |
869 | // Compiler::unwindEmitFunc: Report the unwind information to the VM for a |
870 | // given main function or funclet. Reports the hot section, then the cold |
871 | // section if necessary. |
872 | // |
873 | // Arguments: |
874 | // func - The main function or funclet to reserve unwind info for. |
875 | // pHotCode - Pointer to the beginning of the memory with the function and funclet hot code. |
876 | // pColdCode - Pointer to the beginning of the memory with the function and funclet cold code. |
877 | // |
878 | void Compiler::unwindEmitFunc(FuncInfoDsc* func, void* pHotCode, void* pColdCode) |
879 | { |
880 | // Verify that the JIT enum is in sync with the JIT-EE interface enum |
881 | static_assert_no_msg(FUNC_ROOT == (FuncKind)CORJIT_FUNC_ROOT); |
882 | static_assert_no_msg(FUNC_HANDLER == (FuncKind)CORJIT_FUNC_HANDLER); |
883 | static_assert_no_msg(FUNC_FILTER == (FuncKind)CORJIT_FUNC_FILTER); |
884 | |
885 | unwindEmitFuncHelper(func, pHotCode, pColdCode, true); |
886 | |
887 | if (pColdCode != nullptr) |
888 | { |
889 | unwindEmitFuncHelper(func, pHotCode, pColdCode, false); |
890 | } |
891 | } |
892 | |
893 | #endif // _TARGET_AMD64_ |
894 | |