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
|
;; 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.
#include "kxarm64.h"
DATAAREA
g_vtableResolveCallback DCQ 0 ; Address of virtual dispatch resolution callback
g_universalTransition DCQ 0 ; Address of RhpUniversalTransition thunk
VTableThunkSize EQU 0x20
;; TODO - do something similar to Redhawk's asmoffsets to compute the value at compile time
EETypeVTableOffset EQU 0x18
PointerSize EQU 8
TEXTAREA
;;
;; When an EEType is created, its VTable entries are initially filled with calls to the VTableSlot thunks for the appropriate
;; slot numbers. When the thunk is invoked, it checks whether the slot has already been resolved. If yes, then it just calls
;; the universal thunk. Otherwise, it calls the dispatch resolution callback, which will update the VTable slot and then call
;; the universal thunk.
;;
;;
;; Note: The "__jmpstub__" prefix is used to indicate to debugger
;; that it must step-through this stub when it encounters it while
;; stepping.
;;
;; int VTableResolver_Init(IntPtr *__jmpstub__VTableResolverSlot0,
;; IntPtr vtableResolveCallback,
;; IntPtr universalTransition,
;; int *slotCount)
;; Returns the size of the pre-generated thunks.
;;
LEAF_ENTRY VTableResolver_Init
adr x9, __jmpstub__VTableSlot00
str x9, [x0]
ADDROF x10, g_vtableResolveCallback
str x1, [x10]
ADDROF x11, g_universalTransition
str x2, [x11]
mov x12, 100 ; This file defines 100 slot helpers
str x12, [x3]
mov x0, VTableThunkSize ; Each thunk is VTableThunkSize in bytes
ret
LEAF_END VTableResolver_Init
;; void* VTableResolver_GetCommonCallingStub()
;; Returns the address of the common calling stub.
;;
LEAF_ENTRY VTableResolver_GetCommonCallingStub
adr x0, __jmpstub__VTableResolver_CommonCallingStub
ret
LEAF_END VTableResolver_GetCommonCallingStub
;; __jmpstub__VTableResolver_CommonCallingStub(?)
;; Used when we dynamically need a VTableResolver not pre-generated.
;;
;; xip0 contains a pointer to a VTableResolverStruct
;; struct VTableResolverStruct
;; {
;; IntPtr offsetFromStartOfEETypePtr;
;; IntPtr VTableThunkAddress;
;; };
;;
LEAF_ENTRY __jmpstub__VTableResolver_CommonCallingStub
;; Load the EEType pointer and add (EETypeVTableOffset + $slot_number * PointerSize) to calculate the VTable slot
;; address. Compare the pointer stored in the slot to the address of the thunk being executed. If the values are
;; equal, call the dispatch resolution callback; otherwise, call the function pointer stored in the slot.
;; x9 = EEType pointer (x0 is the "this" pointer for the call being made)
ldr x9, [x0]
;; xip1 = slot offset relative to EEType
ldr xip1, [xip0]
;; x10 = function pointer stored in the slot
ldr x10, [x9,xip1]
;; x11 = address of this thunk
ldr x11, [xip0,#PointerSize]
;; Compare two pointers
cmp x10, x11
;; If the method is not resolved yet, resolve it first
beq __jmpstub__JumpToVTableResolver
;; Otherwise, just call it
br x10
LEAF_END __jmpstub__VTableResolver_CommonCallingStub
;; Calls the dispatch resolution callback with xip1 set to EETypeVTableOffset + ($slot_number * PointerSize)
LEAF_ENTRY __jmpstub__JumpToVTableResolver
ADDROF xip0, g_vtableResolveCallback
ldr xip0, [xip0]
ADDROF x9, g_universalTransition
ldr x9, [x9]
br x9
LEAF_END __jmpstub__VTableResolver_Init
MACRO
VTableThunkDecl $name, $slot_number
;; Force all thunks to be the same size, which allows us to index
ALIGN 16
LEAF_ENTRY __jmpstub__$name
;; Load the EEType pointer and add (EETypeVTableOffset + $slot_number * PointerSize) to calculate the VTable slot
;; address. Compare the pointer stored in the slot to the address of the thunk being executed. If the values are
;; equal, call the dispatch resolution callback; otherwise, call the function pointer stored in the slot.
;; x9 = EEType pointer (x0 is the "this" pointer for the call being made)
ldr x9, [x0]
;; xip1 = slot offset relative to EEType
mov xip1, EETypeVTableOffset + ($slot_number * PointerSize)
;; x10 = function pointer stored in the slot
ldr x10, [x9,xip1]
;; x11 = address of this thunk
adr x11, __jmpstub__$name
;; Compare two pointers
cmp x10, x11
;; If the method is not resolved yet, resolve it first
beq __jmpstub__JumpToVTableResolver
;; Otherwise, just call it
br x10
LEAF_END __jmpstub__$name
MEND
MACRO
VTableThunkDeclTen $slotnumberDecimal
VTableThunkDecl VTableSlot$slotnumberDecimal0,$slotnumberDecimal0
VTableThunkDecl VTableSlot$slotnumberDecimal1,$slotnumberDecimal1
VTableThunkDecl VTableSlot$slotnumberDecimal2,$slotnumberDecimal2
VTableThunkDecl VTableSlot$slotnumberDecimal3,$slotnumberDecimal3
VTableThunkDecl VTableSlot$slotnumberDecimal4,$slotnumberDecimal4
VTableThunkDecl VTableSlot$slotnumberDecimal5,$slotnumberDecimal5
VTableThunkDecl VTableSlot$slotnumberDecimal6,$slotnumberDecimal6
VTableThunkDecl VTableSlot$slotnumberDecimal7,$slotnumberDecimal7
VTableThunkDecl VTableSlot$slotnumberDecimal8,$slotnumberDecimal8
VTableThunkDecl VTableSlot$slotnumberDecimal9,$slotnumberDecimal9
MEND
MACRO
VTableThunkDeclHundred $slotnumberPerHundred
VTableThunkDeclTen $slotnumberPerHundred0
VTableThunkDeclTen $slotnumberPerHundred1
VTableThunkDeclTen $slotnumberPerHundred2
VTableThunkDeclTen $slotnumberPerHundred3
VTableThunkDeclTen $slotnumberPerHundred4
VTableThunkDeclTen $slotnumberPerHundred5
VTableThunkDeclTen $slotnumberPerHundred6
VTableThunkDeclTen $slotnumberPerHundred7
VTableThunkDeclTen $slotnumberPerHundred8
VTableThunkDeclTen $slotnumberPerHundred9
MEND
VTableThunkDeclHundred ""
END
|