diff options
author | Jiyoung Yun <jy910.yun@samsung.com> | 2016-11-23 19:09:09 +0900 |
---|---|---|
committer | Jiyoung Yun <jy910.yun@samsung.com> | 2016-11-23 19:09:09 +0900 |
commit | 4b4aad7217d3292650e77eec2cf4c198ea9c3b4b (patch) | |
tree | 98110734c91668dfdbb126fcc0e15ddbd93738ca /src/inc/daccess.h | |
parent | fa45f57ed55137c75ac870356a1b8f76c84b229c (diff) | |
download | coreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.tar.gz coreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.tar.bz2 coreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.zip |
Imported Upstream version 1.1.0upstream/1.1.0
Diffstat (limited to 'src/inc/daccess.h')
-rw-r--r-- | src/inc/daccess.h | 2463 |
1 files changed, 2463 insertions, 0 deletions
diff --git a/src/inc/daccess.h b/src/inc/daccess.h new file mode 100644 index 0000000000..b4c5044bd9 --- /dev/null +++ b/src/inc/daccess.h @@ -0,0 +1,2463 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more 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<TADDR> 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<TADDR> 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<TADDR>(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<T>) 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 <stdint.h> + +#include "switches.h" +#include "safemath.h" +#include "corerror.h" + +#ifndef __in +#include <specstrings.h> +#endif + +#define DACCESS_TABLE_RESOURCE "COREXTERNALDATAACCESSRESOURCE" + +#ifdef PAL_STDCPP_COMPAT +#include <type_traits> +#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 <vptr_list.h> +#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<TADDR> t(taBase); + t += ClrSafeInt<TSIZE_T>(dwIndex) * ClrSafeInt<TSIZE_T>(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<typename type, typename DPtrType> +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<typename indexType> + 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<typename indexType> + 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<DPtrType&>(*this); + } + DPtrType& operator-=(size_t val) + { + m_addr -= val * sizeof(type); + return static_cast<DPtrType&>(*this); + } + + DPtrType& operator++() + { + m_addr += sizeof(type); + return static_cast<DPtrType&>(*this); + } + DPtrType& operator--() + { + m_addr -= sizeof(type); + return static_cast<DPtrType&>(*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<typename acc_type, typename store_type> +class __GlobalPtr; + +// Pointer wrapper for objects which are just plain data +// and need no special handling. +template<typename type> +class __DPtr : public __DPtrBase<type,__DPtr<type> > +{ +public: + // constructors - all chain to __DPtrBase constructors + __DPtr< type >(void) : __DPtrBase<type,__DPtr<type> >() {} + __DPtr< type >(TADDR addr) : __DPtrBase<type,__DPtr<type> >(addr) {} + + // construct const from non-const + typedef typename std::remove_const<type>::type mutable_type; + __DPtr< type >(__DPtr<mutable_type> const & rhs) : __DPtrBase<type,__DPtr<type> >(rhs.GetAddr()) {} + + // construct from GlobalPtr + explicit __DPtr< type >(__GlobalPtr< type*, __DPtr< type > > globalPtr) : + __DPtrBase<type,__DPtr<type> >(globalPtr.GetAddr()) {} + + explicit __DPtr< type >(__TPtrBase addr) : __DPtrBase<type,__DPtr<type> >(addr) {} + explicit __DPtr< type >(type const * host) : __DPtrBase<type,__DPtr<type> >(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<typename type> +class __ArrayDPtr : public __DPtrBase<type,__ArrayDPtr<type> > +{ +public: + // constructors - all chain to __DPtrBase constructors + __ArrayDPtr< type >(void) : __DPtrBase<type,__ArrayDPtr<type> >() {} + __ArrayDPtr< type >(TADDR addr) : __DPtrBase<type,__ArrayDPtr<type> >(addr) {} + + // construct const from non-const + typedef typename std::remove_const<type>::type mutable_type; + __ArrayDPtr< type >(__ArrayDPtr<mutable_type> const & rhs) : __DPtrBase<type,__ArrayDPtr<type> >(rhs.GetAddr()) {} + + explicit __ArrayDPtr< type >(__TPtrBase addr) : __DPtrBase<type,__ArrayDPtr<type> >(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<typename type> +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 type> +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<typename type, ULONG32 maxChars = 32760> +class __Str8Ptr : public __DPtr<char> +{ +public: + typedef type _Type; + typedef type* _Ptr; + + __Str8Ptr< type, maxChars >(void) : __DPtr<char>() {} + __Str8Ptr< type, maxChars >(TADDR addr) : __DPtr<char>(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<typename type, ULONG32 maxChars = 32760> +class __Str16Ptr : public __DPtr<WCHAR> +{ +public: + typedef type _Type; + typedef type* _Ptr; + + __Str16Ptr< type, maxChars >(void) : __DPtr<WCHAR>() {} + __Str16Ptr< type, maxChars >(TADDR addr) : __DPtr<WCHAR>(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<typename type> +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<type> & 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<typename type, size_t size> +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<typename acc_type, typename store_type> +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<typename acc_type, typename store_type> +inline bool operator==(const __GlobalPtr<acc_type, store_type>& gptr, + acc_type host) +{ + return DacGetTargetAddrForHostAddr(host, true) == + *__DPtr< TADDR >(DacGlobalBase() + *gptr.m_rvaPtr); +} +template<typename acc_type, typename store_type> +inline bool operator!=(const __GlobalPtr<acc_type, store_type>& gptr, + acc_type host) +{ + return !operator==(gptr, host); +} + +template<typename acc_type, typename store_type> +inline bool operator==(acc_type host, + const __GlobalPtr<acc_type, store_type>& gptr) +{ + return DacGetTargetAddrForHostAddr(host, true) == + *__DPtr< TADDR >(DacGlobalBase() + *gptr.m_rvaPtr); +} +template<typename acc_type, typename store_type> +inline bool operator!=(acc_type host, + const __GlobalPtr<acc_type, store_type>& 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<TADDR> 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<typename acc_type, typename store_type> + __VoidPtr(__GlobalPtr<acc_type, store_type> 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<TADDR> 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<typename acc_type, typename store_type> + inline TADDR getTaddr(__GlobalPtr<acc_type, store_type> 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<typename type> + TADDR getTaddr(__GlobalVal<type> const &gval); + template<typename type, size_t size> + TADDR getTaddr(__GlobalArray<type, size> 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<typename T> 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<TADDR> + { + 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 ... <further details> +// 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<TargetType>(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<PTR_Foo> 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<PTR_InstantiatedMethodDesc>(pMD) +// pMD = dac_cast<PTR_MethodDesc>(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<PTR_AppDomain>(pBD) +// dac_cast<PTR_BaseDomain>(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<TADDR>(h) +// PTR_TO_TADDR(p) ==> dac_cast<TADDR>(p) +// PTR_Foo(PTR_HOST_TO_TADDR(h)) ==> dac_cast<PTR_Foo>(h) +// +//---------------------------------------------------------------------------- +template <typename Tgt, typename Src> +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<Tgt>::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<typename type> +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 <corhdr.h> +#include <clrdata.h> +#include <xclrdata.h> +#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__ |