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
6//
7
8
9#include "common.h"
10#include "dynamicmethod.h"
11#include "object.h"
12#include "method.hpp"
13#include "comdelegate.h"
14#include "field.h"
15#include "contractimpl.h"
16#include "nibblemapmacros.h"
17#include "stringliteralmap.h"
18#include "virtualcallstub.h"
19
20
21#ifndef DACCESS_COMPILE
22
23// get the method table for dynamic methods
24DynamicMethodTable* DomainFile::GetDynamicMethodTable()
25{
26 CONTRACT (DynamicMethodTable*)
27 {
28 INSTANCE_CHECK;
29 THROWS;
30 GC_TRIGGERS;
31 MODE_ANY;
32 INJECT_FAULT(COMPlusThrowOM());
33 POSTCONDITION(CheckPointer(m_pDynamicMethodTable));
34 }
35 CONTRACT_END;
36
37 if (!m_pDynamicMethodTable)
38 DynamicMethodTable::CreateDynamicMethodTable(&m_pDynamicMethodTable, GetModule(), GetAppDomain());
39
40
41 RETURN m_pDynamicMethodTable;
42}
43
44void ReleaseDynamicMethodTable(DynamicMethodTable *pDynMT)
45{
46 WRAPPER_NO_CONTRACT;
47 if (pDynMT)
48 {
49 pDynMT->Destroy();
50 }
51}
52
53void DynamicMethodTable::CreateDynamicMethodTable(DynamicMethodTable **ppLocation, Module *pModule, AppDomain *pDomain)
54{
55 CONTRACT_VOID
56 {
57 THROWS;
58 GC_TRIGGERS;
59 MODE_ANY;
60 INJECT_FAULT(COMPlusThrowOM());
61 PRECONDITION(CheckPointer(ppLocation));
62 PRECONDITION(CheckPointer(pModule));
63 POSTCONDITION(CheckPointer(*ppLocation));
64 }
65 CONTRACT_END;
66
67 AllocMemTracker amt;
68
69 LoaderHeap* pHeap = pDomain->GetHighFrequencyHeap();
70 _ASSERTE(pHeap);
71
72 if (*ppLocation) RETURN;
73
74 DynamicMethodTable* pDynMT = (DynamicMethodTable*)
75 amt.Track(pHeap->AllocMem(S_SIZE_T(sizeof(DynamicMethodTable))));
76
77 // Note: Memory allocated on loader heap is zero filled
78 // memset((void*)pDynMT, 0, sizeof(DynamicMethodTable));
79
80 if (*ppLocation) RETURN;
81
82 LOG((LF_BCL, LL_INFO100, "Level2 - Creating DynamicMethodTable {0x%p}...\n", pDynMT));
83
84 Holder<DynamicMethodTable*, DoNothing, ReleaseDynamicMethodTable> dynMTHolder(pDynMT);
85 pDynMT->m_Crst.Init(CrstDynamicMT);
86 pDynMT->m_Module = pModule;
87 pDynMT->m_pDomain = pDomain;
88 pDynMT->MakeMethodTable(&amt);
89
90 if (*ppLocation) RETURN;
91
92 if (FastInterlockCompareExchangePointer(ppLocation, pDynMT, NULL) != NULL)
93 {
94 LOG((LF_BCL, LL_INFO100, "Level2 - Another thread got here first - deleting DynamicMethodTable {0x%p}...\n", pDynMT));
95 RETURN;
96 }
97
98 dynMTHolder.SuppressRelease();
99
100 amt.SuppressRelease();
101 LOG((LF_BCL, LL_INFO10, "Level1 - DynamicMethodTable created {0x%p}...\n", pDynMT));
102 RETURN;
103}
104
105void DynamicMethodTable::MakeMethodTable(AllocMemTracker *pamTracker)
106{
107 CONTRACTL
108 {
109 THROWS;
110 GC_TRIGGERS;
111 MODE_ANY;
112 INJECT_FAULT(COMPlusThrowOM());
113 }
114 CONTRACTL_END;
115
116 m_pMethodTable = CreateMinimalMethodTable(m_Module, m_pDomain->GetHighFrequencyHeap(), pamTracker);
117}
118
119void DynamicMethodTable::Destroy()
120{
121 CONTRACTL
122 {
123 NOTHROW;
124 GC_TRIGGERS;
125 MODE_ANY;
126 }
127 CONTRACTL_END;
128
129 // Go over all DynamicMethodDescs and make sure that they are destroyed
130
131 if (m_pMethodTable != NULL)
132 {
133 MethodTable::IntroducedMethodIterator it(m_pMethodTable);
134 for (; it.IsValid(); it.Next())
135 {
136 DynamicMethodDesc *pMD = (DynamicMethodDesc*)it.GetMethodDesc();
137 pMD->Destroy(TRUE /* fDomainUnload */);
138 }
139 }
140
141 m_Crst.Destroy();
142 LOG((LF_BCL, LL_INFO10, "Level1 - DynamicMethodTable destroyed {0x%p}\n", this));
143}
144
145void DynamicMethodTable::AddMethodsToList()
146{
147 CONTRACT_VOID
148 {
149 THROWS;
150 GC_TRIGGERS;
151 MODE_ANY;
152 INJECT_FAULT(COMPlusThrowOM());
153 }
154 CONTRACT_END;
155
156 AllocMemTracker amt;
157
158 LoaderHeap* pHeap = m_pDomain->GetHighFrequencyHeap();
159 _ASSERTE(pHeap);
160
161 //
162 // allocate as many chunks as needed to hold the methods
163 //
164 MethodDescChunk* pChunk = MethodDescChunk::CreateChunk(pHeap, 0 /* one chunk of maximum size */,
165 mcDynamic, TRUE /* fNonVtableSlot */, TRUE /* fNativeCodeSlot */, FALSE /* fComPlusCallInfo */, m_pMethodTable, &amt);
166 if (m_DynamicMethodList) RETURN;
167
168 int methodCount = pChunk->GetCount();
169
170 BYTE* pResolvers = (BYTE*)amt.Track(pHeap->AllocMem(S_SIZE_T(sizeof(LCGMethodResolver)) * S_SIZE_T(methodCount)));
171 if (m_DynamicMethodList) RETURN;
172
173 DynamicMethodDesc *pNewMD = (DynamicMethodDesc *)pChunk->GetFirstMethodDesc();
174 DynamicMethodDesc *pPrevMD = NULL;
175 // now go through all the methods in the chunk and link them
176 for(int i = 0; i < methodCount; i++)
177 {
178 _ASSERTE(pNewMD->GetClassification() == mcDynamic);
179
180 pNewMD->SetMemberDef(0);
181 pNewMD->SetSlot(MethodTable::NO_SLOT); // we can't ever use the slot for dynamic methods
182 pNewMD->SetStatic();
183
184 pNewMD->m_dwExtendedFlags = mdPublic | mdStatic | DynamicMethodDesc::nomdLCGMethod;
185
186 LCGMethodResolver* pResolver = new (pResolvers) LCGMethodResolver();
187 pResolver->m_pDynamicMethod = pNewMD;
188 pResolver->m_DynamicMethodTable = this;
189 pNewMD->m_pResolver = pResolver;
190
191 pNewMD->SetTemporaryEntryPoint(m_pDomain->GetLoaderAllocator(), &amt);
192
193#ifdef _DEBUG
194 pNewMD->m_pDebugMethodTable.SetValue(m_pMethodTable);
195#endif
196
197 if (pPrevMD)
198 {
199 pPrevMD->GetLCGMethodResolver()->m_next = pNewMD;
200 }
201 pPrevMD = pNewMD;
202 pNewMD = (DynamicMethodDesc *)(dac_cast<TADDR>(pNewMD) + pNewMD->SizeOf());
203
204 pResolvers += sizeof(LCGMethodResolver);
205 }
206
207 if (m_DynamicMethodList) RETURN;
208
209 {
210 // publish method list and method table
211 LockHolder lh(this);
212 if (m_DynamicMethodList) RETURN;
213
214 // publish the new method descs on the method table
215 m_pMethodTable->GetClass()->AddChunk(pChunk);
216 m_DynamicMethodList = (DynamicMethodDesc*)pChunk->GetFirstMethodDesc();
217 }
218
219 amt.SuppressRelease();
220}
221
222DynamicMethodDesc* DynamicMethodTable::GetDynamicMethod(BYTE *psig, DWORD sigSize, PTR_CUTF8 name)
223{
224 CONTRACT (DynamicMethodDesc*)
225 {
226 INSTANCE_CHECK;
227 THROWS;
228 GC_TRIGGERS;
229 MODE_ANY;
230 INJECT_FAULT(COMPlusThrowOM());
231 PRECONDITION(CheckPointer(psig));
232 PRECONDITION(sigSize > 0);
233 POSTCONDITION(CheckPointer(RETVAL));
234 }
235 CONTRACT_END;
236
237 LOG((LF_BCL, LL_INFO10000, "Level4 - Getting DynamicMethod\n"));
238
239 DynamicMethodDesc *pNewMD = NULL;
240
241 for (;;)
242 {
243 {
244 LockHolder lh(this);
245 pNewMD = m_DynamicMethodList;
246 if (pNewMD)
247 {
248 m_DynamicMethodList = pNewMD->GetLCGMethodResolver()->m_next;
249#ifdef _DEBUG
250 m_Used++;
251#endif
252 break;
253 }
254 }
255
256 LOG((LF_BCL, LL_INFO1000, "Level4 - DynamicMethod unavailable\n"));
257
258 // need to create more methoddescs
259 AddMethodsToList();
260 }
261 _ASSERTE(pNewMD != NULL);
262
263 // Reset the method desc into pristine state
264
265 // Note: Reset has THROWS contract since it may allocate jump stub. It will never throw here
266 // since it will always reuse the existing jump stub.
267 pNewMD->Reset();
268
269 LOG((LF_BCL, LL_INFO1000, "Level3 - DynamicMethod obtained {0x%p} (used %d)\n", pNewMD, m_Used));
270
271 // the store sig part of the method desc
272 pNewMD->SetStoredMethodSig((PCCOR_SIGNATURE)psig, sigSize);
273 // the dynamic part of the method desc
274 pNewMD->m_pszMethodName.SetValueMaybeNull(name);
275
276 pNewMD->m_dwExtendedFlags = mdPublic | mdStatic | DynamicMethodDesc::nomdLCGMethod;
277
278#ifdef _DEBUG
279 pNewMD->m_pszDebugMethodName = name;
280 pNewMD->m_pszDebugClassName = (LPUTF8)"dynamicclass";
281 pNewMD->m_pszDebugMethodSignature = "DynamicMethod Signature not available";
282#endif // _DEBUG
283
284#ifdef HAVE_GCCOVER
285 pNewMD->m_GcCover = NULL;
286#endif
287
288 pNewMD->SetNotInline(TRUE);
289 pNewMD->GetLCGMethodResolver()->Reset();
290
291 RETURN pNewMD;
292}
293
294void DynamicMethodTable::LinkMethod(DynamicMethodDesc *pMethod)
295{
296 CONTRACT_VOID
297 {
298 NOTHROW;
299 GC_TRIGGERS;
300 MODE_ANY;
301 PRECONDITION(CheckPointer(pMethod));
302 }
303 CONTRACT_END;
304
305 LOG((LF_BCL, LL_INFO10000, "Level4 - Returning DynamicMethod to free list {0x%p} (used %d)\n", pMethod, m_Used));
306 {
307 LockHolder lh(this);
308 pMethod->GetLCGMethodResolver()->m_next = m_DynamicMethodList;
309 m_DynamicMethodList = pMethod;
310#ifdef _DEBUG
311 m_Used--;
312#endif
313 }
314
315 RETURN;
316}
317
318
319//
320// CodeHeap implementation
321//
322HeapList* HostCodeHeap::CreateCodeHeap(CodeHeapRequestInfo *pInfo, EEJitManager *pJitManager)
323{
324 CONTRACT (HeapList*)
325 {
326 THROWS;
327 GC_NOTRIGGER;
328 MODE_ANY;
329 INJECT_FAULT(COMPlusThrowOM());
330 POSTCONDITION((RETVAL != NULL) || !pInfo->getThrowOnOutOfMemoryWithinRange());
331 }
332 CONTRACT_END;
333
334 NewHolder<HostCodeHeap> pCodeHeap(new HostCodeHeap(pJitManager));
335
336 HeapList *pHp = pCodeHeap->InitializeHeapList(pInfo);
337 if (pHp == NULL)
338 {
339 _ASSERTE(!pInfo->getThrowOnOutOfMemoryWithinRange());
340 RETURN NULL;
341 }
342
343 LOG((LF_BCL, LL_INFO100, "Level2 - CodeHeap creation {0x%p} - base addr 0x%p, size available 0x%p, nibble map ptr 0x%p\n",
344 (HostCodeHeap*)pCodeHeap, pCodeHeap->m_pBaseAddr, pCodeHeap->m_TotalBytesAvailable, pCodeHeap->m_pHeapList->pHdrMap));
345
346 pCodeHeap.SuppressRelease();
347
348 LOG((LF_BCL, LL_INFO10, "Level1 - CodeHeap created {0x%p}\n", (HostCodeHeap*)pCodeHeap));
349 RETURN pHp;
350}
351
352HostCodeHeap::HostCodeHeap(EEJitManager *pJitManager)
353{
354 CONTRACTL
355 {
356 THROWS;
357 GC_NOTRIGGER;
358 MODE_ANY;
359 INJECT_FAULT(COMPlusThrowOM());
360 }
361 CONTRACTL_END;
362
363 m_pBaseAddr = NULL;
364 m_pLastAvailableCommittedAddr = NULL;
365 m_TotalBytesAvailable = 0;
366 m_ApproximateLargestBlock = 0;
367 m_AllocationCount = 0;
368 m_pHeapList = NULL;
369 m_pJitManager = (PTR_EEJitManager)pJitManager;
370 m_pFreeList = NULL;
371 m_pAllocator = NULL;
372 m_pNextHeapToRelease = NULL;
373}
374
375HostCodeHeap::~HostCodeHeap()
376{
377 LIMITED_METHOD_CONTRACT;
378
379 if (m_pHeapList != NULL && m_pHeapList->pHdrMap != NULL)
380 delete[] m_pHeapList->pHdrMap;
381
382 if (m_pBaseAddr)
383 ClrVirtualFree(m_pBaseAddr, 0, MEM_RELEASE);
384 LOG((LF_BCL, LL_INFO10, "Level1 - CodeHeap destroyed {0x%p}\n", this));
385}
386
387HeapList* HostCodeHeap::InitializeHeapList(CodeHeapRequestInfo *pInfo)
388{
389 CONTRACTL
390 {
391 THROWS;
392 GC_NOTRIGGER;
393 MODE_ANY;
394 }
395 CONTRACTL_END;
396
397 size_t ReserveBlockSize = pInfo->getRequestSize();
398
399 // Add TrackAllocation, HeapList and very conservative padding to make sure we have enough for the allocation
400 ReserveBlockSize += sizeof(TrackAllocation) + sizeof(HeapList) + HOST_CODEHEAP_SIZE_ALIGN + 0x100;
401
402 // reserve ReserveBlockSize rounded-up to VIRTUAL_ALLOC_RESERVE_GRANULARITY of memory
403 ReserveBlockSize = ALIGN_UP(ReserveBlockSize, VIRTUAL_ALLOC_RESERVE_GRANULARITY);
404
405 if (pInfo->m_loAddr != NULL || pInfo->m_hiAddr != NULL)
406 {
407 m_pBaseAddr = ClrVirtualAllocWithinRange(pInfo->m_loAddr, pInfo->m_hiAddr,
408 ReserveBlockSize, MEM_RESERVE, PAGE_NOACCESS);
409 if (!m_pBaseAddr)
410 {
411 if (pInfo->getThrowOnOutOfMemoryWithinRange())
412 ThrowOutOfMemoryWithinRange();
413 return NULL;
414 }
415 }
416 else
417 {
418 // top up the ReserveBlockSize to suggested minimum
419 ReserveBlockSize = max(ReserveBlockSize, pInfo->getReserveSize());
420
421 m_pBaseAddr = ClrVirtualAllocExecutable(ReserveBlockSize, MEM_RESERVE, PAGE_NOACCESS);
422 if (!m_pBaseAddr)
423 ThrowOutOfMemory();
424 }
425
426 m_pLastAvailableCommittedAddr = m_pBaseAddr;
427 m_TotalBytesAvailable = ReserveBlockSize;
428 m_ApproximateLargestBlock = ReserveBlockSize;
429 m_pAllocator = pInfo->m_pAllocator;
430
431 TrackAllocation *pTracker = AllocMemory_NoThrow(0, sizeof(HeapList), sizeof(void*), 0);
432 if (pTracker == NULL)
433 {
434 // This should only ever happen with fault injection
435 _ASSERTE(g_pConfig->ShouldInjectFault(INJECTFAULT_DYNAMICCODEHEAP));
436 ThrowOutOfMemory();
437 }
438
439 HeapList* pHp = (HeapList *)(pTracker + 1);
440
441 pHp->hpNext = NULL;
442 pHp->pHeap = (PTR_CodeHeap)this;
443 // wire it back
444 m_pHeapList = (PTR_HeapList)pHp;
445
446 LOG((LF_BCL, LL_INFO100, "Level2 - CodeHeap creation {0x%p} - size available 0x%p, private data ptr [0x%p, 0x%p]\n",
447 (HostCodeHeap*)this, m_TotalBytesAvailable, pTracker, pTracker->size));
448
449 // It is imporant to exclude the CLRPersonalityRoutine from the tracked range
450 pHp->startAddress = dac_cast<TADDR>(m_pBaseAddr) + pTracker->size;
451 pHp->mapBase = ROUND_DOWN_TO_PAGE(pHp->startAddress); // round down to next lower page align
452 pHp->pHdrMap = NULL;
453 pHp->endAddress = pHp->startAddress;
454
455 pHp->maxCodeHeapSize = m_TotalBytesAvailable - pTracker->size;
456 pHp->reserveForJumpStubs = 0;
457
458#ifdef _WIN64
459 emitJump((LPBYTE)pHp->CLRPersonalityRoutine, (void *)ProcessCLRException);
460#endif
461
462 size_t nibbleMapSize = HEAP2MAPSIZE(ROUND_UP_TO_PAGE(pHp->maxCodeHeapSize));
463 pHp->pHdrMap = new DWORD[nibbleMapSize / sizeof(DWORD)];
464 ZeroMemory(pHp->pHdrMap, nibbleMapSize);
465
466 return pHp;
467}
468
469HostCodeHeap::TrackAllocation* HostCodeHeap::AllocFromFreeList(size_t header, size_t size, DWORD alignment, size_t reserveForJumpStubs)
470{
471 CONTRACTL
472 {
473 NOTHROW;
474 GC_NOTRIGGER;
475 MODE_ANY;
476 }
477 CONTRACTL_END;
478
479 if (m_pFreeList)
480 {
481 LOG((LF_BCL, LL_INFO100, "Level2 - CodeHeap [0x%p] - Alloc size corrected 0x%X for free list\n", this, size));
482 // walk the list looking for a block with enough capacity
483 TrackAllocation *pCurrent = m_pFreeList;
484 TrackAllocation *pPrevious = NULL;
485 while (pCurrent)
486 {
487 BYTE* pPointer = ALIGN_UP((BYTE*)(pCurrent + 1) + header, alignment);
488 size_t realSize = ALIGN_UP(pPointer + size, sizeof(void*)) - (BYTE*)pCurrent;
489 if (pCurrent->size >= realSize + reserveForJumpStubs)
490 {
491 // found a block
492 LOG((LF_BCL, LL_INFO100, "Level2 - CodeHeap [0x%p] - Block found, size 0x%X\n", this, pCurrent->size));
493
494 // The space left is not big enough for a new block, let's just
495 // update the TrackAllocation record for the current block
496 if (pCurrent->size - realSize < max(HOST_CODEHEAP_SIZE_ALIGN, sizeof(TrackAllocation)))
497 {
498 LOG((LF_BCL, LL_INFO100, "Level2 - CodeHeap [0x%p] - Item removed %p, size 0x%X\n", this, pCurrent, pCurrent->size));
499 // remove current
500 if (pPrevious)
501 {
502 pPrevious->pNext = pCurrent->pNext;
503 }
504 else
505 {
506 m_pFreeList = pCurrent->pNext;
507 }
508 }
509 else
510 {
511 // create a new TrackAllocation after the memory we just allocated and insert it into the free list
512 TrackAllocation *pNewCurrent = (TrackAllocation*)((BYTE*)pCurrent + realSize);
513 pNewCurrent->pNext = pCurrent->pNext;
514 pNewCurrent->size = pCurrent->size - realSize;
515 LOG((LF_BCL, LL_INFO100, "Level2 - CodeHeap [0x%p] - Item changed %p, new size 0x%X\n", this, pNewCurrent, pNewCurrent->size));
516 if (pPrevious)
517 {
518 pPrevious->pNext = pNewCurrent;
519 }
520 else
521 {
522 m_pFreeList = pNewCurrent;
523 }
524
525 // We only need to update the size of the current block if we are creating a new block
526 pCurrent->size = realSize;
527 }
528
529 pCurrent->pHeap = this;
530
531 LOG((LF_BCL, LL_INFO100, "Level2 - CodeHeap [0x%p] - Allocation returned %p, size 0x%X - data -> %p\n", this, pCurrent, pCurrent->size, pPointer));
532 return pCurrent;
533 }
534 pPrevious = pCurrent;
535 pCurrent = pCurrent->pNext;
536 }
537 }
538 LOG((LF_BCL, LL_INFO100, "Level2 - CodeHeap [0x%p] - No block in free list for size 0x%X\n", this, size));
539 return NULL;
540}
541
542void HostCodeHeap::AddToFreeList(TrackAllocation *pBlockToInsert)
543{
544 CONTRACTL
545 {
546 NOTHROW;
547 GC_NOTRIGGER;
548 MODE_ANY;
549 }
550 CONTRACTL_END;
551
552 LOG((LF_BCL, LL_INFO100, "Level2 - CodeHeap [0x%p] - Add to FreeList [%p, 0x%X]\n", this, pBlockToInsert, pBlockToInsert->size));
553
554 // append to the list in the proper position and coalesce if needed
555 if (m_pFreeList)
556 {
557 TrackAllocation *pCurrent = m_pFreeList;
558 TrackAllocation *pPrevious = NULL;
559 while (pCurrent)
560 {
561 if (pCurrent > pBlockToInsert)
562 {
563 // found the point of insertion
564 pBlockToInsert->pNext = pCurrent;
565 if (pPrevious)
566 {
567 pPrevious->pNext = pBlockToInsert;
568 LOG((LF_BCL, LL_INFO100, "Level2 - CodeHeap [0x%p] - Insert block [%p, 0x%X] -> [%p, 0x%X] -> [%p, 0x%X]\n", this,
569 pPrevious, pPrevious->size,
570 pBlockToInsert, pBlockToInsert->size,
571 pCurrent, pCurrent->size));
572 }
573 else
574 {
575 m_pFreeList = pBlockToInsert;
576 LOG((LF_BCL, LL_INFO100, "Level2 - CodeHeap [0x%p] - Insert block [%p, 0x%X] to head\n", this, pBlockToInsert, pBlockToInsert->size));
577 }
578
579 // check for coalescing
580 if ((BYTE*)pBlockToInsert + pBlockToInsert->size == (BYTE*)pCurrent)
581 {
582 // coalesce with next
583 LOG((LF_BCL, LL_INFO100, "Level2 - CodeHeap [0x%p] - Coalesce block [%p, 0x%X] with [%p, 0x%X] - new size 0x%X\n", this,
584 pBlockToInsert, pBlockToInsert->size,
585 pCurrent, pCurrent->size,
586 pCurrent->size + pBlockToInsert->size));
587 pBlockToInsert->pNext = pCurrent->pNext;
588 pBlockToInsert->size += pCurrent->size;
589 }
590
591 if (pPrevious && (BYTE*)pPrevious + pPrevious->size == (BYTE*)pBlockToInsert)
592 {
593 // coalesce with previous
594 LOG((LF_BCL, LL_INFO100, "Level2 - CodeHeap [0x%p] - Coalesce block [%p, 0x%X] with [%p, 0x%X] - new size 0x%X\n", this,
595 pPrevious, pPrevious->size,
596 pBlockToInsert, pBlockToInsert->size,
597 pPrevious->size + pBlockToInsert->size));
598 pPrevious->pNext = pBlockToInsert->pNext;
599 pPrevious->size += pBlockToInsert->size;
600 }
601
602 return;
603 }
604 pPrevious = pCurrent;
605 pCurrent = pCurrent->pNext;
606 }
607 _ASSERTE(pPrevious && pCurrent == NULL);
608 pBlockToInsert->pNext = NULL;
609 // last in the list
610 if ((BYTE*)pPrevious + pPrevious->size == (BYTE*)pBlockToInsert)
611 {
612 // coalesce with previous
613 LOG((LF_BCL, LL_INFO100, "Level2 - CodeHeap [0x%p] - Coalesce block [%p, 0x%X] with [%p, 0x%X] - new size 0x%X\n", this,
614 pPrevious, pPrevious->size,
615 pBlockToInsert, pBlockToInsert->size,
616 pPrevious->size + pBlockToInsert->size));
617 pPrevious->size += pBlockToInsert->size;
618 }
619 else
620 {
621 pPrevious->pNext = pBlockToInsert;
622 LOG((LF_BCL, LL_INFO100, "Level2 - CodeHeap [0x%p] - Insert block [%p, 0x%X] to end after [%p, 0x%X]\n", this,
623 pBlockToInsert, pBlockToInsert->size,
624 pPrevious, pPrevious->size));
625 }
626
627 return;
628
629 }
630 // first in the list
631 pBlockToInsert->pNext = m_pFreeList;
632 m_pFreeList = pBlockToInsert;
633 LOG((LF_BCL, LL_INFO100, "Level2 - CodeHeap [0x%p] - Insert block [%p, 0x%X] to head\n", this,
634 m_pFreeList, m_pFreeList->size));
635}
636
637void* HostCodeHeap::AllocMemForCode_NoThrow(size_t header, size_t size, DWORD alignment, size_t reserveForJumpStubs)
638{
639 CONTRACTL
640 {
641 NOTHROW;
642 GC_NOTRIGGER;
643 MODE_ANY;
644 }
645 CONTRACTL_END;
646
647 _ASSERTE(header == sizeof(CodeHeader));
648 _ASSERTE(alignment <= HOST_CODEHEAP_SIZE_ALIGN);
649
650 // The code allocator has to guarantee that there is only one entrypoint per nibble map entry.
651 // It is guaranteed because of HostCodeHeap allocator always aligns the size up to HOST_CODEHEAP_SIZE_ALIGN,
652 // and because the size of nibble map entries (BYTES_PER_BUCKET) is smaller than HOST_CODEHEAP_SIZE_ALIGN.
653 // Assert the later fact here.
654 _ASSERTE(HOST_CODEHEAP_SIZE_ALIGN >= BYTES_PER_BUCKET);
655
656 header += sizeof(TrackAllocation*);
657
658 TrackAllocation* pTracker = AllocMemory_NoThrow(header, size, alignment, reserveForJumpStubs);
659 if (pTracker == NULL)
660 return NULL;
661
662 BYTE * pCode = ALIGN_UP((BYTE*)(pTracker + 1) + header, alignment);
663
664 // Pointer to the TrackAllocation record is stored just before the code header
665 CodeHeader * pHdr = (CodeHeader *)pCode - 1;
666 *((TrackAllocation **)(pHdr) - 1) = pTracker;
667
668 _ASSERTE(pCode + size <= (BYTE*)pTracker + pTracker->size);
669
670 // ref count the whole heap
671 m_AllocationCount++;
672 LOG((LF_BCL, LL_INFO100, "Level2 - CodeHeap [0x%p] - ref count %d\n", this, m_AllocationCount));
673
674 return pCode;
675}
676
677HostCodeHeap::TrackAllocation* HostCodeHeap::AllocMemory_NoThrow(size_t header, size_t size, DWORD alignment, size_t reserveForJumpStubs)
678{
679 CONTRACTL
680 {
681 NOTHROW;
682 GC_NOTRIGGER;
683 MODE_ANY;
684 }
685 CONTRACTL_END;
686
687#ifdef _DEBUG
688 if (g_pConfig->ShouldInjectFault(INJECTFAULT_DYNAMICCODEHEAP))
689 {
690 char *a = new (nothrow) char;
691 if (a == NULL)
692 return NULL;
693 delete a;
694 }
695#endif // _DEBUG
696
697 // Skip walking the free list if the cached size of the largest block is not enough
698 size_t totalRequiredSize = ALIGN_UP(sizeof(TrackAllocation) + header + size + (alignment - 1) + reserveForJumpStubs, sizeof(void*));
699 if (totalRequiredSize > m_ApproximateLargestBlock)
700 return NULL;
701
702 LOG((LF_BCL, LL_INFO100, "Level2 - CodeHeap [0x%p] - Allocation requested 0x%X\n", this, size));
703
704 TrackAllocation* pTracker = AllocFromFreeList(header, size, alignment, reserveForJumpStubs);
705 if (!pTracker)
706 {
707 // walk free list to end to find available space
708 size_t availableInFreeList = 0;
709 TrackAllocation *pCurrentBlock = m_pFreeList;
710 TrackAllocation *pLastBlock = NULL;
711 while (pCurrentBlock)
712 {
713 pLastBlock = pCurrentBlock;
714 pCurrentBlock = pCurrentBlock->pNext;
715 }
716 if (pLastBlock && (BYTE*)pLastBlock + pLastBlock->size == m_pLastAvailableCommittedAddr)
717 {
718 availableInFreeList = pLastBlock->size;
719 }
720
721 _ASSERTE(totalRequiredSize > availableInFreeList);
722 size_t sizeToCommit = totalRequiredSize - availableInFreeList;
723 sizeToCommit = ROUND_UP_TO_PAGE(sizeToCommit);
724
725 if (m_pLastAvailableCommittedAddr + sizeToCommit <= m_pBaseAddr + m_TotalBytesAvailable)
726 {
727 if (NULL == ClrVirtualAlloc(m_pLastAvailableCommittedAddr, sizeToCommit, MEM_COMMIT, PAGE_EXECUTE_READWRITE))
728 {
729 LOG((LF_BCL, LL_ERROR, "CodeHeap [0x%p] - VirtualAlloc failed\n", this));
730 return NULL;
731 }
732
733 TrackAllocation *pBlockToInsert = (TrackAllocation*)(void*)m_pLastAvailableCommittedAddr;
734 pBlockToInsert->pNext = NULL;
735 pBlockToInsert->size = sizeToCommit;
736 m_pLastAvailableCommittedAddr += sizeToCommit;
737 AddToFreeList(pBlockToInsert);
738 pTracker = AllocFromFreeList(header, size, alignment, reserveForJumpStubs);
739 _ASSERTE(pTracker != NULL);
740 }
741 else
742 {
743 LOG((LF_BCL, LL_INFO100, "Level2 - CodeHeap [0x%p] - allocation failed:\n\tm_pLastAvailableCommittedAddr: 0x%X\n\tsizeToCommit: 0x%X\n\tm_pBaseAddr: 0x%X\n\tm_TotalBytesAvailable: 0x%X\n", this, m_pLastAvailableCommittedAddr, sizeToCommit, m_pBaseAddr, m_TotalBytesAvailable));
744 // Update largest available block size
745 m_ApproximateLargestBlock = totalRequiredSize - 1;
746 }
747 }
748
749 return pTracker;
750}
751
752#endif //!DACCESS_COMPILE
753
754#ifdef DACCESS_COMPILE
755void HostCodeHeap::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
756{
757 WRAPPER_NO_CONTRACT;
758
759 DAC_ENUM_DTHIS();
760
761 TADDR addr = dac_cast<TADDR>(m_pBaseAddr);
762 size_t size = dac_cast<TADDR>(m_pLastAvailableCommittedAddr) - addr;
763
764#if (_DEBUG)
765 // Test hook: when testing on debug builds, we want an easy way to test that the while
766 // correctly terminates in the face of ridiculous stuff from the target.
767 if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DumpGeneration_IntentionallyCorruptDataFromTarget) == 1)
768 {
769 // Pretend the object is very large.
770 size |= 0xf0000000;
771 }
772#endif // (_DEBUG)
773
774 while (size)
775 {
776 ULONG32 enumSize;
777
778 if (size > 0x80000000)
779 {
780 enumSize = 0x80000000;
781 }
782 else
783 {
784 enumSize = (ULONG32)size;
785 }
786
787 // If we can't read the target memory, stop immediately so we don't work
788 // with broken data.
789 if (!DacEnumMemoryRegion(addr, enumSize))
790 break;
791
792 addr += enumSize;
793 size -= enumSize;
794 }
795}
796#endif // DACCESS_COMPILE
797
798// static
799struct HostCodeHeap::TrackAllocation * HostCodeHeap::GetTrackAllocation(TADDR codeStart)
800{
801 LIMITED_METHOD_CONTRACT;
802
803 CodeHeader * pHdr = dac_cast<PTR_CodeHeader>(PCODEToPINSTR(codeStart)) - 1;
804
805 // Pointer to the TrackAllocation record is stored just before the code header
806 return *((TrackAllocation **)(pHdr) - 1);
807}
808
809HostCodeHeap* HostCodeHeap::GetCodeHeap(TADDR codeStart)
810{
811 WRAPPER_NO_CONTRACT;
812 return HostCodeHeap::GetTrackAllocation(codeStart)->pHeap;
813}
814
815
816#ifndef DACCESS_COMPILE
817
818void HostCodeHeap::FreeMemForCode(void * codeStart)
819{
820 LIMITED_METHOD_CONTRACT;
821
822 TrackAllocation *pTracker = HostCodeHeap::GetTrackAllocation((TADDR)codeStart);
823 AddToFreeList(pTracker);
824
825 m_ApproximateLargestBlock += pTracker->size;
826
827 m_AllocationCount--;
828 LOG((LF_BCL, LL_INFO100, "Level2 - CodeHeap released [0x%p, vt(0x%x)] - ref count %d\n", this, *(size_t*)this, m_AllocationCount));
829
830 if (m_AllocationCount == 0)
831 {
832 m_pJitManager->AddToCleanupList(this);
833 }
834}
835
836//
837// Implementation for DynamicMethodDesc declared in method.hpp
838//
839void DynamicMethodDesc::Destroy(BOOL fDomainUnload)
840{
841 CONTRACTL
842 {
843 if (fDomainUnload) NOTHROW; else THROWS;
844 GC_TRIGGERS;
845 MODE_ANY;
846 }
847 CONTRACTL_END;
848
849 _ASSERTE(IsDynamicMethod());
850 LoaderAllocator *pLoaderAllocator = GetLoaderAllocatorForCode();
851
852 LOG((LF_BCL, LL_INFO1000, "Level3 - Destroying DynamicMethod {0x%p}\n", this));
853 if (!m_pSig.IsNull())
854 {
855 delete[] (BYTE*)m_pSig.GetValue();
856 m_pSig.SetValueMaybeNull(NULL);
857 }
858 m_cSig = 0;
859 if (!m_pszMethodName.IsNull())
860 {
861 delete[] m_pszMethodName.GetValue();
862 m_pszMethodName.SetValueMaybeNull(NULL);
863 }
864
865 GetLCGMethodResolver()->Destroy(fDomainUnload);
866
867 if (pLoaderAllocator->IsCollectible() && !fDomainUnload)
868 {
869 if (pLoaderAllocator->Release())
870 {
871 GCX_PREEMP();
872 LoaderAllocator::GCLoaderAllocators(pLoaderAllocator);
873 }
874 }
875}
876
877//
878// The resolver object is reused when the method is destroyed,
879// this will reset its state for the next use.
880//
881void LCGMethodResolver::Reset()
882{
883 m_DynamicStringLiterals = NULL;
884 m_recordCodePointer = NULL;
885 m_UsedIndCellList = NULL;
886 m_pJumpStubCache = NULL;
887 m_next = NULL;
888 m_Code = NULL;
889}
890
891//
892// Recycle all the indcells in m_UsedIndCellList by adding them to the free list
893//
894void LCGMethodResolver::RecycleIndCells()
895{
896 CONTRACTL {
897 NOTHROW;
898 GC_TRIGGERS;
899 MODE_ANY;
900 } CONTRACTL_END;
901
902 // Append the list of indirection cells used by this dynamic method to the free list
903 IndCellList * list = m_UsedIndCellList;
904 if (list)
905 {
906 BYTE * cellhead = list->indcell;
907 BYTE * cellprev = NULL;
908 BYTE * cellcurr = NULL;
909
910 // Build a linked list of indirection cells from m_UsedIndCellList.
911 // No need to lock newlist because this method is only called during the finalization of
912 // DynamicResolver.DestroyScout and at that time no one else should be modifying m_UsedIndCellList.
913 while (list)
914 {
915 cellcurr = list->indcell;
916 _ASSERTE(cellcurr != NULL);
917
918 if (cellprev)
919 *((BYTE**)cellprev) = cellcurr;
920
921 list = list->pNext;
922 cellprev = cellcurr;
923 }
924
925 // Insert the linked list to the free list of the VirtualCallStubManager of the current domain.
926 // We should use GetLoaderAllocatorForCode because that is where the ind cell was allocated.
927 LoaderAllocator *pLoaderAllocator = GetDynamicMethod()->GetLoaderAllocatorForCode();
928 VirtualCallStubManager *pMgr = pLoaderAllocator->GetVirtualCallStubManager();
929 pMgr->InsertIntoRecycledIndCellList_Locked(cellhead, cellcurr);
930 m_UsedIndCellList = NULL;
931 }
932}
933
934void LCGMethodResolver::Destroy(BOOL fDomainUnload)
935{
936 CONTRACTL {
937 NOTHROW;
938 GC_TRIGGERS;
939 MODE_ANY;
940 } CONTRACTL_END;
941
942 LOG((LF_BCL, LL_INFO100, "Level2 - Resolver - Destroying Resolver {0x%p}\n", this));
943 if (m_Code)
944 {
945 delete[] m_Code;
946 m_Code = NULL;
947 }
948 m_CodeSize = 0;
949 if (!m_LocalSig.IsNull())
950 {
951 delete[] m_LocalSig.GetPtr();
952 m_LocalSig = SigPointer();
953 }
954
955 // Get the global string literal interning map
956 GlobalStringLiteralMap* pStringLiteralMap = SystemDomain::GetGlobalStringLiteralMapNoCreate();
957
958 // release references to all the string literals used in this Dynamic Method
959 if (pStringLiteralMap != NULL)
960 {
961 // lock the global string literal interning map
962 // we cannot use GetGlobalStringLiteralMap() here because it might throw
963 CrstHolder gch(pStringLiteralMap->GetHashTableCrstGlobal());
964
965 // Access to m_DynamicStringLiterals doesn't need to be syncrhonized because
966 // this can be run in only one thread: the finalizer thread.
967 while (m_DynamicStringLiterals != NULL)
968 {
969 m_DynamicStringLiterals->m_pEntry->Release();
970 m_DynamicStringLiterals = m_DynamicStringLiterals->m_pNext;
971 }
972 }
973
974
975 if (!fDomainUnload)
976 {
977 // No need to recycle if the domain is unloading.
978 // Note that we need to do this before m_jitTempData is deleted
979 RecycleIndCells();
980 }
981
982 m_jitMetaHeap.Delete();
983 m_jitTempData.Delete();
984
985
986 // Per-appdomain resources has been reclaimed already if the appdomain is being unloaded. Do not try to
987 // release them again.
988 if (!fDomainUnload)
989 {
990 if (m_recordCodePointer)
991 {
992#if defined(_TARGET_AMD64_)
993 // Remove the unwind information (if applicable)
994 UnwindInfoTable::UnpublishUnwindInfoForMethod((TADDR)m_recordCodePointer);
995#endif // defined(_TARGET_AMD64_)
996
997 HostCodeHeap *pHeap = HostCodeHeap::GetCodeHeap((TADDR)m_recordCodePointer);
998 LOG((LF_BCL, LL_INFO1000, "Level3 - Resolver {0x%p} - Release reference to heap {%p, vt(0x%x)} \n", this, pHeap, *(size_t*)pHeap));
999 pHeap->m_pJitManager->FreeCodeMemory(pHeap, m_recordCodePointer);
1000
1001 m_recordCodePointer = NULL;
1002 }
1003
1004 if (m_pJumpStubCache != NULL)
1005 {
1006 JumpStubBlockHeader* current = m_pJumpStubCache->m_pBlocks;
1007 while (current)
1008 {
1009 JumpStubBlockHeader* next = current->m_next;
1010
1011 HostCodeHeap *pHeap = current->GetHostCodeHeap();
1012 LOG((LF_BCL, LL_INFO1000, "Level3 - Resolver {0x%p} - Release reference to heap {%p, vt(0x%x)} \n", current, pHeap, *(size_t*)pHeap));
1013 pHeap->m_pJitManager->FreeCodeMemory(pHeap, current);
1014
1015 current = next;
1016 }
1017 m_pJumpStubCache->m_pBlocks = NULL;
1018
1019 delete m_pJumpStubCache;
1020 m_pJumpStubCache = NULL;
1021 }
1022
1023 if (m_managedResolver)
1024 {
1025 ::DestroyLongWeakHandle(m_managedResolver);
1026 m_managedResolver = NULL;
1027 }
1028
1029 m_DynamicMethodTable->LinkMethod(m_pDynamicMethod);
1030 }
1031}
1032
1033void LCGMethodResolver::FreeCompileTimeState()
1034{
1035 CONTRACTL {
1036 NOTHROW;
1037 GC_NOTRIGGER;
1038 MODE_ANY;
1039 } CONTRACTL_END;
1040
1041 //m_jitTempData.Delete();
1042}
1043
1044
1045
1046void LCGMethodResolver::GetJitContext(SecurityControlFlags * securityControlFlags,
1047 TypeHandle *typeOwner)
1048{
1049 CONTRACTL {
1050 STANDARD_VM_CHECK;
1051 PRECONDITION(CheckPointer(securityControlFlags));
1052 PRECONDITION(CheckPointer(typeOwner));
1053 } CONTRACTL_END;
1054
1055 GCX_COOP();
1056 GetJitContextCoop(securityControlFlags, typeOwner);
1057}
1058
1059void LCGMethodResolver::GetJitContextCoop(SecurityControlFlags * securityControlFlags,
1060 TypeHandle *typeOwner)
1061{
1062 CONTRACTL {
1063 THROWS;
1064 GC_TRIGGERS;
1065 MODE_COOPERATIVE;
1066 SO_INTOLERANT;
1067 INJECT_FAULT(COMPlusThrowOM(););
1068 PRECONDITION(CheckPointer(securityControlFlags));
1069 PRECONDITION(CheckPointer(typeOwner));
1070 } CONTRACTL_END;
1071
1072 MethodDescCallSite getJitContext(METHOD__RESOLVER__GET_JIT_CONTEXT, m_managedResolver);
1073
1074 OBJECTREF resolver = ObjectFromHandle(m_managedResolver);
1075 _ASSERTE(resolver); // gc root must be up the stack
1076
1077 ARG_SLOT args[] =
1078 {
1079 ObjToArgSlot(resolver),
1080 PtrToArgSlot(securityControlFlags),
1081 };
1082
1083 REFLECTCLASSBASEREF refType = (REFLECTCLASSBASEREF)getJitContext.Call_RetOBJECTREF(args);
1084 *typeOwner = refType != NULL ? refType->GetType() : TypeHandle();
1085
1086}
1087
1088ChunkAllocator* LCGMethodResolver::GetJitMetaHeap()
1089{
1090 LIMITED_METHOD_CONTRACT;
1091 return &m_jitMetaHeap;
1092}
1093
1094BYTE* LCGMethodResolver::GetCodeInfo(unsigned *pCodeSize, unsigned *pStackSize, CorInfoOptions *pOptions, unsigned *pEHSize)
1095{
1096 STANDARD_VM_CONTRACT;
1097
1098 _ASSERTE(pCodeSize);
1099
1100 if (!m_Code)
1101 {
1102 GCX_COOP();
1103
1104 LOG((LF_BCL, LL_INFO100000, "Level5 - DM-JIT: Getting CodeInfo on resolver 0x%p...\n", this));
1105 // get the code - Byte[] Resolver.GetCodeInfo(ref ushort stackSize, ref int EHCount)
1106 MethodDescCallSite getCodeInfo(METHOD__RESOLVER__GET_CODE_INFO, m_managedResolver);
1107
1108 OBJECTREF resolver = ObjectFromHandle(m_managedResolver);
1109 VALIDATEOBJECTREF(resolver); // gc root must be up the stack
1110
1111 DWORD initLocals = 0, EHSize = 0;
1112 unsigned short stackSize = 0;
1113 ARG_SLOT args[] =
1114 {
1115 ObjToArgSlot(resolver),
1116 PtrToArgSlot(&stackSize),
1117 PtrToArgSlot(&initLocals),
1118 PtrToArgSlot(&EHSize),
1119 };
1120 U1ARRAYREF dataArray = (U1ARRAYREF) getCodeInfo.Call_RetOBJECTREF(args);
1121 DWORD codeSize = dataArray->GetNumComponents();
1122 NewHolder<BYTE> code(new BYTE[codeSize]);
1123 memcpy(code, dataArray->GetDataPtr(), codeSize);
1124 m_CodeSize = codeSize;
1125 _ASSERTE(FitsIn<unsigned short>(stackSize));
1126 m_StackSize = static_cast<unsigned short>(stackSize);
1127 m_Options = (initLocals) ? CORINFO_OPT_INIT_LOCALS : (CorInfoOptions)0;
1128 _ASSERTE(FitsIn<unsigned short>(EHSize));
1129 m_EHSize = static_cast<unsigned short>(EHSize);
1130 m_Code = (BYTE*)code;
1131 code.SuppressRelease();
1132 LOG((LF_BCL, LL_INFO100000, "Level5 - DM-JIT: CodeInfo {0x%p} on resolver %p\n", m_Code, this));
1133 }
1134
1135 *pCodeSize = m_CodeSize;
1136 if (pStackSize)
1137 *pStackSize = m_StackSize;
1138 if (pOptions)
1139 *pOptions = m_Options;
1140 if (pEHSize)
1141 *pEHSize = m_EHSize;
1142 return m_Code;
1143
1144}
1145
1146//---------------------------------------------------------------------------------------
1147//
1148SigPointer
1149LCGMethodResolver::GetLocalSig()
1150{
1151 STANDARD_VM_CONTRACT;
1152
1153 if (m_LocalSig.IsNull())
1154 {
1155 GCX_COOP();
1156
1157 LOG((LF_BCL, LL_INFO100000, "Level5 - DM-JIT: Getting LocalSig on resolver 0x%p...\n", this));
1158
1159 MethodDescCallSite getLocalsSignature(METHOD__RESOLVER__GET_LOCALS_SIGNATURE, m_managedResolver);
1160
1161 OBJECTREF resolver = ObjectFromHandle(m_managedResolver);
1162 VALIDATEOBJECTREF(resolver); // gc root must be up the stack
1163
1164 ARG_SLOT args[] =
1165 {
1166 ObjToArgSlot(resolver)
1167 };
1168 U1ARRAYREF dataArray = (U1ARRAYREF) getLocalsSignature.Call_RetOBJECTREF(args);
1169 DWORD localSigSize = dataArray->GetNumComponents();
1170 NewHolder<COR_SIGNATURE> localSig(new COR_SIGNATURE[localSigSize]);
1171 memcpy((void *)localSig, dataArray->GetDataPtr(), localSigSize);
1172
1173 m_LocalSig = SigPointer((PCCOR_SIGNATURE)localSig, localSigSize);
1174 localSig.SuppressRelease();
1175 LOG((LF_BCL, LL_INFO100000, "Level5 - DM-JIT: LocalSig {0x%p} on resolver %p\n", m_LocalSig.GetPtr(), this));
1176 }
1177
1178 return m_LocalSig;
1179} // LCGMethodResolver::GetLocalSig
1180
1181//---------------------------------------------------------------------------------------
1182//
1183OBJECTHANDLE
1184LCGMethodResolver::ConstructStringLiteral(mdToken metaTok)
1185{
1186 STANDARD_VM_CONTRACT;
1187
1188 GCX_COOP();
1189
1190 OBJECTHANDLE string = NULL;
1191 STRINGREF strRef = GetStringLiteral(metaTok);
1192
1193 GCPROTECT_BEGIN(strRef);
1194
1195 if (strRef != NULL)
1196 {
1197 // Instead of storing the string literal in the appdomain specific string literal map,
1198 // we store it in the dynamic method specific string liternal list
1199 // This way we can release it when the dynamic method is collected.
1200 string = (OBJECTHANDLE)GetOrInternString(&strRef);
1201 }
1202
1203 GCPROTECT_END();
1204
1205 return string;
1206}
1207
1208//---------------------------------------------------------------------------------------
1209//
1210BOOL
1211LCGMethodResolver::IsValidStringRef(mdToken metaTok)
1212{
1213 STANDARD_VM_CONTRACT;
1214
1215 GCX_COOP();
1216
1217 return GetStringLiteral(metaTok) != NULL;
1218}
1219
1220//---------------------------------------------------------------------------------------
1221//
1222STRINGREF
1223LCGMethodResolver::GetStringLiteral(
1224 mdToken token)
1225{
1226 CONTRACTL {
1227 THROWS;
1228 GC_TRIGGERS;
1229 MODE_COOPERATIVE;
1230 } CONTRACTL_END;
1231
1232 MethodDescCallSite getStringLiteral(METHOD__RESOLVER__GET_STRING_LITERAL, m_managedResolver);
1233
1234 OBJECTREF resolver = ObjectFromHandle(m_managedResolver);
1235 VALIDATEOBJECTREF(resolver); // gc root must be up the stack
1236
1237 ARG_SLOT args[] = {
1238 ObjToArgSlot(resolver),
1239 token,
1240 };
1241 return getStringLiteral.Call_RetSTRINGREF(args);
1242}
1243
1244// This method will get the interned string by calling GetInternedString on the
1245// global string liternal interning map. It will also store the returned entry
1246// in m_DynamicStringLiterals
1247STRINGREF* LCGMethodResolver::GetOrInternString(STRINGREF *pProtectedStringRef)
1248{
1249 CONTRACTL {
1250 THROWS;
1251 GC_TRIGGERS;
1252 MODE_COOPERATIVE;
1253 PRECONDITION(CheckPointer(pProtectedStringRef));
1254 } CONTRACTL_END;
1255
1256 // Get the global string literal interning map
1257 GlobalStringLiteralMap* pStringLiteralMap = SystemDomain::GetGlobalStringLiteralMap();
1258
1259 // Calculating the hash: EEUnicodeHashTableHelper::GetHash
1260 EEStringData StringData = EEStringData((*pProtectedStringRef)->GetStringLength(), (*pProtectedStringRef)->GetBuffer());
1261 DWORD dwHash = pStringLiteralMap->GetHash(&StringData);
1262
1263 // lock the global string literal interning map
1264 CrstHolder gch(pStringLiteralMap->GetHashTableCrstGlobal());
1265
1266 StringLiteralEntryHolder pEntry(pStringLiteralMap->GetInternedString(pProtectedStringRef, dwHash, /* bAddIfNotFound */ TRUE));
1267
1268 DynamicStringLiteral* pStringLiteral = (DynamicStringLiteral*)m_jitTempData.New(sizeof(DynamicStringLiteral));
1269 pStringLiteral->m_pEntry = pEntry.Extract();
1270
1271 // Add to m_DynamicStringLiterals:
1272 // we don't need to check for duplicate because the string literal entries in
1273 // the global string literal map are ref counted.
1274 pStringLiteral->m_pNext = m_DynamicStringLiterals;
1275 m_DynamicStringLiterals = pStringLiteral;
1276
1277 return pStringLiteral->m_pEntry->GetStringObject();
1278
1279}
1280
1281// AddToUsedIndCellList adds a IndCellList link to the beginning of m_UsedIndCellList. It is called by
1282// code:CEEInfo::getCallInfo when a indirection cell is allocated for m_pDynamicMethod.
1283// All the indirection cells usded by m_pDynamicMethod will be recycled when this resolver
1284// is finalized, see code:LCGMethodResolver::RecycleIndCells
1285void LCGMethodResolver::AddToUsedIndCellList(BYTE * indcell)
1286{
1287 CONTRACTL {
1288 STANDARD_VM_CHECK;
1289 PRECONDITION(CheckPointer(indcell));
1290 } CONTRACTL_END;
1291
1292 IndCellList * link = (IndCellList *)m_jitTempData.New(sizeof(IndCellList));
1293 link->indcell = indcell;
1294
1295 // Insert into m_UsedIndCellList
1296 while (true)
1297 {
1298 link->pNext = m_UsedIndCellList;
1299 if (InterlockedCompareExchangeT(&m_UsedIndCellList, link, link->pNext) == link->pNext)
1300 break;
1301 }
1302
1303}
1304
1305void LCGMethodResolver::ResolveToken(mdToken token, TypeHandle * pTH, MethodDesc ** ppMD, FieldDesc ** ppFD)
1306{
1307 STANDARD_VM_CONTRACT;
1308
1309 GCX_COOP();
1310
1311 PREPARE_SIMPLE_VIRTUAL_CALLSITE(METHOD__RESOLVER__RESOLVE_TOKEN, ObjectFromHandle(m_managedResolver));
1312
1313 DECLARE_ARGHOLDER_ARRAY(args, 5);
1314
1315 args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(ObjectFromHandle(m_managedResolver));
1316 args[ARGNUM_1] = DWORD_TO_ARGHOLDER(token);
1317 args[ARGNUM_2] = pTH;
1318 args[ARGNUM_3] = ppMD;
1319 args[ARGNUM_4] = ppFD;
1320
1321 CALL_MANAGED_METHOD_NORET(args);
1322
1323 _ASSERTE(*ppMD == NULL || *ppFD == NULL);
1324
1325 if (pTH->IsNull())
1326 {
1327 if (*ppMD != NULL) *pTH = (*ppMD)->GetMethodTable();
1328 else
1329 if (*ppFD != NULL) *pTH = (*ppFD)->GetEnclosingMethodTable();
1330 }
1331
1332 _ASSERTE(!pTH->IsNull());
1333}
1334
1335//---------------------------------------------------------------------------------------
1336//
1337SigPointer
1338LCGMethodResolver::ResolveSignature(
1339 mdToken token)
1340{
1341 STANDARD_VM_CONTRACT;
1342
1343 GCX_COOP();
1344
1345 U1ARRAYREF dataArray = NULL;
1346
1347 PREPARE_SIMPLE_VIRTUAL_CALLSITE(METHOD__RESOLVER__RESOLVE_SIGNATURE, ObjectFromHandle(m_managedResolver));
1348
1349 DECLARE_ARGHOLDER_ARRAY(args, 3);
1350
1351 args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(ObjectFromHandle(m_managedResolver));
1352 args[ARGNUM_1] = DWORD_TO_ARGHOLDER(token);
1353 args[ARGNUM_2] = DWORD_TO_ARGHOLDER(0);
1354
1355 CALL_MANAGED_METHOD_RETREF(dataArray, U1ARRAYREF, args);
1356
1357 if (dataArray == NULL)
1358 COMPlusThrow(kInvalidProgramException);
1359
1360 DWORD cbSig = dataArray->GetNumComponents();
1361 PCCOR_SIGNATURE pSig = (PCCOR_SIGNATURE)m_jitTempData.New(cbSig);
1362 memcpy((void *)pSig, dataArray->GetDataPtr(), cbSig);
1363 return SigPointer(pSig, cbSig);
1364} // LCGMethodResolver::ResolveSignature
1365
1366//---------------------------------------------------------------------------------------
1367//
1368SigPointer
1369LCGMethodResolver::ResolveSignatureForVarArg(
1370 mdToken token)
1371{
1372 STANDARD_VM_CONTRACT;
1373
1374 GCX_COOP();
1375
1376 U1ARRAYREF dataArray = NULL;
1377
1378 PREPARE_SIMPLE_VIRTUAL_CALLSITE(METHOD__RESOLVER__RESOLVE_SIGNATURE, ObjectFromHandle(m_managedResolver));
1379
1380 DECLARE_ARGHOLDER_ARRAY(args, 3);
1381
1382 args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(ObjectFromHandle(m_managedResolver));
1383 args[ARGNUM_1] = DWORD_TO_ARGHOLDER(token);
1384 args[ARGNUM_2] = DWORD_TO_ARGHOLDER(1);
1385
1386 CALL_MANAGED_METHOD_RETREF(dataArray, U1ARRAYREF, args);
1387
1388 if (dataArray == NULL)
1389 COMPlusThrow(kInvalidProgramException);
1390
1391 DWORD cbSig = dataArray->GetNumComponents();
1392 PCCOR_SIGNATURE pSig = (PCCOR_SIGNATURE)m_jitTempData.New(cbSig);
1393 memcpy((void *)pSig, dataArray->GetDataPtr(), cbSig);
1394 return SigPointer(pSig, cbSig);
1395} // LCGMethodResolver::ResolveSignatureForVarArg
1396
1397//---------------------------------------------------------------------------------------
1398//
1399void LCGMethodResolver::GetEHInfo(unsigned EHnumber, CORINFO_EH_CLAUSE* clause)
1400{
1401 STANDARD_VM_CONTRACT;
1402
1403 GCX_COOP();
1404
1405 // attempt to get the raw EHInfo first
1406 {
1407 U1ARRAYREF dataArray;
1408
1409 PREPARE_SIMPLE_VIRTUAL_CALLSITE(METHOD__RESOLVER__GET_RAW_EH_INFO, ObjectFromHandle(m_managedResolver));
1410
1411 DECLARE_ARGHOLDER_ARRAY(args, 1);
1412
1413 args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(ObjectFromHandle(m_managedResolver));
1414
1415 CALL_MANAGED_METHOD_RETREF(dataArray, U1ARRAYREF, args);
1416
1417 if (dataArray != NULL)
1418 {
1419 COR_ILMETHOD_SECT_EH* pEH = (COR_ILMETHOD_SECT_EH*)dataArray->GetDataPtr();
1420
1421 COR_ILMETHOD_SECT_EH_CLAUSE_FAT ehClause;
1422 const COR_ILMETHOD_SECT_EH_CLAUSE_FAT* ehInfo;
1423 ehInfo = (COR_ILMETHOD_SECT_EH_CLAUSE_FAT*)pEH->EHClause(EHnumber, &ehClause);
1424
1425 clause->Flags = (CORINFO_EH_CLAUSE_FLAGS)ehInfo->GetFlags();
1426 clause->TryOffset = ehInfo->GetTryOffset();
1427 clause->TryLength = ehInfo->GetTryLength();
1428 clause->HandlerOffset = ehInfo->GetHandlerOffset();
1429 clause->HandlerLength = ehInfo->GetHandlerLength();
1430 clause->ClassToken = ehInfo->GetClassToken();
1431 clause->FilterOffset = ehInfo->GetFilterOffset();
1432 return;
1433 }
1434 }
1435
1436 // failed, get the info off the ilgenerator
1437 {
1438 PREPARE_SIMPLE_VIRTUAL_CALLSITE(METHOD__RESOLVER__GET_EH_INFO, ObjectFromHandle(m_managedResolver));
1439
1440 DECLARE_ARGHOLDER_ARRAY(args, 3);
1441
1442 args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(ObjectFromHandle(m_managedResolver));
1443 args[ARGNUM_1] = DWORD_TO_ARGHOLDER(EHnumber);
1444 args[ARGNUM_2] = PTR_TO_ARGHOLDER(clause);
1445
1446 CALL_MANAGED_METHOD_NORET(args);
1447 }
1448}
1449
1450#endif // !DACCESS_COMPILE
1451
1452
1453// Get the associated managed resolver. This method will be called during a GC so it should not throw, trigger a GC or cause the
1454// object in question to be validated.
1455OBJECTREF LCGMethodResolver::GetManagedResolver()
1456{
1457 LIMITED_METHOD_CONTRACT;
1458 return ObjectFromHandle(m_managedResolver);
1459}
1460
1461
1462//
1463// ChunkAllocator implementation
1464//
1465ChunkAllocator::~ChunkAllocator()
1466{
1467 LIMITED_METHOD_CONTRACT;
1468 Delete();
1469}
1470
1471void ChunkAllocator::Delete()
1472{
1473 LIMITED_METHOD_CONTRACT;
1474 BYTE *next = NULL;
1475 LOG((LF_BCL, LL_INFO10, "Level1 - DM - Allocator [0x%p] - deleting...\n", this));
1476 while (m_pData)
1477 {
1478 LOG((LF_BCL, LL_INFO10, "Level1 - DM - Allocator [0x%p] - delete block {0x%p}\n", this, m_pData));
1479 next = ((BYTE**)m_pData)[0];
1480 delete[] m_pData;
1481 m_pData = next;
1482 }
1483}
1484
1485void* ChunkAllocator::New(size_t size)
1486{
1487 CONTRACTL
1488 {
1489 THROWS;
1490 GC_NOTRIGGER;
1491 SO_TOLERANT;
1492 MODE_ANY;
1493 }
1494 CONTRACTL_END;
1495 // We need to align it, otherwise we might get DataMisalignedException on IA64
1496 size = ALIGN_UP(size, sizeof(void *));
1497
1498 BYTE *pNewBlock = NULL;
1499 LOG((LF_BCL, LL_INFO100, "Level2 - DM - Allocator [0x%p] - allocation requested 0x%X, available 0x%X\n", this, size, (m_pData) ? ((size_t*)m_pData)[1] : 0));
1500 if (m_pData)
1501 {
1502 // we may have room available
1503 size_t available = ((size_t*)m_pData)[1];
1504 if (size <= available)
1505 {
1506 LOG((LF_BCL, LL_INFO100, "Level2 - DM - Allocator [0x%p] - reusing block {0x%p}\n", this, m_pData));
1507 ((size_t*)m_pData)[1] = available - size;
1508 pNewBlock = (m_pData + CHUNK_SIZE - available);
1509 LOG((LF_BCL, LL_INFO100, "Level2 - DM - Allocator [0x%p] - ptr -> 0x%p, available 0x%X\n", this, pNewBlock, ((size_t*)m_pData)[1]));
1510 return pNewBlock;
1511 }
1512 }
1513
1514 // no available - need to allocate a new buffer
1515 if (size + (sizeof(void*) * 2) < CHUNK_SIZE)
1516 {
1517 // make the allocation
1518 NewHolder<BYTE> newBlock(new BYTE[CHUNK_SIZE]);
1519 pNewBlock = (BYTE*)newBlock;
1520 ((size_t*)pNewBlock)[1] = CHUNK_SIZE - size - (sizeof(void*) * 2);
1521 LOG((LF_BCL, LL_INFO10, "Level1 - DM - Allocator [0x%p] - new block {0x%p}\n", this, pNewBlock));
1522 newBlock.SuppressRelease();
1523 }
1524 else
1525 {
1526 // request bigger than default size this is going to be a single block
1527 NewHolder<BYTE> newBlock(new BYTE[size + (sizeof(void*) * 2)]);
1528 pNewBlock = (BYTE*)newBlock;
1529 ((size_t*)pNewBlock)[1] = 0; // no available bytes left
1530 LOG((LF_BCL, LL_INFO10, "Level1 - DM - Allocator [0x%p] - new BIG block {0x%p}\n", this, pNewBlock));
1531 newBlock.SuppressRelease();
1532 }
1533
1534 // all we have left to do is to link the block.
1535 // We leave at the top the block with more bytes available
1536 if (m_pData)
1537 {
1538 if (((size_t*)pNewBlock)[1] > ((size_t*)m_pData)[1])
1539 {
1540 ((BYTE**)pNewBlock)[0] = m_pData;
1541 m_pData = pNewBlock;
1542 }
1543 else
1544 {
1545 ((BYTE**)pNewBlock)[0] = ((BYTE**)m_pData)[0];
1546 ((BYTE**)m_pData)[0] = pNewBlock;
1547 }
1548 }
1549 else
1550 {
1551 // this is the first allocation
1552 m_pData = pNewBlock;
1553 ((BYTE**)m_pData)[0] = NULL;
1554 }
1555
1556 pNewBlock += (sizeof(void*) * 2);
1557 LOG((LF_BCL, LL_INFO100, "Level2 - DM - Allocator [0x%p] - ptr -> 0x%p, available 0x%X\n", this, pNewBlock, ((size_t*)m_pData)[1]));
1558 return pNewBlock;
1559}
1560
1561