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.H |
6 | // |
7 | |
8 | // |
9 | // A StubLinker object provides a way to link several location-independent |
10 | // code sources into one executable stub, resolving references, |
11 | // and choosing the shortest possible instruction size. The StubLinker |
12 | // abstracts out the notion of a "reference" so it is completely CPU |
13 | // independent. This StubLinker is intended not only to create method |
14 | // stubs but to create the PCode-marshaling stubs for Native/Direct. |
15 | // |
16 | // A StubLinker's typical life-cycle is: |
17 | // |
18 | // 1. Create a new StubLinker (it accumulates state for the stub being |
19 | // generated.) |
20 | // 2. Emit code bytes and references (requiring fixups) into the StubLinker. |
21 | // 3. Call the Link() method to produce the final stub. |
22 | // 4. Destroy the StubLinker. |
23 | // |
24 | // StubLinkers are not multithread-aware: they're intended to be |
25 | // used entirely on a single thread. Also, StubLinker's report errors |
26 | // using COMPlusThrow. StubLinker's do have a destructor: to prevent |
27 | // C++ object unwinding from clashing with COMPlusThrow, |
28 | // you must use COMPLUSCATCH to ensure the StubLinker's cleanup in the |
29 | // event of an exception: the following code would do it: |
30 | // |
31 | // StubLinker stublink; |
32 | // Inner(); |
33 | // |
34 | // |
35 | // // Have to separate into inner function because VC++ forbids |
36 | // // mixing __try & local objects in the same function. |
37 | // void Inner() { |
38 | // COMPLUSTRY { |
39 | // ... do stuff ... |
40 | // pLinker->Link(); |
41 | // } COMPLUSCATCH { |
42 | // } |
43 | // } |
44 | // |
45 | |
46 | |
47 | // This file should only be included via the platform-specific cgencpu.h. |
48 | |
49 | #include "cgensys.h" |
50 | |
51 | #ifndef __stublink_h__ |
52 | #define __stublink_h__ |
53 | |
54 | #include "crst.h" |
55 | #include "util.hpp" |
56 | #include "eecontract.h" |
57 | |
58 | //------------------------------------------------------------------------- |
59 | // Forward refs |
60 | //------------------------------------------------------------------------- |
61 | class InstructionFormat; |
62 | class Stub; |
63 | class InterceptStub; |
64 | class CheckDuplicatedStructLayouts; |
65 | class CodeBasedStubCache; |
66 | struct CodeLabel; |
67 | |
68 | struct CodeRun; |
69 | struct LabelRef; |
70 | struct CodeElement; |
71 | struct IntermediateUnwindInfo; |
72 | |
73 | #if !defined(_TARGET_X86_) && !defined(FEATURE_PAL) |
74 | #define STUBLINKER_GENERATES_UNWIND_INFO |
75 | #endif // !_TARGET_X86_ && !FEATURE_PAL |
76 | |
77 | |
78 | #ifdef STUBLINKER_GENERATES_UNWIND_INFO |
79 | |
80 | typedef DPTR(struct StubUnwindInfoHeaderSuffix) PTR_StubUnwindInfoHeaderSuffix; |
81 | struct StubUnwindInfoHeaderSuffix |
82 | { |
83 | UCHAR nUnwindInfoSize; // Size of unwind info in bytes |
84 | }; |
85 | |
86 | // Variable-sized struct that preceeds a Stub when the stub requires unwind |
87 | // information. Followed by a StubUnwindInfoHeaderSuffix. |
88 | typedef DPTR(struct StubUnwindInfoHeader) PTR_StubUnwindInfoHeader; |
89 | struct StubUnwindInfoHeader |
90 | { |
91 | PTR_StubUnwindInfoHeader pNext; |
92 | T_RUNTIME_FUNCTION FunctionEntry; |
93 | UNWIND_INFO UnwindInfo; // variable length |
94 | |
95 | // Computes the size needed for this variable-sized struct. |
96 | static SIZE_T ComputeSize(UINT nUnwindInfoSize); |
97 | |
98 | void Init (); |
99 | |
100 | bool IsRegistered (); |
101 | }; |
102 | |
103 | // List of stub address ranges, in increasing address order. |
104 | struct StubUnwindInfoHeapSegment |
105 | { |
106 | PBYTE pbBaseAddress; |
107 | SIZE_T cbSegment; |
108 | StubUnwindInfoHeader *pUnwindHeaderList; |
109 | StubUnwindInfoHeapSegment *pNext; |
110 | |
111 | #ifdef _WIN64 |
112 | class UnwindInfoTable* pUnwindInfoTable; // Used to publish unwind info to ETW stack crawler |
113 | #endif |
114 | }; |
115 | |
116 | VOID UnregisterUnwindInfoInLoaderHeap (UnlockedLoaderHeap *pHeap); |
117 | |
118 | #endif // STUBLINKER_GENERATES_UNWIND_INFO |
119 | |
120 | |
121 | //------------------------------------------------------------------------- |
122 | // A non-multithreaded object that fixes up and emits one executable stub. |
123 | //------------------------------------------------------------------------- |
124 | class StubLinker |
125 | { |
126 | public: |
127 | //--------------------------------------------------------------- |
128 | // Construction |
129 | //--------------------------------------------------------------- |
130 | StubLinker(); |
131 | |
132 | |
133 | //--------------------------------------------------------------- |
134 | // Create a new undefined label. Label must be assigned to a code |
135 | // location using EmitLabel() prior to final linking. |
136 | // Throws exception on failure. |
137 | //--------------------------------------------------------------- |
138 | CodeLabel* NewCodeLabel(); |
139 | |
140 | //--------------------------------------------------------------- |
141 | // Create a new undefined label for which we want the absolute |
142 | // address, not offset. Label must be assigned to a code |
143 | // location using EmitLabel() prior to final linking. |
144 | // Throws exception on failure. |
145 | //--------------------------------------------------------------- |
146 | CodeLabel* NewAbsoluteCodeLabel(); |
147 | |
148 | //--------------------------------------------------------------- |
149 | // Combines NewCodeLabel() and EmitLabel() for convenience. |
150 | // Throws exception on failure. |
151 | //--------------------------------------------------------------- |
152 | CodeLabel* EmitNewCodeLabel(); |
153 | |
154 | |
155 | //--------------------------------------------------------------- |
156 | // Returns final location of label as an offset from the start |
157 | // of the stub. Can only be called after linkage. |
158 | //--------------------------------------------------------------- |
159 | UINT32 GetLabelOffset(CodeLabel *pLabel); |
160 | |
161 | //--------------------------------------------------------------- |
162 | // Append code bytes. |
163 | //--------------------------------------------------------------- |
164 | VOID EmitBytes(const BYTE *pBytes, UINT numBytes); |
165 | VOID Emit8 (unsigned __int8 u8); |
166 | VOID Emit16(unsigned __int16 u16); |
167 | VOID Emit32(unsigned __int32 u32); |
168 | VOID Emit64(unsigned __int64 u64); |
169 | VOID EmitPtr(const VOID *pval); |
170 | |
171 | //--------------------------------------------------------------- |
172 | // Emit a UTF8 string |
173 | //--------------------------------------------------------------- |
174 | VOID EmitUtf8(LPCUTF8 pUTF8) |
175 | { |
176 | WRAPPER_NO_CONTRACT; |
177 | |
178 | LPCUTF8 p = pUTF8; |
179 | while (*(p++)) { |
180 | //nothing |
181 | } |
182 | EmitBytes((const BYTE *)pUTF8, (unsigned int)(p-pUTF8-1)); |
183 | } |
184 | |
185 | //--------------------------------------------------------------- |
186 | // Append an instruction containing a reference to a label. |
187 | // |
188 | // target - the label being referenced. |
189 | // instructionFormat - a platform-specific InstructionFormat object |
190 | // that gives properties about the reference. |
191 | // variationCode - uninterpreted data passed to the pInstructionFormat methods. |
192 | //--------------------------------------------------------------- |
193 | VOID EmitLabelRef(CodeLabel* target, const InstructionFormat & instructionFormat, UINT variationCode); |
194 | |
195 | |
196 | //--------------------------------------------------------------- |
197 | // Sets the label to point to the current "instruction pointer" |
198 | // It is invalid to call EmitLabel() twice on |
199 | // the same label. |
200 | //--------------------------------------------------------------- |
201 | VOID EmitLabel(CodeLabel* pCodeLabel); |
202 | |
203 | //--------------------------------------------------------------- |
204 | // Emits the patch label for the stub. |
205 | // Throws exception on failure. |
206 | //--------------------------------------------------------------- |
207 | void EmitPatchLabel(); |
208 | |
209 | //--------------------------------------------------------------- |
210 | // Create a new label to an external address. |
211 | // Throws exception on failure. |
212 | //--------------------------------------------------------------- |
213 | CodeLabel* NewExternalCodeLabel(LPVOID pExternalAddress); |
214 | CodeLabel* NewExternalCodeLabel(PCODE pExternalAddress) |
215 | { |
216 | return NewExternalCodeLabel((LPVOID)pExternalAddress); |
217 | } |
218 | |
219 | //--------------------------------------------------------------- |
220 | // Push and Pop can be used to keep track of stack growth. |
221 | // These should be adjusted by opcodes written to the stream. |
222 | // |
223 | // Note that popping & pushing stack size as opcodes are emitted |
224 | // is naive & may not be accurate in many cases, |
225 | // so complex stubs may have to manually adjust the stack size. |
226 | // However it should work for the vast majority of cases we care |
227 | // about. |
228 | //--------------------------------------------------------------- |
229 | void Push(UINT size); |
230 | void Pop(UINT size); |
231 | |
232 | INT GetStackSize() { LIMITED_METHOD_CONTRACT; return m_stackSize; } |
233 | void SetStackSize(SHORT size) { LIMITED_METHOD_CONTRACT; m_stackSize = size; } |
234 | |
235 | void SetDataOnly(BOOL fDataOnly = TRUE) { LIMITED_METHOD_CONTRACT; m_fDataOnly = fDataOnly; } |
236 | |
237 | #ifdef _TARGET_ARM_ |
238 | void DescribeProlog(UINT cCalleeSavedRegs, UINT cbStackFrame, BOOL fPushArgRegs); |
239 | #elif defined(_TARGET_ARM64_) |
240 | void DescribeProlog(UINT cIntRegArgs, UINT cVecRegArgs, UINT cCalleeSavedRegs, UINT cbStackFrame); |
241 | UINT GetSavedRegArgsOffset(); |
242 | UINT GetStackFrameSize(); |
243 | #endif |
244 | |
245 | //=========================================================================== |
246 | // Unwind information |
247 | |
248 | // Records location of preserved or parameter register |
249 | VOID UnwindSavedReg (UCHAR reg, ULONG SPRelativeOffset); |
250 | VOID UnwindPushedReg (UCHAR reg); |
251 | |
252 | // Records "sub rsp, xxx" |
253 | VOID UnwindAllocStack (SHORT FrameSizeIncrement); |
254 | |
255 | // Records frame pointer register |
256 | VOID UnwindSetFramePointer (UCHAR reg); |
257 | |
258 | // In DEBUG, emits a call to m_pUnwindInfoCheckLabel (via |
259 | // EmitUnwindInfoCheckWorker). Code at that label will call to a |
260 | // helper that will attempt to RtlVirtualUnwind through the stub. The |
261 | // helper will preserve ALL registers. |
262 | VOID EmitUnwindInfoCheck(); |
263 | |
264 | #if defined(_DEBUG) && defined(STUBLINKER_GENERATES_UNWIND_INFO) && !defined(CROSSGEN_COMPILE) |
265 | protected: |
266 | |
267 | // Injects a call to the given label. |
268 | virtual VOID EmitUnwindInfoCheckWorker (CodeLabel *pCheckLabel) { _ASSERTE(!"override me" ); } |
269 | |
270 | // Emits a call to a helper that will attempt to RtlVirtualUnwind |
271 | // through the stub. The helper will preserve ALL registers. |
272 | virtual VOID EmitUnwindInfoCheckSubfunction() { _ASSERTE(!"override me" ); } |
273 | #endif |
274 | |
275 | public: |
276 | |
277 | //--------------------------------------------------------------- |
278 | // Generate the actual stub. The returned stub has a refcount of 1. |
279 | // No other methods (other than the destructor) should be called |
280 | // after calling Link(). |
281 | // |
282 | // Throws exception on failure. |
283 | //--------------------------------------------------------------- |
284 | Stub *Link(LoaderHeap *heap, DWORD flags = 0); |
285 | |
286 | //--------------------------------------------------------------- |
287 | // Generate the actual stub. The returned stub has a refcount of 1. |
288 | // No other methods (other than the destructor) should be called |
289 | // after calling Link(). The linked stub must have its increment |
290 | // increased by one prior to calling this method. This method |
291 | // does not increment the reference count of the interceptee. |
292 | // |
293 | // Throws exception on failure. |
294 | //--------------------------------------------------------------- |
295 | Stub *LinkInterceptor(Stub* interceptee, void *pRealAddr) |
296 | { WRAPPER_NO_CONTRACT; return LinkInterceptor(NULL,interceptee, pRealAddr); } |
297 | Stub *LinkInterceptor(LoaderHeap *heap, Stub* interceptee, void *pRealAddr); |
298 | |
299 | private: |
300 | CodeElement *m_pCodeElements; // stored in *reverse* order |
301 | CodeLabel *m_pFirstCodeLabel; // linked list of CodeLabels |
302 | LabelRef *m_pFirstLabelRef; // linked list of references |
303 | CodeLabel *m_pPatchLabel; // label of stub patch offset |
304 | // currently just for multicast |
305 | // frames. |
306 | SHORT m_stackSize; // count of pushes/pops |
307 | CQuickHeap m_quickHeap; // throwaway heap for |
308 | // labels, and |
309 | // internals. |
310 | BOOL m_fDataOnly; // the stub contains only data - does not need FlushInstructionCache |
311 | |
312 | #ifdef _TARGET_ARM_ |
313 | protected: |
314 | BOOL m_fProlog; // True if DescribeProlog has been called |
315 | UINT m_cCalleeSavedRegs; // Count of callee saved registers (0 == none, 1 == r4, 2 == |
316 | // r4-r5 etc. up to 8 == r4-r11) |
317 | UINT m_cbStackFrame; // Count of bytes in the stack frame (excl of saved regs) |
318 | BOOL m_fPushArgRegs; // If true, r0-r3 are saved before callee saved regs |
319 | #endif // _TARGET_ARM_ |
320 | |
321 | #ifdef _TARGET_ARM64_ |
322 | protected: |
323 | BOOL m_fProlog; // True if DescribeProlog has been called |
324 | UINT m_cIntRegArgs; // Count of int register arguments (x0 - x7) |
325 | UINT m_cVecRegArgs; // Count of FP register arguments (v0 - v7) |
326 | UINT m_cCalleeSavedRegs; // Count of callee saved registers (x19 - x28) |
327 | UINT m_cbStackSpace; // Additional stack space for return buffer and stack alignment |
328 | #endif // _TARGET_ARM64_ |
329 | |
330 | #ifdef STUBLINKER_GENERATES_UNWIND_INFO |
331 | |
332 | #ifdef _DEBUG |
333 | CodeLabel *m_pUnwindInfoCheckLabel; // subfunction to call to unwind info check helper. |
334 | // On AMD64, the prologue is restricted to 256 |
335 | // bytes, so this reduces the size of the injected |
336 | // code from 14 to 5 bytes. |
337 | #endif |
338 | |
339 | #ifdef _TARGET_AMD64_ |
340 | IntermediateUnwindInfo *m_pUnwindInfoList; |
341 | UINT m_nUnwindSlots; // number of slots to allocate at end, == UNWIND_INFO::CountOfCodes |
342 | BOOL m_fHaveFramePointer; // indicates stack operations no longer need to be recorded |
343 | |
344 | // |
345 | // Returns total UnwindInfoSize, including RUNTIME_FUNCTION entry |
346 | // |
347 | UINT UnwindInfoSize(UINT codeSize) |
348 | { |
349 | if (m_nUnwindSlots == 0) return 0; |
350 | |
351 | return sizeof(T_RUNTIME_FUNCTION) + offsetof(UNWIND_INFO, UnwindCode) + m_nUnwindSlots * sizeof(UNWIND_CODE); |
352 | } |
353 | #endif // _TARGET_AMD64_ |
354 | |
355 | #ifdef _TARGET_ARM_ |
356 | #define MAX_UNWIND_CODE_WORDS 5 /* maximum number of 32-bit words to store unwind codes */ |
357 | // Cache information about the stack frame set up in the prolog and use it in the generation of the |
358 | // epilog. |
359 | private: |
360 | // Reserve fixed size block that's big enough to fit any unwind info we can have |
361 | static const int c_nUnwindInfoSize = sizeof(T_RUNTIME_FUNCTION) + sizeof(DWORD) + MAX_UNWIND_CODE_WORDS *4; |
362 | |
363 | // |
364 | // Returns total UnwindInfoSize, including RUNTIME_FUNCTION entry |
365 | // |
366 | UINT UnwindInfoSize(UINT codeSize) |
367 | { |
368 | if (!m_fProlog) return 0; |
369 | |
370 | return c_nUnwindInfoSize; |
371 | } |
372 | #endif // _TARGET_ARM_ |
373 | |
374 | #ifdef _TARGET_ARM64_ |
375 | #define MAX_UNWIND_CODE_WORDS 5 /* maximum number of 32-bit words to store unwind codes */ |
376 | |
377 | private: |
378 | // Reserve fixed size block that's big enough to fit any unwind info we can have |
379 | static const int c_nUnwindInfoSize = sizeof(T_RUNTIME_FUNCTION) + sizeof(DWORD) + MAX_UNWIND_CODE_WORDS *4; |
380 | UINT UnwindInfoSize(UINT codeSize) |
381 | { |
382 | if (!m_fProlog) return 0; |
383 | |
384 | return c_nUnwindInfoSize; |
385 | } |
386 | |
387 | #endif // _TARGET_ARM64_ |
388 | |
389 | #endif // STUBLINKER_GENERATES_UNWIND_INFO |
390 | |
391 | CodeRun *AppendNewEmptyCodeRun(); |
392 | |
393 | |
394 | // Returns pointer to last CodeElement or NULL. |
395 | CodeElement *GetLastCodeElement() |
396 | { |
397 | LIMITED_METHOD_CONTRACT; |
398 | return m_pCodeElements; |
399 | } |
400 | |
401 | // Appends a new CodeElement. |
402 | VOID AppendCodeElement(CodeElement *pCodeElement); |
403 | |
404 | |
405 | // Calculates the size of the stub code that is allocate |
406 | // immediately after the stub object. Returns the |
407 | // total size. GlobalSize contains the size without |
408 | // that data part. |
409 | virtual int CalculateSize(int* globalsize); |
410 | |
411 | // Writes out the code element into memory following the |
412 | // stub object. |
413 | bool EmitStub(Stub* pStub, int globalsize, LoaderHeap* pHeap); |
414 | |
415 | CodeRun *GetLastCodeRunIfAny(); |
416 | |
417 | bool EmitUnwindInfo(Stub* pStub, int globalsize, LoaderHeap* pHeap); |
418 | |
419 | #if defined(_TARGET_AMD64_) && defined(STUBLINKER_GENERATES_UNWIND_INFO) |
420 | UNWIND_CODE *AllocUnwindInfo (UCHAR Op, UCHAR nExtraSlots = 0); |
421 | #endif // defined(_TARGET_AMD64_) && defined(STUBLINKER_GENERATES_UNWIND_INFO) |
422 | }; |
423 | |
424 | //************************************************************************ |
425 | // CodeLabel |
426 | //************************************************************************ |
427 | struct CodeLabel |
428 | { |
429 | // Link pointer for StubLink's list of labels |
430 | CodeLabel *m_next; |
431 | |
432 | // if FALSE, label refers to some code within the same stub |
433 | // if TRUE, label refers to some externally supplied address. |
434 | BOOL m_fExternal; |
435 | |
436 | // if TRUE, means we want the actual address of the label and |
437 | // not an offset to it |
438 | BOOL m_fAbsolute; |
439 | |
440 | union { |
441 | |
442 | // Internal |
443 | struct { |
444 | // Indicates the position of the label, expressed |
445 | // as an offset into a CodeRun. |
446 | CodeRun *m_pCodeRun; |
447 | UINT m_localOffset; |
448 | |
449 | } i; |
450 | |
451 | |
452 | // External |
453 | struct { |
454 | LPVOID m_pExternalAddress; |
455 | } e; |
456 | }; |
457 | }; |
458 | |
459 | enum NewStubFlags |
460 | { |
461 | NEWSTUB_FL_INTERCEPT = 0x00000001, |
462 | NEWSTUB_FL_MULTICAST = 0x00000002, |
463 | NEWSTUB_FL_EXTERNAL = 0x00000004, |
464 | NEWSTUB_FL_LOADERHEAP = 0x00000008 |
465 | }; |
466 | |
467 | |
468 | //------------------------------------------------------------------------- |
469 | // An executable stub. These can only be created by the StubLinker(). |
470 | // Each stub has a reference count (which is maintained in a thread-safe |
471 | // manner.) When the ref-count goes to zero, the stub automatically |
472 | // cleans itself up. |
473 | //------------------------------------------------------------------------- |
474 | typedef DPTR(class Stub) PTR_Stub; |
475 | typedef DPTR(PTR_Stub) PTR_PTR_Stub; |
476 | class Stub |
477 | { |
478 | friend class CheckDuplicatedStructLayouts; |
479 | friend class CheckAsmOffsets; |
480 | |
481 | protected: |
482 | enum |
483 | { |
484 | MULTICAST_DELEGATE_BIT = 0x80000000, |
485 | EXTERNAL_ENTRY_BIT = 0x40000000, |
486 | LOADER_HEAP_BIT = 0x20000000, |
487 | INTERCEPT_BIT = 0x10000000, |
488 | UNWIND_INFO_BIT = 0x08000000, |
489 | |
490 | PATCH_OFFSET_MASK = UNWIND_INFO_BIT - 1, |
491 | MAX_PATCH_OFFSET = PATCH_OFFSET_MASK + 1, |
492 | }; |
493 | |
494 | static_assert_no_msg(PATCH_OFFSET_MASK < UNWIND_INFO_BIT); |
495 | |
496 | public: |
497 | //------------------------------------------------------------------- |
498 | // Inc the refcount. |
499 | //------------------------------------------------------------------- |
500 | VOID IncRef(); |
501 | |
502 | |
503 | //------------------------------------------------------------------- |
504 | // Dec the refcount. |
505 | // Returns true if the count went to zero and the stub was deleted |
506 | //------------------------------------------------------------------- |
507 | BOOL DecRef(); |
508 | |
509 | |
510 | |
511 | //------------------------------------------------------------------- |
512 | // Used for throwing out unused stubs from stub caches. This |
513 | // method cannot be 100% accurate due to race conditions. This |
514 | // is ok because stub cache management is robust in the face |
515 | // of missed or premature cleanups. |
516 | //------------------------------------------------------------------- |
517 | BOOL HeuristicLooksOrphaned() |
518 | { |
519 | LIMITED_METHOD_CONTRACT; |
520 | _ASSERTE(m_signature == kUsedStub); |
521 | return (m_refcount == 1); |
522 | } |
523 | |
524 | //------------------------------------------------------------------- |
525 | // Used by the debugger to help step through stubs |
526 | //------------------------------------------------------------------- |
527 | BOOL IsIntercept() |
528 | { |
529 | LIMITED_METHOD_CONTRACT; |
530 | return (m_patchOffset & INTERCEPT_BIT) != 0; |
531 | } |
532 | |
533 | BOOL IsMulticastDelegate() |
534 | { |
535 | LIMITED_METHOD_CONTRACT; |
536 | return (m_patchOffset & MULTICAST_DELEGATE_BIT) != 0; |
537 | } |
538 | |
539 | //------------------------------------------------------------------- |
540 | // For stubs which execute user code, a patch offset needs to be set |
541 | // to tell the debugger how far into the stub code the debugger has |
542 | // to step until the frame is set up. |
543 | //------------------------------------------------------------------- |
544 | USHORT GetPatchOffset() |
545 | { |
546 | LIMITED_METHOD_CONTRACT; |
547 | |
548 | return (USHORT)(m_patchOffset & PATCH_OFFSET_MASK); |
549 | } |
550 | |
551 | void SetPatchOffset(USHORT offset) |
552 | { |
553 | LIMITED_METHOD_CONTRACT; |
554 | _ASSERTE(GetPatchOffset() == 0); |
555 | m_patchOffset |= offset; |
556 | _ASSERTE(GetPatchOffset() == offset); |
557 | } |
558 | |
559 | TADDR GetPatchAddress() |
560 | { |
561 | WRAPPER_NO_CONTRACT; |
562 | |
563 | return dac_cast<TADDR>(GetEntryPointInternal()) + GetPatchOffset(); |
564 | } |
565 | |
566 | //------------------------------------------------------------------- |
567 | // Unwind information. |
568 | //------------------------------------------------------------------- |
569 | |
570 | #ifdef STUBLINKER_GENERATES_UNWIND_INFO |
571 | |
572 | BOOL HasUnwindInfo() |
573 | { |
574 | LIMITED_METHOD_CONTRACT; |
575 | return (m_patchOffset & UNWIND_INFO_BIT) != 0; |
576 | } |
577 | |
578 | StubUnwindInfoHeaderSuffix *GetUnwindInfoHeaderSuffix() |
579 | { |
580 | CONTRACTL |
581 | { |
582 | NOTHROW; |
583 | GC_NOTRIGGER; |
584 | FORBID_FAULT; |
585 | SO_TOLERANT; |
586 | } |
587 | CONTRACTL_END |
588 | |
589 | _ASSERTE(HasUnwindInfo()); |
590 | |
591 | TADDR info = dac_cast<TADDR>(this); |
592 | |
593 | if (IsIntercept()) |
594 | { |
595 | info -= 2 * sizeof(TADDR); |
596 | } |
597 | |
598 | return PTR_StubUnwindInfoHeaderSuffix |
599 | (info - sizeof(StubUnwindInfoHeaderSuffix)); |
600 | } |
601 | |
602 | StubUnwindInfoHeader *GetUnwindInfoHeader() |
603 | { |
604 | CONTRACTL |
605 | { |
606 | NOTHROW; |
607 | GC_NOTRIGGER; |
608 | FORBID_FAULT; |
609 | } |
610 | CONTRACTL_END |
611 | |
612 | StubUnwindInfoHeaderSuffix *pSuffix = GetUnwindInfoHeaderSuffix(); |
613 | |
614 | TADDR suffixEnd = dac_cast<TADDR>(pSuffix) + sizeof(*pSuffix); |
615 | |
616 | return PTR_StubUnwindInfoHeader(suffixEnd - |
617 | StubUnwindInfoHeader::ComputeSize(pSuffix->nUnwindInfoSize)); |
618 | } |
619 | |
620 | #endif // STUBLINKER_GENERATES_UNWIND_INFO |
621 | |
622 | //------------------------------------------------------------------- |
623 | // Returns pointer to the start of the allocation containing this Stub. |
624 | //------------------------------------------------------------------- |
625 | TADDR GetAllocationBase(); |
626 | |
627 | //------------------------------------------------------------------- |
628 | // Return executable entrypoint after checking the ref count. |
629 | //------------------------------------------------------------------- |
630 | PCODE GetEntryPoint() |
631 | { |
632 | WRAPPER_NO_CONTRACT; |
633 | SUPPORTS_DAC; |
634 | |
635 | _ASSERTE(m_signature == kUsedStub); |
636 | _ASSERTE(m_refcount > 0); |
637 | |
638 | TADDR pEntryPoint = dac_cast<TADDR>(GetEntryPointInternal()); |
639 | |
640 | #ifdef _TARGET_ARM_ |
641 | |
642 | #ifndef THUMB_CODE |
643 | #define THUMB_CODE 1 |
644 | #endif |
645 | |
646 | pEntryPoint |= THUMB_CODE; |
647 | #endif |
648 | |
649 | return pEntryPoint; |
650 | } |
651 | |
652 | UINT GetNumCodeBytes() |
653 | { |
654 | WRAPPER_NO_CONTRACT; |
655 | SUPPORTS_DAC; |
656 | |
657 | return m_numCodeBytes; |
658 | } |
659 | |
660 | //------------------------------------------------------------------- |
661 | // Return start of the stub blob |
662 | //------------------------------------------------------------------- |
663 | PTR_CBYTE GetBlob() |
664 | { |
665 | WRAPPER_NO_CONTRACT; |
666 | SUPPORTS_DAC; |
667 | |
668 | _ASSERTE(m_signature == kUsedStub); |
669 | _ASSERTE(m_refcount > 0); |
670 | |
671 | return GetEntryPointInternal(); |
672 | } |
673 | |
674 | //------------------------------------------------------------------- |
675 | // Return the Stub as in GetEntryPoint and size of the stub+code in bytes |
676 | // WARNING: Depending on the stub kind this may be just Stub size as |
677 | // not all stubs have the info about the code size. |
678 | // It's the caller responsibility to determine that |
679 | //------------------------------------------------------------------- |
680 | static Stub* RecoverStubAndSize(PCODE pEntryPoint, DWORD *pSize) |
681 | { |
682 | CONTRACT(Stub*) |
683 | { |
684 | NOTHROW; |
685 | GC_NOTRIGGER; |
686 | MODE_ANY; |
687 | |
688 | PRECONDITION(pEntryPoint && pSize); |
689 | } |
690 | CONTRACT_END; |
691 | |
692 | Stub *pStub = Stub::RecoverStub(pEntryPoint); |
693 | *pSize = sizeof(Stub) + pStub->m_numCodeBytes; |
694 | RETURN pStub; |
695 | } |
696 | |
697 | HRESULT CloneStub(BYTE *pBuffer, DWORD dwBufferSize) |
698 | { |
699 | LIMITED_METHOD_CONTRACT; |
700 | if ((pBuffer == NULL) || |
701 | (dwBufferSize < (sizeof(*this) + m_numCodeBytes))) |
702 | { |
703 | return E_INVALIDARG; |
704 | } |
705 | |
706 | memcpyNoGCRefs(pBuffer, this, sizeof(*this) + m_numCodeBytes); |
707 | reinterpret_cast<Stub *>(pBuffer)->m_refcount = 1; |
708 | |
709 | return S_OK; |
710 | } |
711 | |
712 | //------------------------------------------------------------------- |
713 | // Reverse GetEntryPoint. |
714 | //------------------------------------------------------------------- |
715 | static Stub* RecoverStub(PCODE pEntryPoint) |
716 | { |
717 | STATIC_CONTRACT_NOTHROW; |
718 | STATIC_CONTRACT_GC_NOTRIGGER; |
719 | |
720 | TADDR pStubData = PCODEToPINSTR(pEntryPoint); |
721 | |
722 | Stub *pStub = PTR_Stub(pStubData - sizeof(*pStub)); |
723 | |
724 | #if !defined(DACCESS_COMPILE) |
725 | _ASSERTE(pStub->m_signature == kUsedStub); |
726 | _ASSERTE(pStub->GetEntryPoint() == pEntryPoint); |
727 | #elif defined(_DEBUG) |
728 | // If this isn't really a stub we don't want |
729 | // to continue with it. |
730 | // TODO: This should be removed once IsStub |
731 | // can adverstise whether it's safe to call |
732 | // further StubManager methods. |
733 | if (pStub->m_signature != kUsedStub || |
734 | pStub->GetEntryPoint() != pEntryPoint) |
735 | { |
736 | DacError(E_INVALIDARG); |
737 | } |
738 | #endif |
739 | return pStub; |
740 | } |
741 | |
742 | //------------------------------------------------------------------- |
743 | // Returns TRUE if entry point is not inside the Stub allocation. |
744 | //------------------------------------------------------------------- |
745 | BOOL HasExternalEntryPoint() const |
746 | { |
747 | LIMITED_METHOD_CONTRACT; |
748 | |
749 | return (m_patchOffset & EXTERNAL_ENTRY_BIT) != 0; |
750 | } |
751 | |
752 | //------------------------------------------------------------------- |
753 | // This is the guy that creates stubs. |
754 | //------------------------------------------------------------------- |
755 | static Stub* NewStub(LoaderHeap *pLoaderHeap, UINT numCodeBytes, |
756 | DWORD flags = 0 |
757 | #ifdef STUBLINKER_GENERATES_UNWIND_INFO |
758 | , UINT nUnwindInfoSize = 0 |
759 | #endif |
760 | ); |
761 | |
762 | static Stub* NewStub(PTR_VOID pCode, DWORD flags = 0); |
763 | static Stub* NewStub(PCODE pCode, DWORD flags = 0) |
764 | { |
765 | return NewStub((PTR_VOID)pCode, flags); |
766 | } |
767 | |
768 | //------------------------------------------------------------------- |
769 | // One-time init |
770 | //------------------------------------------------------------------- |
771 | static void Init(); |
772 | |
773 | protected: |
774 | // fMC: Set to true if the stub is a multicast delegate, false otherwise |
775 | void SetupStub(int numCodeBytes, DWORD flags |
776 | #ifdef STUBLINKER_GENERATES_UNWIND_INFO |
777 | , UINT nUnwindInfoSlots |
778 | #endif |
779 | ); |
780 | void DeleteStub(); |
781 | |
782 | //------------------------------------------------------------------- |
783 | // Return executable entrypoint without checking the ref count. |
784 | //------------------------------------------------------------------- |
785 | inline PTR_CBYTE GetEntryPointInternal() |
786 | { |
787 | LIMITED_METHOD_CONTRACT; |
788 | SUPPORTS_DAC; |
789 | |
790 | _ASSERTE(m_signature == kUsedStub); |
791 | |
792 | |
793 | if (HasExternalEntryPoint()) |
794 | { |
795 | return dac_cast<PTR_BYTE>(*dac_cast<PTR_PCODE>(dac_cast<TADDR>(this) + sizeof(*this))); |
796 | } |
797 | else |
798 | { |
799 | // StubLink always puts the entrypoint first. |
800 | return dac_cast<PTR_CBYTE>(this) + sizeof(*this); |
801 | } |
802 | } |
803 | |
804 | ULONG m_refcount; |
805 | ULONG m_patchOffset; |
806 | |
807 | UINT m_numCodeBytes; |
808 | |
809 | #ifdef _DEBUG |
810 | enum { |
811 | kUsedStub = 0x42555453, // 'STUB' |
812 | kFreedStub = 0x46555453, // 'STUF' |
813 | }; |
814 | |
815 | UINT32 m_signature; |
816 | #else |
817 | #ifdef _WIN64 |
818 | //README ALIGNEMENT: in retail mode UINT m_numCodeBytes does not align to 16byte for the code |
819 | // after the Stub struct. This is to pad properly |
820 | UINT m_pad_code_bytes; |
821 | #endif // _WIN64 |
822 | #endif // _DEBUG |
823 | |
824 | #ifdef _DEBUG |
825 | Stub() // Stubs are created by NewStub(), not "new". Hide the |
826 | { LIMITED_METHOD_CONTRACT; } // constructor to enforce this. |
827 | #endif |
828 | |
829 | }; |
830 | |
831 | |
832 | /* |
833 | * The InterceptStub hides a reference to the real stub at a negative offset. |
834 | * When this stub is deleted it decrements the real stub cleaning it up as |
835 | * well. The InterceptStub is created by the Stublinker. |
836 | * |
837 | * <TODO>@TODO: Intercepted stubs need have a routine that will find the |
838 | * last real stub in the chain.</TODO> |
839 | * The stubs are linked - GetInterceptedStub will return either |
840 | * a pointer to the next intercept stub (if there is one), or NULL, |
841 | * indicating end-of-chain. GetRealAddr will return the address of |
842 | * the "real" code, which may, in fact, be another thunk (for example), |
843 | * and thus should be traced as well. |
844 | */ |
845 | |
846 | typedef DPTR(class InterceptStub) PTR_InterceptStub; |
847 | class InterceptStub : public Stub |
848 | { |
849 | friend class Stub; |
850 | public: |
851 | //------------------------------------------------------------------- |
852 | // This is the guy that creates stubs. |
853 | //------------------------------------------------------------------- |
854 | static Stub* NewInterceptedStub(LoaderHeap *pHeap, |
855 | UINT numCodeBytes, |
856 | Stub* interceptee, |
857 | void* pRealAddr |
858 | #ifdef STUBLINKER_GENERATES_UNWIND_INFO |
859 | , UINT nUnwindInfoSize = 0 |
860 | #endif |
861 | ); |
862 | |
863 | //--------------------------------------------------------------- |
864 | // Expose key offsets and values for stub generation. |
865 | //--------------------------------------------------------------- |
866 | int GetNegativeOffset() |
867 | { |
868 | LIMITED_METHOD_CONTRACT; |
869 | return sizeof(TADDR) + GetNegativeOffsetRealAddr(); |
870 | } |
871 | |
872 | PTR_PTR_Stub GetInterceptedStub() |
873 | { |
874 | LIMITED_METHOD_CONTRACT; |
875 | return dac_cast<PTR_PTR_Stub>( |
876 | dac_cast<TADDR>(this) - GetNegativeOffset()); |
877 | } |
878 | |
879 | int GetNegativeOffsetRealAddr() |
880 | { |
881 | LIMITED_METHOD_CONTRACT; |
882 | return sizeof(TADDR); |
883 | } |
884 | |
885 | PTR_TADDR GetRealAddr() |
886 | { |
887 | LIMITED_METHOD_CONTRACT; |
888 | return dac_cast<PTR_TADDR>( |
889 | dac_cast<TADDR>(this) - GetNegativeOffsetRealAddr()); |
890 | } |
891 | |
892 | static Stub* NewInterceptedStub(void* pCode, |
893 | Stub* interceptee, |
894 | void* pRealAddr); |
895 | |
896 | protected: |
897 | void ReleaseInterceptedStub(); |
898 | |
899 | #ifdef _DEBUG |
900 | InterceptStub() // Intercept stubs are only created by NewInterceptedStub. |
901 | { LIMITED_METHOD_CONTRACT; } |
902 | #endif |
903 | }; |
904 | |
905 | //------------------------------------------------------------------------- |
906 | // Each platform encodes the "branch" instruction in a different |
907 | // way. We use objects derived from InstructionFormat to abstract this |
908 | // information away. InstructionFormats don't contain any variable data |
909 | // so they should be allocated statically. |
910 | // |
911 | // Note that StubLinker does not create or define any InstructionFormats. |
912 | // The client does. |
913 | // |
914 | // The following example shows how to define a InstructionFormat for the |
915 | // X86 jump near instruction which takes on two forms: |
916 | // |
917 | // EB xx jmp rel8 ;; SHORT JMP (signed 8-bit offset) |
918 | // E9 xxxxxxxx jmp rel32 ;; NEAR JMP (signed 32-bit offset) |
919 | // |
920 | // InstructionFormat's provide StubLinker the following information: |
921 | // |
922 | // RRT.m_allowedSizes |
923 | // |
924 | // What are the possible sizes that the reference can |
925 | // take? The X86 jump can take either an 8-bit or 32-bit offset |
926 | // so this value is set to (k8|k32). StubLinker will try to |
927 | // use the smallest size possible. |
928 | // |
929 | // |
930 | // RRT.m_fTreatSizesAsSigned |
931 | // Sign-extend or zero-extend smallsizes offsets to the platform |
932 | // code pointer size? For x86, this field is set to TRUE (rel8 |
933 | // is considered signed.) |
934 | // |
935 | // |
936 | // UINT RRT.GetSizeOfInstruction(refsize, variationCode) |
937 | // Returns the total size of the instruction in bytes for a given |
938 | // refsize. For this example: |
939 | // |
940 | // if (refsize==k8) return 2; |
941 | // if (refsize==k32) return 5; |
942 | // |
943 | // |
944 | // UINT RRT.GetSizeOfData(refsize, variationCode) |
945 | // Returns the total size of the seperate data area (if any) that the |
946 | // instruction needs in bytes for a given refsize. For this example |
947 | // on the SH3 |
948 | // if (refsize==k32) return 4; else return 0; |
949 | // |
950 | // The default implem of this returns 0, so CPUs that don't have need |
951 | // for a seperate constant area don't have to worry about it. |
952 | // |
953 | // |
954 | // BOOL CanReach(refsize, variationcode, fExternal, offset) |
955 | // Returns whether the instruction with the given variationcode & |
956 | // refsize can reach the given offset. In the case of External |
957 | // calls, fExternal is set and offset is the target address. In this case an |
958 | // implementation should return TRUE only if refsize is big enough to fit a |
959 | // full machine-sized pointer to anywhere in the address space. |
960 | // |
961 | // |
962 | // VOID RRT.EmitInstruction(UINT refsize, |
963 | // __int64 fixedUpReference, |
964 | // BYTE *pOutBuffer, |
965 | // UINT variationCode, |
966 | // BYTE *pDataBuffer) |
967 | // |
968 | // Given a chosen size (refsize) and the final offset value |
969 | // computed by StubLink (fixedUpReference), write out the |
970 | // instruction into the provided buffer (guaranteed to be |
971 | // big enough provided you told the truth with GetSizeOfInstruction()). |
972 | // If needed (e.g. on SH3) a data buffer is also passed in for |
973 | // storage of constants. |
974 | // |
975 | // For x86 jmp near: |
976 | // |
977 | // if (refsize==k8) { |
978 | // pOutBuffer[0] = 0xeb; |
979 | // pOutBuffer[1] = (__int8)fixedUpReference; |
980 | // } else if (refsize == k32) { |
981 | // pOutBuffer[0] = 0xe9; |
982 | // *((__int32*)(1+pOutBuffer)) = (__int32)fixedUpReference; |
983 | // } else { |
984 | // CRASH("Bad input."); |
985 | // } |
986 | // |
987 | // VOID RRT.GetHotSpotOffset(UINT refsize, UINT variationCode) |
988 | // |
989 | // The reference offset is always relative to some IP: this |
990 | // method tells StubLinker where that IP is relative to the |
991 | // start of the instruction. For X86, the offset is always |
992 | // relative to the start of the *following* instruction so |
993 | // the correct implementation is: |
994 | // |
995 | // return GetSizeOfInstruction(refsize, variationCode); |
996 | // |
997 | // Actually, InstructionFormat() provides a default implementation of this |
998 | // method that does exactly this so X86 need not override this at all. |
999 | // |
1000 | // |
1001 | // The extra "variationCode" argument is an __int32 that StubLinker receives |
1002 | // from EmitLabelRef() and passes uninterpreted to each RRT method. |
1003 | // This allows one RRT to handle a family of related instructions, |
1004 | // for example, the family of conditional jumps on the X86. |
1005 | // |
1006 | //------------------------------------------------------------------------- |
1007 | class InstructionFormat |
1008 | { |
1009 | private: |
1010 | enum |
1011 | { |
1012 | // if you want to add a size, insert it in-order (e.g. a 18-bit size would |
1013 | // go between k16 and k32) and shift all the higher values up. All values |
1014 | // must be a power of 2 since the get ORed together. |
1015 | |
1016 | _k8, |
1017 | #ifdef INSTRFMT_K9 |
1018 | _k9, |
1019 | #endif |
1020 | #ifdef INSTRFMT_K13 |
1021 | _k13, |
1022 | #endif |
1023 | _k16, |
1024 | #ifdef INSTRFMT_K24 |
1025 | _k24, |
1026 | #endif |
1027 | #ifdef INSTRFMT_K26 |
1028 | _k26, |
1029 | #endif |
1030 | _k32, |
1031 | #ifdef INSTRFMT_K64SMALL |
1032 | _k64Small, |
1033 | #endif |
1034 | #ifdef INSTRFMT_K64 |
1035 | _k64, |
1036 | #endif |
1037 | _kAllowAlways, |
1038 | }; |
1039 | |
1040 | public: |
1041 | |
1042 | enum |
1043 | { |
1044 | k8 = (1 << _k8), |
1045 | #ifdef INSTRFMT_K9 |
1046 | k9 = (1 << _k9), |
1047 | #endif |
1048 | #ifdef INSTRFMT_K13 |
1049 | k13 = (1 << _k13), |
1050 | #endif |
1051 | k16 = (1 << _k16), |
1052 | #ifdef INSTRFMT_K24 |
1053 | k24 = (1 << _k24), |
1054 | #endif |
1055 | #ifdef INSTRFMT_K26 |
1056 | k26 = (1 << _k26), |
1057 | #endif |
1058 | k32 = (1 << _k32), |
1059 | #ifdef INSTRFMT_K64SMALL |
1060 | k64Small = (1 << _k64Small), |
1061 | #endif |
1062 | #ifdef INSTRFMT_K64 |
1063 | k64 = (1 << _k64), |
1064 | #endif |
1065 | kAllowAlways= (1 << _kAllowAlways), |
1066 | kMax = kAllowAlways, |
1067 | }; |
1068 | |
1069 | const UINT m_allowedSizes; // OR mask using above "k" values |
1070 | InstructionFormat(UINT allowedSizes) : m_allowedSizes(allowedSizes) |
1071 | { |
1072 | LIMITED_METHOD_CONTRACT; |
1073 | } |
1074 | |
1075 | virtual UINT GetSizeOfInstruction(UINT refsize, UINT variationCode) = 0; |
1076 | virtual VOID EmitInstruction(UINT refsize, __int64 fixedUpReference, BYTE *pCodeBuffer, UINT variationCode, BYTE *pDataBuffer) = 0; |
1077 | virtual UINT GetHotSpotOffset(UINT refsize, UINT variationCode) |
1078 | { |
1079 | WRAPPER_NO_CONTRACT; |
1080 | // Default implementation: the offset is added to the |
1081 | // start of the following instruction. |
1082 | return GetSizeOfInstruction(refsize, variationCode); |
1083 | } |
1084 | |
1085 | virtual UINT GetSizeOfData(UINT refsize, UINT variationCode) |
1086 | { |
1087 | LIMITED_METHOD_CONTRACT; |
1088 | // Default implementation: 0 extra bytes needed (most CPUs) |
1089 | return 0; |
1090 | } |
1091 | |
1092 | virtual BOOL CanReach(UINT refsize, UINT variationCode, BOOL fExternal, INT_PTR offset) |
1093 | { |
1094 | LIMITED_METHOD_CONTRACT; |
1095 | |
1096 | if (fExternal) { |
1097 | // For external, we don't have enough info to predict |
1098 | // the offset yet so we only accept if the offset size |
1099 | // is at least as large as the native pointer size. |
1100 | switch(refsize) { |
1101 | case InstructionFormat::k8: // intentional fallthru |
1102 | case InstructionFormat::k16: // intentional fallthru |
1103 | #ifdef INSTRFMT_K24 |
1104 | case InstructionFormat::k24: // intentional fallthru |
1105 | #endif |
1106 | #ifdef INSTRFMT_K26 |
1107 | case InstructionFormat::k26: // intentional fallthru |
1108 | #endif |
1109 | return FALSE; // no 8 or 16-bit platforms |
1110 | |
1111 | case InstructionFormat::k32: |
1112 | return sizeof(LPVOID) <= 4; |
1113 | #ifdef INSTRFMT_K64 |
1114 | case InstructionFormat::k64: |
1115 | return sizeof(LPVOID) <= 8; |
1116 | #endif |
1117 | case InstructionFormat::kAllowAlways: |
1118 | return TRUE; |
1119 | |
1120 | default: |
1121 | _ASSERTE(0); |
1122 | return FALSE; |
1123 | } |
1124 | } else { |
1125 | switch(refsize) |
1126 | { |
1127 | case InstructionFormat::k8: |
1128 | return FitsInI1(offset); |
1129 | |
1130 | case InstructionFormat::k16: |
1131 | return FitsInI2(offset); |
1132 | |
1133 | #ifdef INSTRFMT_K24 |
1134 | case InstructionFormat::k24: |
1135 | return FitsInI2(offset>>8); |
1136 | #endif |
1137 | |
1138 | #ifdef INSTRFMT_K26 |
1139 | case InstructionFormat::k26: |
1140 | return FitsInI2(offset>>10); |
1141 | #endif |
1142 | case InstructionFormat::k32: |
1143 | return FitsInI4(offset); |
1144 | #ifdef INSTRFMT_K64 |
1145 | case InstructionFormat::k64: |
1146 | // intentional fallthru |
1147 | #endif |
1148 | case InstructionFormat::kAllowAlways: |
1149 | return TRUE; |
1150 | default: |
1151 | _ASSERTE(0); |
1152 | return FALSE; |
1153 | |
1154 | } |
1155 | } |
1156 | } |
1157 | }; |
1158 | |
1159 | |
1160 | |
1161 | |
1162 | |
1163 | //------------------------------------------------------------------------- |
1164 | // This stub cache associates stubs with an integer key. For some clients, |
1165 | // this might represent the size of the argument stack in some cpu-specific |
1166 | // units (for the x86, the size is expressed in DWORDS.) For other clients, |
1167 | // this might take into account the style of stub (e.g. whether it returns |
1168 | // an object reference or not). |
1169 | //------------------------------------------------------------------------- |
1170 | class ArgBasedStubCache |
1171 | { |
1172 | public: |
1173 | ArgBasedStubCache(UINT fixedSize = NUMFIXEDSLOTS); |
1174 | ~ArgBasedStubCache(); |
1175 | |
1176 | //----------------------------------------------------------------- |
1177 | // Retrieves the stub associated with the given key. |
1178 | //----------------------------------------------------------------- |
1179 | Stub *GetStub(UINT_PTR key); |
1180 | |
1181 | //----------------------------------------------------------------- |
1182 | // Tries to associate the stub with the given key. |
1183 | // It may fail because another thread might swoop in and |
1184 | // do the association before you do. Thus, you must use the |
1185 | // return value stub rather than the pStub. |
1186 | //----------------------------------------------------------------- |
1187 | Stub* AttemptToSetStub(UINT_PTR key, Stub *pStub); |
1188 | |
1189 | |
1190 | // Suggestions for number of slots |
1191 | enum { |
1192 | #ifdef _DEBUG |
1193 | NUMFIXEDSLOTS = 3, |
1194 | #else |
1195 | NUMFIXEDSLOTS = 16, |
1196 | #endif |
1197 | }; |
1198 | |
1199 | #ifdef _DEBUG |
1200 | VOID Dump(); //Diagnostic dump |
1201 | #endif |
1202 | |
1203 | private: |
1204 | |
1205 | // How many low-numbered keys have direct access? |
1206 | UINT m_numFixedSlots; |
1207 | |
1208 | // For 'm_numFixedSlots' low-numbered keys, we store them in an array. |
1209 | Stub **m_aStub; |
1210 | |
1211 | |
1212 | struct SlotEntry |
1213 | { |
1214 | Stub *m_pStub; |
1215 | UINT_PTR m_key; |
1216 | SlotEntry *m_pNext; |
1217 | }; |
1218 | |
1219 | // High-numbered keys are stored in a sparse linked list. |
1220 | SlotEntry *m_pSlotEntries; |
1221 | |
1222 | |
1223 | Crst m_crst; |
1224 | }; |
1225 | |
1226 | |
1227 | #define CPUSTUBLINKER StubLinkerCPU |
1228 | |
1229 | class NDirectStubLinker; |
1230 | class CPUSTUBLINKER; |
1231 | |
1232 | #endif // __stublink_h__ |
1233 | |