This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
/
daccess.h
2462 lines (2221 loc) · 90.2 KB
/
daccess.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// 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);
#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_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;
#ifdef _TARGET_ARM_
ULONG fn__ThePreStubCompactARM;
#endif // _TARGET_ARM_
ULONG fn__ThePreStubPatchLabel;
ULONG fn__PrecodeFixupThunk;
#ifdef FEATURE_PREJIT
ULONG fn__StubDispatchFixupStub;
ULONG fn__StubDispatchFixupPatchLabel;
#endif
#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);
#ifdef FEATURE_EH_FUNCLETS
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);
#endif // FEATURE_EH_FUNCLETS
#if defined(FEATURE_PAL)
// call back through data target to unwind out-of-process
HRESULT DacVirtualUnwind(ULONG32 threadId, PT_CONTEXT context, PT_KNONVOLATILE_CONTEXT_POINTERS contextPointers);
#endif // FEATURE_PAL
#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];
}