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 | // stublink.cpp |
6 | // |
7 | |
8 | |
9 | |
10 | #include "common.h" |
11 | |
12 | #include "threads.h" |
13 | #include "excep.h" |
14 | #include "stublink.h" |
15 | #include "perfcounters.h" |
16 | #include "stubgen.h" |
17 | #include "stublink.inl" |
18 | |
19 | #include "rtlfunctions.h" |
20 | |
21 | #define S_BYTEPTR(x) S_SIZE_T((SIZE_T)(x)) |
22 | |
23 | #ifndef DACCESS_COMPILE |
24 | |
25 | |
26 | //************************************************************************ |
27 | // CodeElement |
28 | // |
29 | // There are two types of CodeElements: CodeRuns (a stream of uninterpreted |
30 | // code bytes) and LabelRefs (an instruction containing |
31 | // a fixup.) |
32 | //************************************************************************ |
33 | struct CodeElement |
34 | { |
35 | enum CodeElementType { |
36 | kCodeRun = 0, |
37 | kLabelRef = 1, |
38 | }; |
39 | |
40 | |
41 | CodeElementType m_type; // kCodeRun or kLabelRef |
42 | CodeElement *m_next; // ptr to next CodeElement |
43 | |
44 | // Used as workspace during Link(): holds the offset relative to |
45 | // the start of the final stub. |
46 | UINT m_globaloffset; |
47 | UINT m_dataoffset; |
48 | }; |
49 | |
50 | |
51 | //************************************************************************ |
52 | // CodeRun: A run of uninterrupted code bytes. |
53 | //************************************************************************ |
54 | |
55 | #ifdef _DEBUG |
56 | #define CODERUNSIZE 3 |
57 | #else |
58 | #define CODERUNSIZE 32 |
59 | #endif |
60 | |
61 | struct CodeRun : public CodeElement |
62 | { |
63 | UINT m_numcodebytes; // how many bytes are actually used |
64 | BYTE m_codebytes[CODERUNSIZE]; |
65 | }; |
66 | |
67 | //************************************************************************ |
68 | // LabelRef: An instruction containing an embedded label reference |
69 | //************************************************************************ |
70 | struct LabelRef : public CodeElement |
71 | { |
72 | // provides platform-specific information about the instruction |
73 | InstructionFormat *m_pInstructionFormat; |
74 | |
75 | // a variation code (interpretation is specific to the InstructionFormat) |
76 | // typically used to customize an instruction (e.g. with a condition |
77 | // code.) |
78 | UINT m_variationCode; |
79 | |
80 | |
81 | CodeLabel *m_target; |
82 | |
83 | // Workspace during the link phase |
84 | UINT m_refsize; |
85 | |
86 | |
87 | // Pointer to next LabelRef |
88 | LabelRef *m_nextLabelRef; |
89 | }; |
90 | |
91 | |
92 | //************************************************************************ |
93 | // IntermediateUnwindInfo |
94 | //************************************************************************ |
95 | |
96 | #ifdef STUBLINKER_GENERATES_UNWIND_INFO |
97 | |
98 | |
99 | #ifdef _TARGET_AMD64_ |
100 | // List of unwind operations, queued in StubLinker::m_pUnwindInfoList. |
101 | struct IntermediateUnwindInfo |
102 | { |
103 | IntermediateUnwindInfo *pNext; |
104 | CodeRun *pCodeRun; |
105 | UINT LocalOffset; |
106 | UNWIND_CODE rgUnwindCode[1]; // variable length, depends on first entry's UnwindOp |
107 | }; |
108 | #endif // _TARGET_AMD64_ |
109 | |
110 | |
111 | StubUnwindInfoHeapSegment *g_StubHeapSegments; |
112 | CrstStatic g_StubUnwindInfoHeapSegmentsCrst; |
113 | #ifdef _DEBUG // for unit test |
114 | void *__DEBUG__g_StubHeapSegments = &g_StubHeapSegments; |
115 | #endif |
116 | |
117 | |
118 | // |
119 | // Callback registered via RtlInstallFunctionTableCallback. Called by |
120 | // RtlpLookupDynamicFunctionEntry to locate RUNTIME_FUNCTION entry for a PC |
121 | // found within a portion of a heap that contains stub code. |
122 | // |
123 | T_RUNTIME_FUNCTION* |
124 | FindStubFunctionEntry ( |
125 | WIN64_ONLY(IN ULONG64 ControlPc) |
126 | NOT_WIN64(IN ULONG ControlPc), |
127 | IN PVOID Context |
128 | ) |
129 | { |
130 | CONTRACTL |
131 | { |
132 | NOTHROW; |
133 | GC_NOTRIGGER; |
134 | FORBID_FAULT; |
135 | SO_TOLERANT; |
136 | } |
137 | CONTRACTL_END |
138 | |
139 | CONSISTENCY_CHECK(DYNFNTABLE_STUB == IdentifyDynamicFunctionTableTypeFromContext(Context)); |
140 | |
141 | StubUnwindInfoHeapSegment *pStubHeapSegment = (StubUnwindInfoHeapSegment*)DecodeDynamicFunctionTableContext(Context); |
142 | |
143 | // |
144 | // The RUNTIME_FUNCTION entry contains ULONG offsets relative to the |
145 | // segment base. Stub::EmitUnwindInfo ensures that this cast is valid. |
146 | // |
147 | ULONG RelativeAddress = (ULONG)((BYTE*)ControlPc - pStubHeapSegment->pbBaseAddress); |
148 | |
149 | LOG((LF_STUBS, LL_INFO100000, "ControlPc %p, RelativeAddress 0x%x, pStubHeapSegment %p, pStubHeapSegment->pbBaseAddress %p\n" , |
150 | ControlPc, |
151 | RelativeAddress, |
152 | pStubHeapSegment, |
153 | pStubHeapSegment->pbBaseAddress)); |
154 | |
155 | // |
156 | // Search this segment's list of stubs for an entry that includes the |
157 | // segment-relative offset. |
158 | // |
159 | for (StubUnwindInfoHeader *pHeader = pStubHeapSegment->pUnwindHeaderList; |
160 | pHeader; |
161 | pHeader = pHeader->pNext) |
162 | { |
163 | // The entry points are in increasing address order. |
164 | if (RelativeAddress >= RUNTIME_FUNCTION__BeginAddress(&pHeader->FunctionEntry)) |
165 | { |
166 | T_RUNTIME_FUNCTION *pCurFunction = &pHeader->FunctionEntry; |
167 | T_RUNTIME_FUNCTION *pPrevFunction = NULL; |
168 | |
169 | LOG((LF_STUBS, LL_INFO100000, "pCurFunction %p, pCurFunction->BeginAddress 0x%x, pCurFunction->EndAddress 0x%x\n" , |
170 | pCurFunction, |
171 | RUNTIME_FUNCTION__BeginAddress(pCurFunction), |
172 | RUNTIME_FUNCTION__EndAddress(pCurFunction, (TADDR)pStubHeapSegment->pbBaseAddress))); |
173 | |
174 | CONSISTENCY_CHECK((RUNTIME_FUNCTION__EndAddress(pCurFunction, (TADDR)pStubHeapSegment->pbBaseAddress) > RUNTIME_FUNCTION__BeginAddress(pCurFunction))); |
175 | CONSISTENCY_CHECK((!pPrevFunction || RUNTIME_FUNCTION__EndAddress(pPrevFunction, (TADDR)pStubHeapSegment->pbBaseAddress) <= RUNTIME_FUNCTION__BeginAddress(pCurFunction))); |
176 | |
177 | // The entry points are in increasing address order. They're |
178 | // also contiguous, so after we're sure it's after the start of |
179 | // the first function (checked above), we only need to test |
180 | // the end address. |
181 | if (RelativeAddress < RUNTIME_FUNCTION__EndAddress(pCurFunction, (TADDR)pStubHeapSegment->pbBaseAddress)) |
182 | { |
183 | CONSISTENCY_CHECK((RelativeAddress >= RUNTIME_FUNCTION__BeginAddress(pCurFunction))); |
184 | |
185 | return pCurFunction; |
186 | } |
187 | } |
188 | } |
189 | |
190 | // |
191 | // Return NULL to indicate that there is no RUNTIME_FUNCTION/unwind |
192 | // information for this offset. |
193 | // |
194 | return NULL; |
195 | } |
196 | |
197 | |
198 | bool UnregisterUnwindInfoInLoaderHeapCallback (PVOID pvArgs, PVOID pvAllocationBase, SIZE_T cbReserved) |
199 | { |
200 | CONTRACTL |
201 | { |
202 | NOTHROW; |
203 | GC_TRIGGERS; |
204 | } |
205 | CONTRACTL_END; |
206 | |
207 | // |
208 | // There may be multiple StubUnwindInfoHeapSegment's associated with a region. |
209 | // |
210 | |
211 | LOG((LF_STUBS, LL_INFO1000, "Looking for stub unwind info for LoaderHeap segment %p size %p\n" , pvAllocationBase, cbReserved)); |
212 | |
213 | CrstHolder crst(&g_StubUnwindInfoHeapSegmentsCrst); |
214 | |
215 | StubUnwindInfoHeapSegment *pStubHeapSegment; |
216 | for (StubUnwindInfoHeapSegment **ppPrevStubHeapSegment = &g_StubHeapSegments; |
217 | (pStubHeapSegment = *ppPrevStubHeapSegment); ) |
218 | { |
219 | LOG((LF_STUBS, LL_INFO10000, " have unwind info for address %p size %p\n" , pStubHeapSegment->pbBaseAddress, pStubHeapSegment->cbSegment)); |
220 | |
221 | // If heap region ends before stub segment |
222 | if ((BYTE*)pvAllocationBase + cbReserved <= pStubHeapSegment->pbBaseAddress) |
223 | { |
224 | // The list is ordered, so address range is between segments |
225 | break; |
226 | } |
227 | |
228 | // The given heap segment base address may fall within a prereserved |
229 | // region that was given to the heap when the heap was constructed, so |
230 | // pvAllocationBase may be > pbBaseAddress. Also, there could be |
231 | // multiple segments for each heap region, so pvAllocationBase may be |
232 | // < pbBaseAddress. So...there is no meaningful relationship between |
233 | // pvAllocationBase and pbBaseAddress. |
234 | |
235 | // If heap region starts before end of stub segment |
236 | if ((BYTE*)pvAllocationBase < pStubHeapSegment->pbBaseAddress + pStubHeapSegment->cbSegment) |
237 | { |
238 | _ASSERTE((BYTE*)pvAllocationBase + cbReserved <= pStubHeapSegment->pbBaseAddress + pStubHeapSegment->cbSegment); |
239 | |
240 | DeleteEEFunctionTable(pStubHeapSegment); |
241 | #ifdef _TARGET_AMD64_ |
242 | if (pStubHeapSegment->pUnwindInfoTable != 0) |
243 | delete pStubHeapSegment->pUnwindInfoTable; |
244 | #endif |
245 | *ppPrevStubHeapSegment = pStubHeapSegment->pNext; |
246 | |
247 | delete pStubHeapSegment; |
248 | } |
249 | else |
250 | { |
251 | ppPrevStubHeapSegment = &pStubHeapSegment->pNext; |
252 | } |
253 | } |
254 | |
255 | return false; // Keep enumerating |
256 | } |
257 | |
258 | |
259 | VOID UnregisterUnwindInfoInLoaderHeap (UnlockedLoaderHeap *pHeap) |
260 | { |
261 | CONTRACTL |
262 | { |
263 | NOTHROW; |
264 | GC_TRIGGERS; |
265 | PRECONDITION(pHeap->m_fPermitStubsWithUnwindInfo); |
266 | } |
267 | CONTRACTL_END; |
268 | |
269 | pHeap->EnumPageRegions(&UnregisterUnwindInfoInLoaderHeapCallback, NULL /* pvArgs */); |
270 | |
271 | #ifdef _DEBUG |
272 | pHeap->m_fStubUnwindInfoUnregistered = TRUE; |
273 | #endif // _DEBUG |
274 | } |
275 | |
276 | |
277 | class StubUnwindInfoSegmentBoundaryReservationList |
278 | { |
279 | struct ReservationList |
280 | { |
281 | ReservationList *pNext; |
282 | |
283 | static ReservationList *FromStub (Stub *pStub) |
284 | { |
285 | return (ReservationList*)(pStub+1); |
286 | } |
287 | |
288 | Stub *GetStub () |
289 | { |
290 | return (Stub*)this - 1; |
291 | } |
292 | }; |
293 | |
294 | ReservationList *m_pList; |
295 | |
296 | public: |
297 | |
298 | StubUnwindInfoSegmentBoundaryReservationList () |
299 | { |
300 | LIMITED_METHOD_CONTRACT; |
301 | |
302 | m_pList = NULL; |
303 | } |
304 | |
305 | ~StubUnwindInfoSegmentBoundaryReservationList () |
306 | { |
307 | LIMITED_METHOD_CONTRACT; |
308 | |
309 | ReservationList *pList = m_pList; |
310 | while (pList) |
311 | { |
312 | ReservationList *pNext = pList->pNext; |
313 | |
314 | pList->GetStub()->DecRef(); |
315 | |
316 | pList = pNext; |
317 | } |
318 | } |
319 | |
320 | void AddStub (Stub *pStub) |
321 | { |
322 | LIMITED_METHOD_CONTRACT; |
323 | |
324 | ReservationList *pList = ReservationList::FromStub(pStub); |
325 | |
326 | pList->pNext = m_pList; |
327 | m_pList = pList; |
328 | } |
329 | }; |
330 | |
331 | |
332 | #endif // STUBLINKER_GENERATES_UNWIND_INFO |
333 | |
334 | |
335 | //************************************************************************ |
336 | // StubLinker |
337 | //************************************************************************ |
338 | |
339 | //--------------------------------------------------------------- |
340 | // Construction |
341 | //--------------------------------------------------------------- |
342 | StubLinker::StubLinker() |
343 | { |
344 | CONTRACTL |
345 | { |
346 | NOTHROW; |
347 | GC_NOTRIGGER; |
348 | SO_TOLERANT; |
349 | } |
350 | CONTRACTL_END; |
351 | |
352 | m_pCodeElements = NULL; |
353 | m_pFirstCodeLabel = NULL; |
354 | m_pFirstLabelRef = NULL; |
355 | m_pPatchLabel = NULL; |
356 | m_stackSize = 0; |
357 | m_fDataOnly = FALSE; |
358 | #ifdef _TARGET_ARM_ |
359 | m_fProlog = FALSE; |
360 | m_cCalleeSavedRegs = 0; |
361 | m_cbStackFrame = 0; |
362 | m_fPushArgRegs = FALSE; |
363 | #endif |
364 | #ifdef STUBLINKER_GENERATES_UNWIND_INFO |
365 | #ifdef _DEBUG |
366 | m_pUnwindInfoCheckLabel = NULL; |
367 | #endif |
368 | #ifdef _TARGET_AMD64_ |
369 | m_pUnwindInfoList = NULL; |
370 | m_nUnwindSlots = 0; |
371 | m_fHaveFramePointer = FALSE; |
372 | #endif |
373 | #ifdef _TARGET_ARM64_ |
374 | m_fProlog = FALSE; |
375 | m_cIntRegArgs = 0; |
376 | m_cVecRegArgs = 0; |
377 | m_cCalleeSavedRegs = 0; |
378 | m_cbStackSpace = 0; |
379 | #endif |
380 | #endif // STUBLINKER_GENERATES_UNWIND_INFO |
381 | } |
382 | |
383 | |
384 | |
385 | //--------------------------------------------------------------- |
386 | // Append code bytes. |
387 | //--------------------------------------------------------------- |
388 | VOID StubLinker::EmitBytes(const BYTE *pBytes, UINT numBytes) |
389 | { |
390 | CONTRACTL |
391 | { |
392 | THROWS; |
393 | GC_NOTRIGGER; |
394 | SO_TOLERANT; |
395 | } |
396 | CONTRACTL_END; |
397 | |
398 | CodeElement *pLastCodeElement = GetLastCodeElement(); |
399 | while (numBytes != 0) { |
400 | |
401 | if (pLastCodeElement != NULL && |
402 | pLastCodeElement->m_type == CodeElement::kCodeRun) { |
403 | CodeRun *pCodeRun = (CodeRun*)pLastCodeElement; |
404 | UINT numbytessrc = numBytes; |
405 | UINT numbytesdst = CODERUNSIZE - pCodeRun->m_numcodebytes; |
406 | if (numbytesdst <= numbytessrc) { |
407 | CopyMemory(&(pCodeRun->m_codebytes[pCodeRun->m_numcodebytes]), |
408 | pBytes, |
409 | numbytesdst); |
410 | pCodeRun->m_numcodebytes = CODERUNSIZE; |
411 | pLastCodeElement = NULL; |
412 | pBytes += numbytesdst; |
413 | numBytes -= numbytesdst; |
414 | } else { |
415 | CopyMemory(&(pCodeRun->m_codebytes[pCodeRun->m_numcodebytes]), |
416 | pBytes, |
417 | numbytessrc); |
418 | pCodeRun->m_numcodebytes += numbytessrc; |
419 | pBytes += numbytessrc; |
420 | numBytes = 0; |
421 | } |
422 | |
423 | } else { |
424 | pLastCodeElement = AppendNewEmptyCodeRun(); |
425 | } |
426 | } |
427 | } |
428 | |
429 | |
430 | //--------------------------------------------------------------- |
431 | // Append code bytes. |
432 | //--------------------------------------------------------------- |
433 | VOID StubLinker::Emit8 (unsigned __int8 val) |
434 | { |
435 | CONTRACTL |
436 | { |
437 | THROWS; |
438 | GC_NOTRIGGER; |
439 | } |
440 | CONTRACTL_END; |
441 | |
442 | CodeRun *pCodeRun = GetLastCodeRunIfAny(); |
443 | if (pCodeRun && (CODERUNSIZE - pCodeRun->m_numcodebytes) >= sizeof(val)) { |
444 | *((unsigned __int8 *)(pCodeRun->m_codebytes + pCodeRun->m_numcodebytes)) = val; |
445 | pCodeRun->m_numcodebytes += sizeof(val); |
446 | } else { |
447 | EmitBytes((BYTE*)&val, sizeof(val)); |
448 | } |
449 | } |
450 | |
451 | //--------------------------------------------------------------- |
452 | // Append code bytes. |
453 | //--------------------------------------------------------------- |
454 | VOID StubLinker::Emit16(unsigned __int16 val) |
455 | { |
456 | CONTRACTL |
457 | { |
458 | THROWS; |
459 | GC_NOTRIGGER; |
460 | SO_TOLERANT; |
461 | } |
462 | CONTRACTL_END; |
463 | |
464 | CodeRun *pCodeRun = GetLastCodeRunIfAny(); |
465 | if (pCodeRun && (CODERUNSIZE - pCodeRun->m_numcodebytes) >= sizeof(val)) { |
466 | SET_UNALIGNED_16(pCodeRun->m_codebytes + pCodeRun->m_numcodebytes, val); |
467 | pCodeRun->m_numcodebytes += sizeof(val); |
468 | } else { |
469 | EmitBytes((BYTE*)&val, sizeof(val)); |
470 | } |
471 | } |
472 | |
473 | //--------------------------------------------------------------- |
474 | // Append code bytes. |
475 | //--------------------------------------------------------------- |
476 | VOID StubLinker::Emit32(unsigned __int32 val) |
477 | { |
478 | CONTRACTL |
479 | { |
480 | THROWS; |
481 | GC_NOTRIGGER; |
482 | SO_TOLERANT; |
483 | } |
484 | CONTRACTL_END; |
485 | |
486 | CodeRun *pCodeRun = GetLastCodeRunIfAny(); |
487 | if (pCodeRun && (CODERUNSIZE - pCodeRun->m_numcodebytes) >= sizeof(val)) { |
488 | SET_UNALIGNED_32(pCodeRun->m_codebytes + pCodeRun->m_numcodebytes, val); |
489 | pCodeRun->m_numcodebytes += sizeof(val); |
490 | } else { |
491 | EmitBytes((BYTE*)&val, sizeof(val)); |
492 | } |
493 | } |
494 | |
495 | //--------------------------------------------------------------- |
496 | // Append code bytes. |
497 | //--------------------------------------------------------------- |
498 | VOID StubLinker::Emit64(unsigned __int64 val) |
499 | { |
500 | CONTRACTL |
501 | { |
502 | THROWS; |
503 | GC_NOTRIGGER; |
504 | } |
505 | CONTRACTL_END; |
506 | |
507 | CodeRun *pCodeRun = GetLastCodeRunIfAny(); |
508 | if (pCodeRun && (CODERUNSIZE - pCodeRun->m_numcodebytes) >= sizeof(val)) { |
509 | SET_UNALIGNED_64(pCodeRun->m_codebytes + pCodeRun->m_numcodebytes, val); |
510 | pCodeRun->m_numcodebytes += sizeof(val); |
511 | } else { |
512 | EmitBytes((BYTE*)&val, sizeof(val)); |
513 | } |
514 | } |
515 | |
516 | //--------------------------------------------------------------- |
517 | // Append pointer value. |
518 | //--------------------------------------------------------------- |
519 | VOID StubLinker::EmitPtr(const VOID *val) |
520 | { |
521 | CONTRACTL |
522 | { |
523 | THROWS; |
524 | GC_NOTRIGGER; |
525 | SO_TOLERANT; |
526 | } |
527 | CONTRACTL_END; |
528 | |
529 | CodeRun *pCodeRun = GetLastCodeRunIfAny(); |
530 | if (pCodeRun && (CODERUNSIZE - pCodeRun->m_numcodebytes) >= sizeof(val)) { |
531 | SET_UNALIGNED_PTR(pCodeRun->m_codebytes + pCodeRun->m_numcodebytes, (UINT_PTR)val); |
532 | pCodeRun->m_numcodebytes += sizeof(val); |
533 | } else { |
534 | EmitBytes((BYTE*)&val, sizeof(val)); |
535 | } |
536 | } |
537 | |
538 | |
539 | //--------------------------------------------------------------- |
540 | // Create a new undefined label. Label must be assigned to a code |
541 | // location using EmitLabel() prior to final linking. |
542 | // Throws COM+ exception on failure. |
543 | //--------------------------------------------------------------- |
544 | CodeLabel* StubLinker::NewCodeLabel() |
545 | { |
546 | CONTRACTL |
547 | { |
548 | THROWS; |
549 | GC_NOTRIGGER; |
550 | SO_TOLERANT; |
551 | } |
552 | CONTRACTL_END; |
553 | |
554 | CodeLabel *pCodeLabel = (CodeLabel*)(m_quickHeap.Alloc(sizeof(CodeLabel))); |
555 | _ASSERTE(pCodeLabel); // QuickHeap throws exceptions rather than returning NULL |
556 | pCodeLabel->m_next = m_pFirstCodeLabel; |
557 | pCodeLabel->m_fExternal = FALSE; |
558 | pCodeLabel->m_fAbsolute = FALSE; |
559 | pCodeLabel->i.m_pCodeRun = NULL; |
560 | m_pFirstCodeLabel = pCodeLabel; |
561 | return pCodeLabel; |
562 | |
563 | |
564 | } |
565 | |
566 | CodeLabel* StubLinker::NewAbsoluteCodeLabel() |
567 | { |
568 | CONTRACTL |
569 | { |
570 | THROWS; |
571 | GC_NOTRIGGER; |
572 | } |
573 | CONTRACTL_END; |
574 | |
575 | CodeLabel *pCodeLabel = NewCodeLabel(); |
576 | pCodeLabel->m_fAbsolute = TRUE; |
577 | return pCodeLabel; |
578 | } |
579 | |
580 | |
581 | //--------------------------------------------------------------- |
582 | // Sets the label to point to the current "instruction pointer". |
583 | // It is invalid to call EmitLabel() twice on |
584 | // the same label. |
585 | //--------------------------------------------------------------- |
586 | VOID StubLinker::EmitLabel(CodeLabel* pCodeLabel) |
587 | { |
588 | CONTRACTL |
589 | { |
590 | THROWS; |
591 | GC_NOTRIGGER; |
592 | SO_TOLERANT; |
593 | } |
594 | CONTRACTL_END; |
595 | |
596 | _ASSERTE(!(pCodeLabel->m_fExternal)); //can't emit an external label |
597 | _ASSERTE(pCodeLabel->i.m_pCodeRun == NULL); //must only emit label once |
598 | CodeRun *pLastCodeRun = GetLastCodeRunIfAny(); |
599 | if (!pLastCodeRun) { |
600 | pLastCodeRun = AppendNewEmptyCodeRun(); |
601 | } |
602 | pCodeLabel->i.m_pCodeRun = pLastCodeRun; |
603 | pCodeLabel->i.m_localOffset = pLastCodeRun->m_numcodebytes; |
604 | } |
605 | |
606 | |
607 | //--------------------------------------------------------------- |
608 | // Combines NewCodeLabel() and EmitLabel() for convenience. |
609 | // Throws COM+ exception on failure. |
610 | //--------------------------------------------------------------- |
611 | CodeLabel* StubLinker::EmitNewCodeLabel() |
612 | { |
613 | CONTRACTL |
614 | { |
615 | THROWS; |
616 | GC_NOTRIGGER; |
617 | SO_TOLERANT; |
618 | } |
619 | CONTRACTL_END; |
620 | |
621 | CodeLabel* label = NewCodeLabel(); |
622 | EmitLabel(label); |
623 | return label; |
624 | } |
625 | |
626 | |
627 | //--------------------------------------------------------------- |
628 | // Creates & emits the patch offset label for the stub |
629 | //--------------------------------------------------------------- |
630 | VOID StubLinker::EmitPatchLabel() |
631 | { |
632 | CONTRACTL |
633 | { |
634 | THROWS; |
635 | GC_NOTRIGGER; |
636 | SO_TOLERANT; |
637 | } |
638 | CONTRACTL_END; |
639 | |
640 | // |
641 | // Note that it's OK to have re-emit the patch label, |
642 | // just use the later one. |
643 | // |
644 | |
645 | m_pPatchLabel = EmitNewCodeLabel(); |
646 | } |
647 | |
648 | //--------------------------------------------------------------- |
649 | // Returns final location of label as an offset from the start |
650 | // of the stub. Can only be called after linkage. |
651 | //--------------------------------------------------------------- |
652 | UINT32 StubLinker::GetLabelOffset(CodeLabel *pLabel) |
653 | { |
654 | CONTRACTL |
655 | { |
656 | NOTHROW; |
657 | GC_NOTRIGGER; |
658 | SO_TOLERANT; |
659 | } |
660 | CONTRACTL_END; |
661 | |
662 | _ASSERTE(!(pLabel->m_fExternal)); |
663 | return pLabel->i.m_localOffset + pLabel->i.m_pCodeRun->m_globaloffset; |
664 | } |
665 | |
666 | |
667 | //--------------------------------------------------------------- |
668 | // Create a new label to an external address. |
669 | // Throws COM+ exception on failure. |
670 | //--------------------------------------------------------------- |
671 | CodeLabel* StubLinker::NewExternalCodeLabel(LPVOID pExternalAddress) |
672 | { |
673 | CONTRACTL |
674 | { |
675 | THROWS; |
676 | GC_NOTRIGGER; |
677 | SO_TOLERANT; |
678 | |
679 | PRECONDITION(CheckPointer(pExternalAddress)); |
680 | } |
681 | CONTRACTL_END; |
682 | |
683 | CodeLabel *pCodeLabel = (CodeLabel*)(m_quickHeap.Alloc(sizeof(CodeLabel))); |
684 | _ASSERTE(pCodeLabel); // QuickHeap throws exceptions rather than returning NULL |
685 | pCodeLabel->m_next = m_pFirstCodeLabel; |
686 | pCodeLabel->m_fExternal = TRUE; |
687 | pCodeLabel->m_fAbsolute = FALSE; |
688 | pCodeLabel->e.m_pExternalAddress = pExternalAddress; |
689 | m_pFirstCodeLabel = pCodeLabel; |
690 | return pCodeLabel; |
691 | } |
692 | |
693 | |
694 | |
695 | |
696 | //--------------------------------------------------------------- |
697 | // Append an instruction containing a reference to a label. |
698 | // |
699 | // target - the label being referenced. |
700 | // instructionFormat - a platform-specific InstructionFormat object |
701 | // that gives properties about the reference. |
702 | // variationCode - uninterpreted data passed to the pInstructionFormat methods. |
703 | //--------------------------------------------------------------- |
704 | VOID StubLinker::EmitLabelRef(CodeLabel* target, const InstructionFormat & instructionFormat, UINT variationCode) |
705 | { |
706 | CONTRACTL |
707 | { |
708 | THROWS; |
709 | GC_NOTRIGGER; |
710 | SO_TOLERANT; |
711 | } |
712 | CONTRACTL_END; |
713 | |
714 | LabelRef *pLabelRef = (LabelRef *)(m_quickHeap.Alloc(sizeof(LabelRef))); |
715 | _ASSERTE(pLabelRef); // m_quickHeap throws an exception rather than returning NULL |
716 | pLabelRef->m_type = LabelRef::kLabelRef; |
717 | pLabelRef->m_pInstructionFormat = (InstructionFormat*)&instructionFormat; |
718 | pLabelRef->m_variationCode = variationCode; |
719 | pLabelRef->m_target = target; |
720 | |
721 | pLabelRef->m_nextLabelRef = m_pFirstLabelRef; |
722 | m_pFirstLabelRef = pLabelRef; |
723 | |
724 | AppendCodeElement(pLabelRef); |
725 | |
726 | |
727 | } |
728 | |
729 | |
730 | |
731 | |
732 | |
733 | //--------------------------------------------------------------- |
734 | // Internal helper routine. |
735 | //--------------------------------------------------------------- |
736 | CodeRun *StubLinker::GetLastCodeRunIfAny() |
737 | { |
738 | CONTRACTL |
739 | { |
740 | NOTHROW; |
741 | GC_NOTRIGGER; |
742 | SO_TOLERANT; |
743 | } |
744 | CONTRACTL_END; |
745 | |
746 | CodeElement *pLastCodeElem = GetLastCodeElement(); |
747 | if (pLastCodeElem == NULL || pLastCodeElem->m_type != CodeElement::kCodeRun) { |
748 | return NULL; |
749 | } else { |
750 | return (CodeRun*)pLastCodeElem; |
751 | } |
752 | } |
753 | |
754 | |
755 | //--------------------------------------------------------------- |
756 | // Internal helper routine. |
757 | //--------------------------------------------------------------- |
758 | CodeRun *StubLinker::AppendNewEmptyCodeRun() |
759 | { |
760 | CONTRACTL |
761 | { |
762 | THROWS; |
763 | GC_NOTRIGGER; |
764 | } |
765 | CONTRACTL_END; |
766 | |
767 | CodeRun *pNewCodeRun = (CodeRun*)(m_quickHeap.Alloc(sizeof(CodeRun))); |
768 | _ASSERTE(pNewCodeRun); // QuickHeap throws exceptions rather than returning NULL |
769 | pNewCodeRun->m_type = CodeElement::kCodeRun; |
770 | pNewCodeRun->m_numcodebytes = 0; |
771 | AppendCodeElement(pNewCodeRun); |
772 | return pNewCodeRun; |
773 | |
774 | } |
775 | |
776 | //--------------------------------------------------------------- |
777 | // Internal helper routine. |
778 | //--------------------------------------------------------------- |
779 | VOID StubLinker::AppendCodeElement(CodeElement *pCodeElement) |
780 | { |
781 | CONTRACTL |
782 | { |
783 | NOTHROW; |
784 | GC_NOTRIGGER; |
785 | SO_TOLERANT; |
786 | } |
787 | CONTRACTL_END; |
788 | |
789 | pCodeElement->m_next = m_pCodeElements; |
790 | m_pCodeElements = pCodeElement; |
791 | } |
792 | |
793 | |
794 | |
795 | //--------------------------------------------------------------- |
796 | // Is the current LabelRef's size big enough to reach the target? |
797 | //--------------------------------------------------------------- |
798 | static BOOL LabelCanReach(LabelRef *pLabelRef) |
799 | { |
800 | CONTRACTL |
801 | { |
802 | NOTHROW; |
803 | GC_NOTRIGGER; |
804 | SO_TOLERANT; |
805 | } |
806 | CONTRACTL_END; |
807 | |
808 | InstructionFormat *pIF = pLabelRef->m_pInstructionFormat; |
809 | |
810 | if (pLabelRef->m_target->m_fExternal) |
811 | { |
812 | return pLabelRef->m_pInstructionFormat->CanReach( |
813 | pLabelRef->m_refsize, pLabelRef->m_variationCode, TRUE, (INT_PTR)pLabelRef->m_target->e.m_pExternalAddress); |
814 | } |
815 | else |
816 | { |
817 | UINT targetglobaloffset = pLabelRef->m_target->i.m_pCodeRun->m_globaloffset + |
818 | pLabelRef->m_target->i.m_localOffset; |
819 | UINT srcglobaloffset = pLabelRef->m_globaloffset + |
820 | pIF->GetHotSpotOffset(pLabelRef->m_refsize, |
821 | pLabelRef->m_variationCode); |
822 | INT offset = (INT)(targetglobaloffset - srcglobaloffset); |
823 | |
824 | return pLabelRef->m_pInstructionFormat->CanReach( |
825 | pLabelRef->m_refsize, pLabelRef->m_variationCode, FALSE, offset); |
826 | } |
827 | } |
828 | |
829 | //--------------------------------------------------------------- |
830 | // Generate the actual stub. The returned stub has a refcount of 1. |
831 | // No other methods (other than the destructor) should be called |
832 | // after calling Link(). |
833 | // |
834 | // Throws COM+ exception on failure. |
835 | //--------------------------------------------------------------- |
836 | Stub *StubLinker::LinkInterceptor(LoaderHeap *pHeap, Stub* interceptee, void *pRealAddr) |
837 | { |
838 | STANDARD_VM_CONTRACT; |
839 | |
840 | int globalsize = 0; |
841 | int size = CalculateSize(&globalsize); |
842 | |
843 | _ASSERTE(!pHeap || pHeap->IsExecutable()); |
844 | |
845 | StubHolder<Stub> pStub; |
846 | |
847 | #ifdef STUBLINKER_GENERATES_UNWIND_INFO |
848 | StubUnwindInfoSegmentBoundaryReservationList ReservedStubs; |
849 | |
850 | for (;;) |
851 | #endif |
852 | { |
853 | pStub = InterceptStub::NewInterceptedStub(pHeap, size, interceptee, |
854 | pRealAddr |
855 | #ifdef STUBLINKER_GENERATES_UNWIND_INFO |
856 | , UnwindInfoSize(globalsize) |
857 | #endif |
858 | ); |
859 | bool fSuccess; fSuccess = EmitStub(pStub, globalsize, pHeap); |
860 | |
861 | #ifdef STUBLINKER_GENERATES_UNWIND_INFO |
862 | if (fSuccess) |
863 | { |
864 | break; |
865 | } |
866 | else |
867 | { |
868 | ReservedStubs.AddStub(pStub); |
869 | pStub.SuppressRelease(); |
870 | } |
871 | #else |
872 | CONSISTENCY_CHECK_MSG(fSuccess, ("EmitStub should always return true" )); |
873 | #endif |
874 | } |
875 | |
876 | return pStub.Extract(); |
877 | } |
878 | |
879 | //--------------------------------------------------------------- |
880 | // Generate the actual stub. The returned stub has a refcount of 1. |
881 | // No other methods (other than the destructor) should be called |
882 | // after calling Link(). |
883 | // |
884 | // Throws COM+ exception on failure. |
885 | //--------------------------------------------------------------- |
886 | Stub *StubLinker::Link(LoaderHeap *pHeap, DWORD flags) |
887 | { |
888 | STANDARD_VM_CONTRACT; |
889 | |
890 | int globalsize = 0; |
891 | int size = CalculateSize(&globalsize); |
892 | |
893 | #ifndef CROSSGEN_COMPILE |
894 | _ASSERTE(!pHeap || pHeap->IsExecutable()); |
895 | #endif |
896 | |
897 | StubHolder<Stub> pStub; |
898 | |
899 | #ifdef STUBLINKER_GENERATES_UNWIND_INFO |
900 | StubUnwindInfoSegmentBoundaryReservationList ReservedStubs; |
901 | |
902 | for (;;) |
903 | #endif |
904 | { |
905 | pStub = Stub::NewStub( |
906 | pHeap, |
907 | size, |
908 | flags |
909 | #ifdef STUBLINKER_GENERATES_UNWIND_INFO |
910 | , UnwindInfoSize(globalsize) |
911 | #endif |
912 | ); |
913 | ASSERT(pStub != NULL); |
914 | |
915 | bool fSuccess; fSuccess = EmitStub(pStub, globalsize, pHeap); |
916 | |
917 | #ifdef STUBLINKER_GENERATES_UNWIND_INFO |
918 | if (fSuccess) |
919 | { |
920 | break; |
921 | } |
922 | else |
923 | { |
924 | ReservedStubs.AddStub(pStub); |
925 | pStub.SuppressRelease(); |
926 | } |
927 | #else |
928 | CONSISTENCY_CHECK_MSG(fSuccess, ("EmitStub should always return true" )); |
929 | #endif |
930 | } |
931 | |
932 | return pStub.Extract(); |
933 | } |
934 | |
935 | int StubLinker::CalculateSize(int* pGlobalSize) |
936 | { |
937 | CONTRACTL |
938 | { |
939 | NOTHROW; |
940 | GC_NOTRIGGER; |
941 | SO_TOLERANT; |
942 | } |
943 | CONTRACTL_END; |
944 | |
945 | _ASSERTE(pGlobalSize); |
946 | |
947 | #if defined(_DEBUG) && defined(STUBLINKER_GENERATES_UNWIND_INFO) && !defined(CROSSGEN_COMPILE) |
948 | if (m_pUnwindInfoCheckLabel) |
949 | { |
950 | EmitLabel(m_pUnwindInfoCheckLabel); |
951 | EmitUnwindInfoCheckSubfunction(); |
952 | m_pUnwindInfoCheckLabel = NULL; |
953 | } |
954 | #endif |
955 | |
956 | #ifdef _DEBUG |
957 | // Don't want any undefined labels |
958 | for (CodeLabel *pCodeLabel = m_pFirstCodeLabel; |
959 | pCodeLabel != NULL; |
960 | pCodeLabel = pCodeLabel->m_next) { |
961 | if ((!(pCodeLabel->m_fExternal)) && pCodeLabel->i.m_pCodeRun == NULL) { |
962 | _ASSERTE(!"Forgot to define a label before asking StubLinker to link." ); |
963 | } |
964 | } |
965 | #endif //_DEBUG |
966 | |
967 | //------------------------------------------------------------------- |
968 | // Tentatively set all of the labelref sizes to their smallest possible |
969 | // value. |
970 | //------------------------------------------------------------------- |
971 | for (LabelRef *pLabelRef = m_pFirstLabelRef; |
972 | pLabelRef != NULL; |
973 | pLabelRef = pLabelRef->m_nextLabelRef) { |
974 | |
975 | for (UINT bitmask = 1; bitmask <= InstructionFormat::kMax; bitmask = bitmask << 1) { |
976 | if (pLabelRef->m_pInstructionFormat->m_allowedSizes & bitmask) { |
977 | pLabelRef->m_refsize = bitmask; |
978 | break; |
979 | } |
980 | } |
981 | |
982 | } |
983 | |
984 | UINT globalsize; |
985 | UINT datasize; |
986 | BOOL fSomethingChanged; |
987 | do { |
988 | fSomethingChanged = FALSE; |
989 | |
990 | |
991 | // Layout each code element. |
992 | globalsize = 0; |
993 | datasize = 0; |
994 | CodeElement *pCodeElem; |
995 | for (pCodeElem = m_pCodeElements; pCodeElem; pCodeElem = pCodeElem->m_next) { |
996 | |
997 | switch (pCodeElem->m_type) { |
998 | case CodeElement::kCodeRun: |
999 | globalsize += ((CodeRun*)pCodeElem)->m_numcodebytes; |
1000 | break; |
1001 | |
1002 | case CodeElement::kLabelRef: { |
1003 | LabelRef *pLabelRef = (LabelRef*)pCodeElem; |
1004 | globalsize += pLabelRef->m_pInstructionFormat->GetSizeOfInstruction( pLabelRef->m_refsize, |
1005 | pLabelRef->m_variationCode ); |
1006 | datasize += pLabelRef->m_pInstructionFormat->GetSizeOfData( pLabelRef->m_refsize, |
1007 | pLabelRef->m_variationCode ); |
1008 | } |
1009 | break; |
1010 | |
1011 | default: |
1012 | _ASSERTE(0); |
1013 | } |
1014 | |
1015 | // Record a temporary global offset; this is actually |
1016 | // wrong by a fixed value. We'll fix up after we know the |
1017 | // size of the entire stub. |
1018 | pCodeElem->m_globaloffset = 0 - globalsize; |
1019 | |
1020 | // also record the data offset. Note the link-list we walk is in |
1021 | // *reverse* order so we visit the last instruction first |
1022 | // so what we record now is in fact the offset from the *end* of |
1023 | // the data block. We fix it up later. |
1024 | pCodeElem->m_dataoffset = 0 - datasize; |
1025 | } |
1026 | |
1027 | // Now fix up the global offsets. |
1028 | for (pCodeElem = m_pCodeElements; pCodeElem; pCodeElem = pCodeElem->m_next) { |
1029 | pCodeElem->m_globaloffset += globalsize; |
1030 | pCodeElem->m_dataoffset += datasize; |
1031 | } |
1032 | |
1033 | |
1034 | // Now, iterate thru the LabelRef's and check if any of them |
1035 | // have to be resized. |
1036 | for (LabelRef *pLabelRef = m_pFirstLabelRef; |
1037 | pLabelRef != NULL; |
1038 | pLabelRef = pLabelRef->m_nextLabelRef) { |
1039 | |
1040 | |
1041 | if (!LabelCanReach(pLabelRef)) { |
1042 | fSomethingChanged = TRUE; |
1043 | |
1044 | UINT bitmask = pLabelRef->m_refsize << 1; |
1045 | // Find the next largest size. |
1046 | // (we could be smarter about this and eliminate intermediate |
1047 | // sizes based on the tentative offset.) |
1048 | for (; bitmask <= InstructionFormat::kMax; bitmask = bitmask << 1) { |
1049 | if (pLabelRef->m_pInstructionFormat->m_allowedSizes & bitmask) { |
1050 | pLabelRef->m_refsize = bitmask; |
1051 | break; |
1052 | } |
1053 | } |
1054 | #ifdef _DEBUG |
1055 | if (bitmask > InstructionFormat::kMax) { |
1056 | // CANNOT REACH target even with kMax |
1057 | _ASSERTE(!"Stub instruction cannot reach target: must choose a different instruction!" ); |
1058 | } |
1059 | #endif |
1060 | } |
1061 | } |
1062 | |
1063 | |
1064 | } while (fSomethingChanged); // Keep iterating until all LabelRef's can reach |
1065 | |
1066 | |
1067 | // We now have the correct layout write out the stub. |
1068 | |
1069 | // Compute stub code+data size after aligning data correctly |
1070 | if(globalsize % DATA_ALIGNMENT) |
1071 | globalsize += (DATA_ALIGNMENT - (globalsize % DATA_ALIGNMENT)); |
1072 | |
1073 | *pGlobalSize = globalsize; |
1074 | return globalsize + datasize; |
1075 | } |
1076 | |
1077 | bool StubLinker::EmitStub(Stub* pStub, int globalsize, LoaderHeap* pHeap) |
1078 | { |
1079 | STANDARD_VM_CONTRACT; |
1080 | |
1081 | BYTE *pCode = (BYTE*)(pStub->GetBlob()); |
1082 | BYTE *pData = pCode+globalsize; // start of data area |
1083 | { |
1084 | int lastCodeOffset = 0; |
1085 | |
1086 | // Write out each code element. |
1087 | for (CodeElement* pCodeElem = m_pCodeElements; pCodeElem; pCodeElem = pCodeElem->m_next) { |
1088 | int currOffset = 0; |
1089 | |
1090 | switch (pCodeElem->m_type) { |
1091 | case CodeElement::kCodeRun: |
1092 | CopyMemory(pCode + pCodeElem->m_globaloffset, |
1093 | ((CodeRun*)pCodeElem)->m_codebytes, |
1094 | ((CodeRun*)pCodeElem)->m_numcodebytes); |
1095 | currOffset = pCodeElem->m_globaloffset + ((CodeRun *)pCodeElem)->m_numcodebytes; |
1096 | break; |
1097 | |
1098 | case CodeElement::kLabelRef: { |
1099 | LabelRef *pLabelRef = (LabelRef*)pCodeElem; |
1100 | InstructionFormat *pIF = pLabelRef->m_pInstructionFormat; |
1101 | __int64 fixupval; |
1102 | |
1103 | LPBYTE srcglobaladdr = pCode + |
1104 | pLabelRef->m_globaloffset + |
1105 | pIF->GetHotSpotOffset(pLabelRef->m_refsize, |
1106 | pLabelRef->m_variationCode); |
1107 | LPBYTE targetglobaladdr; |
1108 | if (!(pLabelRef->m_target->m_fExternal)) { |
1109 | targetglobaladdr = pCode + |
1110 | pLabelRef->m_target->i.m_pCodeRun->m_globaloffset + |
1111 | pLabelRef->m_target->i.m_localOffset; |
1112 | } else { |
1113 | targetglobaladdr = (LPBYTE)(pLabelRef->m_target->e.m_pExternalAddress); |
1114 | } |
1115 | if ((pLabelRef->m_target->m_fAbsolute)) { |
1116 | fixupval = (__int64)(size_t)targetglobaladdr; |
1117 | } else |
1118 | fixupval = (__int64)(targetglobaladdr - srcglobaladdr); |
1119 | |
1120 | pLabelRef->m_pInstructionFormat->EmitInstruction( |
1121 | pLabelRef->m_refsize, |
1122 | fixupval, |
1123 | pCode + pCodeElem->m_globaloffset, |
1124 | pLabelRef->m_variationCode, |
1125 | pData + pCodeElem->m_dataoffset); |
1126 | |
1127 | currOffset = |
1128 | pCodeElem->m_globaloffset + |
1129 | pLabelRef->m_pInstructionFormat->GetSizeOfInstruction( pLabelRef->m_refsize, |
1130 | pLabelRef->m_variationCode ); |
1131 | } |
1132 | break; |
1133 | |
1134 | default: |
1135 | _ASSERTE(0); |
1136 | } |
1137 | lastCodeOffset = (currOffset > lastCodeOffset) ? currOffset : lastCodeOffset; |
1138 | } |
1139 | |
1140 | // Fill in zeros at the end, if necessary |
1141 | if (lastCodeOffset < globalsize) |
1142 | ZeroMemory(pCode + lastCodeOffset, globalsize - lastCodeOffset); |
1143 | } |
1144 | |
1145 | // Fill in patch offset, if we have one |
1146 | // Note that these offsets are relative to the start of the stub, |
1147 | // not the code, so you'll have to add sizeof(Stub) to get to the |
1148 | // right spot. |
1149 | if (m_pPatchLabel != NULL) |
1150 | { |
1151 | UINT32 uLabelOffset = GetLabelOffset(m_pPatchLabel); |
1152 | _ASSERTE(FitsIn<USHORT>(uLabelOffset)); |
1153 | pStub->SetPatchOffset(static_cast<USHORT>(uLabelOffset)); |
1154 | |
1155 | LOG((LF_CORDB, LL_INFO100, "SL::ES: patch offset:0x%x\n" , |
1156 | pStub->GetPatchOffset())); |
1157 | } |
1158 | |
1159 | #ifdef STUBLINKER_GENERATES_UNWIND_INFO |
1160 | if (pStub->HasUnwindInfo()) |
1161 | { |
1162 | if (!EmitUnwindInfo(pStub, globalsize, pHeap)) |
1163 | return false; |
1164 | } |
1165 | #endif // STUBLINKER_GENERATES_UNWIND_INFO |
1166 | |
1167 | if (!m_fDataOnly) |
1168 | { |
1169 | FlushInstructionCache(GetCurrentProcess(), pCode, globalsize); |
1170 | } |
1171 | |
1172 | _ASSERTE(m_fDataOnly || DbgIsExecutable(pCode, globalsize)); |
1173 | |
1174 | return true; |
1175 | } |
1176 | |
1177 | |
1178 | #ifdef STUBLINKER_GENERATES_UNWIND_INFO |
1179 | #if defined(_TARGET_AMD64_) |
1180 | |
1181 | // See RtlVirtualUnwind in base\ntos\rtl\amd64\exdsptch.c |
1182 | |
1183 | static_assert_no_msg(kRAX == (FIELD_OFFSET(CONTEXT, Rax) - FIELD_OFFSET(CONTEXT, Rax)) / sizeof(ULONG64)); |
1184 | static_assert_no_msg(kRCX == (FIELD_OFFSET(CONTEXT, Rcx) - FIELD_OFFSET(CONTEXT, Rax)) / sizeof(ULONG64)); |
1185 | static_assert_no_msg(kRDX == (FIELD_OFFSET(CONTEXT, Rdx) - FIELD_OFFSET(CONTEXT, Rax)) / sizeof(ULONG64)); |
1186 | static_assert_no_msg(kRBX == (FIELD_OFFSET(CONTEXT, Rbx) - FIELD_OFFSET(CONTEXT, Rax)) / sizeof(ULONG64)); |
1187 | static_assert_no_msg(kRBP == (FIELD_OFFSET(CONTEXT, Rbp) - FIELD_OFFSET(CONTEXT, Rax)) / sizeof(ULONG64)); |
1188 | static_assert_no_msg(kRSI == (FIELD_OFFSET(CONTEXT, Rsi) - FIELD_OFFSET(CONTEXT, Rax)) / sizeof(ULONG64)); |
1189 | static_assert_no_msg(kRDI == (FIELD_OFFSET(CONTEXT, Rdi) - FIELD_OFFSET(CONTEXT, Rax)) / sizeof(ULONG64)); |
1190 | static_assert_no_msg(kR8 == (FIELD_OFFSET(CONTEXT, R8 ) - FIELD_OFFSET(CONTEXT, Rax)) / sizeof(ULONG64)); |
1191 | static_assert_no_msg(kR9 == (FIELD_OFFSET(CONTEXT, R9 ) - FIELD_OFFSET(CONTEXT, Rax)) / sizeof(ULONG64)); |
1192 | static_assert_no_msg(kR10 == (FIELD_OFFSET(CONTEXT, R10) - FIELD_OFFSET(CONTEXT, Rax)) / sizeof(ULONG64)); |
1193 | static_assert_no_msg(kR11 == (FIELD_OFFSET(CONTEXT, R11) - FIELD_OFFSET(CONTEXT, Rax)) / sizeof(ULONG64)); |
1194 | static_assert_no_msg(kR12 == (FIELD_OFFSET(CONTEXT, R12) - FIELD_OFFSET(CONTEXT, Rax)) / sizeof(ULONG64)); |
1195 | static_assert_no_msg(kR13 == (FIELD_OFFSET(CONTEXT, R13) - FIELD_OFFSET(CONTEXT, Rax)) / sizeof(ULONG64)); |
1196 | static_assert_no_msg(kR14 == (FIELD_OFFSET(CONTEXT, R14) - FIELD_OFFSET(CONTEXT, Rax)) / sizeof(ULONG64)); |
1197 | static_assert_no_msg(kR15 == (FIELD_OFFSET(CONTEXT, R15) - FIELD_OFFSET(CONTEXT, Rax)) / sizeof(ULONG64)); |
1198 | |
1199 | VOID StubLinker::UnwindSavedReg (UCHAR reg, ULONG SPRelativeOffset) |
1200 | { |
1201 | USHORT FrameOffset = (USHORT)(SPRelativeOffset / 8); |
1202 | |
1203 | if ((ULONG)FrameOffset == SPRelativeOffset) |
1204 | { |
1205 | UNWIND_CODE *pUnwindCode = AllocUnwindInfo(UWOP_SAVE_NONVOL); |
1206 | pUnwindCode->OpInfo = reg; |
1207 | pUnwindCode[1].FrameOffset = FrameOffset; |
1208 | } |
1209 | else |
1210 | { |
1211 | UNWIND_CODE *pUnwindCode = AllocUnwindInfo(UWOP_SAVE_NONVOL_FAR); |
1212 | pUnwindCode->OpInfo = reg; |
1213 | pUnwindCode[1].FrameOffset = (USHORT)SPRelativeOffset; |
1214 | pUnwindCode[2].FrameOffset = (USHORT)(SPRelativeOffset >> 16); |
1215 | } |
1216 | } |
1217 | |
1218 | VOID StubLinker::UnwindPushedReg (UCHAR reg) |
1219 | { |
1220 | m_stackSize += sizeof(void*); |
1221 | |
1222 | if (m_fHaveFramePointer) |
1223 | return; |
1224 | |
1225 | UNWIND_CODE *pUnwindCode = AllocUnwindInfo(UWOP_PUSH_NONVOL); |
1226 | pUnwindCode->OpInfo = reg; |
1227 | } |
1228 | |
1229 | VOID StubLinker::UnwindAllocStack (SHORT FrameSizeIncrement) |
1230 | { |
1231 | CONTRACTL |
1232 | { |
1233 | THROWS; |
1234 | GC_NOTRIGGER; |
1235 | SO_TOLERANT; |
1236 | } CONTRACTL_END; |
1237 | |
1238 | if (! ClrSafeInt<SHORT>::addition(m_stackSize, FrameSizeIncrement, m_stackSize)) |
1239 | COMPlusThrowArithmetic(); |
1240 | |
1241 | if (m_fHaveFramePointer) |
1242 | return; |
1243 | |
1244 | UCHAR OpInfo = (UCHAR)((FrameSizeIncrement - 8) / 8); |
1245 | |
1246 | if (OpInfo*8 + 8 == FrameSizeIncrement) |
1247 | { |
1248 | UNWIND_CODE *pUnwindCode = AllocUnwindInfo(UWOP_ALLOC_SMALL); |
1249 | pUnwindCode->OpInfo = OpInfo; |
1250 | } |
1251 | else |
1252 | { |
1253 | USHORT FrameOffset = (USHORT)FrameSizeIncrement; |
1254 | BOOL fNeedExtraSlot = ((ULONG)FrameOffset != (ULONG)FrameSizeIncrement); |
1255 | |
1256 | UNWIND_CODE *pUnwindCode = AllocUnwindInfo(UWOP_ALLOC_LARGE, fNeedExtraSlot); |
1257 | |
1258 | pUnwindCode->OpInfo = fNeedExtraSlot; |
1259 | |
1260 | pUnwindCode[1].FrameOffset = FrameOffset; |
1261 | |
1262 | if (fNeedExtraSlot) |
1263 | pUnwindCode[2].FrameOffset = (USHORT)(FrameSizeIncrement >> 16); |
1264 | } |
1265 | } |
1266 | |
1267 | VOID StubLinker::UnwindSetFramePointer (UCHAR reg) |
1268 | { |
1269 | _ASSERTE(!m_fHaveFramePointer); |
1270 | |
1271 | UNWIND_CODE *pUnwindCode = AllocUnwindInfo(UWOP_SET_FPREG); |
1272 | pUnwindCode->OpInfo = reg; |
1273 | |
1274 | m_fHaveFramePointer = TRUE; |
1275 | } |
1276 | |
1277 | UNWIND_CODE *StubLinker::AllocUnwindInfo (UCHAR Op, UCHAR nExtraSlots /*= 0*/) |
1278 | { |
1279 | CONTRACTL |
1280 | { |
1281 | THROWS; |
1282 | GC_NOTRIGGER; |
1283 | SO_TOLERANT; |
1284 | } CONTRACTL_END; |
1285 | |
1286 | _ASSERTE(Op < sizeof(UnwindOpExtraSlotTable)); |
1287 | |
1288 | UCHAR nSlotsAlloc = UnwindOpExtraSlotTable[Op] + nExtraSlots; |
1289 | |
1290 | IntermediateUnwindInfo *pUnwindInfo = (IntermediateUnwindInfo*)m_quickHeap.Alloc( sizeof(IntermediateUnwindInfo) |
1291 | + nSlotsAlloc * sizeof(UNWIND_CODE)); |
1292 | m_nUnwindSlots += 1 + nSlotsAlloc; |
1293 | |
1294 | pUnwindInfo->pNext = m_pUnwindInfoList; |
1295 | m_pUnwindInfoList = pUnwindInfo; |
1296 | |
1297 | UNWIND_CODE *pUnwindCode = &pUnwindInfo->rgUnwindCode[0]; |
1298 | |
1299 | pUnwindCode->UnwindOp = Op; |
1300 | |
1301 | CodeRun *pCodeRun = GetLastCodeRunIfAny(); |
1302 | _ASSERTE(pCodeRun != NULL); |
1303 | |
1304 | pUnwindInfo->pCodeRun = pCodeRun; |
1305 | pUnwindInfo->LocalOffset = pCodeRun->m_numcodebytes; |
1306 | |
1307 | EmitUnwindInfoCheck(); |
1308 | |
1309 | return pUnwindCode; |
1310 | } |
1311 | #endif // defined(_TARGET_AMD64_) |
1312 | |
1313 | struct FindBlockArgs |
1314 | { |
1315 | BYTE *pCode; |
1316 | BYTE *pBlockBase; |
1317 | SIZE_T cbBlockSize; |
1318 | }; |
1319 | |
1320 | bool FindBlockCallback (PTR_VOID pvArgs, PTR_VOID pvAllocationBase, SIZE_T cbReserved) |
1321 | { |
1322 | CONTRACTL |
1323 | { |
1324 | NOTHROW; |
1325 | GC_TRIGGERS; |
1326 | } |
1327 | CONTRACTL_END; |
1328 | |
1329 | FindBlockArgs* pArgs = (FindBlockArgs*)pvArgs; |
1330 | if (pArgs->pCode >= pvAllocationBase && (pArgs->pCode < ((BYTE *)pvAllocationBase + cbReserved))) |
1331 | { |
1332 | pArgs->pBlockBase = (BYTE*)pvAllocationBase; |
1333 | pArgs->cbBlockSize = cbReserved; |
1334 | return true; |
1335 | } |
1336 | |
1337 | return false; |
1338 | } |
1339 | |
1340 | bool StubLinker::EmitUnwindInfo(Stub* pStub, int globalsize, LoaderHeap* pHeap) |
1341 | { |
1342 | STANDARD_VM_CONTRACT; |
1343 | |
1344 | BYTE *pCode = (BYTE*)(pStub->GetEntryPoint()); |
1345 | |
1346 | // |
1347 | // Determine the lower bound of the address space containing the stub. |
1348 | // |
1349 | |
1350 | FindBlockArgs findBlockArgs; |
1351 | findBlockArgs.pCode = pCode; |
1352 | findBlockArgs.pBlockBase = NULL; |
1353 | |
1354 | pHeap->EnumPageRegions(&FindBlockCallback, &findBlockArgs); |
1355 | |
1356 | if (findBlockArgs.pBlockBase == NULL) |
1357 | { |
1358 | // REVISIT_TODO better exception |
1359 | COMPlusThrowOM(); |
1360 | } |
1361 | |
1362 | BYTE *pbRegionBaseAddress = findBlockArgs.pBlockBase; |
1363 | |
1364 | #ifdef _DEBUG |
1365 | static SIZE_T MaxSegmentSize = -1; |
1366 | if (MaxSegmentSize == (SIZE_T)-1) |
1367 | MaxSegmentSize = EEConfig::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_MaxStubUnwindInfoSegmentSize, DYNAMIC_FUNCTION_TABLE_MAX_RANGE); |
1368 | #else |
1369 | const SIZE_T MaxSegmentSize = DYNAMIC_FUNCTION_TABLE_MAX_RANGE; |
1370 | #endif |
1371 | |
1372 | // |
1373 | // The RUNTIME_FUNCTION offsets are ULONGs. If the region size is > |
1374 | // ULONG_MAX, then we'll shift the base address to the next 4gb and |
1375 | // register a separate function table. |
1376 | // |
1377 | // But...RtlInstallFunctionTableCallback has a 2gb restriction...so |
1378 | // make that LONG_MAX. |
1379 | // |
1380 | |
1381 | StubUnwindInfoHeader *pHeader = pStub->GetUnwindInfoHeader(); |
1382 | _ASSERTE(IS_ALIGNED(pHeader, sizeof(void*))); |
1383 | |
1384 | BYTE *pbBaseAddress = pbRegionBaseAddress; |
1385 | |
1386 | while ((size_t)((BYTE*)pHeader - pbBaseAddress) > MaxSegmentSize) |
1387 | { |
1388 | pbBaseAddress += MaxSegmentSize; |
1389 | } |
1390 | |
1391 | // |
1392 | // If the unwind info/code straddle a 2gb boundary, then we're stuck. |
1393 | // Rather than add a lot more bit twiddling code to deal with this |
1394 | // exceptionally rare case, we'll signal the caller to keep this allocation |
1395 | // temporarily and allocate another. This repeats until we eventually get |
1396 | // an allocation that doesn't straddle a 2gb boundary. Afterwards the old |
1397 | // allocations are freed. |
1398 | // |
1399 | |
1400 | if ((size_t)(pCode + globalsize - pbBaseAddress) > MaxSegmentSize) |
1401 | { |
1402 | return false; |
1403 | } |
1404 | |
1405 | // Ensure that the first RUNTIME_FUNCTION struct ends up pointer aligned, |
1406 | // so that the StubUnwindInfoHeader struct is aligned. UNWIND_INFO |
1407 | // includes one UNWIND_CODE. |
1408 | _ASSERTE(IS_ALIGNED(pStub, sizeof(void*))); |
1409 | _ASSERTE(0 == (FIELD_OFFSET(StubUnwindInfoHeader, FunctionEntry) % sizeof(void*))); |
1410 | |
1411 | StubUnwindInfoHeader * pUnwindInfoHeader = pStub->GetUnwindInfoHeader(); |
1412 | |
1413 | #ifdef _TARGET_AMD64_ |
1414 | |
1415 | UNWIND_CODE *pDestUnwindCode = &pUnwindInfoHeader->UnwindInfo.UnwindCode[0]; |
1416 | #ifdef _DEBUG |
1417 | UNWIND_CODE *pDestUnwindCodeLimit = (UNWIND_CODE*)pStub->GetUnwindInfoHeaderSuffix(); |
1418 | #endif |
1419 | |
1420 | UINT FrameRegister = 0; |
1421 | |
1422 | // |
1423 | // Resolve the unwind operation offsets, and fill in the UNWIND_INFO and |
1424 | // RUNTIME_FUNCTION structs preceeding the stub. The unwind codes are recorded |
1425 | // in decreasing address order. |
1426 | // |
1427 | |
1428 | for (IntermediateUnwindInfo *pUnwindInfoList = m_pUnwindInfoList; pUnwindInfoList != NULL; pUnwindInfoList = pUnwindInfoList->pNext) |
1429 | { |
1430 | UNWIND_CODE *pUnwindCode = &pUnwindInfoList->rgUnwindCode[0]; |
1431 | UCHAR op = pUnwindCode[0].UnwindOp; |
1432 | |
1433 | if (UWOP_SET_FPREG == op) |
1434 | { |
1435 | FrameRegister = pUnwindCode[0].OpInfo; |
1436 | } |
1437 | |
1438 | // |
1439 | // Compute number of slots used by this encoding. |
1440 | // |
1441 | |
1442 | UINT nSlots; |
1443 | |
1444 | if (UWOP_ALLOC_LARGE == op) |
1445 | { |
1446 | nSlots = 2 + pUnwindCode[0].OpInfo; |
1447 | } |
1448 | else |
1449 | { |
1450 | _ASSERTE(UnwindOpExtraSlotTable[op] != (UCHAR)-1); |
1451 | nSlots = 1 + UnwindOpExtraSlotTable[op]; |
1452 | } |
1453 | |
1454 | // |
1455 | // Compute offset and ensure that it will fit in the encoding. |
1456 | // |
1457 | |
1458 | SIZE_T CodeOffset = pUnwindInfoList->pCodeRun->m_globaloffset |
1459 | + pUnwindInfoList->LocalOffset; |
1460 | |
1461 | if (CodeOffset != (SIZE_T)(UCHAR)CodeOffset) |
1462 | { |
1463 | // REVISIT_TODO better exception |
1464 | COMPlusThrowOM(); |
1465 | } |
1466 | |
1467 | // |
1468 | // Copy the encoding data, overwrite the new offset, and advance |
1469 | // to the next encoding. |
1470 | // |
1471 | |
1472 | _ASSERTE(pDestUnwindCode + nSlots <= pDestUnwindCodeLimit); |
1473 | |
1474 | CopyMemory(pDestUnwindCode, pUnwindCode, nSlots * sizeof(UNWIND_CODE)); |
1475 | |
1476 | pDestUnwindCode->CodeOffset = (UCHAR)CodeOffset; |
1477 | |
1478 | pDestUnwindCode += nSlots; |
1479 | } |
1480 | |
1481 | // |
1482 | // Fill in the UNWIND_INFO struct |
1483 | // |
1484 | UNWIND_INFO *pUnwindInfo = &pUnwindInfoHeader->UnwindInfo; |
1485 | _ASSERTE(IS_ALIGNED(pUnwindInfo, sizeof(ULONG))); |
1486 | |
1487 | // PrologueSize may be 0 if all unwind directives at offset 0. |
1488 | SIZE_T PrologueSize = m_pUnwindInfoList->pCodeRun->m_globaloffset |
1489 | + m_pUnwindInfoList->LocalOffset; |
1490 | |
1491 | UINT nEntryPointSlots = m_nUnwindSlots; |
1492 | |
1493 | if ( PrologueSize != (SIZE_T)(UCHAR)PrologueSize |
1494 | || nEntryPointSlots > UCHAR_MAX) |
1495 | { |
1496 | // REVISIT_TODO better exception |
1497 | COMPlusThrowOM(); |
1498 | } |
1499 | |
1500 | _ASSERTE(nEntryPointSlots); |
1501 | |
1502 | pUnwindInfo->Version = 1; |
1503 | pUnwindInfo->Flags = 0; |
1504 | pUnwindInfo->SizeOfProlog = (UCHAR)PrologueSize; |
1505 | pUnwindInfo->CountOfUnwindCodes = (UCHAR)nEntryPointSlots; |
1506 | pUnwindInfo->FrameRegister = FrameRegister; |
1507 | pUnwindInfo->FrameOffset = 0; |
1508 | |
1509 | // |
1510 | // Fill in the RUNTIME_FUNCTION struct for this prologue. |
1511 | // |
1512 | PT_RUNTIME_FUNCTION pCurFunction = &pUnwindInfoHeader->FunctionEntry; |
1513 | _ASSERTE(IS_ALIGNED(pCurFunction, sizeof(ULONG))); |
1514 | |
1515 | S_UINT32 sBeginAddress = S_BYTEPTR(pCode) - S_BYTEPTR(pbBaseAddress); |
1516 | if (sBeginAddress.IsOverflow()) |
1517 | COMPlusThrowArithmetic(); |
1518 | pCurFunction->BeginAddress = sBeginAddress.Value(); |
1519 | |
1520 | S_UINT32 sEndAddress = S_BYTEPTR(pCode) + S_BYTEPTR(globalsize) - S_BYTEPTR(pbBaseAddress); |
1521 | if (sEndAddress.IsOverflow()) |
1522 | COMPlusThrowArithmetic(); |
1523 | pCurFunction->EndAddress = sEndAddress.Value(); |
1524 | |
1525 | S_UINT32 sTemp = S_BYTEPTR(pUnwindInfo) - S_BYTEPTR(pbBaseAddress); |
1526 | if (sTemp.IsOverflow()) |
1527 | COMPlusThrowArithmetic(); |
1528 | RUNTIME_FUNCTION__SetUnwindInfoAddress(pCurFunction, sTemp.Value()); |
1529 | #elif defined(_TARGET_ARM_) |
1530 | // |
1531 | // Fill in the RUNTIME_FUNCTION struct for this prologue. |
1532 | // |
1533 | UNWIND_INFO *pUnwindInfo = &pUnwindInfoHeader->UnwindInfo; |
1534 | |
1535 | PT_RUNTIME_FUNCTION pCurFunction = &pUnwindInfoHeader->FunctionEntry; |
1536 | _ASSERTE(IS_ALIGNED(pCurFunction, sizeof(ULONG))); |
1537 | |
1538 | S_UINT32 sBeginAddress = S_BYTEPTR(pCode) - S_BYTEPTR(pbBaseAddress); |
1539 | if (sBeginAddress.IsOverflow()) |
1540 | COMPlusThrowArithmetic(); |
1541 | RUNTIME_FUNCTION__SetBeginAddress(pCurFunction, sBeginAddress.Value()); |
1542 | |
1543 | S_UINT32 sTemp = S_BYTEPTR(pUnwindInfo) - S_BYTEPTR(pbBaseAddress); |
1544 | if (sTemp.IsOverflow()) |
1545 | COMPlusThrowArithmetic(); |
1546 | RUNTIME_FUNCTION__SetUnwindInfoAddress(pCurFunction, sTemp.Value()); |
1547 | |
1548 | //Get the exact function Length. Cannot use globalsize as it is explicitly made to be |
1549 | // 4 byte aligned |
1550 | CodeRun *pLastCodeElem = GetLastCodeRunIfAny(); |
1551 | _ASSERTE(pLastCodeElem != NULL); |
1552 | |
1553 | int functionLength = pLastCodeElem->m_numcodebytes + pLastCodeElem->m_globaloffset; |
1554 | |
1555 | // cannot encode functionLength greater than (2 * 0xFFFFF) |
1556 | if (functionLength > 2 * 0xFFFFF) |
1557 | COMPlusThrowArithmetic(); |
1558 | |
1559 | _ASSERTE(functionLength <= globalsize); |
1560 | |
1561 | BYTE * pUnwindCodes = (BYTE *)pUnwindInfo + sizeof(DWORD); |
1562 | |
1563 | // Not emitting compact unwind info as there are very few (4) dynamic stubs with unwind info. |
1564 | // Benefit of the optimization does not outweigh the cost of adding the code for it. |
1565 | |
1566 | //UnwindInfo for prolog |
1567 | if (m_cbStackFrame != 0) |
1568 | { |
1569 | if(m_cbStackFrame < 512) |
1570 | { |
1571 | *pUnwindCodes++ = (BYTE)0xF8; // 16-bit sub/add sp,#x |
1572 | *pUnwindCodes++ = (BYTE)(m_cbStackFrame >> 18); |
1573 | *pUnwindCodes++ = (BYTE)(m_cbStackFrame >> 10); |
1574 | *pUnwindCodes++ = (BYTE)(m_cbStackFrame >> 2); |
1575 | } |
1576 | else |
1577 | { |
1578 | *pUnwindCodes++ = (BYTE)0xFA; // 32-bit sub/add sp,#x |
1579 | *pUnwindCodes++ = (BYTE)(m_cbStackFrame >> 18); |
1580 | *pUnwindCodes++ = (BYTE)(m_cbStackFrame >> 10); |
1581 | *pUnwindCodes++ = (BYTE)(m_cbStackFrame >> 2); |
1582 | } |
1583 | |
1584 | if(m_cbStackFrame >= 4096) |
1585 | { |
1586 | // r4 register is used as param to checkStack function and must have been saved in prolog |
1587 | _ASSERTE(m_cCalleeSavedRegs > 0); |
1588 | *pUnwindCodes++ = (BYTE)0xFB; // nop 16 bit for bl r12 |
1589 | *pUnwindCodes++ = (BYTE)0xFC; // nop 32 bit for movt r12, checkStack |
1590 | *pUnwindCodes++ = (BYTE)0xFC; // nop 32 bit for movw r12, checkStack |
1591 | |
1592 | // Ensure that mov r4, m_cbStackFrame fits in a 32-bit instruction |
1593 | if(m_cbStackFrame > 65535) |
1594 | COMPlusThrow(kNotSupportedException); |
1595 | *pUnwindCodes++ = (BYTE)0xFC; // nop 32 bit for mov r4, m_cbStackFrame |
1596 | } |
1597 | } |
1598 | |
1599 | // Unwind info generated will be incorrect when m_cCalleeSavedRegs = 0. |
1600 | // The unwind code will say that the size of push/pop instruction |
1601 | // size is 16bits when actually the opcode generated by |
1602 | // ThumbEmitPop & ThumbEMitPush will be 32bits. |
1603 | // Currently no stubs has m_cCalleeSavedRegs as 0 |
1604 | // therfore just adding the assert. |
1605 | _ASSERTE(m_cCalleeSavedRegs > 0); |
1606 | |
1607 | if (m_cCalleeSavedRegs <= 4) |
1608 | { |
1609 | *pUnwindCodes++ = (BYTE)(0xD4 + (m_cCalleeSavedRegs - 1)); // push/pop {r4-rX} |
1610 | } |
1611 | else |
1612 | { |
1613 | _ASSERTE(m_cCalleeSavedRegs <= 8); |
1614 | *pUnwindCodes++ = (BYTE)(0xDC + (m_cCalleeSavedRegs - 5)); // push/pop {r4-rX} |
1615 | } |
1616 | |
1617 | if (m_fPushArgRegs) |
1618 | { |
1619 | *pUnwindCodes++ = (BYTE)0x04; // push {r0-r3} / add sp,#16 |
1620 | *pUnwindCodes++ = (BYTE)0xFD; // bx lr |
1621 | } |
1622 | else |
1623 | { |
1624 | *pUnwindCodes++ = (BYTE)0xFF; // end |
1625 | } |
1626 | |
1627 | ptrdiff_t epilogUnwindCodeIndex = 0; |
1628 | |
1629 | //epilog differs from prolog |
1630 | if(m_cbStackFrame >= 4096) |
1631 | { |
1632 | //Index of the first unwind code of the epilog |
1633 | epilogUnwindCodeIndex = pUnwindCodes - (BYTE *)pUnwindInfo - sizeof(DWORD); |
1634 | |
1635 | *pUnwindCodes++ = (BYTE)0xF8; // sub/add sp,#x |
1636 | *pUnwindCodes++ = (BYTE)(m_cbStackFrame >> 18); |
1637 | *pUnwindCodes++ = (BYTE)(m_cbStackFrame >> 10); |
1638 | *pUnwindCodes++ = (BYTE)(m_cbStackFrame >> 2); |
1639 | |
1640 | if (m_cCalleeSavedRegs <= 4) |
1641 | { |
1642 | *pUnwindCodes++ = (BYTE)(0xD4 + (m_cCalleeSavedRegs - 1)); // push/pop {r4-rX} |
1643 | } |
1644 | else |
1645 | { |
1646 | *pUnwindCodes++ = (BYTE)(0xDC + (m_cCalleeSavedRegs - 5)); // push/pop {r4-rX} |
1647 | } |
1648 | |
1649 | if (m_fPushArgRegs) |
1650 | { |
1651 | *pUnwindCodes++ = (BYTE)0x04; // push {r0-r3} / add sp,#16 |
1652 | *pUnwindCodes++ = (BYTE)0xFD; // bx lr |
1653 | } |
1654 | else |
1655 | { |
1656 | *pUnwindCodes++ = (BYTE)0xFF; // end |
1657 | } |
1658 | |
1659 | } |
1660 | |
1661 | // Number of 32-bit unwind codes |
1662 | size_t codeWordsCount = (ALIGN_UP((size_t)pUnwindCodes, sizeof(void*)) - (size_t)pUnwindInfo - sizeof(DWORD))/4; |
1663 | |
1664 | _ASSERTE(epilogUnwindCodeIndex < 32); |
1665 | |
1666 | //Check that MAX_UNWIND_CODE_WORDS is sufficient to store all unwind Codes |
1667 | _ASSERTE(codeWordsCount <= MAX_UNWIND_CODE_WORDS); |
1668 | |
1669 | *(DWORD *)pUnwindInfo = |
1670 | ((functionLength) / 2) | |
1671 | (1 << 21) | |
1672 | ((int)epilogUnwindCodeIndex << 23)| |
1673 | ((int)codeWordsCount << 28); |
1674 | |
1675 | #elif defined(_TARGET_ARM64_) |
1676 | if (!m_fProlog) |
1677 | { |
1678 | // If EmitProlog isn't called. This is a leaf function which doesn't need any unwindInfo |
1679 | T_RUNTIME_FUNCTION *pCurFunction = NULL; |
1680 | } |
1681 | else |
1682 | { |
1683 | |
1684 | // |
1685 | // Fill in the RUNTIME_FUNCTION struct for this prologue. |
1686 | // |
1687 | UNWIND_INFO *pUnwindInfo = &(pUnwindInfoHeader->UnwindInfo); |
1688 | |
1689 | T_RUNTIME_FUNCTION *pCurFunction = &(pUnwindInfoHeader->FunctionEntry); |
1690 | |
1691 | _ASSERTE(IS_ALIGNED(pCurFunction, sizeof(void*))); |
1692 | |
1693 | S_UINT32 sBeginAddress = S_BYTEPTR(pCode) - S_BYTEPTR(pbBaseAddress); |
1694 | if (sBeginAddress.IsOverflow()) |
1695 | COMPlusThrowArithmetic(); |
1696 | |
1697 | S_UINT32 sTemp = S_BYTEPTR(pUnwindInfo) - S_BYTEPTR(pbBaseAddress); |
1698 | if (sTemp.IsOverflow()) |
1699 | COMPlusThrowArithmetic(); |
1700 | |
1701 | RUNTIME_FUNCTION__SetBeginAddress(pCurFunction, sBeginAddress.Value()); |
1702 | RUNTIME_FUNCTION__SetUnwindInfoAddress(pCurFunction, sTemp.Value()); |
1703 | |
1704 | CodeRun *pLastCodeElem = GetLastCodeRunIfAny(); |
1705 | _ASSERTE(pLastCodeElem != NULL); |
1706 | |
1707 | int functionLength = pLastCodeElem->m_numcodebytes + pLastCodeElem->m_globaloffset; |
1708 | |
1709 | // .xdata has 18 bits for function length and it is to store the total length of the function in bytes, divided by 4 |
1710 | // If the function is larger than 1M, then multiple pdata and xdata records must be used, which we don't support right now. |
1711 | if (functionLength > 4 * 0x3FFFF) |
1712 | COMPlusThrowArithmetic(); |
1713 | |
1714 | _ASSERTE(functionLength <= globalsize); |
1715 | |
1716 | // No support for extended code words and/or extended epilog. |
1717 | // ASSERTION: first 10 bits of the pUnwindInfo, which holds the #codewords and #epilogcount, cannot be 0 |
1718 | // And no space for exception scope data also means that no support for exceptions for the stubs |
1719 | // generated with this stublinker. |
1720 | BYTE * pUnwindCodes = (BYTE *)pUnwindInfo + sizeof(DWORD); |
1721 | |
1722 | |
1723 | // Emitting the unwind codes: |
1724 | // The unwind codes are emited in Epilog order. |
1725 | // |
1726 | // 6. Integer argument registers |
1727 | // Although we might be saving the argument registers in the prolog we don't need |
1728 | // to report them to the OS. (they are not expressible anyways) |
1729 | |
1730 | // 5. Floating point argument registers: |
1731 | // Similar to Integer argument registers, no reporting |
1732 | // |
1733 | |
1734 | // 4. Set the frame pointer |
1735 | // ASSUMPTION: none of the Stubs generated with this stublinker change SP value outside of epilog and prolog |
1736 | // when that is the case we can skip reporting setting up the frame pointer |
1737 | |
1738 | // With skiping Step #4, #5 and #6 Prolog and Epilog becomes reversible. so they can share the unwind codes |
1739 | int epilogUnwindCodeIndex = 0; |
1740 | |
1741 | unsigned cStackFrameSizeInQWORDs = GetStackFrameSize()/16; |
1742 | // 3. Store FP/LR |
1743 | // save_fplr |
1744 | *pUnwindCodes++ = (BYTE)(0x40 | (m_cbStackSpace>>3)); |
1745 | |
1746 | // 2. Callee-saved registers |
1747 | // |
1748 | if (m_cCalleeSavedRegs > 0) |
1749 | { |
1750 | unsigned offset = 2 + m_cbStackSpace/8; // 2 is for fp,lr |
1751 | if ((m_cCalleeSavedRegs %2) ==1) |
1752 | { |
1753 | // save_reg |
1754 | *pUnwindCodes++ = (BYTE) (0xD0 | ((m_cCalleeSavedRegs-1)>>2)); |
1755 | *pUnwindCodes++ = (BYTE) ((BYTE)((m_cCalleeSavedRegs-1) << 6) | ((offset + m_cCalleeSavedRegs - 1) & 0x3F)); |
1756 | } |
1757 | for (int i=(m_cCalleeSavedRegs/2)*2-2; i>=0; i-=2) |
1758 | { |
1759 | if (i!=0) |
1760 | { |
1761 | // save_next |
1762 | *pUnwindCodes++ = 0xE6; |
1763 | } |
1764 | else |
1765 | { |
1766 | // save_regp |
1767 | *pUnwindCodes++ = 0xC8; |
1768 | *pUnwindCodes++ = (BYTE)(offset & 0x3F); |
1769 | } |
1770 | } |
1771 | } |
1772 | |
1773 | // 1. SP Relocation |
1774 | // |
1775 | // EmitProlog is supposed to reject frames larger than 504 bytes. |
1776 | // Assert that here. |
1777 | _ASSERTE(cStackFrameSizeInQWORDs <= 0x3F); |
1778 | if (cStackFrameSizeInQWORDs <= 0x1F) |
1779 | { |
1780 | // alloc_s |
1781 | *pUnwindCodes++ = (BYTE)(cStackFrameSizeInQWORDs); |
1782 | } |
1783 | else |
1784 | { |
1785 | // alloc_m |
1786 | *pUnwindCodes++ = (BYTE)(0xC0 | (cStackFrameSizeInQWORDs >> 8)); |
1787 | *pUnwindCodes++ = (BYTE)(cStackFrameSizeInQWORDs); |
1788 | } |
1789 | |
1790 | // End |
1791 | *pUnwindCodes++ = 0xE4; |
1792 | |
1793 | // Number of 32-bit unwind codes |
1794 | int codeWordsCount = (int)(ALIGN_UP((size_t)pUnwindCodes, sizeof(DWORD)) - (size_t)pUnwindInfo - sizeof(DWORD))/4; |
1795 | |
1796 | //Check that MAX_UNWIND_CODE_WORDS is sufficient to store all unwind Codes |
1797 | _ASSERTE(codeWordsCount <= MAX_UNWIND_CODE_WORDS); |
1798 | |
1799 | *(DWORD *)pUnwindInfo = |
1800 | ((functionLength) / 4) | |
1801 | (1 << 21) | // E bit |
1802 | (epilogUnwindCodeIndex << 22)| |
1803 | (codeWordsCount << 27); |
1804 | } // end else (!m_fProlog) |
1805 | #else |
1806 | PORTABILITY_ASSERT("StubLinker::EmitUnwindInfo" ); |
1807 | T_RUNTIME_FUNCTION *pCurFunction = NULL; |
1808 | #endif |
1809 | |
1810 | // |
1811 | // Get a StubUnwindInfoHeapSegment for this base address |
1812 | // |
1813 | |
1814 | CrstHolder crst(&g_StubUnwindInfoHeapSegmentsCrst); |
1815 | |
1816 | StubUnwindInfoHeapSegment *pStubHeapSegment; |
1817 | StubUnwindInfoHeapSegment **ppPrevStubHeapSegment; |
1818 | for (ppPrevStubHeapSegment = &g_StubHeapSegments; |
1819 | (pStubHeapSegment = *ppPrevStubHeapSegment); |
1820 | (ppPrevStubHeapSegment = &pStubHeapSegment->pNext)) |
1821 | { |
1822 | if (pbBaseAddress < pStubHeapSegment->pbBaseAddress) |
1823 | { |
1824 | // The list is ordered, so address is between segments |
1825 | pStubHeapSegment = NULL; |
1826 | break; |
1827 | } |
1828 | |
1829 | if (pbBaseAddress == pStubHeapSegment->pbBaseAddress) |
1830 | { |
1831 | // Found an existing segment |
1832 | break; |
1833 | } |
1834 | } |
1835 | |
1836 | if (!pStubHeapSegment) |
1837 | { |
1838 | // |
1839 | // RtlInstallFunctionTableCallback will only accept a ULONG for the |
1840 | // region size. We've already checked above that the RUNTIME_FUNCTION |
1841 | // offsets will work relative to pbBaseAddress. |
1842 | // |
1843 | |
1844 | SIZE_T cbSegment = findBlockArgs.cbBlockSize; |
1845 | |
1846 | if (cbSegment > MaxSegmentSize) |
1847 | cbSegment = MaxSegmentSize; |
1848 | |
1849 | NewHolder<StubUnwindInfoHeapSegment> pNewStubHeapSegment = new StubUnwindInfoHeapSegment(); |
1850 | |
1851 | |
1852 | pNewStubHeapSegment->pbBaseAddress = pbBaseAddress; |
1853 | pNewStubHeapSegment->cbSegment = cbSegment; |
1854 | pNewStubHeapSegment->pUnwindHeaderList = NULL; |
1855 | #ifdef _TARGET_AMD64_ |
1856 | pNewStubHeapSegment->pUnwindInfoTable = NULL; |
1857 | #endif |
1858 | |
1859 | // Insert the new stub into list |
1860 | pNewStubHeapSegment->pNext = *ppPrevStubHeapSegment; |
1861 | *ppPrevStubHeapSegment = pNewStubHeapSegment; |
1862 | pNewStubHeapSegment.SuppressRelease(); |
1863 | |
1864 | // Use new segment for the stub |
1865 | pStubHeapSegment = pNewStubHeapSegment; |
1866 | |
1867 | InstallEEFunctionTable( |
1868 | pNewStubHeapSegment, |
1869 | pbBaseAddress, |
1870 | (ULONG)cbSegment, |
1871 | &FindStubFunctionEntry, |
1872 | pNewStubHeapSegment, |
1873 | DYNFNTABLE_STUB); |
1874 | } |
1875 | |
1876 | // |
1877 | // Link the new stub into the segment. |
1878 | // |
1879 | |
1880 | pHeader->pNext = pStubHeapSegment->pUnwindHeaderList; |
1881 | pStubHeapSegment->pUnwindHeaderList = pHeader; |
1882 | |
1883 | #ifdef _TARGET_AMD64_ |
1884 | // Publish Unwind info to ETW stack crawler |
1885 | UnwindInfoTable::AddToUnwindInfoTable( |
1886 | &pStubHeapSegment->pUnwindInfoTable, pCurFunction, |
1887 | (TADDR) pStubHeapSegment->pbBaseAddress, |
1888 | (TADDR) pStubHeapSegment->pbBaseAddress + pStubHeapSegment->cbSegment); |
1889 | #endif |
1890 | |
1891 | #ifdef _DEBUG |
1892 | _ASSERTE(pHeader->IsRegistered()); |
1893 | _ASSERTE( &pHeader->FunctionEntry |
1894 | == FindStubFunctionEntry((ULONG64)pCode, EncodeDynamicFunctionTableContext(pStubHeapSegment, DYNFNTABLE_STUB))); |
1895 | #endif |
1896 | |
1897 | return true; |
1898 | } |
1899 | #endif // STUBLINKER_GENERATES_UNWIND_INFO |
1900 | |
1901 | #ifdef _TARGET_ARM_ |
1902 | void StubLinker::DescribeProlog(UINT cCalleeSavedRegs, UINT cbStackFrame, BOOL fPushArgRegs) |
1903 | { |
1904 | m_fProlog = TRUE; |
1905 | m_cCalleeSavedRegs = cCalleeSavedRegs; |
1906 | m_cbStackFrame = cbStackFrame; |
1907 | m_fPushArgRegs = fPushArgRegs; |
1908 | } |
1909 | #elif defined(_TARGET_ARM64_) |
1910 | void StubLinker::DescribeProlog(UINT cIntRegArgs, UINT cVecRegArgs, UINT cCalleeSavedRegs, UINT cbStackSpace) |
1911 | { |
1912 | m_fProlog = TRUE; |
1913 | m_cIntRegArgs = cIntRegArgs; |
1914 | m_cVecRegArgs = cVecRegArgs; |
1915 | m_cCalleeSavedRegs = cCalleeSavedRegs; |
1916 | m_cbStackSpace = cbStackSpace; |
1917 | } |
1918 | |
1919 | UINT StubLinker::GetSavedRegArgsOffset() |
1920 | { |
1921 | _ASSERTE(m_fProlog); |
1922 | // This is the offset from SP |
1923 | // We're assuming that the stublinker will push the arg registers to the bottom of the stack frame |
1924 | return m_cbStackSpace + (2+ m_cCalleeSavedRegs)*sizeof(void*); // 2 is for FP and LR |
1925 | } |
1926 | |
1927 | UINT StubLinker::GetStackFrameSize() |
1928 | { |
1929 | _ASSERTE(m_fProlog); |
1930 | return m_cbStackSpace + (2 + m_cCalleeSavedRegs + m_cIntRegArgs + m_cVecRegArgs)*sizeof(void*); |
1931 | } |
1932 | |
1933 | |
1934 | #endif // ifdef _TARGET_ARM_, elif defined(_TARGET_ARM64_) |
1935 | |
1936 | #endif // #ifndef DACCESS_COMPILE |
1937 | |
1938 | #ifndef DACCESS_COMPILE |
1939 | |
1940 | //------------------------------------------------------------------- |
1941 | // Inc the refcount. |
1942 | //------------------------------------------------------------------- |
1943 | VOID Stub::IncRef() |
1944 | { |
1945 | CONTRACTL |
1946 | { |
1947 | NOTHROW; |
1948 | GC_NOTRIGGER; |
1949 | } |
1950 | CONTRACTL_END; |
1951 | |
1952 | _ASSERTE(m_signature == kUsedStub); |
1953 | FastInterlockIncrement((LONG*)&m_refcount); |
1954 | } |
1955 | |
1956 | //------------------------------------------------------------------- |
1957 | // Dec the refcount. |
1958 | //------------------------------------------------------------------- |
1959 | BOOL Stub::DecRef() |
1960 | { |
1961 | CONTRACTL |
1962 | { |
1963 | NOTHROW; |
1964 | GC_TRIGGERS; |
1965 | } |
1966 | CONTRACTL_END; |
1967 | |
1968 | _ASSERTE(m_signature == kUsedStub); |
1969 | int count = FastInterlockDecrement((LONG*)&m_refcount); |
1970 | if (count <= 0) { |
1971 | if(m_patchOffset & INTERCEPT_BIT) |
1972 | { |
1973 | ((InterceptStub*)this)->ReleaseInterceptedStub(); |
1974 | } |
1975 | |
1976 | DeleteStub(); |
1977 | return TRUE; |
1978 | } |
1979 | return FALSE; |
1980 | } |
1981 | |
1982 | VOID Stub::DeleteStub() |
1983 | { |
1984 | CONTRACTL |
1985 | { |
1986 | NOTHROW; |
1987 | GC_TRIGGERS; |
1988 | } |
1989 | CONTRACTL_END; |
1990 | |
1991 | COUNTER_ONLY(GetPerfCounters().m_Interop.cStubs--); |
1992 | |
1993 | #ifdef STUBLINKER_GENERATES_UNWIND_INFO |
1994 | if (HasUnwindInfo()) |
1995 | { |
1996 | StubUnwindInfoHeader *pHeader = GetUnwindInfoHeader(); |
1997 | |
1998 | // |
1999 | // Check if the stub has been linked into a StubUnwindInfoHeapSegment. |
2000 | // |
2001 | if (pHeader->IsRegistered()) |
2002 | { |
2003 | CrstHolder crst(&g_StubUnwindInfoHeapSegmentsCrst); |
2004 | |
2005 | // |
2006 | // Find the segment containing the stub. |
2007 | // |
2008 | StubUnwindInfoHeapSegment **ppPrevSegment = &g_StubHeapSegments; |
2009 | StubUnwindInfoHeapSegment *pSegment = *ppPrevSegment; |
2010 | |
2011 | if (pSegment) |
2012 | { |
2013 | PBYTE pbCode = (PBYTE)GetEntryPointInternal(); |
2014 | #ifdef _TARGET_AMD64_ |
2015 | UnwindInfoTable::RemoveFromUnwindInfoTable(&pSegment->pUnwindInfoTable, |
2016 | (TADDR) pSegment->pbBaseAddress, (TADDR) pbCode); |
2017 | #endif |
2018 | for (StubUnwindInfoHeapSegment *pNextSegment = pSegment->pNext; |
2019 | pNextSegment; |
2020 | ppPrevSegment = &pSegment->pNext, pSegment = pNextSegment, pNextSegment = pSegment->pNext) |
2021 | { |
2022 | // The segments are sorted by pbBaseAddress. |
2023 | if (pbCode < pNextSegment->pbBaseAddress) |
2024 | break; |
2025 | } |
2026 | } |
2027 | |
2028 | // The stub was marked as registered, so a segment should exist. |
2029 | _ASSERTE(pSegment); |
2030 | |
2031 | if (pSegment) |
2032 | { |
2033 | |
2034 | // |
2035 | // Find this stub's location in the segment's list. |
2036 | // |
2037 | StubUnwindInfoHeader *pCurHeader; |
2038 | StubUnwindInfoHeader **ppPrevHeaderList; |
2039 | for (ppPrevHeaderList = &pSegment->pUnwindHeaderList; |
2040 | (pCurHeader = *ppPrevHeaderList); |
2041 | (ppPrevHeaderList = &pCurHeader->pNext)) |
2042 | { |
2043 | if (pHeader == pCurHeader) |
2044 | break; |
2045 | } |
2046 | |
2047 | // The stub was marked as registered, so we should find it in the segment's list. |
2048 | _ASSERTE(pCurHeader); |
2049 | |
2050 | if (pCurHeader) |
2051 | { |
2052 | // |
2053 | // Remove the stub from the segment's list. |
2054 | // |
2055 | *ppPrevHeaderList = pHeader->pNext; |
2056 | |
2057 | // |
2058 | // If the segment's list is now empty, delete the segment. |
2059 | // |
2060 | if (!pSegment->pUnwindHeaderList) |
2061 | { |
2062 | DeleteEEFunctionTable(pSegment); |
2063 | #ifdef _TARGET_AMD64_ |
2064 | if (pSegment->pUnwindInfoTable != 0) |
2065 | delete pSegment->pUnwindInfoTable; |
2066 | #endif |
2067 | *ppPrevSegment = pSegment->pNext; |
2068 | delete pSegment; |
2069 | } |
2070 | } |
2071 | } |
2072 | } |
2073 | } |
2074 | #endif |
2075 | |
2076 | // a size of 0 is a signal to Nirvana to flush the entire cache |
2077 | //FlushInstructionCache(GetCurrentProcess(),0,0); |
2078 | |
2079 | if ((m_patchOffset & LOADER_HEAP_BIT) == 0) |
2080 | { |
2081 | #ifdef _DEBUG |
2082 | m_signature = kFreedStub; |
2083 | FillMemory(this+1, m_numCodeBytes, 0xcc); |
2084 | #endif |
2085 | |
2086 | #ifndef FEATURE_PAL |
2087 | DeleteExecutable((BYTE*)GetAllocationBase()); |
2088 | #else |
2089 | delete [] (BYTE*)GetAllocationBase(); |
2090 | #endif |
2091 | } |
2092 | } |
2093 | |
2094 | TADDR Stub::GetAllocationBase() |
2095 | { |
2096 | CONTRACTL |
2097 | { |
2098 | NOTHROW; |
2099 | GC_NOTRIGGER; |
2100 | FORBID_FAULT; |
2101 | } |
2102 | CONTRACTL_END |
2103 | |
2104 | TADDR info = dac_cast<TADDR>(this); |
2105 | SIZE_T cbPrefix = 0; |
2106 | |
2107 | if (IsIntercept()) |
2108 | { |
2109 | cbPrefix += 2 * sizeof(TADDR); |
2110 | } |
2111 | |
2112 | #ifdef STUBLINKER_GENERATES_UNWIND_INFO |
2113 | if (HasUnwindInfo()) |
2114 | { |
2115 | StubUnwindInfoHeaderSuffix *pSuffix = |
2116 | PTR_StubUnwindInfoHeaderSuffix(info - cbPrefix - |
2117 | sizeof(*pSuffix)); |
2118 | |
2119 | cbPrefix += StubUnwindInfoHeader::ComputeSize(pSuffix->nUnwindInfoSize); |
2120 | } |
2121 | #endif // STUBLINKER_GENERATES_UNWIND_INFO |
2122 | |
2123 | if (!HasExternalEntryPoint()) |
2124 | { |
2125 | cbPrefix = ALIGN_UP(cbPrefix + sizeof(Stub), CODE_SIZE_ALIGN) - sizeof(Stub); |
2126 | } |
2127 | |
2128 | return info - cbPrefix; |
2129 | } |
2130 | |
2131 | Stub* Stub::NewStub(PTR_VOID pCode, DWORD flags) |
2132 | { |
2133 | CONTRACTL |
2134 | { |
2135 | THROWS; |
2136 | GC_NOTRIGGER; |
2137 | } |
2138 | CONTRACTL_END; |
2139 | |
2140 | Stub* pStub = NewStub(NULL, 0, flags | NEWSTUB_FL_EXTERNAL); |
2141 | _ASSERTE(pStub->HasExternalEntryPoint()); |
2142 | |
2143 | *(PTR_VOID *)(pStub + 1) = pCode; |
2144 | |
2145 | return pStub; |
2146 | } |
2147 | |
2148 | //------------------------------------------------------------------- |
2149 | // Stub allocation done here. |
2150 | //------------------------------------------------------------------- |
2151 | /*static*/ Stub* Stub::NewStub( |
2152 | LoaderHeap *pHeap, |
2153 | UINT numCodeBytes, |
2154 | DWORD flags |
2155 | #ifdef STUBLINKER_GENERATES_UNWIND_INFO |
2156 | , UINT nUnwindInfoSize |
2157 | #endif |
2158 | ) |
2159 | { |
2160 | CONTRACTL |
2161 | { |
2162 | THROWS; |
2163 | GC_NOTRIGGER; |
2164 | } |
2165 | CONTRACTL_END; |
2166 | |
2167 | #ifdef STUBLINKER_GENERATES_UNWIND_INFO |
2168 | _ASSERTE(!nUnwindInfoSize || !pHeap || pHeap->m_fPermitStubsWithUnwindInfo); |
2169 | #endif // STUBLINKER_GENERATES_UNWIND_INFO |
2170 | |
2171 | COUNTER_ONLY(GetPerfCounters().m_Interop.cStubs++); |
2172 | |
2173 | S_SIZE_T size = S_SIZE_T(sizeof(Stub)); |
2174 | |
2175 | if (flags & NEWSTUB_FL_INTERCEPT) |
2176 | { |
2177 | size += sizeof(Stub *) + sizeof(void*); |
2178 | } |
2179 | |
2180 | #ifdef STUBLINKER_GENERATES_UNWIND_INFO |
2181 | if (nUnwindInfoSize != 0) |
2182 | { |
2183 | size += StubUnwindInfoHeader::ComputeSize(nUnwindInfoSize); |
2184 | } |
2185 | #endif |
2186 | |
2187 | if (flags & NEWSTUB_FL_EXTERNAL) |
2188 | { |
2189 | _ASSERTE(numCodeBytes == 0); |
2190 | size += sizeof(PTR_PCODE); |
2191 | } |
2192 | else |
2193 | { |
2194 | size.AlignUp(CODE_SIZE_ALIGN); |
2195 | size += numCodeBytes; |
2196 | } |
2197 | |
2198 | if (size.IsOverflow()) |
2199 | COMPlusThrowArithmetic(); |
2200 | |
2201 | size_t totalSize = size.Value(); |
2202 | |
2203 | BYTE *pBlock; |
2204 | if (pHeap == NULL) |
2205 | { |
2206 | #ifndef FEATURE_PAL |
2207 | pBlock = new (executable) BYTE[totalSize]; |
2208 | #else |
2209 | pBlock = new BYTE[totalSize]; |
2210 | #endif |
2211 | } |
2212 | else |
2213 | { |
2214 | pBlock = (BYTE*)(void*) pHeap->AllocAlignedMem(totalSize, CODE_SIZE_ALIGN); |
2215 | flags |= NEWSTUB_FL_LOADERHEAP; |
2216 | } |
2217 | |
2218 | // Make sure that the payload of the stub is aligned |
2219 | Stub* pStub = (Stub*)((pBlock + totalSize) - |
2220 | (sizeof(Stub) + ((flags & NEWSTUB_FL_EXTERNAL) ? sizeof(PTR_PCODE) : numCodeBytes))); |
2221 | |
2222 | pStub->SetupStub( |
2223 | numCodeBytes, |
2224 | flags |
2225 | #ifdef STUBLINKER_GENERATES_UNWIND_INFO |
2226 | , nUnwindInfoSize |
2227 | #endif |
2228 | ); |
2229 | |
2230 | _ASSERTE((BYTE *)pStub->GetAllocationBase() == pBlock); |
2231 | |
2232 | return pStub; |
2233 | } |
2234 | |
2235 | void Stub::SetupStub(int numCodeBytes, DWORD flags |
2236 | #ifdef STUBLINKER_GENERATES_UNWIND_INFO |
2237 | , UINT nUnwindInfoSize |
2238 | #endif |
2239 | ) |
2240 | { |
2241 | CONTRACTL |
2242 | { |
2243 | NOTHROW; |
2244 | GC_NOTRIGGER; |
2245 | } |
2246 | CONTRACTL_END; |
2247 | |
2248 | #ifdef _DEBUG |
2249 | m_signature = kUsedStub; |
2250 | #else |
2251 | #ifdef _WIN64 |
2252 | m_pad_code_bytes = 0; |
2253 | #endif |
2254 | #endif |
2255 | |
2256 | m_numCodeBytes = numCodeBytes; |
2257 | |
2258 | m_refcount = 1; |
2259 | m_patchOffset = 0; |
2260 | |
2261 | if((flags & NEWSTUB_FL_INTERCEPT) != 0) |
2262 | m_patchOffset |= INTERCEPT_BIT; |
2263 | if((flags & NEWSTUB_FL_LOADERHEAP) != 0) |
2264 | m_patchOffset |= LOADER_HEAP_BIT; |
2265 | if((flags & NEWSTUB_FL_MULTICAST) != 0) |
2266 | m_patchOffset |= MULTICAST_DELEGATE_BIT; |
2267 | if ((flags & NEWSTUB_FL_EXTERNAL) != 0) |
2268 | m_patchOffset |= EXTERNAL_ENTRY_BIT; |
2269 | |
2270 | #ifdef STUBLINKER_GENERATES_UNWIND_INFO |
2271 | if (nUnwindInfoSize) |
2272 | { |
2273 | m_patchOffset |= UNWIND_INFO_BIT; |
2274 | |
2275 | StubUnwindInfoHeaderSuffix * pSuffix = GetUnwindInfoHeaderSuffix(); |
2276 | pSuffix->nUnwindInfoSize = (BYTE)nUnwindInfoSize; |
2277 | |
2278 | StubUnwindInfoHeader * pHeader = GetUnwindInfoHeader(); |
2279 | pHeader->Init(); |
2280 | } |
2281 | #endif |
2282 | } |
2283 | |
2284 | //------------------------------------------------------------------- |
2285 | // One-time init |
2286 | //------------------------------------------------------------------- |
2287 | /*static*/ void Stub::Init() |
2288 | { |
2289 | CONTRACTL |
2290 | { |
2291 | THROWS; |
2292 | GC_NOTRIGGER; |
2293 | } |
2294 | CONTRACTL_END; |
2295 | |
2296 | #ifdef STUBLINKER_GENERATES_UNWIND_INFO |
2297 | g_StubUnwindInfoHeapSegmentsCrst.Init(CrstStubUnwindInfoHeapSegments); |
2298 | #endif |
2299 | } |
2300 | |
2301 | /*static*/ Stub* InterceptStub::NewInterceptedStub(void* pCode, |
2302 | Stub* interceptee, |
2303 | void* pRealAddr) |
2304 | { |
2305 | CONTRACTL |
2306 | { |
2307 | THROWS; |
2308 | GC_NOTRIGGER; |
2309 | } |
2310 | CONTRACTL_END; |
2311 | |
2312 | InterceptStub *pStub = (InterceptStub *) NewStub(pCode, NEWSTUB_FL_INTERCEPT); |
2313 | |
2314 | *pStub->GetInterceptedStub() = interceptee; |
2315 | *pStub->GetRealAddr() = (TADDR)pRealAddr; |
2316 | |
2317 | LOG((LF_CORDB, LL_INFO10000, "For Stub 0x%x, set intercepted stub to 0x%x\n" , |
2318 | pStub, interceptee)); |
2319 | |
2320 | return pStub; |
2321 | } |
2322 | |
2323 | //------------------------------------------------------------------- |
2324 | // Stub allocation done here. |
2325 | //------------------------------------------------------------------- |
2326 | /*static*/ Stub* InterceptStub::NewInterceptedStub(LoaderHeap *pHeap, |
2327 | UINT numCodeBytes, |
2328 | Stub* interceptee, |
2329 | void* pRealAddr |
2330 | #ifdef STUBLINKER_GENERATES_UNWIND_INFO |
2331 | , UINT nUnwindInfoSize |
2332 | #endif |
2333 | ) |
2334 | { |
2335 | CONTRACTL |
2336 | { |
2337 | THROWS; |
2338 | GC_NOTRIGGER; |
2339 | } |
2340 | CONTRACTL_END; |
2341 | |
2342 | InterceptStub *pStub = (InterceptStub *) NewStub( |
2343 | pHeap, |
2344 | numCodeBytes, |
2345 | NEWSTUB_FL_INTERCEPT |
2346 | #ifdef STUBLINKER_GENERATES_UNWIND_INFO |
2347 | , nUnwindInfoSize |
2348 | #endif |
2349 | ); |
2350 | |
2351 | *pStub->GetInterceptedStub() = interceptee; |
2352 | *pStub->GetRealAddr() = (TADDR)pRealAddr; |
2353 | |
2354 | LOG((LF_CORDB, LL_INFO10000, "For Stub 0x%x, set intercepted stub to 0x%x\n" , |
2355 | pStub, interceptee)); |
2356 | |
2357 | return pStub; |
2358 | } |
2359 | |
2360 | //------------------------------------------------------------------- |
2361 | // Release the stub that is owned by this stub |
2362 | //------------------------------------------------------------------- |
2363 | void InterceptStub::ReleaseInterceptedStub() |
2364 | { |
2365 | CONTRACTL |
2366 | { |
2367 | NOTHROW; |
2368 | GC_TRIGGERS; |
2369 | } |
2370 | CONTRACTL_END; |
2371 | |
2372 | Stub** intercepted = GetInterceptedStub(); |
2373 | // If we own the stub then decrement it. It can be null if the |
2374 | // linked stub is actually a jitted stub. |
2375 | if(*intercepted) |
2376 | (*intercepted)->DecRef(); |
2377 | } |
2378 | |
2379 | //------------------------------------------------------------------- |
2380 | // Constructor |
2381 | //------------------------------------------------------------------- |
2382 | ArgBasedStubCache::ArgBasedStubCache(UINT fixedSlots) |
2383 | : m_numFixedSlots(fixedSlots), |
2384 | m_crst(CrstArgBasedStubCache) |
2385 | { |
2386 | WRAPPER_NO_CONTRACT; |
2387 | |
2388 | m_aStub = new Stub * [m_numFixedSlots]; |
2389 | _ASSERTE(m_aStub != NULL); |
2390 | |
2391 | for (unsigned __int32 i = 0; i < m_numFixedSlots; i++) { |
2392 | m_aStub[i] = NULL; |
2393 | } |
2394 | m_pSlotEntries = NULL; |
2395 | } |
2396 | |
2397 | |
2398 | //------------------------------------------------------------------- |
2399 | // Destructor |
2400 | //------------------------------------------------------------------- |
2401 | ArgBasedStubCache::~ArgBasedStubCache() |
2402 | { |
2403 | CONTRACTL |
2404 | { |
2405 | NOTHROW; |
2406 | GC_NOTRIGGER; |
2407 | } |
2408 | CONTRACTL_END; |
2409 | |
2410 | for (unsigned __int32 i = 0; i < m_numFixedSlots; i++) { |
2411 | Stub *pStub = m_aStub[i]; |
2412 | if (pStub) { |
2413 | pStub->DecRef(); |
2414 | } |
2415 | } |
2416 | // a size of 0 is a signal to Nirvana to flush the entire cache |
2417 | // not sure if this is needed, but should have no CLR perf impact since size is 0. |
2418 | FlushInstructionCache(GetCurrentProcess(),0,0); |
2419 | |
2420 | SlotEntry **ppSlotEntry = &m_pSlotEntries; |
2421 | SlotEntry *pCur; |
2422 | while (NULL != (pCur = *ppSlotEntry)) { |
2423 | Stub *pStub = pCur->m_pStub; |
2424 | pStub->DecRef(); |
2425 | *ppSlotEntry = pCur->m_pNext; |
2426 | delete pCur; |
2427 | } |
2428 | delete [] m_aStub; |
2429 | } |
2430 | |
2431 | |
2432 | |
2433 | //------------------------------------------------------------------- |
2434 | // Queries/retrieves a previously cached stub. |
2435 | // |
2436 | // If there is no stub corresponding to the given index, |
2437 | // this function returns NULL. |
2438 | // |
2439 | // Otherwise, this function returns the stub after |
2440 | // incrementing its refcount. |
2441 | //------------------------------------------------------------------- |
2442 | Stub *ArgBasedStubCache::GetStub(UINT_PTR key) |
2443 | { |
2444 | CONTRACTL |
2445 | { |
2446 | NOTHROW; |
2447 | GC_TRIGGERS; |
2448 | MODE_ANY; |
2449 | } |
2450 | CONTRACTL_END; |
2451 | |
2452 | Stub *pStub; |
2453 | |
2454 | CrstHolder ch(&m_crst); |
2455 | |
2456 | if (key < m_numFixedSlots) { |
2457 | pStub = m_aStub[key]; |
2458 | } else { |
2459 | pStub = NULL; |
2460 | for (SlotEntry *pSlotEntry = m_pSlotEntries; |
2461 | pSlotEntry != NULL; |
2462 | pSlotEntry = pSlotEntry->m_pNext) { |
2463 | |
2464 | if (pSlotEntry->m_key == key) { |
2465 | pStub = pSlotEntry->m_pStub; |
2466 | break; |
2467 | } |
2468 | } |
2469 | } |
2470 | if (pStub) { |
2471 | pStub->IncRef(); |
2472 | } |
2473 | return pStub; |
2474 | } |
2475 | |
2476 | |
2477 | //------------------------------------------------------------------- |
2478 | // Tries to associate a stub with a given index. This association |
2479 | // may fail because some other thread may have beaten you to it |
2480 | // just before you make the call. |
2481 | // |
2482 | // If the association succeeds, "pStub" is installed, and it is |
2483 | // returned back to the caller. The stub's refcount is incremented |
2484 | // twice (one to reflect the cache's ownership, and one to reflect |
2485 | // the caller's ownership.) |
2486 | // |
2487 | // If the association fails because another stub is already installed, |
2488 | // then the incumbent stub is returned to the caller and its refcount |
2489 | // is incremented once (to reflect the caller's ownership.) |
2490 | // |
2491 | // If the association fails due to lack of memory, NULL is returned |
2492 | // and no one's refcount changes. |
2493 | // |
2494 | // This routine is intended to be called like this: |
2495 | // |
2496 | // Stub *pCandidate = MakeStub(); // after this, pCandidate's rc is 1 |
2497 | // Stub *pWinner = cache->SetStub(idx, pCandidate); |
2498 | // pCandidate->DecRef(); |
2499 | // pCandidate = 0xcccccccc; // must not use pCandidate again. |
2500 | // if (!pWinner) { |
2501 | // OutOfMemoryError; |
2502 | // } |
2503 | // // If the association succeeded, pWinner's refcount is 2 and so |
2504 | // // is pCandidate's (because it *is* pWinner);. |
2505 | // // If the association failed, pWinner's refcount is still 2 |
2506 | // // and pCandidate got destroyed by the last DecRef(). |
2507 | // // Either way, pWinner is now the official index holder. It |
2508 | // // has a refcount of 2 (one for the cache's ownership, and |
2509 | // // one belonging to this code.) |
2510 | //------------------------------------------------------------------- |
2511 | Stub* ArgBasedStubCache::AttemptToSetStub(UINT_PTR key, Stub *pStub) |
2512 | { |
2513 | CONTRACTL |
2514 | { |
2515 | THROWS; |
2516 | GC_TRIGGERS; |
2517 | MODE_ANY; |
2518 | } |
2519 | CONTRACTL_END; |
2520 | |
2521 | CrstHolder ch(&m_crst); |
2522 | |
2523 | if (key < m_numFixedSlots) { |
2524 | if (m_aStub[key]) { |
2525 | pStub = m_aStub[key]; |
2526 | } else { |
2527 | m_aStub[key] = pStub; |
2528 | pStub->IncRef(); // IncRef on cache's behalf |
2529 | } |
2530 | } else { |
2531 | SlotEntry *pSlotEntry; |
2532 | for (pSlotEntry = m_pSlotEntries; |
2533 | pSlotEntry != NULL; |
2534 | pSlotEntry = pSlotEntry->m_pNext) { |
2535 | |
2536 | if (pSlotEntry->m_key == key) { |
2537 | pStub = pSlotEntry->m_pStub; |
2538 | break; |
2539 | } |
2540 | } |
2541 | if (!pSlotEntry) { |
2542 | pSlotEntry = new SlotEntry; |
2543 | pSlotEntry->m_pStub = pStub; |
2544 | pStub->IncRef(); // IncRef on cache's behalf |
2545 | pSlotEntry->m_key = key; |
2546 | pSlotEntry->m_pNext = m_pSlotEntries; |
2547 | m_pSlotEntries = pSlotEntry; |
2548 | } |
2549 | } |
2550 | if (pStub) { |
2551 | pStub->IncRef(); // IncRef because we're returning it to caller |
2552 | } |
2553 | return pStub; |
2554 | } |
2555 | |
2556 | |
2557 | |
2558 | #ifdef _DEBUG |
2559 | // Diagnostic dump |
2560 | VOID ArgBasedStubCache::Dump() |
2561 | { |
2562 | CONTRACTL |
2563 | { |
2564 | NOTHROW; |
2565 | GC_NOTRIGGER; |
2566 | MODE_ANY; |
2567 | } |
2568 | CONTRACTL_END; |
2569 | |
2570 | printf("--------------------------------------------------------------\n" ); |
2571 | printf("ArgBasedStubCache dump (%lu fixed entries):\n" , m_numFixedSlots); |
2572 | for (UINT32 i = 0; i < m_numFixedSlots; i++) { |
2573 | |
2574 | printf(" Fixed slot %lu: " , (ULONG)i); |
2575 | Stub *pStub = m_aStub[i]; |
2576 | if (!pStub) { |
2577 | printf("empty\n" ); |
2578 | } else { |
2579 | printf("%lxh - refcount is %lu\n" , |
2580 | (size_t)(pStub->GetEntryPoint()), |
2581 | (ULONG)( *( ( ((ULONG*)(pStub->GetEntryPoint())) - 1)))); |
2582 | } |
2583 | } |
2584 | |
2585 | for (SlotEntry *pSlotEntry = m_pSlotEntries; |
2586 | pSlotEntry != NULL; |
2587 | pSlotEntry = pSlotEntry->m_pNext) { |
2588 | |
2589 | printf(" Dyna. slot %lu: " , (ULONG)(pSlotEntry->m_key)); |
2590 | Stub *pStub = pSlotEntry->m_pStub; |
2591 | printf("%lxh - refcount is %lu\n" , |
2592 | (size_t)(pStub->GetEntryPoint()), |
2593 | (ULONG)( *( ( ((ULONG*)(pStub->GetEntryPoint())) - 1)))); |
2594 | |
2595 | } |
2596 | |
2597 | |
2598 | printf("--------------------------------------------------------------\n" ); |
2599 | } |
2600 | #endif |
2601 | |
2602 | #endif // #ifndef DACCESS_COMPILE |
2603 | |
2604 | |