// // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. // //***************************************************************************** // File: daccess.h // // // Support for external access of runtime data structures. These // macros and templates hide the details of pointer and data handling // so that data structures and code can be compiled to work both // in-process and through a special memory access layer. // // This code assumes the existence of two different pieces of code, // the target, the runtime code that is going to be examined, and // the host, the code that's doing the examining. Access to the // target is abstracted so the target may be a live process on the // same machine, a live process on a different machine, a dump file // or whatever. No assumptions should be made about accessibility // of the target. // // This code assumes that the data in the target is static. Any // time the target's data changes the interfaces must be reset so // that potentially stale data is discarded. // // This code is intended for read access and there is no // way to write data back currently. // // DAC-ized code: // - is read-only (non-invasive). So DACized codepaths can not trigger a GC. // - has no Thread* object. In reality, DAC-ized codepaths are // ReadProcessMemory calls from out-of-process. Conceptually, they // are like a pure-native (preemptive) thread. //// // This means that in particular, you cannot DACize a GCTRIGGERS function. // Neither can you DACize a function that throws if this will involve // allocating a new exception object. There may be // exceptions to these rules if you can guarantee that the DACized // part of the code path cannot cause a garbage collection (see // EditAndContinueModule::ResolveField for an example). // If you need to DACize a function that may trigger // a GC, it is probably best to refactor the function so that the DACized // part of the code path is in a separate function. For instance, // functions with GetOrCreate() semantics are hard to DAC-ize because // they the Create portion is inherently invasive. Instead, consider refactoring // into a GetOrFail() function that DAC can call; and then make GetOrCreate() // a wrapper around that. // // This code works by hiding the details of access to target memory. // Access is divided into two types: // 1. DPTR - access to a piece of data. // 2. VPTR - access to a class with a vtable. The class can only have // a single vtable pointer at the beginning of the class instance. // Things only need to be declared as VPTRs when it is necessary to // call virtual functions in the host. In that case the access layer // must do extra work to provide a host vtable for the object when // it is retrieved so that virtual functions can be called. // // When compiling with DACCESS_COMPILE the macros turn into templates // which replace pointers with smart pointers that know how to fetch // data from the target process and provide a host process version of it. // Normal data structure access will transparently receive a host copy // of the data and proceed, so code such as // typedef DPTR(Class) PTR_Class; // PTR_Class cls; // int val = cls->m_Int; // will work without modification. The appropriate operators are overloaded // to provide transparent access, such as the -> operator in this case. // Note that the convention is to create an appropriate typedef for // each type that will be accessed. This hides the particular details // of the type declaration and makes the usage look more like regular code. // // The ?PTR classes also have an implicit base type cast operator to // produce a host-pointer instance of the given type. For example // Class* cls = PTR_Class(addr); // works by implicit conversion from the PTR_Class created by wrapping // to a host-side Class instance. Again, this means that existing code // can work without modification. // // Code Example: // // typedef struct _rangesection // { // PTR_IJitManager pjit; // PTR_RangeSection pright; // PTR_RangeSection pleft; // ... Other fields omitted ... // } RangeSection; // // RangeSection* pRS = m_RangeTree; // // while (pRS != NULL) // { // if (currentPC < pRS->LowAddress) // pRS=pRS->pleft; // else if (currentPC > pRS->HighAddress) // pRS=pRS->pright; // else // { // return pRS->pjit; // } // } // // This code does not require any modifications. The global reference // provided by m_RangeTree will be a host version of the RangeSection // instantiated by conversion. The references to pRS->pleft and // pRS->pright will refer to DPTRs due to the modified declaration. // In the assignment statement the compiler will automatically use // the implicit conversion from PTR_RangeSection to RangeSection*, // causing a host instance to be created. Finally, if an appropriate // section is found the use of pRS->pjit will cause an implicit // conversion from PTR_IJitManager to IJitManager. The VPTR code // will look at target memory to determine the actual derived class // for the JitManager and instantiate the right class in the host so // that host virtual functions can be used just as they would in // the target. // // There are situations where code modifications are required, though. // // 1. Any time the actual value of an address matters, such as using // it as a search key in a tree, the target address must be used. // // An example of this is the RangeSection tree used to locate JIT // managers. A portion of this code is shown above. Each // RangeSection node in the tree describes a range of addresses // managed by the JitMan. These addresses are just being used as // values, not to dereference through, so there are not DPTRs. When // searching the range tree for an address the address used in the // search must be a target address as that's what values are kept in // the RangeSections. In the code shown above, currentPC must be a // target address as the RangeSections in the tree are all target // addresses. Use dac_cast to retrieve the target address // of a ?PTR, as well as to convert a host address to the // target address used to retrieve that particular instance. Do not // use dac_cast with any raw target pointer types (such as BYTE*). // // 2. Any time an address is modified, such as by address arithmetic, // the arithmetic must be performed on the target address. // // When a host instance is created it is created for the type in use. // There is no particular relation to any other instance, so address // arithmetic cannot be used to get from one instance to any other // part of memory. For example // char* Func(Class* cls) // { // // String follows the basic Class data. // return (char*)(cls + 1); // } // does not work with external access because the Class* used would // have retrieved only a Class worth of data. There is no string // following the host instance. Instead, this code should use // dac_cast to get the target address of the Class // instance, add sizeof(*cls) and then create a new ?PTR to access // the desired data. Note that the newly retrieved data will not // be contiguous with the Class instance, so address arithmetic // will still not work. // // Previous Code: // // BOOL IsTarget(LPVOID ip) // { // StubCallInstrs* pStubCallInstrs = GetStubCallInstrs(); // // if (ip == (LPVOID) &(pStubCallInstrs->m_op)) // { // return TRUE; // } // // Modified Code: // // BOOL IsTarget(LPVOID ip) // { // StubCallInstrs* pStubCallInstrs = GetStubCallInstrs(); // // if ((TADDR)ip == dac_cast(pStubCallInstrs) + // (TADDR)offsetof(StubCallInstrs, m_op)) // { // return TRUE; // } // // The parameter ip is a target address, so the host pStubCallInstrs // cannot be used to derive an address from. The member & reference // has to be replaced with a conversion from host to target address // followed by explicit offsetting for the field. // // PTR_HOST_MEMBER_TADDR is a convenience macro that encapsulates // these two operations, so the above code could also be: // // if ((TADDR)ip == // PTR_HOST_MEMBER_TADDR(StubCallInstrs, pStubCallInstrs, m_op)) // // 3. Any time the amount of memory referenced through an address // changes, such as by casting to a different type, a new ?PTR // must be created. // // Host instances are created and stored based on both the target // address and size of access. The access code has no way of knowing // all possible ways that data will be retrieved for a given address // so if code changes the way it accesses through an address a new // ?PTR must be used, which may lead to a difference instance and // different host address. This means that pointer identity does not hold // across casts, so code like // Class* cls = PTR_Class(addr); // Class2* cls2 = PTR_Class2(addr); // return cls == cls2; // will fail because the host-side instances have no relation to each // other. That isn't a problem, since by rule #1 you shouldn't be // relying on specific host address values. // // Previous Code: // // return (ArrayClass *) m_pMethTab->GetClass(); // // Modified Code: // // return PTR_ArrayClass(m_pMethTab->GetClass()); // // The ?PTR templates have an implicit conversion from a host pointer // to a target address, so the cast above constructs a new // PTR_ArrayClass by implicitly converting the host pointer result // from GetClass() to its target address and using that as the address // of the new PTR_ArrayClass. As mentioned, the actual host-side // pointer values may not be the same. // // Host pointer identity can be assumed as long as the type of access // is the same. In the example above, if both accesses were of type // Class then the host pointer will be the same, so it is safe to // retrieve the target address of an instance and then later get // a new host pointer for the target address using the same type as // the host pointer in that case will be the same. This is enabled // by caching all of the retrieved host instances. This cache is searched // by the addr:size pair and when there's a match the existing instance // is reused. This increases performance and also allows simple // pointer identity to hold. It does mean that host memory grows // in proportion to the amount of target memory being referenced, // so retrieving extraneous data should be avoided. // The host-side data cache grows until the Flush() method is called, // at which point all host-side data is discarded. No host // instance pointers should be held across a Flush(). // // Accessing into an object can lead to some unusual behavior. For // example, the SList class relies on objects to contain an SLink // instance that it uses for list maintenance. This SLink can be // embedded anywhere in the larger object. The SList access is always // purely to an SLink, so when using the access layer it will only // retrieve an SLink's worth of data. The SList template will then // do some address arithmetic to determine the start of the real // object and cast the resulting pointer to the final object type. // When using the access layer this results in a new ?PTR being // created and used, so a new instance will result. The internal // SLink instance will have no relation to the new object instance // even though in target address terms one is embedded in the other. // The assumption of data stability means that this won't cause // a problem, but care must be taken with the address arithmetic, // as layed out in rules #2 and #3. // // 4. Global address references cannot be used. Any reference to a // global piece of code or data, such as a function address, global // variable or class static variable, must be changed. // // The external access code may load at a different base address than // the target process code. Global addresses are therefore not // meaningful and must be replaced with something else. There isn't // a single solution, so replacements must be done on a case-by-case // basis. // // The simplest case is a global or class static variable. All // declarations must be replaced with a special declaration that // compiles into a modified accessor template value when compiled for // external data access. Uses of the variable automatically are fixed // up by the template instance. Note that assignment to the global // must be independently ifdef'ed as the external access layer should // not make any modifications. // // Macros allow for simple declaration of a class static and global // values that compile into an appropriate templated value. // // Previous Code: // // static RangeSection* m_RangeTree; // RangeSection* ExecutionManager::m_RangeTree; // // extern ThreadStore* g_pThreadStore; // ThreadStore* g_pThreadStore = &StaticStore; // class SystemDomain : public BaseDomain { // ... // ArrayListStatic m_appDomainIndexList; // ... // } // // SystemDomain::m_appDomainIndexList; // // extern DWORD gThreadTLSIndex; // // DWORD gThreadTLSIndex = TLS_OUT_OF_INDEXES; // // Modified Code: // // typedef DPTR(RangeSection) PTR_RangeSection; // SPTR_DECL(RangeSection, m_RangeTree); // SPTR_IMPL(RangeSection, ExecutionManager, m_RangeTree); // // typedef DPTR(ThreadStore) PTR_ThreadStore // GPTR_DECL(ThreadStore, g_pThreadStore); // GPTR_IMPL_INIT(ThreadStore, g_pThreadStore, &StaticStore); // // class SystemDomain : public BaseDomain { // ... // SVAL_DECL(ArrayListStatic; m_appDomainIndexList); // ... // } // // SVAL_IMPL(ArrayListStatic, SystemDomain, m_appDomainIndexList); // // GVAL_DECL(DWORD, gThreadTLSIndex); // // GVAL_IMPL_INIT(DWORD, gThreadTLSIndex, TLS_OUT_OF_INDEXES); // // When declaring the variable, the first argument declares the // variable's type and the second argument declares the variable's // name. When defining the variable the arguments are similar, with // an extra class name parameter for the static class variable case. // If an initializer is needed the IMPL_INIT macro should be used. // // Things get slightly more complicated when declaring an embedded // array. In this case the data element is not a single element and // therefore cannot be represented by a ?PTR. In the case of a global // array, you should use the GARY_DECL and GARY_IMPL macros. // We durrently have no support for declaring static array data members // or initialized arrays. Array data members that are dynamically allocated // need to be treated as pointer members. To reference individual elements // you must use pointer arithmetic (see rule 2 above). An array declared // as a local variable within a function does not need to be DACized. // // // All uses of ?VAL_DECL must have a corresponding entry given in the // DacGlobals structure in src\inc\dacvars.h. For SVAL_DECL the entry // is class__name. For GVAL_DECL the entry is dac__name. You must add // these entries in dacvars.h using the DEFINE_DACVAR macro. Note that // these entries also are used for dumping memory in mini dumps and // heap dumps. If it's not appropriate to dump a variable, (e.g., // it's an array or some other value that is not important to have // in a minidump) a second macro, DEFINE_DACVAR_NO_DUMP, will allow // you to make the required entry in the DacGlobals structure without // dumping its value. // // For convenience, here is a list of the various variable declaration and // initialization macros: // SVAL_DECL(type, name) static non-pointer data class MyClass // member declared within { // the class declaration // static int i; // SVAL_DECL(int, i); // } // // SVAL_IMPL(type, cls, name) static non-pointer data // int MyClass::i; // member defined outside SVAL_IMPL(int, MyClass, i); // the class declaration // // SVAL_IMPL_INIT(type, cls, static non-pointer data // int MyClass::i = 0; // name, val) member defined and SVAL_IMPL_INIT(int, MyClass, i, 0); // initialized outside the // class declaration // ------------------------------------------------------------------------------------------------ // SPTR_DECL(type, name) static pointer data class MyClass // member declared within { // the class declaration // static int * pInt; // SPTR_DECL(int, pInt); // } // // SPTR_IMPL(type, cls, name) static pointer data // int * MyClass::pInt; // member defined outside SPTR_IMPL(int, MyClass, pInt); // the class declaration // // SPTR_IMPL_INIT(type, cls, static pointer data // int * MyClass::pInt = NULL; // name, val) member defined and SPTR_IMPL_INIT(int, MyClass, pInt, NULL); // initialized outside the // class declaration // ------------------------------------------------------------------------------------------------ // GVAL_DECL(type, name) extern declaration of // extern int g_i // global non-pointer GVAL_DECL(int, g_i); // variable // // GVAL_IMPL(type, name) declaration of a // int g_i // global non-pointer GVAL_IMPL(int, g_i); // variable // // GVAL_IMPL_INIT (type, declaration and // int g_i = 0; // name, initialization of a GVAL_IMPL_INIT(int, g_i, 0); // val) global non-pointer // variable // ****Note**** // If you use GVAL_? to declare a global variable of a structured type and you need to // access a member of the type, you cannot use the dot operator. Instead, you must take the // address of the variable and use the arrow operator. For example: // struct // { // int x; // char ch; // } MyStruct; // GVAL_IMPL(MyStruct, g_myStruct); // int i = (&g_myStruct)->x; // ------------------------------------------------------------------------------------------------ // GPTR_DECL(type, name) extern declaration of // extern int * g_pInt // global pointer GPTR_DECL(int, g_pInt); // variable // // GPTR_IMPL(type, name) declaration of a // int * g_pInt // global pointer GPTR_IMPL(int, g_pInt); // variable // // GPTR_IMPL_INIT (type, declaration and // int * g_pInt = 0; // name, initialization of a GPTR_IMPL_INIT(int, g_pInt, NULL); // val) global pointer // variable // ------------------------------------------------------------------------------------------------ // GARY_DECL(type, name) extern declaration of // extern int g_rgIntList[MAX_ELEMENTS]; // a global array GPTR_DECL(int, g_rgIntList, MAX_ELEMENTS); // variable // // GARY_IMPL(type, name) declaration of a // int g_rgIntList[MAX_ELEMENTS]; // global pointer GPTR_IMPL(int, g_rgIntList, MAX_ELEMENTS); // variable // // // Certain pieces of code, such as the stack walker, rely on identifying // an object from its vtable address. As the target vtable addresses // do not necessarily correspond to the vtables used in the host, these // references must be translated. The access layer maintains translation // tables for all classes used with VPTR and can return the target // vtable pointer for any host vtable in the known list of VPTR classes. // // ----- Errors: // // All errors in the access layer are reported via exceptions. The // formal access layer methods catch all such exceptions and turn // them into the appropriate error, so this generally isn't visible // to users of the access layer. // // ----- DPTR Declaration: // // Create a typedef for the type with typedef DPTR(type) PTR_type; // Replace type* with PTR_type. // // ----- VPTR Declaration: // // VPTR can only be used on classes that have a single vtable // pointer at the beginning of the object. This should be true // for a normal single-inheritance object. // // All of the classes that may be instantiated need to be identified // and marked. In the base class declaration add either // VPTR_BASE_VTABLE_CLASS if the class is abstract or // VPTR_BASE_CONCRETE_VTABLE_CLASS if the class is concrete. In each // derived class add VPTR_VTABLE_CLASS. If you end up with compile or // link errors for an unresolved method called VPtrSize you missed a // derived class declaration. // // As described above, dac can only handle classes with a single // vtable. However, there's a special case for multiple inheritance // situations when only one of the classes is needed for dac. If // the base class needed is the first class in the derived class's // layout then it can be used with dac via using the VPTR_MULTI_CLASS // macros. Use with extreme care. // // All classes to be instantiated must be listed in src\inc\vptr_list.h. // // Create a typedef for the type with typedef VPTR(type) PTR_type; // When using a VPTR, replace Class* with PTR_Class. // // ----- Specific Macros: // // PTR_TO_TADDR(ptr) // Retrieves the raw target address for a ?PTR. // See code:dac_cast for the preferred alternative // // PTR_HOST_TO_TADDR(host) // Given a host address of an instance produced by a ?PTR reference, // return the original target address. The host address must // be an exact match for an instance. // See code:dac_cast for the preferred alternative // // PTR_HOST_INT_TO_TADDR(host) // Given a host address which resides somewhere within an instance // produced by a ?PTR reference (a host interior pointer) return the // corresponding target address. This is useful for evaluating // relative pointers (e.g. RelativePointer) where calculating the // target address requires knowledge of the target address of the // relative pointer field itself. This lookup is slower than that for // a non-interior host pointer so use it sparingly. // // VPTR_HOST_VTABLE_TO_TADDR(host) // Given the host vtable pointer for a known VPTR class, return // the target vtable pointer. // // PTR_HOST_MEMBER_TADDR(type, host, memb) // Retrieves the target address of a host instance pointer and // offsets it by the given member's offset within the type. // // PTR_HOST_INT_MEMBER_TADDR(type, host, memb) // As above but will work for interior host pointers (see the // description of PTR_HOST_INT_TO_TADDR for an explanation of host // interior pointers). // // PTR_READ(addr, size) // Reads a block of memory from the target and returns a host // pointer for it. Useful for reading blocks of data from the target // whose size is only known at runtime, such as raw code for a jitted // method. If the data being read is actually an object, use SPTR // instead to get better type semantics. // // DAC_EMPTY() // DAC_EMPTY_ERR() // DAC_EMPTY_RET(retVal) // DAC_UNEXPECTED() // Provides an empty method implementation when compiled // for DACCESS_COMPILE. For example, use to stub out methods needed // for vtable entries but otherwise unused. // // These macros are designed to turn into normal code when compiled // without DACCESS_COMPILE. // //***************************************************************************** #ifndef __daccess_h__ #define __daccess_h__ #include #include "switches.h" #include "safemath.h" #include "corerror.h" #ifndef __in #include #endif #define DACCESS_TABLE_RESOURCE "COREXTERNALDATAACCESSRESOURCE" #ifdef PAL_STDCPP_COMPAT #include #else #include "clr_std/type_traits" #include "crosscomp.h" #endif // Information stored in the DAC table of interest to the DAC implementation // Note that this information is shared between all instantiations of ClrDataAccess, so initialize // it just once in code:ClrDataAccess.GetDacGlobals (rather than use fields in ClrDataAccess); struct DacTableInfo { // On Windows, the first DWORD is the 32-bit timestamp read out of the runtime dll's debug directory. // The remaining 3 DWORDS must all be 0. // On Mac, this is the 16-byte UUID of the runtime dll. // It is used to validate that mscorwks is the same version as mscordacwks DWORD dwID0; DWORD dwID1; DWORD dwID2; DWORD dwID3; }; // The header of the DAC table. This includes the number of globals, the number of vptrs, and // the DacTableInfo structure. We need the DacTableInfo and DacTableHeader structs outside // of a DACCESS_COMPILE since soshost walks the Dac table headers to find the UUID of CoreCLR // in the target process. struct DacTableHeader { ULONG numGlobals; ULONG numVptrs; DacTableInfo info; }; // // This version of things wraps pointer access in // templates which understand how to retrieve data // through an access layer. In this case no assumptions // can be made that the current compilation processor or // pointer types match the target's processor or pointer types. // // Define TADDR as a non-pointer value so use of it as a pointer // will not work properly. Define it as unsigned so // pointer comparisons aren't affected by sign. // This requires special casting to ULONG64 to sign-extend if necessary. typedef ULONG_PTR TADDR; // TSIZE_T used for counts or ranges that need to span the size of a // target pointer. For cross-plat, this may be different than SIZE_T // which reflects the host pointer size. typedef SIZE_T TSIZE_T; // // The following table contains all the global information that data access needs to begin // operation. All of the values stored here are RVAs. DacGlobalBase() returns the current // base address to combine with to get a full target address. // typedef struct _DacGlobals { #ifdef FEATURE_PAL static void Initialize(); void InitializeEntries(TADDR baseAddress); #ifdef FEATURE_SVR_GC void InitializeSVREntries(TADDR baseAddress); #endif // FEATURE_SVR_GC #endif // FEATURE_PAL // These will define all of the dac related mscorwks static and global variables #define DEFINE_DACVAR(id_type, size, id, var) id_type id; #define DEFINE_DACVAR_SVR(id_type, size, id, var) id_type id; #define DEFINE_DACVAR_NO_DUMP(id_type, size, id, var) id_type id; #include "dacvars.h" // Global functions. ULONG fn__ThreadpoolMgr__AsyncTimerCallbackCompletion; ULONG fn__DACNotifyCompilationFinished; ULONG fn__ThePreStub; ULONG fn__ThePreStubPatchLabel; ULONG fn__PrecodeFixupThunk; ULONG fn__StubDispatchFixupStub; ULONG fn__StubDispatchFixupPatchLabel;; #ifdef FEATURE_COMINTEROP ULONG fn__Unknown_AddRef; ULONG fn__Unknown_AddRefSpecial; ULONG fn__Unknown_AddRefInner; #endif // Vtable pointer values for all classes that must // be instanted using vtable pointers as the identity. #define VPTR_CLASS(name) ULONG name##__vtAddr; #define VPTR_MULTI_CLASS(name, keyBase) ULONG name##__##keyBase##__mvtAddr; #include #undef VPTR_CLASS #undef VPTR_MULTI_CLASS } DacGlobals; #ifdef DACCESS_COMPILE extern DacTableInfo g_dacTableInfo; extern DacGlobals g_dacGlobals; #ifdef __cplusplus extern "C" { #endif // These two functions are largely just for marking code // that is not fully converted. DacWarning prints a debug // message, while DacNotImpl throws a not-implemented exception. void __cdecl DacWarning(__in __in_z char* format, ...); void DacNotImpl(void); void DacError(HRESULT err); void DECLSPEC_NORETURN DacError_NoRet(HRESULT err); TADDR DacGlobalBase(void); HRESULT DacReadAll(TADDR addr, PVOID buffer, ULONG32 size, bool throwEx); HRESULT DacWriteAll(TADDR addr, PVOID buffer, ULONG32 size, bool throwEx); HRESULT DacAllocVirtual(TADDR addr, ULONG32 size, ULONG32 typeFlags, ULONG32 protectFlags, bool throwEx, TADDR* mem); HRESULT DacFreeVirtual(TADDR mem, ULONG32 size, ULONG32 typeFlags, bool throwEx); PVOID DacInstantiateTypeByAddress(TADDR addr, ULONG32 size, bool throwEx); PVOID DacInstantiateTypeByAddressNoReport(TADDR addr, ULONG32 size, bool throwEx); PVOID DacInstantiateClassByVTable(TADDR addr, ULONG32 minSize, bool throwEx); // Copy a null-terminated ascii or unicode string from the target to the host. // Note that most of the work here is to find the null terminator. If you know the exact length, // then you can also just call DacInstantiateTypebyAddress. PSTR DacInstantiateStringA(TADDR addr, ULONG32 maxChars, bool throwEx); PWSTR DacInstantiateStringW(TADDR addr, ULONG32 maxChars, bool throwEx); TADDR DacGetTargetAddrForHostAddr(LPCVOID ptr, bool throwEx); TADDR DacGetTargetAddrForHostInteriorAddr(LPCVOID ptr, bool throwEx); TADDR DacGetTargetVtForHostVt(LPCVOID vtHost, bool throwEx); PWSTR DacGetVtNameW(TADDR targetVtable); // Report a region of memory to the debugger bool DacEnumMemoryRegion(TADDR addr, TSIZE_T size, bool fExpectSuccess = true); // Report a region of memory to the debugger bool DacUpdateMemoryRegion(TADDR addr, TSIZE_T bufferSize, BYTE* buffer); HRESULT DacWriteHostInstance(PVOID host, bool throwEx); // This is meant to mimic the RethrowTerminalExceptions/ // SwallowAllExceptions/RethrowTransientExceptions macros to allow minidump // gathering cancelation for details see // code:ClrDataAccess.EnumMemoryRegionsWrapper // This is usable in EX_TRY exactly how RethrowTerminalExceptions et cetera #define RethrowCancelExceptions \ if (GET_EXCEPTION()->GetHR() == COR_E_OPERATIONCANCELED) \ { \ EX_RETHROW; \ } // Occasionally it's necessary to allocate some host memory for // instance data that's created on the fly and so doesn't directly // correspond to target memory. These are held and freed on flush // like other instances but can't be looked up by address. PVOID DacAllocHostOnlyInstance(ULONG32 size, bool throwEx); // Determines whether ASSERTs should be raised when inconsistencies in the target are detected bool DacTargetConsistencyAssertsEnabled(); // Host instances can be marked as they are enumerated in // order to break cycles. This function returns true if // the instance is already marked, otherwise it marks the // instance and returns false. bool DacHostPtrHasEnumMark(LPCVOID host); // Determines if EnumMemoryRegions has been called on a method descriptor. // This helps perf for minidumps of apps with large managed stacks. bool DacHasMethodDescBeenEnumerated(LPCVOID pMD); // Sets a flag indicating that EnumMemoryRegions on a method desciptor // has been successfully called. The function returns true if // this flag had been previously set. bool DacSetMethodDescEnumerated(LPCVOID pMD); // Determines if a method descriptor is valid BOOL DacValidateMD(LPCVOID pMD); // Enumerate the instructions around a call site to help debugger stack walking heuristics void DacEnumCodeForStackwalk(TADDR taCallEnd); // Given the address and the size of a memory range which is stored in the buffer, replace all the patches // in the buffer with the real opcodes. This is especially important on X64 where the unwinder needs to // disassemble the native instructions. class MemoryRange; HRESULT DacReplacePatchesInHostMemory(MemoryRange range, PVOID pBuffer); // // Convenience macros for EnumMemoryRegions implementations. // // Enumerate the given host instance and return // true if the instance hasn't already been enumerated. #define DacEnumHostDPtrMem(host) \ (!DacHostPtrHasEnumMark(host) ? \ (DacEnumMemoryRegion(PTR_HOST_TO_TADDR(host), sizeof(*host)), \ true) : false) #define DacEnumHostSPtrMem(host, type) \ (!DacHostPtrHasEnumMark(host) ? \ (DacEnumMemoryRegion(PTR_HOST_TO_TADDR(host), \ type::DacSize(PTR_HOST_TO_TADDR(host))), \ true) : false) #define DacEnumHostVPtrMem(host) \ (!DacHostPtrHasEnumMark(host) ? \ (DacEnumMemoryRegion(PTR_HOST_TO_TADDR(host), (host)->VPtrSize()), \ true) : false) // Check enumeration of 'this' and return if this has already been // enumerated. Making this the first line of an object's EnumMemoryRegions // method will prevent cycles. #define DAC_CHECK_ENUM_THIS() \ if (DacHostPtrHasEnumMark(this)) return #define DAC_ENUM_DTHIS() \ if (!DacEnumHostDPtrMem(this)) return #define DAC_ENUM_STHIS(type) \ if (!DacEnumHostSPtrMem(this, type)) return #define DAC_ENUM_VTHIS() \ if (!DacEnumHostVPtrMem(this)) return #ifdef __cplusplus } class ReflectionModule; interface IMDInternalImport* DacGetMDImport(const class PEFile* peFile, bool throwEx); interface IMDInternalImport* DacGetMDImport(const ReflectionModule* reflectionModule, bool throwEx); int DacGetIlMethodSize(TADDR methAddr); struct COR_ILMETHOD* DacGetIlMethod(TADDR methAddr); #if defined(WIN64EXCEPTIONS) struct _UNWIND_INFO * DacGetUnwindInfo(TADDR taUnwindInfo); // virtually unwind a CONTEXT out-of-process struct _KNONVOLATILE_CONTEXT_POINTERS; BOOL DacUnwindStackFrame(T_CONTEXT * pContext, T_KNONVOLATILE_CONTEXT_POINTERS* pContextPointers); #if defined(FEATURE_PAL) // call back through data target to unwind out-of-process HRESULT DacVirtualUnwind(ULONG32 threadId, PCONTEXT context, PT_KNONVOLATILE_CONTEXT_POINTERS contextPointers); #endif // FEATURE_PAL #endif // _WIN64 #ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS class SString; void DacMdCacheAddEEName(TADDR taEE, const SString& ssEEName); bool DacMdCacheGetEEName(TADDR taEE, SString & ssEEName); #endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS // // Computes (taBase + (dwIndex * dwElementSize()), with overflow checks. // // Arguments: // taBase the base TADDR value // dwIndex the index of the offset // dwElementSize the size of each element (to multiply the offset by) // // Return value: // The resulting TADDR, or throws CORDB_E_TARGET_INCONSISTENT on overlow. // // Notes: // The idea here is that overflows during address arithmetic suggest that we're operating on corrupt // pointers. It helps to improve reliability to detect the cases we can (like overflow) and fail. Note // that this is just a heuristic, not a security measure. We can't trust target data regardless - // failing on overflow is just one easy case of corruption to detect. There is no need to use checked // arithmetic everywhere in the DAC infrastructure, this is intended just for the places most likely to // help catch bugs (eg. __DPtr::operator[]). // inline TADDR DacTAddrOffset( TADDR taBase, TSIZE_T dwIndex, TSIZE_T dwElementSize ) { ClrSafeInt t(taBase); t += ClrSafeInt(dwIndex) * ClrSafeInt(dwElementSize); if( t.IsOverflow() ) { // Pointer arithmetic overflow - probably due to corrupt target data DacError(CORDBG_E_TARGET_INCONSISTENT); } return t.Value(); } // Base pointer wrapper which provides common behavior. class __TPtrBase { public: __TPtrBase(void) { // Make uninitialized pointers obvious. m_addr = (TADDR)-1; } __TPtrBase(TADDR addr) { m_addr = addr; } bool operator!() const { return m_addr == 0; } // We'd like to have an implicit conversion to bool here since the C++ // standard says all pointer types are implicitly converted to bool. // Unfortunately, that would cause ambiguous overload errors for uses // of operator== and operator!=. Instead callers will have to compare // directly against NULL. bool operator==(TADDR addr) const { return m_addr == addr; } bool operator!=(TADDR addr) const { return m_addr != addr; } bool operator<(TADDR addr) const { return m_addr < addr; } bool operator>(TADDR addr) const { return m_addr > addr; } bool operator<=(TADDR addr) const { return m_addr <= addr; } bool operator>=(TADDR addr) const { return m_addr >= addr; } TADDR GetAddr(void) const { return m_addr; } TADDR SetAddr(TADDR addr) { m_addr = addr; return addr; } protected: TADDR m_addr; }; // Pointer wrapper base class for various forms of normal data. // This has the common functionality between __DPtr and __ArrayDPtr. // The DPtrType type parameter is the actual derived type in use. This is necessary so that // inhereted functions preserve exact return types. template class __DPtrBase : public __TPtrBase { public: typedef type _Type; typedef type* _Ptr; protected: // Constructors // All protected - this type should not be used directly - use one of the derived types instead. __DPtrBase< type, DPtrType >(void) : __TPtrBase() {} __DPtrBase< type, DPtrType >(TADDR addr) : __TPtrBase(addr) {} explicit __DPtrBase< type, DPtrType >(__TPtrBase addr) { m_addr = addr.GetAddr(); } explicit __DPtrBase< type, DPtrType >(type const * host) { m_addr = DacGetTargetAddrForHostAddr(host, true); } public: DPtrType& operator=(const __TPtrBase& ptr) { m_addr = ptr.GetAddr(); return DPtrType(m_addr); } DPtrType& operator=(TADDR addr) { m_addr = addr; return DPtrType(m_addr); } type& operator*(void) const { return *(type*)DacInstantiateTypeByAddress(m_addr, sizeof(type), true); } bool operator==(const DPtrType& ptr) const { return m_addr == ptr.GetAddr(); } bool operator==(TADDR addr) const { return m_addr == addr; } bool operator!=(const DPtrType& ptr) const { return !operator==(ptr); } bool operator!=(TADDR addr) const { return m_addr != addr; } bool operator<(const DPtrType& ptr) const { return m_addr < ptr.GetAddr(); } bool operator>(const DPtrType& ptr) const { return m_addr > ptr.GetAddr(); } bool operator<=(const DPtrType& ptr) const { return m_addr <= ptr.GetAddr(); } bool operator>=(const DPtrType& ptr) const { return m_addr >= ptr.GetAddr(); } // Array index operator // we want an operator[] for all possible numeric types (rather than rely on // implicit numeric conversions on the argument) to prevent ambiguity with // DPtr's implicit conversion to type* and the built-in operator[]. // @dbgtodo : we could also use this technique to simplify other operators below. template type& operator[](indexType index) { // Compute the address of the element. TADDR elementAddr; if( index >= 0 ) { elementAddr = DacTAddrOffset(m_addr, index, sizeof(type)); } else { // Don't bother trying to do overflow checking for negative indexes - they are rare compared to // positive ones. ClrSafeInt doesn't support signed datatypes yet (although we should be able to add it // pretty easily). elementAddr = m_addr + index * sizeof(type); } // Marshal over a single instance and return a reference to it. return *(type*) DacInstantiateTypeByAddress(elementAddr, sizeof(type), true); } template type const & operator[](indexType index) const { return (*const_cast<__DPtrBase*>(this))[index]; } //------------------------------------------------------------------------- // operator+ DPtrType operator+(unsigned short val) { return DPtrType(DacTAddrOffset(m_addr, val, sizeof(type))); } DPtrType operator+(short val) { return DPtrType(m_addr + val * sizeof(type)); } // size_t is unsigned int on Win32, so we need // to ifdef here to make sure the unsigned int // and size_t overloads don't collide. size_t // is marked __w64 so a simple unsigned int // will not work on Win32, it has to be size_t. DPtrType operator+(size_t val) { return DPtrType(DacTAddrOffset(m_addr, val, sizeof(type))); } #if defined (_WIN64) DPtrType operator+(unsigned int val) { return DPtrType(DacTAddrOffset(m_addr, val, sizeof(type))); } #endif DPtrType operator+(int val) { return DPtrType(m_addr + val * sizeof(type)); } // Because of the size difference between long and int on non MS compilers, // we only need to define these operators on Windows. These provide compatible // overloads for DWORD addition operations. #ifdef _MSC_VER DPtrType operator+(unsigned long val) { return DPtrType(DacTAddrOffset(m_addr, val, sizeof(type))); } DPtrType operator+(long val) { return DPtrType(m_addr + val * sizeof(type)); } #endif //------------------------------------------------------------------------- // operator- DPtrType operator-(unsigned short val) { return DPtrType(m_addr - val * sizeof(type)); } DPtrType operator-(short val) { return DPtrType(m_addr - val * sizeof(type)); } // size_t is unsigned int on Win32, so we need // to ifdef here to make sure the unsigned int // and size_t overloads don't collide. size_t // is marked __w64 so a simple unsigned int // will not work on Win32, it has to be size_t. DPtrType operator-(size_t val) { return DPtrType(m_addr - val * sizeof(type)); } #ifdef _WIN64 DPtrType operator-(unsigned int val) { return DPtrType(m_addr - val * sizeof(type)); } #endif DPtrType operator-(int val) { return DPtrType(m_addr - val * sizeof(type)); } // Because of the size difference between long and int on non MS compilers, // we only need to define these operators on Windows. These provide compatible // overloads for DWORD addition operations. #ifdef _MSC_VER // for now, everything else is 32 bit DPtrType operator-(unsigned long val) { return DPtrType(m_addr - val * sizeof(type)); } DPtrType operator-(long val) { return DPtrType(m_addr - val * sizeof(type)); } #endif size_t operator-(const DPtrType& val) { return (m_addr - val.m_addr) / sizeof(type); } //------------------------------------------------------------------------- DPtrType& operator+=(size_t val) { m_addr += val * sizeof(type); return static_cast(*this); } DPtrType& operator-=(size_t val) { m_addr -= val * sizeof(type); return static_cast(*this); } DPtrType& operator++() { m_addr += sizeof(type); return static_cast(*this); } DPtrType& operator--() { m_addr -= sizeof(type); return static_cast(*this); } DPtrType operator++(int postfix) { DPtrType orig = DPtrType(*this); m_addr += sizeof(type); return orig; } DPtrType operator--(int postfix) { DPtrType orig = DPtrType(*this); m_addr -= sizeof(type); return orig; } bool IsValid(void) const { return m_addr && DacInstantiateTypeByAddress(m_addr, sizeof(type), false) != NULL; } void EnumMem(void) const { DacEnumMemoryRegion(m_addr, sizeof(type)); } }; // forward declaration template class __GlobalPtr; // Pointer wrapper for objects which are just plain data // and need no special handling. template class __DPtr : public __DPtrBase > { public: // constructors - all chain to __DPtrBase constructors __DPtr< type >(void) : __DPtrBase >() {} __DPtr< type >(TADDR addr) : __DPtrBase >(addr) {} // construct const from non-const typedef typename std::remove_const::type mutable_type; __DPtr< type >(__DPtr const & rhs) : __DPtrBase >(rhs.GetAddr()) {} // construct from GlobalPtr explicit __DPtr< type >(__GlobalPtr< type*, __DPtr< type > > globalPtr) : __DPtrBase >(globalPtr.GetAddr()) {} explicit __DPtr< type >(__TPtrBase addr) : __DPtrBase >(addr) {} explicit __DPtr< type >(type const * host) : __DPtrBase >(host) {} operator type*() const { return (type*)DacInstantiateTypeByAddress(this->m_addr, sizeof(type), true); } type* operator->() const { return (type*)DacInstantiateTypeByAddress(this->m_addr, sizeof(type), true); } }; #define DPTR(type) __DPtr< type > // A restricted form of DPtr that doesn't have any conversions to pointer types. // This is useful for pointer types that almost always represent arrays, as opposed // to pointers to single instances (eg. PTR_BYTE). In these cases, allowing implicit // conversions to (for eg.) BYTE* would usually result in incorrect usage (eg. pointer // arithmetic and array indexing), since only a single instance has been marshalled to the host. // If you really must marshal a single instance (eg. converting T* to PTR_T is too painful for now), // then use code:DacUnsafeMarshalSingleElement so we can identify such unsafe code. template class __ArrayDPtr : public __DPtrBase > { public: // constructors - all chain to __DPtrBase constructors __ArrayDPtr< type >(void) : __DPtrBase >() {} __ArrayDPtr< type >(TADDR addr) : __DPtrBase >(addr) {} // construct const from non-const typedef typename std::remove_const::type mutable_type; __ArrayDPtr< type >(__ArrayDPtr const & rhs) : __DPtrBase >(rhs.GetAddr()) {} explicit __ArrayDPtr< type >(__TPtrBase addr) : __DPtrBase >(addr) {} // Note that there is also no explicit constructor from host instances (type*). // Going this direction is less problematic, but often still represents risky coding. }; #define ArrayDPTR(type) __ArrayDPtr< type > // Pointer wrapper for objects which are just plain data // but whose size is not the same as the base type size. // This can be used for prefetching data for arrays or // for cases where an object has a variable size. template class __SPtr : public __TPtrBase { public: typedef type _Type; typedef type* _Ptr; __SPtr< type >(void) : __TPtrBase() {} __SPtr< type >(TADDR addr) : __TPtrBase(addr) {} explicit __SPtr< type >(__TPtrBase addr) { m_addr = addr.GetAddr(); } explicit __SPtr< type >(type* host) { m_addr = DacGetTargetAddrForHostAddr(host, true); } __SPtr< type >& operator=(const __TPtrBase& ptr) { m_addr = ptr.GetAddr(); return *this; } __SPtr< type >& operator=(TADDR addr) { m_addr = addr; return *this; } operator type*() const { if (m_addr) { return (type*)DacInstantiateTypeByAddress(m_addr, type::DacSize(m_addr), true); } else { return (type*)NULL; } } type* operator->() const { if (m_addr) { return (type*)DacInstantiateTypeByAddress(m_addr, type::DacSize(m_addr), true); } else { return (type*)NULL; } } type& operator*(void) const { if (!m_addr) { DacError(E_INVALIDARG); } return *(type*)DacInstantiateTypeByAddress(m_addr, type::DacSize(m_addr), true); } bool IsValid(void) const { return m_addr && DacInstantiateTypeByAddress(m_addr, type::DacSize(m_addr), false) != NULL; } void EnumMem(void) const { if (m_addr) { DacEnumMemoryRegion(m_addr, type::DacSize(m_addr)); } } }; #define SPTR(type) __SPtr< type > // Pointer wrapper for objects which have a single leading // vtable, such as objects in a single-inheritance tree. // The base class of all such trees must have use // VPTR_BASE_VTABLE_CLASS in their declaration and all // instantiable members of the tree must be listed in vptr_list.h. template class __VPtr : public __TPtrBase { public: // VPtr::_Type has to be a pointer as // often the type is an abstract class. // This type is not expected to be used anyway. typedef type* _Type; typedef type* _Ptr; __VPtr< type >(void) : __TPtrBase() {} __VPtr< type >(TADDR addr) : __TPtrBase(addr) {} explicit __VPtr< type >(__TPtrBase addr) { m_addr = addr.GetAddr(); } explicit __VPtr< type >(type* host) { m_addr = DacGetTargetAddrForHostAddr(host, true); } __VPtr< type >& operator=(const __TPtrBase& ptr) { m_addr = ptr.GetAddr(); return *this; } __VPtr< type >& operator=(TADDR addr) { m_addr = addr; return *this; } operator type*() const { return (type*)DacInstantiateClassByVTable(m_addr, sizeof(type), true); } type* operator->() const { return (type*)DacInstantiateClassByVTable(m_addr, sizeof(type), true); } bool operator==(const __VPtr< type >& ptr) const { return m_addr == ptr.m_addr; } bool operator==(TADDR addr) const { return m_addr == addr; } bool operator!=(const __VPtr< type >& ptr) const { return !operator==(ptr); } bool operator!=(TADDR addr) const { return m_addr != addr; } bool IsValid(void) const { return m_addr && DacInstantiateClassByVTable(m_addr, sizeof(type), false) != NULL; } void EnumMem(void) const { if (IsValid()) { DacEnumMemoryRegion(m_addr, (operator->())->VPtrSize()); } } }; #define VPTR(type) __VPtr< type > // Pointer wrapper for 8-bit strings. template class __Str8Ptr : public __DPtr { public: typedef type _Type; typedef type* _Ptr; __Str8Ptr< type, maxChars >(void) : __DPtr() {} __Str8Ptr< type, maxChars >(TADDR addr) : __DPtr(addr) {} explicit __Str8Ptr< type, maxChars >(__TPtrBase addr) { m_addr = addr.GetAddr(); } explicit __Str8Ptr< type, maxChars >(type* host) { m_addr = DacGetTargetAddrForHostAddr(host, true); } __Str8Ptr< type, maxChars >& operator=(const __TPtrBase& ptr) { m_addr = ptr.GetAddr(); return *this; } __Str8Ptr< type, maxChars >& operator=(TADDR addr) { m_addr = addr; return *this; } operator type*() const { return (type*)DacInstantiateStringA(m_addr, maxChars, true); } bool IsValid(void) const { return m_addr && DacInstantiateStringA(m_addr, maxChars, false) != NULL; } void EnumMem(void) const { char* str = DacInstantiateStringA(m_addr, maxChars, false); if (str) { DacEnumMemoryRegion(m_addr, strlen(str) + 1); } } }; #define S8PTR(type) __Str8Ptr< type > #define S8PTRMAX(type, maxChars) __Str8Ptr< type, maxChars > // Pointer wrapper for 16-bit strings. template class __Str16Ptr : public __DPtr { public: typedef type _Type; typedef type* _Ptr; __Str16Ptr< type, maxChars >(void) : __DPtr() {} __Str16Ptr< type, maxChars >(TADDR addr) : __DPtr(addr) {} explicit __Str16Ptr< type, maxChars >(__TPtrBase addr) { m_addr = addr.GetAddr(); } explicit __Str16Ptr< type, maxChars >(type* host) { m_addr = DacGetTargetAddrForHostAddr(host, true); } __Str16Ptr< type, maxChars >& operator=(const __TPtrBase& ptr) { m_addr = ptr.GetAddr(); return *this; } __Str16Ptr< type, maxChars >& operator=(TADDR addr) { m_addr = addr; return *this; } operator type*() const { return (type*)DacInstantiateStringW(m_addr, maxChars, true); } bool IsValid(void) const { return m_addr && DacInstantiateStringW(m_addr, maxChars, false) != NULL; } void EnumMem(void) const { char* str = DacInstantiateStringW(m_addr, maxChars, false); if (str) { DacEnumMemoryRegion(m_addr, strlen(str) + 1); } } }; #define S16PTR(type) __Str16Ptr< type > #define S16PTRMAX(type, maxChars) __Str16Ptr< type, maxChars > template class __GlobalVal { public: __GlobalVal< type >(PULONG rvaPtr) { m_rvaPtr = rvaPtr; } operator type() const { return (type)*__DPtr< type >(DacGlobalBase() + *m_rvaPtr); } __DPtr< type > operator&() const { return __DPtr< type >(DacGlobalBase() + *m_rvaPtr); } // @dbgtodo dac support: This updates values in the host. This seems extremely dangerous // to do silently. I'd prefer that a specific (searchable) write function // was used. Try disabling this and see what fails... __GlobalVal & operator=(const type & val) { type* ptr = __DPtr< type >(DacGlobalBase() + *m_rvaPtr); // Update the host copy; *ptr = val; // Write back to the target. DacWriteHostInstance(ptr, true); return *this; } bool IsValid(void) const { return __DPtr< type >(DacGlobalBase() + *m_rvaPtr).IsValid(); } void EnumMem(void) const { TADDR p = DacGlobalBase() + *m_rvaPtr; __DPtr< type >(p).EnumMem(); } private: PULONG m_rvaPtr; }; template class __GlobalArray { public: __GlobalArray< type, size >(PULONG rvaPtr) { m_rvaPtr = rvaPtr; } __DPtr< type > operator&() const { return __DPtr< type >(DacGlobalBase() + *m_rvaPtr); } type& operator[](unsigned int index) const { return __DPtr< type >(DacGlobalBase() + *m_rvaPtr)[index]; } bool IsValid(void) const { // Only validates the base pointer, not the full array range. return __DPtr< type >(DacGlobalBase() + *m_rvaPtr).IsValid(); } void EnumMem(void) const { DacEnumMemoryRegion(DacGlobalBase() + *m_rvaPtr, sizeof(type) * size); } private: PULONG m_rvaPtr; }; template class __GlobalPtr { public: __GlobalPtr< acc_type, store_type >(PULONG rvaPtr) { m_rvaPtr = rvaPtr; } __DPtr< store_type > operator&() const { return __DPtr< store_type >(DacGlobalBase() + *m_rvaPtr); } store_type & operator=(store_type & val) { store_type* ptr = __DPtr< store_type >(DacGlobalBase() + *m_rvaPtr); // Update the host copy; *ptr = val; // Write back to the target. DacWriteHostInstance(ptr, true); return val; } acc_type operator->() const { return (acc_type)*__DPtr< store_type >(DacGlobalBase() + *m_rvaPtr); } operator acc_type() const { return (acc_type)*__DPtr< store_type >(DacGlobalBase() + *m_rvaPtr); } operator store_type() const { return *__DPtr< store_type >(DacGlobalBase() + *m_rvaPtr); } bool operator!() const { return !*__DPtr< store_type >(DacGlobalBase() + *m_rvaPtr); } typename store_type::_Type& operator[](int index) { return (*__DPtr< store_type >(DacGlobalBase() + *m_rvaPtr))[index]; } typename store_type::_Type& operator[](unsigned int index) { return (*__DPtr< store_type >(DacGlobalBase() + *m_rvaPtr))[index]; } TADDR GetAddr() const { return (*__DPtr< store_type >(DacGlobalBase() + *m_rvaPtr)).GetAddr(); } TADDR GetAddrRaw () const { return DacGlobalBase() + *m_rvaPtr; } // This is only testing the the pointer memory is available but does not verify // the memory that it points to. // bool IsValidPtr(void) const { return __DPtr< store_type >(DacGlobalBase() + *m_rvaPtr).IsValid(); } bool IsValid(void) const { return __DPtr< store_type >(DacGlobalBase() + *m_rvaPtr).IsValid() && (*__DPtr< store_type >(DacGlobalBase() + *m_rvaPtr)).IsValid(); } void EnumMem(void) const { __DPtr< store_type > ptr(DacGlobalBase() + *m_rvaPtr); ptr.EnumMem(); if (ptr.IsValid()) { (*ptr).EnumMem(); } } PULONG m_rvaPtr; }; template inline bool operator==(const __GlobalPtr& gptr, acc_type host) { return DacGetTargetAddrForHostAddr(host, true) == *__DPtr< TADDR >(DacGlobalBase() + *gptr.m_rvaPtr); } template inline bool operator!=(const __GlobalPtr& gptr, acc_type host) { return !operator==(gptr, host); } template inline bool operator==(acc_type host, const __GlobalPtr& gptr) { return DacGetTargetAddrForHostAddr(host, true) == *__DPtr< TADDR >(DacGlobalBase() + *gptr.m_rvaPtr); } template inline bool operator!=(acc_type host, const __GlobalPtr& gptr) { return !operator==(host, gptr); } // // __VoidPtr is a type that behaves like void* but for target pointers. // Behavior of PTR_VOID: // * has void* semantics. Will compile to void* in non-DAC builds (just like // other PTR types. Unlike TADDR, we want pointer semantics. // * NOT assignable from host pointer types or convertible to host pointer // types - ensures we can't confuse host and target pointers (we'll get // compiler errors if we try and cast between them). // * like void*, no pointer arithmetic or dereferencing is allowed // * like TADDR, can be used to construct any __DPtr / __VPtr instance // * representation is the same as a void* (for marshalling / casting) // // One way in which __VoidPtr is unlike void* is that it can't be cast to // pointer or integer types. On the one hand, this is a good thing as it forces // us to keep target pointers separate from other data types. On the other hand // in practice this means we have to use dac_cast in places where we used // to use a (TADDR) cast. Unfortunately C++ provides us no way to allow the // explicit cast to primitive types without also allowing implicit conversions. // // This is very similar in spirit to TADDR. The primary difference is that // PTR_VOID has pointer semantics, where TADDR has integer semantics. When // dacizing uses of void* to TADDR, casts must be inserted everywhere back to // pointer types. If we switch a use of TADDR to PTR_VOID, those casts in // DACCESS_COMPILE regions no longer compile (see above). Also, TADDR supports // pointer arithmetic, but that might not be necessary (could use PTR_BYTE // instead etc.). Ideally we'd probably have just one type for this purpose // (named TADDR but with the semantics of PTR_VOID), but outright conversion // would require too much work. // class __VoidPtr : public __TPtrBase { public: __VoidPtr(void) : __TPtrBase() {} __VoidPtr(TADDR addr) : __TPtrBase(addr) {} // Note, unlike __DPtr, this ctor form is not explicit. We allow implicit // conversions from any pointer type (just like for void*). __VoidPtr(__TPtrBase addr) { m_addr = addr.GetAddr(); } // Like TPtrBase, VoidPtrs can also be created impicitly from all GlobalPtrs template __VoidPtr(__GlobalPtr globalPtr) { m_addr = globalPtr.GetAddr(); } // Note, unlike __DPtr, there is no explicit conversion from host pointer // types. Since void* cannot be marshalled, there is no such thing as // a void* DAC instance in the host. // Also, we don't want an implicit conversion to TADDR because then the // compiler will allow pointer arithmetic (which it wouldn't allow for // void*). Instead, callers can use dac_cast if they want. // Note, unlike __DPtr, any pointer type can be assigned to a __VoidPtr // This is to mirror the assignability of any pointer type to a void* __VoidPtr& operator=(const __TPtrBase& ptr) { m_addr = ptr.GetAddr(); return *this; } __VoidPtr& operator=(TADDR addr) { m_addr = addr; return *this; } // note, no marshalling operators (type* conversion, operator ->, operator*) // A void* can't be marshalled because we don't know how much to copy // PTR_Void can be compared to any other pointer type (because conceptually, // any other pointer type should be implicitly convertible to void*) bool operator==(const __TPtrBase& ptr) const { return m_addr == ptr.GetAddr(); } bool operator==(TADDR addr) const { return m_addr == addr; } bool operator!=(const __TPtrBase& ptr) const { return !operator==(ptr); } bool operator!=(TADDR addr) const { return m_addr != addr; } bool operator<(const __TPtrBase& ptr) const { return m_addr < ptr.GetAddr(); } bool operator>(const __TPtrBase& ptr) const { return m_addr > ptr.GetAddr(); } bool operator<=(const __TPtrBase& ptr) const { return m_addr <= ptr.GetAddr(); } bool operator>=(const __TPtrBase& ptr) const { return m_addr >= ptr.GetAddr(); } }; typedef __VoidPtr PTR_VOID; typedef DPTR(PTR_VOID) PTR_PTR_VOID; // For now we treat pointers to const and non-const void the same in DAC // builds. In general, DAC is read-only anyway and so there isn't a danger of // writing to these pointers. Also, the non-dac builds will ensure // const-correctness. However, if we wanted to support true void* / const void* // behavior, we could probably build the follow functionality by templating // __VoidPtr: // * A PTR_VOID would be implicitly convertable to PTR_CVOID // * An explicit coercion (ideally const_cast) would be required to convert a // PTR_CVOID to a PTR_VOID // * Similarily, an explicit coercion would be required to convert a cost PTR // type (eg. PTR_CBYTE) to a PTR_VOID. typedef __VoidPtr PTR_CVOID; // The special empty ctor declared here allows the whole // class hierarchy to be instantiated easily by the // external access code. The actual class body will be // read externally so no members should be initialized. // // VPTR_ANY_CLASS_METHODS - Defines the following methods for all VPTR classes // // VPtrSize // Returns the size of the dynamic type of the object (as opposed to sizeof // which is based only on the static type). // // VPtrHostVTable // Returns the address of the vtable for this type. // We create a temporary instance of this type in order to read it's vtable pointer // (at offset 0). For this temporary instance, we do not want to initialize any fields, // so we use the marshalling ctor. Since we didn't initialize any fields, we also don't // wan't to run the dtor (marshaled data structures don't normally expect their destructor // or non-DAC constructors to be called in DAC builds anyway). So, rather than create a // normal stack object, or put the object on the heap, we create the temporary object // on the stack using placement-new and alloca, and don't destruct it. // #define VPTR_ANY_CLASS_METHODS(name) \ virtual ULONG32 VPtrSize(void) { SUPPORTS_DAC; return sizeof(name); } \ static PVOID VPtrHostVTable() { \ void * pBuf = _alloca(sizeof(name)); \ name * dummy = new (pBuf) name((TADDR)0, (TADDR)0); \ return *((PVOID*)dummy); } #define VPTR_CLASS_METHODS(name) \ VPTR_ANY_CLASS_METHODS(name) \ static TADDR VPtrTargetVTable() { \ SUPPORTS_DAC; \ return DacGlobalBase() + g_dacGlobals.name##__vtAddr; } #define VPTR_MULTI_CLASS_METHODS(name, keyBase) \ VPTR_ANY_CLASS_METHODS(name) \ static TADDR VPtrTargetVTable() { \ SUPPORTS_DAC; \ return DacGlobalBase() + g_dacGlobals.name##__##keyBase##__mvtAddr; } #define VPTR_VTABLE_CLASS(name, base) \ public: name(TADDR addr, TADDR vtAddr) : base(addr, vtAddr) {} \ VPTR_CLASS_METHODS(name) #define VPTR_VTABLE_CLASS_AND_CTOR(name, base) \ VPTR_VTABLE_CLASS(name, base) #define VPTR_MULTI_VTABLE_CLASS(name, base) \ public: name(TADDR addr, TADDR vtAddr) : base(addr, vtAddr) {} \ VPTR_MULTI_CLASS_METHODS(name, base) // Used for base classes that can be instantiated directly. // The fake vfn is still used to force a vtable even when // all the normal vfns are ifdef'ed out. #define VPTR_BASE_CONCRETE_VTABLE_CLASS(name) \ public: name(TADDR addr, TADDR vtAddr) {} \ VPTR_CLASS_METHODS(name) #define VPTR_BASE_CONCRETE_VTABLE_CLASS_NO_CTOR_BODY(name) \ public: name(TADDR addr, TADDR vtAddr); \ VPTR_CLASS_METHODS(name) // The pure virtual method forces all derivations to use // VPTR_VTABLE_CLASS to compile. #define VPTR_BASE_VTABLE_CLASS(name) \ public: name(TADDR addr, TADDR vtAddr) {} \ virtual ULONG32 VPtrSize(void) = 0; #define VPTR_BASE_VTABLE_CLASS_AND_CTOR(name) \ VPTR_BASE_VTABLE_CLASS(name) #define VPTR_BASE_VTABLE_CLASS_NO_CTOR_BODY(name) \ public: name(TADDR addr, TADDR vtAddr); \ virtual ULONG32 VPtrSize(void) = 0; #define VPTR_ABSTRACT_VTABLE_CLASS(name, base) \ public: name(TADDR addr, TADDR vtAddr) : base(addr, vtAddr) {} #define VPTR_ABSTRACT_VTABLE_CLASS_AND_CTOR(name, base) \ VPTR_ABSTRACT_VTABLE_CLASS(name, base) #define VPTR_ABSTRACT_VTABLE_CLASS_NO_CTOR_BODY(name, base) \ public: name(TADDR addr, TADDR vtAddr); // helper macro to make the vtables unique for DAC #define VPTR_UNIQUE(unique) // Safe access for retrieving the target address of a PTR. #define PTR_TO_TADDR(ptr) ((ptr).GetAddr()) #define GFN_TADDR(name) (DacGlobalBase() + g_dacGlobals.fn__ ## name) // ROTORTODO - g++ 3 doesn't like the use of the operator& in __GlobalVal // here. Putting GVAL_ADDR in to get things to compile while I discuss // this matter with the g++ authors. #define GVAL_ADDR(g) \ ((g).operator&()) // // References to class static and global data. // These all need to be redirected through the global // data table. // #define _SPTR_DECL(acc_type, store_type, var) \ static __GlobalPtr< acc_type, store_type > var #define _SPTR_IMPL(acc_type, store_type, cls, var) \ __GlobalPtr< acc_type, store_type > cls::var(&g_dacGlobals.cls##__##var) #define _SPTR_IMPL_INIT(acc_type, store_type, cls, var, init) \ __GlobalPtr< acc_type, store_type > cls::var(&g_dacGlobals.cls##__##var) #define _SPTR_IMPL_NS(acc_type, store_type, ns, cls, var) \ __GlobalPtr< acc_type, store_type > cls::var(&g_dacGlobals.ns##__##cls##__##var) #define _SPTR_IMPL_NS_INIT(acc_type, store_type, ns, cls, var, init) \ __GlobalPtr< acc_type, store_type > cls::var(&g_dacGlobals.ns##__##cls##__##var) #define _GPTR_DECL(acc_type, store_type, var) \ extern __GlobalPtr< acc_type, store_type > var #define _GPTR_IMPL(acc_type, store_type, var) \ __GlobalPtr< acc_type, store_type > var(&g_dacGlobals.dac__##var) #define _GPTR_IMPL_INIT(acc_type, store_type, var, init) \ __GlobalPtr< acc_type, store_type > var(&g_dacGlobals.dac__##var) #define SVAL_DECL(type, var) \ static __GlobalVal< type > var #define SVAL_IMPL(type, cls, var) \ __GlobalVal< type > cls::var(&g_dacGlobals.cls##__##var) #define SVAL_IMPL_INIT(type, cls, var, init) \ __GlobalVal< type > cls::var(&g_dacGlobals.cls##__##var) #define SVAL_IMPL_NS(type, ns, cls, var) \ __GlobalVal< type > cls::var(&g_dacGlobals.ns##__##cls##__##var) #define SVAL_IMPL_NS_INIT(type, ns, cls, var, init) \ __GlobalVal< type > cls::var(&g_dacGlobals.ns##__##cls##__##var) #define GVAL_DECL(type, var) \ extern __GlobalVal< type > var #define GVAL_IMPL(type, var) \ __GlobalVal< type > var(&g_dacGlobals.dac__##var) #define GVAL_IMPL_INIT(type, var, init) \ __GlobalVal< type > var(&g_dacGlobals.dac__##var) #define GARY_DECL(type, var, size) \ extern __GlobalArray< type, size > var #define GARY_IMPL(type, var, size) \ __GlobalArray< type, size > var(&g_dacGlobals.dac__##var) // Translation from a host pointer back to the target address // that was used to retrieve the data for the host pointer. #define PTR_HOST_TO_TADDR(host) DacGetTargetAddrForHostAddr(host, true) // Translation from a host interior pointer back to the corresponding // target address. The host address must reside within a previously // retrieved instance. #define PTR_HOST_INT_TO_TADDR(host) DacGetTargetAddrForHostInteriorAddr(host, true) // Translation from a host vtable pointer to a target vtable pointer. #define VPTR_HOST_VTABLE_TO_TADDR(host) DacGetTargetVtForHostVt(host, true) // Construct a pointer to a member of the given type. #define PTR_HOST_MEMBER_TADDR(type, host, memb) \ (PTR_HOST_TO_TADDR(host) + (TADDR)offsetof(type, memb)) // Construct a pointer to a member of the given type given an interior // host address. #define PTR_HOST_INT_MEMBER_TADDR(type, host, memb) \ (PTR_HOST_INT_TO_TADDR(host) + (TADDR)offsetof(type, memb)) #define PTR_TO_MEMBER_TADDR(type, ptr, memb) \ (PTR_TO_TADDR(ptr) + (TADDR)offsetof(type, memb)) // Constructs an arbitrary data instance for a piece of // memory in the target. #define PTR_READ(addr, size) \ DacInstantiateTypeByAddress(addr, size, true) // This value is used to intiailize target pointers to NULL. We want this to be TADDR type // (as opposed to, say, __TPtrBase) so that it can be used in the non-explicit ctor overloads, // eg. as an argument default value. // We can't always just use NULL because that's 0 which (in C++) can be any integer or pointer // type (causing an ambiguous overload compiler error when used in explicit ctor forms). #define PTR_NULL ((TADDR)0) // Provides an empty method implementation when compiled // for DACCESS_COMPILE. For example, use to stub out methods needed // for vtable entries but otherwise unused. // Note that these functions are explicitly NOT marked SUPPORTS_DAC so that we'll get a // DacCop warning if any calls to them are detected. // @dbgtodo : It's probably almost always wrong to call any such function, so // we should probably throw a better error (DacNotImpl), and ideally mark the function // DECLSPEC_NORETURN so we don't have to deal with fabricating return values and we can // get compiler warnings (unreachable code) anytime functions marked this way are called. #define DAC_EMPTY() { LIMITED_METHOD_CONTRACT; } #define DAC_EMPTY_ERR() { LIMITED_METHOD_CONTRACT; DacError(E_UNEXPECTED); } #define DAC_EMPTY_RET(retVal) { LIMITED_METHOD_CONTRACT; DacError(E_UNEXPECTED); return retVal; } #define DAC_UNEXPECTED() { LIMITED_METHOD_CONTRACT; DacError_NoRet(E_UNEXPECTED); } #endif // #ifdef __cplusplus // Implementation details for dac_cast, should never be accessed directly. // See code:dac_cast for details and discussion. namespace dac_imp { // Helper functions to get the target address of specific types inline TADDR getTaddr(TADDR addr) { return addr; } inline TADDR getTaddr(__TPtrBase const &tptr) { return PTR_TO_TADDR(tptr); } inline TADDR getTaddr(void const * host) { return PTR_HOST_TO_TADDR((void *)host); } template inline TADDR getTaddr(__GlobalPtr const &gptr) { return PTR_TO_TADDR(gptr); } // It is an error to try dac_cast on a __GlobalVal or a __GlobalArray. Declare // but do not define the methods so that a compile-time error results. template TADDR getTaddr(__GlobalVal const &gval); template TADDR getTaddr(__GlobalArray const &garr); // Helper class to instantiate DAC instances from a TADDR // The default implementation assumes we want to create an instance of a PTR type template struct makeDacInst { static inline T fromTaddr(TADDR addr) { static_assert((std::is_base_of<__TPtrBase, T>::value), "is_base_of constraint violation"); return T(addr); } }; // Partial specialization for creating TADDRs // This is the only other way to create a DAC type instance other than PTR types (above) template<> struct makeDacInst { static inline TADDR fromTaddr(TADDR addr) { return addr; } }; } // namespace dac_imp // DacCop in-line exclusion mechanism // Warnings - official home is DacCop\Shared\Warnings.cs, but we want a way for users to indicate // warning codes in a way that is descriptive to readers (not just code numbers). The names here // don't matter - DacCop just looks at the value enum DacCopWarningCode { // General Rules FieldAccess = 1, PointerArith = 2, PointerComparison = 3, InconsistentMarshalling = 4, CastBetweenAddressSpaces = 5, CastOfMarshalledType = 6, VirtualCallToNonVPtr = 7, UndacizedGlobalVariable = 8, // Function graph related CallUnknown = 701, CallNonDac = 702, CallVirtualUnknown = 704, CallVirtualNonDac = 705, }; // DACCOP_IGNORE is a mechanism to suppress DacCop violations from within the source-code. // See the DacCop wiki for guidance on how best to use this: http://mswikis/clr/dev/Pages/DacCop.aspx // // DACCOP_IGNORE will suppress a DacCop violation for the following (non-compound) statement. // For example: // // The "dual-mode DAC problem" occurs in a few places where a class is used both // // in the host, and marshalled from the target ... // DACCOP_IGNORE(CastBetweenAddressSpaces,"SBuffer has the dual-mode DAC problem"); // TADDR bufAddr = (TADDR)m_buffer; // // A call to DACCOP_IGNORE must occur as it's own statement, and can apply only to following // single-statements (not to compound statement blocks). Occasionally it is necessary to hoist // violation-inducing code out to its own statement (e.g., if it occurs in the conditional of an // if). // // Arguments: // code: a literal value from DacCopWarningCode indicating which violation should be suppressed. // szReasonString: a short description of why this exclusion is necessary. This is intended just // to help readers of the code understand the source of the problem, and what would be required // to fix it. More details can be provided in comments if desired. // inline void DACCOP_IGNORE(DacCopWarningCode code, const char * szReasonString) { // DacCop detects calls to this function. No implementation is necessary. } #else // #ifdef DACCESS_COMPILE // // This version of the macros turns into normal pointers // for unmodified in-proc compilation. // ******************************************************* // !!!!!!!!!!!!!!!!!!!!!!!!!NOTE!!!!!!!!!!!!!!!!!!!!!!!!!! // // Please search this file for the type name to find the // DAC versions of these definitions // // !!!!!!!!!!!!!!!!!!!!!!!!!NOTE!!!!!!!!!!!!!!!!!!!!!!!!!! // ******************************************************* // Declare TADDR as a non-pointer type so that arithmetic // can be done on it directly, as with the DACCESS_COMPILE definition. // This also helps expose pointer usage that may need to be changed. typedef ULONG_PTR TADDR; typedef void* PTR_VOID; typedef LPVOID* PTR_PTR_VOID; typedef const void* PTR_CVOID; #define DPTR(type) type* #define ArrayDPTR(type) type* #define SPTR(type) type* #define VPTR(type) type* #define S8PTR(type) type* #define S8PTRMAX(type, maxChars) type* #define S16PTR(type) type* #define S16PTRMAX(type, maxChars) type* #if defined(FEATURE_PAL) #define VPTR_VTABLE_CLASS(name, base) \ friend struct _DacGlobals; \ public: name(int dummy) : base(dummy) {} #define VPTR_VTABLE_CLASS_AND_CTOR(name, base) \ VPTR_VTABLE_CLASS(name, base) \ name() : base() {} #define VPTR_MULTI_VTABLE_CLASS(name, base) \ friend struct _DacGlobals; \ public: name(int dummy) : base(dummy) {} #define VPTR_BASE_CONCRETE_VTABLE_CLASS(name) \ friend struct _DacGlobals; \ public: name(int dummy) {} #define VPTR_BASE_VTABLE_CLASS(name) \ friend struct _DacGlobals; \ public: name(int dummy) {} #define VPTR_BASE_VTABLE_CLASS_AND_CTOR(name) \ VPTR_BASE_VTABLE_CLASS(name) \ name() {} #define VPTR_ABSTRACT_VTABLE_CLASS(name, base) \ friend struct _DacGlobals; \ public: name(int dummy) : base(dummy) {} #define VPTR_ABSTRACT_VTABLE_CLASS_AND_CTOR(name, base) \ VPTR_ABSTRACT_VTABLE_CLASS(name, base) \ name() : base() {} #else // FEATURE_PAL #define VPTR_VTABLE_CLASS(name, base) #define VPTR_VTABLE_CLASS_AND_CTOR(name, base) #define VPTR_MULTI_VTABLE_CLASS(name, base) #define VPTR_BASE_CONCRETE_VTABLE_CLASS(name) #define VPTR_BASE_VTABLE_CLASS(name) #define VPTR_BASE_VTABLE_CLASS_AND_CTOR(name) #define VPTR_ABSTRACT_VTABLE_CLASS(name, base) #define VPTR_ABSTRACT_VTABLE_CLASS_AND_CTOR(name, base) #endif // FEATURE_PAL // helper macro to make the vtables unique for DAC #define VPTR_UNIQUE(unique) virtual int MakeVTableUniqueForDAC() { STATIC_CONTRACT_SO_TOLERANT; return unique; } #define VPTR_UNIQUE_BaseDomain (100000) #define VPTR_UNIQUE_SystemDomain (VPTR_UNIQUE_BaseDomain + 1) #define VPTR_UNIQUE_ComMethodFrame (VPTR_UNIQUE_SystemDomain + 1) #define VPTR_UNIQUE_StubHelperFrame (VPTR_UNIQUE_ComMethodFrame + 1) #define VPTR_UNIQUE_RedirectedThreadFrame (VPTR_UNIQUE_StubHelperFrame + 1) #define VPTR_UNIQUE_HijackFrame (VPTR_UNIQUE_RedirectedThreadFrame + 1) #define PTR_TO_TADDR(ptr) ((TADDR)(ptr)) #define GFN_TADDR(name) ((TADDR)(name)) #define GVAL_ADDR(g) (&(g)) #define _SPTR_DECL(acc_type, store_type, var) \ static store_type var #define _SPTR_IMPL(acc_type, store_type, cls, var) \ store_type cls::var #define _SPTR_IMPL_INIT(acc_type, store_type, cls, var, init) \ store_type cls::var = init #define _SPTR_IMPL_NS(acc_type, store_type, ns, cls, var) \ store_type cls::var #define _SPTR_IMPL_NS_INIT(acc_type, store_type, ns, cls, var, init) \ store_type cls::var = init #define _GPTR_DECL(acc_type, store_type, var) \ extern store_type var #define _GPTR_IMPL(acc_type, store_type, var) \ store_type var #define _GPTR_IMPL_INIT(acc_type, store_type, var, init) \ store_type var = init #define SVAL_DECL(type, var) \ static type var #define SVAL_IMPL(type, cls, var) \ type cls::var #define SVAL_IMPL_INIT(type, cls, var, init) \ type cls::var = init #define SVAL_IMPL_NS(type, ns, cls, var) \ type cls::var #define SVAL_IMPL_NS_INIT(type, ns, cls, var, init) \ type cls::var = init #define GVAL_DECL(type, var) \ extern type var #define GVAL_IMPL(type, var) \ type var #define GVAL_IMPL_INIT(type, var, init) \ type var = init #define GARY_DECL(type, var, size) \ extern type var[size] #define GARY_IMPL(type, var, size) \ type var[size] #define PTR_HOST_TO_TADDR(host) ((TADDR)(host)) #define PTR_HOST_INT_TO_TADDR(host) ((TADDR)(host)) #define VPTR_HOST_VTABLE_TO_TADDR(host) ((TADDR)(host)) #define PTR_HOST_MEMBER_TADDR(type, host, memb) ((TADDR)&(host)->memb) #define PTR_HOST_INT_MEMBER_TADDR(type, host, memb) ((TADDR)&(host)->memb) #define PTR_TO_MEMBER_TADDR(type, ptr, memb) ((TADDR)&((ptr)->memb)) #define PTR_READ(addr, size) ((PVOID)(addr)) #define PTR_NULL NULL #define DAC_EMPTY() #define DAC_EMPTY_ERR() #define DAC_EMPTY_RET(retVal) #define DAC_UNEXPECTED() #define DACCOP_IGNORE(warningCode, reasonString) #endif // #ifdef DACCESS_COMPILE //---------------------------------------------------------------------------- // dac_cast // Casting utility, to be used for casting one class pointer type to another. // Use as you would use static_cast // // dac_cast is designed to act just as static_cast does when // dealing with pointers and their DAC abstractions. Specifically, // it handles these coversions: // // dac_cast(SourceTypeVal) // // where TargetType <- SourceTypeVal are // // ?PTR(Tgt) <- TADDR - Create PTR type (DPtr etc.) from TADDR // ?PTR(Tgt) <- ?PTR(Src) - Convert one PTR type to another // ?PTR(Tgt) <- Src * - Create PTR type from dac host object instance // TADDR <- ?PTR(Src) - Get TADDR of PTR object (DPtr etc.) // TADDR <- Src * - Get TADDR of dac host object instance // // Note that there is no direct convertion to other host-pointer types (because we don't // know if you want a DPTR or VPTR etc.). However, due to the implicit DAC conversions, // you can just use dac_cast and assign that to a Foo*. // // The beauty of this syntax is that it is consistent regardless // of source and target casting types. You just use dac_cast // and the partial template specialization will do the right thing. // // One important thing to realise is that all "Foo *" types are // assumed to be pointers to host instances that were marshalled by DAC. This should // fail at runtime if it's not the case. // // Some examples would be: // // - Host pointer of one type to a related host pointer of another // type, i.e., MethodDesc * <-> InstantiatedMethodDesc * // Syntax: with MethodDesc *pMD, InstantiatedMethodDesc *pInstMD // pInstMd = dac_cast(pMD) // pMD = dac_cast(pInstMD) // // - (D|V)PTR of one encapsulated pointer type to a (D|V)PTR of // another type, i.e., PTR_AppDomain <-> PTR_BaseDomain // Syntax: with PTR_AppDomain pAD, PTR_BaseDomain pBD // dac_cast(pBD) // dac_cast(pAD) // // Example comparsions of some old and new syntax, where // h is a host pointer, such as "Foo *h;" // p is a DPTR, such as "PTR_Foo p;" // // PTR_HOST_TO_TADDR(h) ==> dac_cast(h) // PTR_TO_TADDR(p) ==> dac_cast(p) // PTR_Foo(PTR_HOST_TO_TADDR(h)) ==> dac_cast(h) // //---------------------------------------------------------------------------- template inline Tgt dac_cast(Src src) { #ifdef DACCESS_COMPILE // In DAC builds, first get a TADDR for the source, then create the // appropriate destination instance. TADDR addr = dac_imp::getTaddr(src); return dac_imp::makeDacInst::fromTaddr(addr); #else // In non-DAC builds, dac_cast is the same as a C-style cast because we need to support: // - casting away const // - conversions between pointers and TADDR // Perhaps we should more precisely restrict it's usage, but we get the precise // restrictions in DAC builds, so it wouldn't buy us much. return (Tgt)(src); #endif } //---------------------------------------------------------------------------- // // Convenience macros which work for either mode. // //---------------------------------------------------------------------------- #define SPTR_DECL(type, var) _SPTR_DECL(type*, PTR_##type, var) #define SPTR_IMPL(type, cls, var) _SPTR_IMPL(type*, PTR_##type, cls, var) #define SPTR_IMPL_INIT(type, cls, var, init) _SPTR_IMPL_INIT(type*, PTR_##type, cls, var, init) #define SPTR_IMPL_NS(type, ns, cls, var) _SPTR_IMPL_NS(type*, PTR_##type, ns, cls, var) #define SPTR_IMPL_NS_INIT(type, ns, cls, var, init) _SPTR_IMPL_NS_INIT(type*, PTR_##type, ns, cls, var, init) #define GPTR_DECL(type, var) _GPTR_DECL(type*, PTR_##type, var) #define GPTR_IMPL(type, var) _GPTR_IMPL(type*, PTR_##type, var) #define GPTR_IMPL_INIT(type, var, init) _GPTR_IMPL_INIT(type*, PTR_##type, var, init) // If you want to marshal a single instance of an ArrayDPtr over to the host and // return a pointer to it, you can use this function. However, this is unsafe because // users of value may assume they can do pointer arithmetic on it. This is exactly // the bugs ArrayDPtr is designed to prevent. See code:__ArrayDPtr for details. template inline type* DacUnsafeMarshalSingleElement( ArrayDPTR(type) arrayPtr ) { return (DPTR(type))(arrayPtr); } //---------------------------------------------------------------------------- // // Forward typedefs for system types. This is a convenient place // to declare things for system types, plus it gives us a central // place to look at when deciding what types may cause issues for // cross-platform compilation. // //---------------------------------------------------------------------------- typedef ArrayDPTR(BYTE) PTR_BYTE; typedef ArrayDPTR(uint8_t) PTR_uint8_t; typedef DPTR(PTR_BYTE) PTR_PTR_BYTE; typedef DPTR(PTR_uint8_t) PTR_PTR_uint8_t; typedef DPTR(PTR_PTR_BYTE) PTR_PTR_PTR_BYTE; typedef ArrayDPTR(signed char) PTR_SBYTE; typedef ArrayDPTR(const BYTE) PTR_CBYTE; typedef DPTR(INT8) PTR_INT8; typedef DPTR(INT16) PTR_INT16; typedef DPTR(WORD) PTR_WORD; typedef DPTR(USHORT) PTR_USHORT; typedef DPTR(DWORD) PTR_DWORD; typedef DPTR(uint32_t) PTR_uint32_t; typedef DPTR(LONG) PTR_LONG; typedef DPTR(ULONG) PTR_ULONG; typedef DPTR(INT32) PTR_INT32; typedef DPTR(UINT32) PTR_UINT32; typedef DPTR(ULONG64) PTR_ULONG64; typedef DPTR(INT64) PTR_INT64; typedef DPTR(UINT64) PTR_UINT64; typedef DPTR(SIZE_T) PTR_SIZE_T; typedef DPTR(size_t) PTR_size_t; typedef DPTR(TADDR) PTR_TADDR; typedef DPTR(int) PTR_int; typedef DPTR(BOOL) PTR_BOOL; typedef DPTR(unsigned) PTR_unsigned; typedef S8PTR(char) PTR_STR; typedef S8PTR(const char) PTR_CSTR; typedef S8PTR(char) PTR_UTF8; typedef S8PTR(const char) PTR_CUTF8; typedef S16PTR(WCHAR) PTR_WSTR; typedef S16PTR(const WCHAR) PTR_CWSTR; typedef DPTR(T_CONTEXT) PTR_CONTEXT; typedef DPTR(PTR_CONTEXT) PTR_PTR_CONTEXT; typedef DPTR(struct _EXCEPTION_POINTERS) PTR_EXCEPTION_POINTERS; typedef DPTR(struct _EXCEPTION_RECORD) PTR_EXCEPTION_RECORD; typedef DPTR(struct _EXCEPTION_REGISTRATION_RECORD) PTR_EXCEPTION_REGISTRATION_RECORD; typedef DPTR(struct IMAGE_COR_VTABLEFIXUP) PTR_IMAGE_COR_VTABLEFIXUP; typedef DPTR(IMAGE_DATA_DIRECTORY) PTR_IMAGE_DATA_DIRECTORY; typedef DPTR(IMAGE_DEBUG_DIRECTORY) PTR_IMAGE_DEBUG_DIRECTORY; typedef DPTR(IMAGE_DOS_HEADER) PTR_IMAGE_DOS_HEADER; typedef DPTR(IMAGE_NT_HEADERS) PTR_IMAGE_NT_HEADERS; typedef DPTR(IMAGE_NT_HEADERS32) PTR_IMAGE_NT_HEADERS32; typedef DPTR(IMAGE_NT_HEADERS64) PTR_IMAGE_NT_HEADERS64; typedef DPTR(IMAGE_SECTION_HEADER) PTR_IMAGE_SECTION_HEADER; typedef DPTR(IMAGE_TLS_DIRECTORY) PTR_IMAGE_TLS_DIRECTORY; #if defined(DACCESS_COMPILE) #include #include #include #endif #ifdef _WIN64 typedef DPTR(T_RUNTIME_FUNCTION) PTR_RUNTIME_FUNCTION; typedef DPTR(struct _UNWIND_INFO) PTR_UNWIND_INFO; #if defined(_TARGET_AMD64_) typedef DPTR(union _UNWIND_CODE) PTR_UNWIND_CODE; #endif // _TARGET_AMD64_ #endif // _WIN64 #ifdef _TARGET_ARM_ typedef DPTR(RUNTIME_FUNCTION) PTR_RUNTIME_FUNCTION; #endif //---------------------------------------------------------------------------- // // A PCODE is a valid PC/IP value -- a pointer to an instruction, possibly including some processor mode bits. // (On ARM, for example, a PCODE value should should have the low-order THUMB_CODE bit set if the code should // be executed in that mode.) // typedef TADDR PCODE; typedef DPTR(PCODE) PTR_PCODE; typedef DPTR(PTR_PCODE) PTR_PTR_PCODE; // There is another concept we should have, "pointer to the start of an instruction" -- a PCODE with any mode bits masked off. // Attempts to introduce this concept, and classify uses of PCODE as one or the other, // turned out to be too hard: either name choice required *many* code changes, and decisions in unfamiliar code. So despite the // the comment above, the PCODE is currently sometimes used for the PINSTR concept. // See PCODEToPINSTR in utilcode.h for conversion from PCODE to PINSTR. //---------------------------------------------------------------------------- // // The access code compile must compile data structures that exactly // match the real structures for access to work. The access code // doesn't want all of the debugging validation code, though, so // distinguish between _DEBUG, for declaring general debugging data // and always-on debug code, and _DEBUG_IMPL, for debugging code // which will be disabled when compiling for external access. // //---------------------------------------------------------------------------- #if !defined(_DEBUG_IMPL) && defined(_DEBUG) && !defined(DACCESS_COMPILE) #define _DEBUG_IMPL 1 #endif // Helper macro for tracking EnumMemoryRegions progress. #if 0 #define EMEM_OUT(args) DacWarning args #else #define EMEM_OUT(args) #endif // Macros like MAIN_CLR_MODULE_NAME* for the DAC module #ifdef FEATURE_MAIN_CLR_MODULE_USES_CORE_NAME #define MAIN_DAC_MODULE_NAME_W W("mscordaccore") #define MAIN_DAC_MODULE_DLL_NAME_W W("mscordaccore.dll") #else #define MAIN_DAC_MODULE_NAME_W W("mscordacwks") #define MAIN_DAC_MODULE_DLL_NAME_W W("mscordacwks.dll") #endif // TARGET_CONSISTENCY_CHECK represents a condition that should not fail unless the DAC target is corrupt. // This is in contrast to ASSERTs in DAC infrastructure code which shouldn't fail regardless of the memory // read from the target. At the moment we treat these the same, but in the future we will want a mechanism // for disabling just the target consistency checks (eg. for tests that intentionally use corrupted targets). // @dbgtodo : Separating asserts and target consistency checks is tracked by DevDiv Bugs 31674 #define TARGET_CONSISTENCY_CHECK(expr,msg) _ASSERTE_MSG(expr,msg) #endif // #ifndef __daccess_h__