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// File: EnC.CPP
6//
7
8//
9// Handles EditAndContinue support in the EE
10// ===========================================================================
11
12
13#include "common.h"
14#include "dbginterface.h"
15#include "dllimport.h"
16#include "eeconfig.h"
17#include "excep.h"
18#include "stackwalk.h"
19
20#ifdef DACCESS_COMPILE
21#include "../debug/daccess/gcinterface.dac.h"
22#endif // DACCESS_COMPILE
23
24#ifdef EnC_SUPPORTED
25
26// can't get this on the helper thread at runtime in ResolveField, so make it static and get when add a field.
27#ifdef _DEBUG
28static int g_BreakOnEnCResolveField = -1;
29#endif
30
31#ifndef DACCESS_COMPILE
32
33
34// Module initialization occurs in two phases: the constructor phase and the Initialize phase.
35//
36// The constructor phase initializes just enough so that Destruct() can be safely called.
37// It cannot throw or fail.
38//
39EditAndContinueModule::EditAndContinueModule(Assembly *pAssembly, mdToken moduleRef, PEFile *file)
40 : Module(pAssembly, moduleRef, file)
41{
42 CONTRACTL
43 {
44 NOTHROW;
45 GC_TRIGGERS;
46 FORBID_FAULT;
47 }
48 CONTRACTL_END
49
50 LOG((LF_ENC,LL_INFO100,"EACM::ctor 0x%x\n", this));
51
52 m_applyChangesCount = CorDB_DEFAULT_ENC_FUNCTION_VERSION;
53}
54
55// Module initialization occurs in two phases: the constructor phase and the Initialize phase.
56//
57// The Initialize() phase completes the initialization after the constructor has run.
58// It can throw exceptions but whether it throws or succeeds, it must leave the Module
59// in a state where Destruct() can be safely called.
60//
61/*virtual*/
62void EditAndContinueModule::Initialize(AllocMemTracker *pamTracker)
63{
64 CONTRACTL
65 {
66 THROWS;
67 GC_TRIGGERS;
68 INJECT_FAULT(COMPlusThrowOM(););
69 }
70 CONTRACTL_END
71
72 LOG((LF_ENC,LL_INFO100,"EACM::Initialize 0x%x\n", this));
73 Module::Initialize(pamTracker);
74}
75
76// Called when the module is being destroyed (eg. AD unload time)
77void EditAndContinueModule::Destruct()
78{
79 LIMITED_METHOD_CONTRACT;
80 LOG((LF_ENC,LL_EVERYTHING,"EACM::Destruct 0x%x\n", this));
81
82 // Call the superclass's Destruct method...
83 Module::Destruct();
84}
85
86//---------------------------------------------------------------------------------------
87//
88// ApplyEditAndContinue - updates this module for an EnC
89//
90// Arguments:
91// cbDeltaMD - number of bytes pointed to by pDeltaMD
92// pDeltaMD - pointer to buffer holding the delta metadata
93// cbDeltaIL - number of bytes pointed to by pDeltaIL
94// pDeltaIL - pointer to buffer holding the delta IL
95//
96// Return Value:
97// S_OK on success.
98// if the edit fails for any reason, at any point in this function,
99// we are toasted, so return out and IDE will end debug session.
100//
101
102HRESULT EditAndContinueModule::ApplyEditAndContinue(
103 DWORD cbDeltaMD,
104 BYTE *pDeltaMD,
105 DWORD cbDeltaIL,
106 BYTE *pDeltaIL)
107{
108 CONTRACTL
109 {
110 THROWS;
111 GC_NOTRIGGER;
112 MODE_COOPERATIVE;
113 }
114 CONTRACTL_END;
115
116 // Update the module's EnC version number
117 ++m_applyChangesCount;
118
119 LOG((LF_ENC, LL_INFO100, "EACM::AEAC:\n"));
120
121#ifdef _DEBUG
122 // Debugging hook to optionally break when this method is called
123 static BOOL shouldBreak = -1;
124 if (shouldBreak == -1)
125 shouldBreak = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_EncApplyChanges);
126 if (shouldBreak > 0) {
127 _ASSERTE(!"EncApplyChanges");
128 }
129
130 // Debugging hook to dump out all edits to dmeta and dil files
131 static BOOL dumpChanges = -1;
132
133 if (dumpChanges == -1)
134
135 dumpChanges = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_EncDumpApplyChanges);
136
137 if (dumpChanges> 0) {
138 SString fn;
139 int ec;
140 fn.Printf(W("ApplyChanges.%d.dmeta"), m_applyChangesCount);
141 FILE *fp;
142 ec = _wfopen_s(&fp, fn.GetUnicode(), W("wb"));
143 _ASSERTE(SUCCEEDED(ec));
144 fwrite(pDeltaMD, 1, cbDeltaMD, fp);
145 fclose(fp);
146 fn.Printf(W("ApplyChanges.%d.dil"), m_applyChangesCount);
147 ec = _wfopen_s(&fp, fn.GetUnicode(), W("wb"));
148 _ASSERTE(SUCCEEDED(ec));
149 fwrite(pDeltaIL, 1, cbDeltaIL, fp);
150 fclose(fp);
151 }
152#endif
153
154 HRESULT hr = S_OK;
155 HENUMInternal enumENC;
156
157 BYTE *pLocalILMemory = NULL;
158 IMDInternalImport *pMDImport = NULL;
159 IMDInternalImport *pNewMDImport = NULL;
160
161 CONTRACT_VIOLATION(GCViolation); // SafeComHolder goes to preemptive mode, which will trigger a GC
162 SafeComHolder<IMDInternalImportENC> pIMDInternalImportENC;
163 SafeComHolder<IMetaDataEmit> pEmitter;
164
165 // Apply the changes. Note that ApplyEditAndContinue() requires read/write metadata. If the metadata is
166 // not already RW, then ApplyEditAndContinue() will perform the conversion, invalidate the current
167 // metadata importer, and return us a new one. We can't let that happen. Other parts of the system are
168 // already using the current metadata importer, some possibly in preemptive GC mode at this very moment.
169 // Instead, we ensure that the metadata is RW by calling ConvertMDInternalToReadWrite(), which will make
170 // a new importer if necessary and ensure that new accesses to the metadata use that while still managing
171 // the lifetime of the old importer. Therefore, we can be sure that ApplyEditAndContinue() won't need to
172 // make a new importer.
173
174 // Ensure the metadata is RW.
175 EX_TRY
176 {
177 // ConvertMetadataToRWForEnC should only ever be called on EnC capable files.
178 _ASSERTE(IsEditAndContinueCapable()); // this also checks that the file is EnC capable
179 GetFile()->ConvertMetadataToRWForEnC();
180 }
181 EX_CATCH_HRESULT(hr);
182
183 IfFailGo(hr);
184
185 // Grab the current importer.
186 pMDImport = GetMDImport();
187
188 // Apply the EnC delta to this module's metadata.
189 IfFailGo(pMDImport->ApplyEditAndContinue(pDeltaMD, cbDeltaMD, &pNewMDImport));
190
191 // The importer should not have changed! We assert that, and back-stop in a retail build just to be sure.
192 if (pNewMDImport != pMDImport)
193 {
194 _ASSERTE( !"ApplyEditAndContinue should not have needed to create a new metadata importer!" );
195 IfFailGo(CORDBG_E_ENC_INTERNAL_ERROR);
196 }
197
198 // get the delta interface
199 IfFailGo(pMDImport->QueryInterface(IID_IMDInternalImportENC, (void **)&pIMDInternalImportENC));
200
201 // get an emitter interface
202 IfFailGo(GetMetaDataPublicInterfaceFromInternal(pMDImport, IID_IMetaDataEmit, (void **)&pEmitter));
203
204 // Copy the deltaIL into our RVAable IL memory
205 pLocalILMemory = new BYTE[cbDeltaIL];
206 memcpy(pLocalILMemory, pDeltaIL, cbDeltaIL);
207
208 // Enumerate all of the EnC delta tokens
209 memset(&enumENC, 0, sizeof(HENUMInternal));
210 IfFailGo(pIMDInternalImportENC->EnumDeltaTokensInit(&enumENC));
211
212 mdToken token;
213 while (pIMDInternalImportENC->EnumNext(&enumENC, &token))
214 {
215 STRESS_LOG3(LF_ENC, LL_INFO100, "EACM::AEAC: updated token 0x%x; type 0x%x; rid 0x%x\n", token, TypeFromToken(token), RidFromToken(token));
216
217 switch (TypeFromToken(token))
218 {
219 case mdtMethodDef:
220
221 // MethodDef token - update/add a method
222 LOG((LF_ENC, LL_INFO10000, "EACM::AEAC: Found method 0x%x\n", token));
223
224 ULONG dwMethodRVA;
225 DWORD dwMethodFlags;
226 IfFailGo(pMDImport->GetMethodImplProps(token, &dwMethodRVA, &dwMethodFlags));
227
228 if (dwMethodRVA >= cbDeltaIL)
229 {
230 LOG((LF_ENC, LL_INFO10000, "EACM::AEAC: failure RVA of %d with cbDeltaIl %d\n", dwMethodRVA, cbDeltaIL));
231 IfFailGo(E_INVALIDARG);
232 }
233
234 SetDynamicIL(token, (TADDR)(pLocalILMemory + dwMethodRVA), FALSE);
235
236 // use module to resolve to method
237 MethodDesc *pMethod;
238 pMethod = LookupMethodDef(token);
239 if (pMethod)
240 {
241 // Method exists already - update it
242 IfFailGo(UpdateMethod(pMethod));
243 }
244 else
245 {
246 // This is a new method token - create a new method
247 IfFailGo(AddMethod(token));
248 }
249
250 break;
251
252 case mdtFieldDef:
253
254 // FieldDef token - add a new field
255 LOG((LF_ENC, LL_INFO10000, "EACM::AEAC: Found field 0x%x\n", token));
256
257 if (LookupFieldDef(token))
258 {
259 // Field already exists - just ignore for now
260 continue;
261 }
262
263 // Field is new - add it
264 IfFailGo(AddField(token));
265 break;
266
267 case mdtTypeRef:
268 EnsureTypeRefCanBeStored(token);
269 break;
270
271 case mdtAssemblyRef:
272 EnsureAssemblyRefCanBeStored(token);
273 break;
274 }
275 }
276
277ErrExit:
278 if (pIMDInternalImportENC)
279 pIMDInternalImportENC->EnumClose(&enumENC);
280
281 return hr;
282}
283
284//---------------------------------------------------------------------------------------
285//
286// UpdateMethod - called when a method has been updated by EnC.
287//
288// The module's metadata has already been updated. Here we notify the
289// debugger of the update, and swap the new IL in as the current
290// version of the method.
291//
292// Arguments:
293// pMethod - the method being updated
294//
295// Return Value:
296// S_OK on success.
297// if the edit fails for any reason, at any point in this function,
298// we are toasted, so return out and IDE will end debug session.
299//
300// Assumptions:
301// The CLR must be suspended for debugging.
302//
303HRESULT EditAndContinueModule::UpdateMethod(MethodDesc *pMethod)
304{
305 CONTRACTL
306 {
307 THROWS;
308 GC_NOTRIGGER;
309 MODE_COOPERATIVE;
310 }
311 CONTRACTL_END;
312
313 // Notify the debugger of the update
314 HRESULT hr = g_pDebugInterface->UpdateFunction(pMethod, m_applyChangesCount);
315 if (FAILED(hr))
316 {
317 return hr;
318 }
319
320 // Notify the JIT that we've got new IL for this method
321 // This will ensure that all new calls to the method will go to the new version.
322 // The runtime does this by never backpatching the methodtable slots in EnC-enabled modules.
323 LOG((LF_ENC, LL_INFO100000, "EACM::UM: Updating function %s to version %d\n", pMethod->m_pszDebugMethodName, m_applyChangesCount));
324
325 // Reset any flags relevant to the old code
326 //
327 // Note that this only works since we've very carefullly made sure that _all_ references
328 // to the Method's code must be to the call/jmp blob immediately in front of the
329 // MethodDesc itself. See MethodDesc::IsEnCMethod()
330 //
331 pMethod->Reset();
332
333 return S_OK;
334}
335
336//---------------------------------------------------------------------------------------
337//
338// AddMethod - called when a new method is added by EnC.
339//
340// The module's metadata has already been updated. Here we notify the
341// debugger of the update, and create and add a new MethodDesc to the class.
342//
343// Arguments:
344// token - methodDef token for the method being added
345//
346// Return Value:
347// S_OK on success.
348// if the edit fails for any reason, at any point in this function,
349// we are toasted, so return out and IDE will end debug session.
350//
351// Assumptions:
352// The CLR must be suspended for debugging.
353//
354HRESULT EditAndContinueModule::AddMethod(mdMethodDef token)
355{
356 CONTRACTL
357 {
358 THROWS;
359 GC_NOTRIGGER;
360 MODE_COOPERATIVE;
361 }
362 CONTRACTL_END;
363
364 mdTypeDef parentTypeDef;
365 HRESULT hr = GetMDImport()->GetParentToken(token, &parentTypeDef);
366 if (FAILED(hr))
367 {
368 LOG((LF_ENC, LL_INFO100, "**Error** EnCModule::AM can't find parent token for method token %p\n", token));
369 return E_FAIL;
370 }
371
372 // see if the class is loaded yet.
373 MethodTable * pParentType = LookupTypeDef(parentTypeDef).AsMethodTable();
374 if (pParentType == NULL)
375 {
376 // Class isn't loaded yet, don't have to modify any existing EE data structures beyond the metadata.
377 // Just notify debugger and return.
378 LOG((LF_ENC, LL_INFO100, "EnCModule::AM class %p not loaded, our work is done\n", parentTypeDef));
379 hr = g_pDebugInterface->UpdateNotYetLoadedFunction(token, this, m_applyChangesCount);
380 return hr;
381 }
382
383 // Add the method to the runtime's Class data structures
384 LOG((LF_ENC, LL_INFO100000, "EACM::AM: Adding function %p\n", token));
385 MethodDesc *pMethod = NULL;
386 hr = EEClass::AddMethod(pParentType, token, 0, &pMethod);
387
388 if (FAILED(hr))
389 {
390 _ASSERTE(!"Failed to add function");
391 LOG((LF_ENC, LL_INFO100000, "**Error** EACM::AM: Failed to add function %p with hr 0x%x\n", token));
392 return hr;
393 }
394
395 // Tell the debugger about the new method so it get's the version number properly
396 hr = g_pDebugInterface->AddFunction(pMethod, m_applyChangesCount);
397 if (FAILED(hr))
398 {
399 _ASSERTE(!"Failed to add function");
400 LOG((LF_ENC, LL_INFO100000, "**Error** EACM::AF: Failed to add method %p to debugger with hr 0x%x\n", token));
401 }
402
403 return hr;
404}
405
406//---------------------------------------------------------------------------------------
407//
408// AddField - called when a new field is added by EnC.
409//
410// The module's metadata has already been updated. Here we notify the
411// debugger of the update,
412//
413// Arguments:
414// token - fieldDef for the field being added
415//
416// Return Value:
417// S_OK on success.
418// if the edit fails for any reason, at any point in this function,
419// we are toasted, so return out and IDE will end debug session.
420//
421// Assumptions:
422// The CLR must be suspended for debugging.
423//
424HRESULT EditAndContinueModule::AddField(mdFieldDef token)
425{
426 CONTRACTL
427 {
428 THROWS;
429 GC_NOTRIGGER;
430 MODE_COOPERATIVE;
431 }
432 CONTRACTL_END;
433
434 mdTypeDef parentTypeDef;
435 HRESULT hr = GetMDImport()->GetParentToken(token, &parentTypeDef);
436
437 if (FAILED(hr))
438 {
439 LOG((LF_ENC, LL_INFO100, "**Error** EnCModule::AF can't find parent token for field token %p\n", token));
440 return E_FAIL;
441 }
442
443 // see if the class is loaded yet. If not we don't need to do anything. When this class is
444 // loaded (with the updated metadata), it will have this field like any other normal field.
445 // If the class hasn't been loaded, than the debugger shouldn't know anything about it
446 // so there shouldn't be any harm in not notifying it of the update. For completeness,
447 // we may want to consider changing this to notify the debugger here as well.
448 MethodTable * pParentType = LookupTypeDef(parentTypeDef).AsMethodTable();
449 if (pParentType == NULL)
450 {
451 LOG((LF_ENC, LL_INFO100, "EnCModule::AF class %p not loaded, our work is done\n", parentTypeDef));
452 return S_OK;
453 }
454
455 // Create a new EnCFieldDesc for the field and add it to the class
456 LOG((LF_ENC, LL_INFO100000, "EACM::AM: Adding field %p\n", token));
457 EnCFieldDesc *pField;
458 hr = EEClass::AddField(pParentType, token, &pField);
459
460 if (FAILED(hr))
461 {
462 LOG((LF_ENC, LL_INFO100000, "**Error** EACM::AF: Failed to add field %p to EE with hr 0x%x\n", token));
463 return hr;
464 }
465
466 // Tell the debugger about the new field
467 hr = g_pDebugInterface->AddField(pField, m_applyChangesCount);
468 if (FAILED(hr))
469 {
470 LOG((LF_ENC, LL_INFO100000, "**Error** EACM::AF: Failed to add field %p to debugger with hr 0x%x\n", token));
471 }
472
473#ifdef _DEBUG
474 if (g_BreakOnEnCResolveField == -1)
475 {
476 g_BreakOnEnCResolveField = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_EnCResolveField);
477 }
478#endif
479
480 return hr;
481}
482
483//---------------------------------------------------------------------------------------
484//
485// JitUpdatedFunction - Jit the new version of a function for EnC.
486//
487// Arguments:
488// pMD - the MethodDesc for the method we want to JIT
489// pOrigContext - context of thread pointing into original version of the function
490//
491// Return value:
492// Return the address of the newly jitted code or NULL on failure.
493//
494PCODE EditAndContinueModule::JitUpdatedFunction( MethodDesc *pMD,
495 CONTEXT *pOrigContext)
496{
497 CONTRACTL
498 {
499 NOTHROW;
500 GC_TRIGGERS;
501 MODE_ANY;
502 }
503 CONTRACTL_END;
504
505 LOG((LF_ENC, LL_INFO100, "EnCModule::JitUpdatedFunction for %s\n",
506 pMD->m_pszDebugMethodName));
507
508 PCODE jittedCode = NULL;
509
510 GCX_COOP();
511
512#ifdef _DEBUG
513 BOOL shouldBreak = CLRConfig::GetConfigValue(
514 CLRConfig::INTERNAL_EncJitUpdatedFunction);
515 if (shouldBreak > 0) {
516 _ASSERTE(!"EncJitUpdatedFunction");
517 }
518#endif
519
520 // Setup a frame so that has context for the exception
521 // so that gc can crawl the stack and do the right thing.
522 _ASSERTE(pOrigContext);
523 Thread *pCurThread = GetThread();
524 _ASSERTE(pCurThread);
525 FrameWithCookie<ResumableFrame> resFrame(pOrigContext);
526 resFrame.Push(pCurThread);
527
528 CONTEXT *pCtxTemp = NULL;
529 // We need to zero out the filter context so a multi-threaded GC doesn't result
530 // in somebody else tracing this thread & concluding that we're in JITted code.
531 // We need to remove the filter context so that if we're in preemptive GC
532 // mode, we'll either have the filter context, or the ResumableFrame,
533 // but not both, set.
534 // Since we're in cooperative mode here, we can swap the two non-atomically here.
535 pCtxTemp = pCurThread->GetFilterContext();
536 _ASSERTE(pCtxTemp != NULL); // currently called from within a filter context, protects us during GC-toggle.
537 pCurThread->SetFilterContext(NULL);
538
539 // get the code address (may jit the fcn if not already jitted)
540 EX_TRY {
541 if (!pMD->IsPointingToNativeCode())
542 {
543 GCX_PREEMP();
544 pMD->DoPrestub(NULL);
545 LOG((LF_ENC, LL_INFO100, "EnCModule::ResumeInUpdatedFunction JIT successful\n"));
546 }
547 else
548 {
549 LOG((LF_ENC, LL_INFO100, "EnCModule::ResumeInUpdatedFunction function already JITed\n"));
550 }
551 jittedCode = pMD->GetNativeCode();
552 } EX_CATCH {
553#ifdef _DEBUG
554 {
555 // This is debug-only code to print out the error string, but SString can throw.
556 // This function is no-throw, and we can't put an EX_TRY inside an EX_CATCH block, so
557 // we just have the violation.
558 CONTRACT_VIOLATION(ThrowsViolation);
559
560 StackSString exceptionMessage;
561 SString errorMessage;
562 GetExceptionMessage(GET_THROWABLE(), exceptionMessage);
563 errorMessage.AppendASCII("**Error: Probable rude edit.**\n\n"
564 "EnCModule::JITUpdatedFunction JIT failed with the following exception:\n\n");
565 errorMessage.Append(exceptionMessage);
566 StackScratchBuffer buffer;
567 DbgAssertDialog(__FILE__, __LINE__, errorMessage.GetANSI(buffer));
568 LOG((LF_ENC, LL_INFO100, errorMessage.GetANSI(buffer)));
569 }
570#endif
571 } EX_END_CATCH(SwallowAllExceptions)
572
573 resFrame.Pop(pCurThread);
574
575 // Restore the filter context here (see comment above)
576 pCurThread->SetFilterContext(pCtxTemp);
577
578 return jittedCode;
579}
580
581
582//-----------------------------------------------------------------------------
583// Called by EnC to resume the code in a new version of the function.
584// This will:
585// 1) jit the new function
586// 2) set the IP to newILOffset within that new function
587// 3) adjust local variables (particularly enregistered vars) to the new func.
588// It will not return.
589//
590// Params:
591// pMD - method desc for method being updated. This is not enc-version aware.
592// oldDebuggerFuncHandle - Debugger DJI to uniquely identify old function.
593// This is enc-version aware.
594// newILOffset - the IL offset to resume execution at within the new function.
595// pOrigContext - context of thread pointing into original version of the function.
596//
597// This function must be called on the thread that's executing the old function.
598// This function does not return. Instead, it will remap this thread directly
599// to be executing the new function.
600//-----------------------------------------------------------------------------
601HRESULT EditAndContinueModule::ResumeInUpdatedFunction(
602 MethodDesc *pMD,
603 void *oldDebuggerFuncHandle,
604 SIZE_T newILOffset,
605 CONTEXT *pOrigContext)
606{
607 LOG((LF_ENC, LL_INFO100, "EnCModule::ResumeInUpdatedFunction for %s at IL offset 0x%x, ",
608 pMD->m_pszDebugMethodName, newILOffset));
609
610#ifdef _DEBUG
611 BOOL shouldBreak = CLRConfig::GetConfigValue(
612 CLRConfig::INTERNAL_EncResumeInUpdatedFunction);
613 if (shouldBreak > 0) {
614 _ASSERTE(!"EncResumeInUpdatedFunction");
615 }
616#endif
617
618 HRESULT hr = E_FAIL;
619
620 // JIT-compile the updated version of the method
621 PCODE jittedCode = JitUpdatedFunction(pMD, pOrigContext);
622 if ( jittedCode == NULL )
623 return CORDBG_E_ENC_JIT_CANT_UPDATE;
624
625 GCX_COOP();
626
627 // This will create a new frame and copy old vars to it
628 // need pointer to old & new code, old & new info
629
630 EECodeInfo oldCodeInfo(GetIP(pOrigContext));
631 _ASSERTE(oldCodeInfo.GetMethodDesc() == pMD);
632
633 // Get the new native offset & IP from the new IL offset
634 LOG((LF_ENC, LL_INFO10000, "EACM::RIUF: About to map IL forwards!\n"));
635 SIZE_T newNativeOffset = 0;
636 g_pDebugInterface->MapILInfoToCurrentNative(pMD,
637 newILOffset,
638 jittedCode,
639 &newNativeOffset);
640
641 EECodeInfo newCodeInfo(jittedCode + newNativeOffset);
642 _ASSERTE(newCodeInfo.GetMethodDesc() == pMD);
643
644 _ASSERTE(newCodeInfo.GetRelOffset() == newNativeOffset);
645
646 _ASSERTE(oldCodeInfo.GetCodeManager() == newCodeInfo.GetCodeManager());
647
648 DWORD oldFrameSize = oldCodeInfo.GetFixedStackSize();
649 DWORD newFrameSize = newCodeInfo.GetFixedStackSize();
650
651 // FixContextAndResume() will replace the old stack frame of the function with the new
652 // one and will initialize that new frame to null. Anything on the stack where that new
653 // frame sits will be wiped out. This could include anything on the stack right up to or beyond our
654 // current stack from in ResumeInUpdatedFunction. In order to prevent our current frame from being
655 // trashed we determine the maximum amount that the stack could grow by and allocate this as a buffer using
656 // alloca. Then we call FixContextAndResume which can safely rely on the stack because none of it's frames
657 // state or anything lower can be reached by the new frame.
658
659 if( newFrameSize > oldFrameSize)
660 {
661 DWORD frameIncrement = newFrameSize - oldFrameSize;
662 (void)alloca(frameIncrement);
663 }
664
665 // Ask the EECodeManager to actually fill in the context and stack for the new frame so that
666 // values of locals etc. are preserved.
667 LOG((LF_ENC, LL_INFO100, "EnCModule::ResumeInUpdatedFunction calling FixContextAndResume oldNativeOffset: 0x%x, newNativeOffset: 0x%x,"
668 "oldFrameSize: 0x%x, newFrameSize: 0x%x\n",
669 oldCodeInfo.GetRelOffset(), newCodeInfo.GetRelOffset(), oldFrameSize, newFrameSize));
670
671 FixContextAndResume(pMD,
672 oldDebuggerFuncHandle,
673 pOrigContext,
674 &oldCodeInfo,
675 &newCodeInfo);
676
677 // At this point we shouldn't have failed, so this is genuinely erroneous.
678 LOG((LF_ENC, LL_ERROR, "**Error** EnCModule::ResumeInUpdatedFunction returned from ResumeAtJit"));
679 _ASSERTE(!"Should not return from FixContextAndResume()");
680
681 hr = E_FAIL;
682
683 // If we fail for any reason we have already potentially trashed with new locals and we have also unwound any
684 // Win32 handlers on the stack so cannot ever return from this function.
685 EEPOLICY_HANDLE_FATAL_ERROR(CORDBG_E_ENC_INTERNAL_ERROR);
686}
687
688//---------------------------------------------------------------------------------------
689//
690// FixContextAndResume - Modify the thread context for EnC remap and resume execution
691//
692// Arguments:
693// pMD - MethodDesc for the method being remapped
694// oldDebuggerFuncHandle - Debugger DJI to uniquely identify old function.
695// pContext - the thread's original CONTEXT when the remap opportunity was hit
696// pOldCodeInfo - collection of various information about the current frame state
697// pNewCodeInfo - information about how we want the frame state to be after the remap
698//
699// Return Value:
700// Doesn't return
701//
702// Notes:
703// WARNING: This method cannot access any stack-data below its frame on the stack
704// (i.e. anything allocated in a caller frame), so all stack-based arguments must
705// EXPLICITLY be copied by value and this method cannot be inlined. We may need to expand
706// the stack frame to accomodate the new method, and so extra buffer space must have
707// been allocated on the stack. Note that passing a struct by value (via C++) is not
708// enough to ensure its data is really copied (on x64, large structs may internally be
709// passed by reference). Thus we explicitly make copies of structs passed in, at the
710// beginning.
711//
712
713NOINLINE void EditAndContinueModule::FixContextAndResume(
714 MethodDesc *pMD,
715 void *oldDebuggerFuncHandle,
716 T_CONTEXT *pContext,
717 EECodeInfo *pOldCodeInfo,
718 EECodeInfo *pNewCodeInfo)
719{
720 STATIC_CONTRACT_MODE_COOPERATIVE;
721 STATIC_CONTRACT_GC_TRIGGERS; // Sends IPC event
722 STATIC_CONTRACT_THROWS;
723
724 // Create local copies of all structs passed as arguments to prevent them from being overwritten
725 CONTEXT context;
726 memcpy(&context, pContext, sizeof(CONTEXT));
727 pContext = &context;
728
729#if defined(_TARGET_AMD64_)
730 // Since we made a copy of the incoming CONTEXT in context, clear any new flags we
731 // don't understand (like XSAVE), since we'll eventually be passing a CONTEXT based
732 // on this copy to RtlRestoreContext, and this copy doesn't have the extra info
733 // required by the XSAVE or other flags.
734 //
735 // FUTURE: No reason to ifdef this for amd64-only, except to make this late fix as
736 // surgical as possible. Would be nice to enable this on x86 early in the next cycle.
737 pContext->ContextFlags &= CONTEXT_ALL;
738#endif // defined(_TARGET_AMD64_)
739
740 EECodeInfo oldCodeInfo;
741 memcpy(&oldCodeInfo, pOldCodeInfo, sizeof(EECodeInfo));
742 pOldCodeInfo = &oldCodeInfo;
743
744 EECodeInfo newCodeInfo;
745 memcpy(&newCodeInfo, pNewCodeInfo, sizeof(EECodeInfo));
746 pNewCodeInfo = &newCodeInfo;
747
748 const ICorDebugInfo::NativeVarInfo *pOldVarInfo = NULL;
749 const ICorDebugInfo::NativeVarInfo *pNewVarInfo = NULL;
750 SIZE_T oldVarInfoCount = 0;
751 SIZE_T newVarInfoCount = 0;
752
753 // Get the var info which the codemanager will use for updating
754 // enregistered variables correctly, or variables whose lifetimes differ
755 // at the update point
756 g_pDebugInterface->GetVarInfo(pMD, oldDebuggerFuncHandle, &oldVarInfoCount, &pOldVarInfo);
757 g_pDebugInterface->GetVarInfo(pMD, NULL, &newVarInfoCount, &pNewVarInfo);
758
759#ifdef _TARGET_X86_
760 // save the frame pointer as FixContextForEnC might step on it.
761 LPVOID oldSP = dac_cast<PTR_VOID>(GetSP(pContext));
762
763 // need to pop the SEH records before write over the stack in FixContextForEnC
764 PopSEHRecords(oldSP);
765#endif
766
767 // Ask the EECodeManager to actually fill in the context and stack for the new frame so that
768 // values of locals etc. are preserved.
769 HRESULT hr = pNewCodeInfo->GetCodeManager()->FixContextForEnC(
770 pContext,
771 pOldCodeInfo,
772 pOldVarInfo, oldVarInfoCount,
773 pNewCodeInfo,
774 pNewVarInfo, newVarInfoCount);
775
776 // If FixContextForEnC succeeded, the stack is potentially trashed with any new locals and we have also unwound
777 // any Win32 handlers on the stack so cannot ever return from this function. If FixContextForEnC failed, can't
778 // assume that the stack is still intact so apply the proper policy for a fatal EE error to bring us down
779 // "gracefully" (it's all relative).
780 if (FAILED(hr))
781 {
782 LOG((LF_ENC, LL_INFO100, "**Error** EnCModule::ResumeInUpdatedFunction for FixContextForEnC failed\n"));
783 EEPOLICY_HANDLE_FATAL_ERROR(hr);
784 }
785
786 // Set the new IP
787 // Note that all we're really doing here is setting the IP register. We unfortunately don't
788 // share any code with the implementation of debugger SetIP, despite the similarities.
789 LOG((LF_ENC, LL_INFO100, "EnCModule::ResumeInUpdatedFunction: Resume at EIP=0x%x\n", pNewCodeInfo->GetCodeAddress()));
790
791 Thread *pCurThread = GetThread();
792 _ASSERTE(pCurThread);
793
794 pCurThread->SetFilterContext(pContext);
795 SetIP(pContext, pNewCodeInfo->GetCodeAddress());
796
797 // Notify the debugger that we're about to resume execution in the new version of the method
798 HRESULT hrIgnore = g_pDebugInterface->RemapComplete(pMD, pNewCodeInfo->GetCodeAddress(), pNewCodeInfo->GetRelOffset());
799
800 // Now jump into the new version of the method. Note that we can't just setup the filter context
801 // and return because we are potentially writing new vars onto the stack.
802 pCurThread->SetFilterContext( NULL );
803
804#if defined(_TARGET_X86_)
805 ResumeAtJit(pContext, oldSP);
806#else
807 RtlRestoreContext(pContext, NULL);
808#endif
809
810 // At this point we shouldn't have failed, so this is genuinely erroneous.
811 LOG((LF_ENC, LL_ERROR, "**Error** EnCModule::ResumeInUpdatedFunction returned from ResumeAtJit"));
812 _ASSERTE(!"Should not return from ResumeAtJit()");
813}
814#endif // #ifndef DACCESS_COMPILE
815
816//---------------------------------------------------------------------------------------
817// ResolveField - get a pointer to the value of a field that was added by EnC
818//
819// Arguments:
820// thisPointer - For instance fields, a pointer to the object instance of interest.
821// For static fields this is unused and should be NULL.
822// pFD - FieldDesc describing the field we're interested in
823// fAllocateNew - If storage doesn't yet exist for this field and fAllocateNew is true
824// then we will attempt to allocate the storage (throwing an exception
825// if it fails). Otherwise, if fAllocateNew is false, then we will just
826// return NULL when the storage is not yet available.
827//
828// Return Value:
829// If storage doesn't yet exist for this field we return NULL, otherwise, we return a pointer
830// to the contents of the field on success.
831//---------------------------------------------------------------------------------------
832PTR_CBYTE EditAndContinueModule::ResolveField(OBJECTREF thisPointer,
833 EnCFieldDesc * pFD)
834{
835 CONTRACTL
836 {
837 GC_NOTRIGGER;
838 NOTHROW;
839 SUPPORTS_DAC;
840 }
841 CONTRACTL_END;
842
843#ifdef _DEBUG
844 if (g_BreakOnEnCResolveField == 1)
845 {
846 _ASSERTE( !"EditAndContinueModule::ResolveField");
847 }
848#endif
849
850 // If it's static, we stash in the EnCFieldDesc
851 if (pFD->IsStatic())
852 {
853 _ASSERTE( thisPointer == NULL );
854 EnCAddedStaticField *pAddedStatic = pFD->GetStaticFieldData();
855 if (!pAddedStatic)
856 {
857 return NULL;
858 }
859
860 _ASSERTE( pAddedStatic->m_pFieldDesc == pFD );
861 return PTR_CBYTE(pAddedStatic->GetFieldData());
862 }
863
864 // not static so get it out of the syncblock
865 SyncBlock * pBlock = NULL;
866
867 // Get the SyncBlock, failing if not available
868 pBlock = thisPointer->PassiveGetSyncBlock();
869 if( pBlock == NULL )
870 {
871 return NULL;
872 }
873
874 EnCSyncBlockInfo * pEnCInfo = NULL;
875
876 // Attempt to get the EnC information from the sync block
877 pEnCInfo = pBlock->GetEnCInfo();
878
879 if (!pEnCInfo)
880 {
881 // No EnC info on this object yet, fail since we don't want to allocate it
882 return NULL;
883 }
884
885 // Lookup the actual field value from the EnCSyncBlockInfo
886 return pEnCInfo->ResolveField(thisPointer, pFD);
887} // EditAndContinueModule::ResolveField
888
889#ifndef DACCESS_COMPILE
890//---------------------------------------------------------------------------------------
891// ResolveOrAllocateField - get a pointer to the value of a field that was added by EnC,
892// allocating storage for it if necessary
893//
894// Arguments:
895// thisPointer - For instance fields, a pointer to the object instance of interest.
896// For static fields this is unused and should be NULL.
897// pFD - FieldDesc describing the field we're interested in
898// Return Value:
899// Returns a pointer to the contents of the field on success. This should only fail due
900// to out-of-memory and will therefore throw an OOM exception.
901//---------------------------------------------------------------------------------------
902PTR_CBYTE EditAndContinueModule::ResolveOrAllocateField(OBJECTREF thisPointer,
903 EnCFieldDesc * pFD)
904{
905 CONTRACTL
906 {
907 GC_TRIGGERS;
908 THROWS;
909 }
910 CONTRACTL_END;
911
912 // first try getting a pre-existing field
913 PTR_CBYTE fieldAddr = ResolveField(thisPointer, pFD);
914 if (fieldAddr != NULL)
915 {
916 return fieldAddr;
917 }
918
919 // we didn't find the field already allocated
920 if (pFD->IsStatic())
921 {
922 _ASSERTE(thisPointer == NULL);
923 EnCAddedStaticField * pAddedStatic = pFD->GetOrAllocateStaticFieldData();
924 _ASSERTE(pAddedStatic->m_pFieldDesc == pFD);
925 return PTR_CBYTE(pAddedStatic->GetFieldData());
926 }
927
928 // not static so get it out of the syncblock
929 SyncBlock* pBlock = NULL;
930
931 // Get the SyncBlock, creating it if necessary
932 pBlock = thisPointer->GetSyncBlock();
933
934 EnCSyncBlockInfo * pEnCInfo = NULL;
935
936 // Attempt to get the EnC information from the sync block
937 pEnCInfo = pBlock->GetEnCInfo();
938
939 if (!pEnCInfo)
940 {
941 // Attach new EnC field info to this object.
942 pEnCInfo = new EnCSyncBlockInfo;
943 if (!pEnCInfo)
944 {
945 COMPlusThrowOM();
946 }
947 pBlock->SetEnCInfo(pEnCInfo);
948 }
949
950 // Lookup the actual field value from the EnCSyncBlockInfo
951 return pEnCInfo->ResolveOrAllocateField(thisPointer, pFD);
952} // EditAndContinueModule::ResolveOrAllocateField
953
954#endif // !DACCESS_COMPILE
955
956//-----------------------------------------------------------------------------
957// Get or optionally create an EnCEEClassData object for the specified
958// EEClass in this module.
959//
960// Arguments:
961// pClass - the EEClass of interest
962// getOnly - if false (the default), we'll create a new entry of none exists yet
963//
964// Note: If called in a DAC build, GetOnly must be TRUE
965//
966PTR_EnCEEClassData EditAndContinueModule::GetEnCEEClassData(MethodTable * pMT, BOOL getOnly /*=FALSE*/ )
967{
968 CONTRACTL
969 {
970 NOTHROW;
971 GC_NOTRIGGER;
972 SUPPORTS_DAC;
973 } CONTRACTL_END;
974
975#ifdef DACCESS_COMPILE
976 _ASSERTE(getOnly == TRUE);
977#endif // DACCESS_COMPILE
978
979 DPTR(PTR_EnCEEClassData) ppData = m_ClassList.Table();
980 DPTR(PTR_EnCEEClassData) ppLast = ppData + m_ClassList.Count();
981
982 // Look for an existing entry for the specified class
983 while (ppData < ppLast)
984 {
985 PREFIX_ASSUME(ppLast != NULL);
986 if ((*ppData)->GetMethodTable() == pMT)
987 return *ppData;
988 ++ppData;
989 }
990
991 // No match found. Return now if we don't want to create a new entry
992 if (getOnly)
993 {
994 return NULL;
995 }
996
997#ifndef DACCESS_COMPILE
998 // Create a new entry and add it to the end our our table
999 EnCEEClassData *pNewData = (EnCEEClassData*)(void*)pMT->GetLoaderAllocator()->GetLowFrequencyHeap()->AllocMem_NoThrow(S_SIZE_T(sizeof(EnCEEClassData)));
1000 pNewData->Init(pMT);
1001 ppData = m_ClassList.Append();
1002 if (!ppData)
1003 return NULL;
1004 *ppData = pNewData;
1005 return pNewData;
1006#else
1007 DacNotImpl();
1008 return NULL;
1009#endif
1010}
1011
1012// Computes the address of this field within the object "o"
1013void *EnCFieldDesc::GetAddress( void *o)
1014{
1015#ifndef DACCESS_COMPILE
1016 CONTRACTL {
1017 THROWS;
1018 GC_TRIGGERS;
1019 } CONTRACTL_END;
1020
1021 // can't throw through FieldDesc::GetInstanceField if FORBIDGC_LOADER_USE_ENABLED
1022 _ASSERTE(! FORBIDGC_LOADER_USE_ENABLED());
1023
1024 EditAndContinueModule *pModule = (EditAndContinueModule*)GetModule();
1025 _ASSERTE(pModule->IsEditAndContinueEnabled());
1026
1027 // EnC added fields aren't just at some static offset in the object like normal fields
1028 // are. Get the EditAndContinueModule to compute the address for us.
1029 return (void *)pModule->ResolveOrAllocateField(ObjectToOBJECTREF((Object *)o), this);
1030#else
1031 DacNotImpl();
1032 return NULL;
1033#endif
1034}
1035
1036#ifndef DACCESS_COMPILE
1037
1038// Do simple field initialization
1039// We do this when the process is suspended for debugging (in a GC_NOTRIGGER).
1040// Full initialization will be done in Fixup when the process is running.
1041void EnCFieldDesc::Init(mdFieldDef token, BOOL fIsStatic)
1042{
1043 CONTRACTL
1044 {
1045 THROWS;
1046 GC_NOTRIGGER;
1047 MODE_COOPERATIVE;
1048 }
1049 CONTRACTL_END;
1050
1051 // Clear out the FieldDesc incase someone attempts to use any of the fields
1052 memset( this, 0, sizeof(EnCFieldDesc) );
1053
1054 // Initialize our members
1055 m_pStaticFieldData = NULL;
1056 m_bNeedsFixup = TRUE;
1057
1058 // Initialize the bare minimum of FieldDesc necessary for now
1059 if (fIsStatic)
1060 FieldDesc::m_isStatic = TRUE;
1061
1062 SetMemberDef(token);
1063
1064 SetEnCNew();
1065}
1066
1067// Allocate a new EnCAddedField instance and hook it up to hold the value for an instance
1068// field which was added by EnC to the specified object. This effectively adds a reference from
1069// the object to the new field value so that the field's lifetime is managed properly.
1070//
1071// Arguments:
1072// pFD - description of the field being added
1073// thisPointer - object instance to attach the new field to
1074//
1075EnCAddedField *EnCAddedField::Allocate(OBJECTREF thisPointer, EnCFieldDesc *pFD)
1076{
1077 CONTRACTL
1078 {
1079 THROWS;
1080 GC_TRIGGERS;
1081 MODE_COOPERATIVE;
1082 }
1083 CONTRACTL_END;
1084
1085 LOG((LF_ENC, LL_INFO1000, "\tEnCAF:Allocate for this %p, FD %p\n", thisPointer, pFD->GetMemberDef()));
1086
1087 // Create a new EnCAddedField instance
1088 EnCAddedField *pEntry = new EnCAddedField;
1089 pEntry->m_pFieldDesc = pFD;
1090
1091 AppDomain *pDomain = (AppDomain*) pFD->GetApproxEnclosingMethodTable()->GetDomain();
1092
1093 // We need to associate the contents of the new field with the object it is attached to
1094 // in a way that mimics the lifetime behavior of a normal field reference. Specifically,
1095 // when the object is collected, the field should also be collected (assuming there are no
1096 // other references), but references to the field shouldn't keep the object alive.
1097 // To achieve this, we have introduced the concept of a "dependent handle" which provides
1098 // the appropriate semantics. The dependent handle has a weak reference to a "primary object"
1099 // (the object getting a new field in this case), and a strong reference to a secondary object.
1100 // When the primary object is collected, the reference to the secondary object is released.
1101 // See the definition of code:HNDTYPE_DEPENDENT and code:Ref_ScanDependentHandles for more details.
1102 //
1103 // We create a helper object and store it as the secondary object in the dependant handle
1104 // so that its liveliness can be maintained along with the primary object.
1105 // The helper then contains an object reference to the real field value that we are adding.
1106 // The reason for doing this is that we cannot hand out the handle address for
1107 // the OBJECTREF address so we need to hand out something else that is hooked up to the handle.
1108
1109 GCPROTECT_BEGIN(thisPointer);
1110 MethodTable *pHelperMT = MscorlibBinder::GetClass(CLASS__ENC_HELPER);
1111 pEntry->m_FieldData = pDomain->CreateDependentHandle(thisPointer, AllocateObject(pHelperMT));
1112 GCPROTECT_END();
1113
1114 LOG((LF_ENC, LL_INFO1000, "\tEnCAF:Allocate created dependent handle %p\n",pEntry->m_FieldData));
1115
1116 // The EnC helper object stores a reference to the actual field value. For fields which are
1117 // reference types, this is simply a normal object reference so we don't need to do anything
1118 // special here.
1119
1120 if (pFD->GetFieldType() != ELEMENT_TYPE_CLASS)
1121 {
1122 // The field is a value type so we need to create storage on the heap to hold a boxed
1123 // copy of the value and have the helper's objectref point there.
1124
1125 OBJECTREF obj = NULL;
1126 if (pFD->IsByValue())
1127 {
1128 // Create a boxed version of the value class. This allows the standard GC algorithm
1129 // to take care of internal pointers into the value class.
1130 obj = AllocateObject(pFD->GetFieldTypeHandleThrowing().GetMethodTable());
1131 }
1132 else
1133 {
1134 // In the case of primitive types, we use a reference to a 1-element array on the heap.
1135 // I'm not sure why we bother treating primitives specially, it seems like we should be able
1136 // to just box any value type including primitives.
1137 obj = AllocatePrimitiveArray(ELEMENT_TYPE_I1, GetSizeForCorElementType(pFD->GetFieldType()));
1138 }
1139 GCPROTECT_BEGIN (obj);
1140
1141 // Get a FieldDesc for the object reference field in the EnC helper object (warning: triggers)
1142 FieldDesc *pHelperField = MscorlibBinder::GetField(FIELD__ENC_HELPER__OBJECT_REFERENCE);
1143
1144 // store the empty boxed object into the helper object
1145 IGCHandleManager *mgr = GCHandleUtilities::GetGCHandleManager();
1146 OBJECTREF pHelperObj = ObjectToOBJECTREF(mgr->GetDependentHandleSecondary(pEntry->m_FieldData));
1147 OBJECTREF *pHelperRef = (OBJECTREF *)pHelperField->GetAddress( pHelperObj->GetAddress() );
1148 SetObjectReference( pHelperRef, obj, pDomain );
1149
1150 GCPROTECT_END ();
1151 }
1152
1153 return pEntry;
1154}
1155#endif // !DACCESS_COMPILE
1156
1157//---------------------------------------------------------------------------------------
1158// EnCSyncBlockInfo::GetEnCFieldAddrFromHelperFieldDesc
1159// Gets the address of an EnC field accounting for its type: valuetype, class or primitive
1160// Arguments:
1161// input: pHelperFieldDesc - FieldDesc for the enc helper object
1162// pHelper - EnC helper (points to list of added fields)
1163// pFD - fieldDesc describing the field of interest
1164// Return value: the address of the EnC added field
1165//---------------------------------------------------------------------------------------
1166PTR_CBYTE EnCSyncBlockInfo::GetEnCFieldAddrFromHelperFieldDesc(FieldDesc * pHelperFieldDesc,
1167 OBJECTREF pHelper,
1168 EnCFieldDesc * pFD)
1169{
1170 WRAPPER_NO_CONTRACT;
1171 SUPPORTS_DAC;
1172
1173 _ASSERTE(pHelperFieldDesc != NULL);
1174 _ASSERTE(pHelper != NULL);
1175
1176 // Get the address of the reference inside the helper object which points to
1177 // the field contents
1178 PTR_OBJECTREF pOR = dac_cast<PTR_OBJECTREF>(pHelperFieldDesc->GetAddress(pHelper->GetAddress()));
1179 _ASSERTE(pOR != NULL);
1180
1181 PTR_CBYTE retAddr = NULL;
1182
1183 // Compute the address to the actual field contents based on the field type
1184 // See the description above Allocate for details
1185 if (pFD->IsByValue())
1186 {
1187 // field value is a value type, we store it boxed so get the pointer to the first field
1188 retAddr = dac_cast<PTR_CBYTE>((*pOR)->UnBox());
1189 }
1190 else if (pFD->GetFieldType() == ELEMENT_TYPE_CLASS)
1191 {
1192 // field value is a reference type, we store the objref directly
1193 retAddr = dac_cast<PTR_CBYTE>(pOR);
1194 }
1195 else
1196 {
1197 // field value is a primitive, we store it inside a 1-element array
1198 OBJECTREF objRef = *pOR;
1199 I1ARRAYREF primitiveArray = dac_cast<I1ARRAYREF>(objRef);
1200 retAddr = dac_cast<PTR_CBYTE>(primitiveArray->GetDirectPointerToNonObjectElements());
1201 }
1202
1203 LOG((LF_ENC, LL_INFO1000, "\tEnCSBI:RF address of %s type member is %p\n",
1204 (pFD->IsByValue() ? "ByValue" : pFD->GetFieldType() == ELEMENT_TYPE_CLASS ? "Class" : "Other"), retAddr));
1205
1206 return retAddr;
1207} // EnCSyncBlockInfo::GetEnCFieldAddrFromHelperFieldDesc
1208
1209//---------------------------------------------------------------------------------------
1210// EnCSyncBlockInfo::ResolveField
1211// Get the address of the data referenced by an instance field that was added with EnC
1212// Arguments:
1213// thisPointer - the object instance whose field to access
1214// pFD - fieldDesc describing the field of interest
1215// Return value: Returns a pointer to the data referenced by an EnC added instance field
1216//---------------------------------------------------------------------------------------
1217PTR_CBYTE EnCSyncBlockInfo::ResolveField(OBJECTREF thisPointer, EnCFieldDesc *pFD)
1218{
1219 CONTRACTL
1220 {
1221 GC_NOTRIGGER;
1222 NOTHROW;
1223 SUPPORTS_DAC;
1224 }
1225 CONTRACTL_END;
1226
1227 // We should only be passed FieldDescs for instance fields
1228 _ASSERTE(!pFD->IsStatic());
1229
1230 PTR_EnCAddedField pEntry = NULL;
1231
1232 LOG((LF_ENC, LL_INFO1000, "EnCSBI:RF for this %p, FD %p\n", thisPointer, pFD->GetMemberDef()));
1233
1234 // This list is not synchronized--it hasn't proved a problem, but we could conceivably see race conditions
1235 // arise here.
1236 // Look for an entry for the requested field in our linked list
1237 pEntry = m_pList;
1238 while (pEntry && pEntry->m_pFieldDesc != pFD)
1239 {
1240 pEntry = pEntry->m_pNext;
1241 }
1242
1243 if (!pEntry)
1244 {
1245 // No existing entry - we have to return NULL
1246 return NULL;
1247 }
1248
1249 // we found a matching entry in the list of EnCAddedFields
1250 // Get the EnC helper object (see the detailed description in Allocate above)
1251#ifdef DACCESS_COMPILE
1252 OBJECTREF pHelper = GetDependentHandleSecondary(pEntry->m_FieldData);
1253#else // DACCESS_COMPILE
1254 IGCHandleManager *mgr = GCHandleUtilities::GetGCHandleManager();
1255 OBJECTREF pHelper = ObjectToOBJECTREF(mgr->GetDependentHandleSecondary(pEntry->m_FieldData));
1256#endif // DACCESS_COMPILE
1257 _ASSERTE(pHelper != NULL);
1258
1259 FieldDesc *pHelperFieldDesc = NULL;
1260
1261 // We _HAVE_ to call GetExistingField b/c (a) we can't throw exceptions, and
1262 // (b) we _DON'T_ want to run class init code, either.
1263 pHelperFieldDesc = MscorlibBinder::GetExistingField(FIELD__ENC_HELPER__OBJECT_REFERENCE);
1264 if (pHelperFieldDesc == NULL)
1265 {
1266 return NULL;
1267 }
1268 else
1269 {
1270 return GetEnCFieldAddrFromHelperFieldDesc(pHelperFieldDesc, pHelper, pFD);
1271 }
1272} // EnCSyncBlockInfo::ResolveField
1273
1274#ifndef DACCESS_COMPILE
1275//---------------------------------------------------------------------------------------
1276// EnCSyncBlockInfo::ResolveOrAllocateField
1277// get the address of an EnC added field, allocating it if it doesn't yet exist
1278// Arguments:
1279// thisPointer - the object instance whose field to access
1280// pFD - fieldDesc describing the field of interest
1281// Return value: Returns a pointer to the data referenced by an instance field that was added with EnC
1282//---------------------------------------------------------------------------------------
1283PTR_CBYTE EnCSyncBlockInfo::ResolveOrAllocateField(OBJECTREF thisPointer, EnCFieldDesc *pFD)
1284{
1285 CONTRACTL
1286 {
1287 GC_TRIGGERS;
1288 WRAPPER(THROWS);
1289 }
1290 CONTRACTL_END;
1291
1292 // We should only be passed FieldDescs for instance fields
1293 _ASSERTE( !pFD->IsStatic() );
1294
1295 // first try to get the address of a pre-existing field (storage has already been allocated)
1296 PTR_CBYTE retAddr = ResolveField(thisPointer, pFD);
1297
1298 if (retAddr != NULL)
1299 {
1300 return retAddr;
1301 }
1302
1303 // if the field doesn't yet have available storage, we'll have to allocate it.
1304 PTR_EnCAddedField pEntry = NULL;
1305
1306 LOG((LF_ENC, LL_INFO1000, "EnCSBI:RF for this %p, FD %p\n", thisPointer, pFD->GetMemberDef()));
1307
1308 // This list is not synchronized--it hasn't proved a problem, but we could conceivably see race conditions
1309 // arise here.
1310 // Because we may have additions to the head of m_pList at any time, we have to keep searching this
1311 // until we either find a match or succeed in allocating a new entry and adding it to the list
1312 do
1313 {
1314 // Look for an entry for the requested field in our linked list (maybe it was just added)
1315 pEntry = m_pList;
1316 while (pEntry && pEntry->m_pFieldDesc != pFD)
1317 {
1318 pEntry = pEntry->m_pNext;
1319 }
1320
1321 if (pEntry)
1322 {
1323 // match found
1324 break;
1325 }
1326
1327 // Allocate an entry and tie it to the object instance
1328 pEntry = EnCAddedField::Allocate(thisPointer, pFD);
1329
1330 // put at front of list so the list is in order of most recently added
1331 pEntry->m_pNext = m_pList;
1332 if (FastInterlockCompareExchangePointer(&m_pList, pEntry, pEntry->m_pNext) == pEntry->m_pNext)
1333 break;
1334
1335 // There was a race and another thread modified the list here, so we need to try again
1336 // We should do this so rarely, and EnC perf is of relatively little
1337 // consequence, we should just be taking a lock here to simplify this code.
1338 // @todo - We leak a GC handle here. Allocate() above alloced a GC handle in m_FieldData.
1339 // There's no dtor for pEntry to free it.
1340 delete pEntry;
1341 } while (TRUE);
1342
1343 // we found a matching entry in the list of EnCAddedFields
1344 // Get the EnC helper object (see the detailed description in Allocate above)
1345 IGCHandleManager *mgr = GCHandleUtilities::GetGCHandleManager();
1346 OBJECTREF pHelper = ObjectToOBJECTREF(mgr->GetDependentHandleSecondary(pEntry->m_FieldData));
1347 _ASSERTE(pHelper != NULL);
1348
1349 FieldDesc * pHelperField = NULL;
1350 GCPROTECT_BEGIN (pHelper);
1351 pHelperField = MscorlibBinder::GetField(FIELD__ENC_HELPER__OBJECT_REFERENCE);
1352 GCPROTECT_END ();
1353
1354 return GetEnCFieldAddrFromHelperFieldDesc(pHelperField, pHelper, pFD);
1355} // EnCSyncBlockInfo::ResolveOrAllocateField
1356
1357// Free all the resources associated with the fields added to this object instance
1358// This is invoked after the object instance has been collected, and the SyncBlock is
1359// being reclaimed.
1360//
1361// Note, this is not threadsafe, and so should only be called when we know no-one else
1362// maybe using this SyncBlockInfo.
1363void EnCSyncBlockInfo::Cleanup()
1364{
1365 CONTRACTL
1366 {
1367 NOTHROW;
1368 GC_NOTRIGGER;
1369 SO_TOLERANT;
1370 MODE_ANY;
1371 }
1372 CONTRACTL_END;
1373 // Walk our linked list of all the fields that were added
1374 EnCAddedField *pEntry = m_pList;
1375 while (pEntry)
1376 {
1377 // Clean up the handle we created in EnCAddedField::Allocate
1378 DestroyDependentHandle(*(OBJECTHANDLE*)&pEntry->m_FieldData);
1379
1380 // Delete this list entry and move onto the next
1381 EnCAddedField *next = pEntry->m_pNext;
1382 delete pEntry;
1383 pEntry = next;
1384 }
1385
1386 // Finally, delete the sync block info itself
1387 delete this;
1388}
1389
1390// Allocate space to hold the value for the new static field
1391EnCAddedStaticField *EnCAddedStaticField::Allocate(EnCFieldDesc *pFD)
1392{
1393 CONTRACTL
1394 {
1395 THROWS;
1396 GC_TRIGGERS;
1397 }
1398 CONTRACTL_END;
1399
1400 AppDomain *pDomain = (AppDomain*) pFD->GetApproxEnclosingMethodTable()->GetDomain();
1401
1402 // Compute the size of the fieldData entry
1403 size_t fieldSize;
1404 if (pFD->IsByValue() || pFD->GetFieldType() == ELEMENT_TYPE_CLASS) {
1405 // We store references to reference types or boxed value types
1406 fieldSize = sizeof(OBJECTREF*);
1407 } else {
1408 // We store primitives inline
1409 fieldSize = GetSizeForCorElementType(pFD->GetFieldType());
1410 }
1411
1412 // allocate an instance with space for the field data
1413 EnCAddedStaticField *pEntry = (EnCAddedStaticField *)
1414 (void*)pDomain->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(offsetof(EnCAddedStaticField, m_FieldData)) + S_SIZE_T(fieldSize));
1415 pEntry->m_pFieldDesc = pFD;
1416
1417 // Create a static objectref to point to the field contents, except for primitives
1418 // which will use the memory available in-line at m_FieldData for storage.
1419 // We use static object refs for static fields as these fields won't go away
1420 // unless the module is unloaded, and they can easily be found by GC.
1421 if (pFD->IsByValue())
1422 {
1423 // create a boxed version of the value class. This allows the standard GC
1424 // algorithm to take care of internal pointers in the value class.
1425 OBJECTREF **pOR = (OBJECTREF**)&pEntry->m_FieldData;
1426 *pOR = pDomain->AllocateStaticFieldObjRefPtrs(1);
1427 OBJECTREF obj = AllocateObject(pFD->GetFieldTypeHandleThrowing().GetMethodTable());
1428 SetObjectReference( *pOR, obj, pDomain );
1429 }
1430 else if (pFD->GetFieldType() == ELEMENT_TYPE_CLASS)
1431 {
1432 // references to reference-types are stored directly in the field data
1433 OBJECTREF **pOR = (OBJECTREF**)&pEntry->m_FieldData;
1434 *pOR = pDomain->AllocateStaticFieldObjRefPtrs(1);
1435 }
1436
1437 return pEntry;
1438}
1439#endif // !DACCESS_COMPILE
1440// GetFieldData - return the ADDRESS where the field data is located
1441PTR_CBYTE EnCAddedStaticField::GetFieldData()
1442{
1443 LIMITED_METHOD_CONTRACT;
1444 SUPPORTS_DAC;
1445
1446 if ( (m_pFieldDesc->IsByValue()) || (m_pFieldDesc->GetFieldType() == ELEMENT_TYPE_CLASS) )
1447 {
1448 // It's indirect via an ObjRef at m_FieldData. This is a TADDR, so we need to make a PTR_CBYTE from
1449 // the ObjRef
1450 return *(PTR_CBYTE *)&m_FieldData;
1451 }
1452 else
1453 {
1454 // An elementry type. It's stored directly in m_FieldData. In this case, we need to get the target
1455 // address of the m_FieldData data member and marshal it via the DAC.
1456 return dac_cast<PTR_CBYTE>(PTR_HOST_MEMBER_TADDR(EnCAddedStaticField, this, m_FieldData));
1457 }
1458}
1459
1460// Gets a pointer to the field's contents (assuming this is a static field)
1461// We'll return NULL if we don't yet have a pointer to the data.
1462// Arguments: none
1463// Return value: address of the static field data if available or NULL otherwise
1464EnCAddedStaticField * EnCFieldDesc::GetStaticFieldData()
1465{
1466 CONTRACTL
1467 {
1468 GC_NOTRIGGER;
1469 NOTHROW;
1470 SUPPORTS_DAC;
1471 }
1472 CONTRACTL_END;
1473
1474 _ASSERTE(IsStatic());
1475
1476 return m_pStaticFieldData;
1477}
1478
1479#ifndef DACCESS_COMPILE
1480// Gets a pointer to the field's contents (assuming this is a static field)
1481// Arguments: none
1482// Return value: address of the field data. If we don't yet have a pointer to the data,
1483// this will allocate space to store it.
1484// May throw OOM.
1485EnCAddedStaticField * EnCFieldDesc::GetOrAllocateStaticFieldData()
1486{
1487 CONTRACTL
1488 {
1489 GC_TRIGGERS;
1490 THROWS;
1491 }
1492 CONTRACTL_END;
1493
1494 _ASSERTE(IsStatic());
1495
1496 // If necessary and requested, allocate space for the static field data
1497 if (!m_pStaticFieldData)
1498 {
1499 m_pStaticFieldData = EnCAddedStaticField::Allocate(this);
1500 }
1501
1502 return m_pStaticFieldData;
1503}
1504#endif // !DACCESS_COMPILE
1505
1506#ifndef DACCESS_COMPILE
1507// Adds the provided new field to the appropriate linked list and updates the appropriate count
1508void EnCEEClassData::AddField(EnCAddedFieldElement *pAddedField)
1509{
1510 LIMITED_METHOD_CONTRACT;
1511 // Determine the appropriate field list and update the field counter
1512 EnCFieldDesc *pFD = &pAddedField->m_fieldDesc;
1513 EnCAddedFieldElement **pList;
1514 if (pFD->IsStatic())
1515 {
1516 ++m_dwNumAddedStaticFields;
1517 pList = &m_pAddedStaticFields;
1518 }
1519 else
1520 {
1521 ++m_dwNumAddedInstanceFields;
1522 pList = &m_pAddedInstanceFields;
1523 }
1524
1525 // If the list is empty, just add this field as the only entry
1526 if (*pList == NULL)
1527 {
1528 *pList = pAddedField;
1529 return;
1530 }
1531
1532 // Otherwise, add this field to the end of the field list
1533 EnCAddedFieldElement *pCur = *pList;
1534 while (pCur->m_next != NULL)
1535 {
1536 pCur = pCur->m_next;
1537 }
1538 pCur->m_next = pAddedField;
1539}
1540
1541#endif // #ifndef DACCESS_COMPILE
1542
1543#ifdef DACCESS_COMPILE
1544
1545void
1546EnCEEClassData::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
1547{
1548 SUPPORTS_DAC;
1549 DAC_ENUM_DTHIS();
1550
1551 if (m_pMT.IsValid())
1552 {
1553 m_pMT->EnumMemoryRegions(flags);
1554 }
1555
1556 PTR_EnCAddedFieldElement elt = m_pAddedInstanceFields;
1557 while (elt.IsValid())
1558 {
1559 elt.EnumMem();
1560 elt = elt->m_next;
1561 }
1562 elt = m_pAddedStaticFields;
1563 while (elt.IsValid())
1564 {
1565 elt.EnumMem();
1566 elt = elt->m_next;
1567 }
1568}
1569
1570void
1571EditAndContinueModule::EnumMemoryRegions(CLRDataEnumMemoryFlags flags,
1572 bool enumThis)
1573{
1574 SUPPORTS_DAC;
1575
1576 if (enumThis)
1577 {
1578 DAC_ENUM_VTHIS();
1579 }
1580
1581 Module::EnumMemoryRegions(flags, false);
1582
1583 m_ClassList.EnumMemoryRegions();
1584
1585 DPTR(PTR_EnCEEClassData) classData = m_ClassList.Table();
1586 DPTR(PTR_EnCEEClassData) classLast = classData + m_ClassList.Count();
1587
1588 while (classData.IsValid() && classData < classLast)
1589 {
1590 if ((*classData).IsValid())
1591 {
1592 (*classData)->EnumMemoryRegions(flags);
1593 }
1594
1595 classData++;
1596 }
1597}
1598
1599#endif // #ifdef DACCESS_COMPILE
1600
1601
1602// Create a field iterator which includes EnC fields in addition to the fields from an
1603// underlying ApproxFieldDescIterator.
1604//
1605// Arguments:
1606// pMT - MethodTable indicating the type of interest
1607// iteratorType - one of the ApproxFieldDescIterator::IteratorType values specifying which fields
1608// are of interest.
1609// fixupEnC - if true, then any partially-initialized EnC FieldDescs will be fixed up to be complete
1610// initialized FieldDescs as they are returned by Next(). This may load types and do
1611// other things to trigger a GC.
1612//
1613EncApproxFieldDescIterator::EncApproxFieldDescIterator(MethodTable *pMT, int iteratorType, BOOL fixupEnC) :
1614 m_nonEnCIter( pMT, iteratorType )
1615{
1616 CONTRACTL
1617 {
1618 NOTHROW;
1619 GC_NOTRIGGER;
1620 SUPPORTS_DAC;
1621 }
1622 CONTRACTL_END
1623
1624 m_fixupEnC = fixupEnC;
1625
1626#ifndef DACCESS_COMPILE
1627 // can't fixup for EnC on the debugger thread
1628 _ASSERTE((g_pDebugInterface->GetRCThreadId() != GetCurrentThreadId()) || fixupEnC == FALSE);
1629#endif
1630
1631 m_pCurrListElem = NULL;
1632 m_encClassData = NULL;
1633 m_encFieldsReturned = 0;
1634
1635 // If this is an EnC module, then grab a pointer to the EnC data
1636 if( pMT->GetModule()->IsEditAndContinueEnabled() )
1637 {
1638 PTR_EditAndContinueModule encMod = PTR_EditAndContinueModule(pMT->GetModule());
1639 m_encClassData = encMod->GetEnCEEClassData( pMT, TRUE);
1640 }
1641}
1642
1643// Iterates through all fields, returns NULL when done.
1644PTR_FieldDesc EncApproxFieldDescIterator::Next()
1645{
1646 CONTRACTL
1647 {
1648 NOTHROW;
1649 if (m_fixupEnC) {GC_TRIGGERS;} else {GC_NOTRIGGER;}
1650 FORBID_FAULT;
1651 SUPPORTS_DAC;
1652 }
1653 CONTRACTL_END
1654
1655 // If we still have non-EnC fields to look at, return one of them
1656 if( m_nonEnCIter.CountRemaining() > 0 )
1657 {
1658 _ASSERTE( m_encFieldsReturned == 0 );
1659 return m_nonEnCIter.Next();
1660 }
1661
1662 // Get the next EnC field Desc if any
1663 PTR_EnCFieldDesc pFD = NextEnC();
1664 if( pFD == NULL )
1665 {
1666 // No more fields
1667 return NULL;
1668 }
1669
1670#ifndef DACCESS_COMPILE
1671 // Fixup the fieldDesc if requested and necessary
1672 if ( m_fixupEnC && (pFD->NeedsFixup()) )
1673 {
1674 // if we get an OOM during fixup, the field will just not get fixed up
1675 EX_TRY
1676 {
1677 FAULT_NOT_FATAL();
1678 pFD->Fixup(pFD->GetMemberDef());
1679 }
1680 EX_CATCH
1681 {
1682 }
1683 EX_END_CATCH(SwallowAllExceptions)
1684 }
1685
1686 // Either it's been fixed up so we can use it, or we're the Debugger RC thread, we can't fix it up,
1687 // but it's ok since our logic will check & make sure we don't try and use it. If haven't asked to
1688 // have the field fixed up, should never be trying to get at non-fixed up field in
1689 // this list. Can't simply fixup the field always because loading triggers GC and many
1690 // code paths can't tolerate that.
1691 _ASSERTE( !(pFD->NeedsFixup()) ||
1692 ( g_pDebugInterface->GetRCThreadId() == GetCurrentThreadId() ) );
1693#endif
1694
1695 return dac_cast<PTR_FieldDesc>(pFD);
1696}
1697
1698// Iterate through EnC added fields.
1699// Returns NULL when done.
1700PTR_EnCFieldDesc EncApproxFieldDescIterator::NextEnC()
1701{
1702 CONTRACTL
1703 {
1704 NOTHROW;
1705 GC_NOTRIGGER;
1706 FORBID_FAULT;
1707 SUPPORTS_DAC;
1708 }
1709 CONTRACTL_END
1710
1711 // If this module doesn't have any EnC data then there aren't any EnC fields
1712 if( m_encClassData == NULL )
1713 {
1714 return NULL;
1715 }
1716
1717 BOOL doInst = ( GetIteratorType() & (int)ApproxFieldDescIterator::INSTANCE_FIELDS);
1718 BOOL doStatic = ( GetIteratorType() & (int)ApproxFieldDescIterator::STATIC_FIELDS);
1719
1720 int cNumAddedInst = doInst ? m_encClassData->GetAddedInstanceFields() : 0;
1721 int cNumAddedStatics = doStatic ? m_encClassData->GetAddedStaticFields() : 0;
1722
1723 // If we haven't returned anything yet
1724 if ( m_encFieldsReturned == 0 )
1725 {
1726 _ASSERTE(m_pCurrListElem == NULL);
1727
1728 // We're at the start of the instance list.
1729 if ( doInst )
1730 {
1731 m_pCurrListElem = m_encClassData->m_pAddedInstanceFields;
1732 }
1733 }
1734
1735 // If we've finished the instance fields (or never wanted to do any)
1736 if ( m_encFieldsReturned == cNumAddedInst)
1737 {
1738 // We should be at the end of the instance list if doInst is true
1739 _ASSERTE(m_pCurrListElem == NULL);
1740
1741 // We're at the start of the statics list.
1742 if ( doStatic )
1743 {
1744 m_pCurrListElem = m_encClassData->m_pAddedStaticFields;
1745 }
1746 }
1747
1748 // If we don't have any elements to return, then we're done
1749 if (m_pCurrListElem == NULL)
1750 {
1751 // Verify that we returned the number we expected to
1752 _ASSERTE( m_encFieldsReturned == cNumAddedInst + cNumAddedStatics );
1753 return NULL;
1754 }
1755
1756 // Advance the list pointer and return the element
1757 m_encFieldsReturned++;
1758 PTR_EnCFieldDesc fd = PTR_EnCFieldDesc(PTR_HOST_MEMBER_TADDR(EnCAddedFieldElement, m_pCurrListElem, m_fieldDesc));
1759 m_pCurrListElem = m_pCurrListElem->m_next;
1760 return fd;
1761}
1762
1763#endif // EnC_SUPPORTED
1764