-
Notifications
You must be signed in to change notification settings - Fork 118
/
block_trampolines.S
232 lines (206 loc) · 6.48 KB
/
block_trampolines.S
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
#include "common.S"
#include "asmconstants.h"
#
# This file defines some trampolines for calling blocks. A block function
# looks like this:
#
# retType blockFn(block*, ...)
#
# An IMP looks like this:
#
# retType imp(id, SEL,...)
#
# The trampoline must find the block pointer and then call the block function
# with the correct first argument, the self pointer moved to the second real
# argument (the first block argument) and the _cmd parameter excised
.file "block_trampolines.S"
#if __x86_64
////////////////////////////////////////////////////////////////////////////////
// x86-64 trampoline
////////////////////////////////////////////////////////////////////////////////
.macro trampoline arg0, arg1
mov -0x1007(%rip), \arg1 # Load the block pointer into the second argument
xchg \arg1, \arg0 # Swap the first and second arguments
jmp *-0x1008(%rip) # Call the block function
.endm
// The Win64 and SysV x86-64 ABIs use different registers
# ifdef _WIN64
# define ARG0 %rcx
# define ARG1 %rdx
# define SARG1 %r8
# else
# define ARG0 %rdi
# define ARG1 %rsi
# define SARG1 %rdx
# endif
# define SARG0 ARG1
#elif __i386
////////////////////////////////////////////////////////////////////////////////
// x86-32 trampoline
////////////////////////////////////////////////////////////////////////////////
#ifdef _WIN32
// Mark this compilation unit as SEH-safe
.text
.def @feat.00;
.scl 3;
.type 0;
.endef
.globl @feat.00
.set @feat.00, 1
.data
#endif
.macro trampoline arg0, arg1
call 1f # Store the instruction pointer on the stack
1:
pop %eax # Load the old instruction pointer
mov \arg0(%esp), %ebx # Load the self parameter
mov %ebx, \arg1(%esp) # Store self as the second argument
mov -0x1005(%eax), %ebx # Load the block pointer to %ebx
mov %ebx, \arg0(%esp) # Store the block pointer in the first argument
jmp *-0x1001(%eax) # Call the block function
.endm
// All arguments on i386 are passed on the stack. These values are stack
// offsets - on other platforms they're register values.
# define ARG0 4
# define ARG1 8
# define SARG0 8
# define SARG1 12
#elif __mips__
////////////////////////////////////////////////////////////////////////////////
// MIPS trampoline
////////////////////////////////////////////////////////////////////////////////
# ifdef _ABI64
.macro trampoline arg0, arg1
move \arg1, \arg0
ld \arg0, -4096($25)
ld $25, -4088($25)
jr $25
.endm
# else
// 32-bit variant. This ought to work with both n32 and o32, because they both
// use 32-bit pointers and both use the same registers for the first four
// arguments (and we only care about the first three).
.macro trampoline arg0, arg1
move \arg1, \arg0
lw \arg0, -4096($25)
lw $25, -4092($25)
jr $25
.endm
# endif
#define ARG0 $a0
#define ARG1 $a1
#define ARG2 $a2
#elif defined(__powerpc__)
////////////////////////////////////////////////////////////////////////////////
// PowerPC trampoline
////////////////////////////////////////////////////////////////////////////////
#if defined(__powerpc64__)
#define LOAD ld
#define OFFSET 8
#else
#define LOAD lwz
#define OFFSET 4
#endif
.macro trampoline arg0, arg1
mfctr %r12 # The block trampoline is always called
# via a function pointer. We can thus
# assume that ctr contains the trampline
# entry point address from the previous
# branch to this trampoline (bctrl).
#if PAGE_SHIFT < 16
addi %r12, %r12, -PAGE_SIZE # Substract page size from entry point
#else
addis %r12, %r12, (-0x1 << (PAGE_SHIFT - 16))
#endif
mr \arg1, \arg0
LOAD \arg0, 0(%r12)
LOAD %r12, OFFSET(%r12)
mtctr %r12 # Move block function pointer into ctr
bctr # Branch to block function
.endm
#define ARG0 %r3
#define ARG1 %r4
#define ARG2 %r5
#define SARG0 ARG1
#define SARG1 ARG2
#elif defined(__riscv) && (__riscv_xlen == 64)
////////////////////////////////////////////////////////////////////////////////
// RISC-V trampoline
////////////////////////////////////////////////////////////////////////////////
.macro trampoline arg0, arg1
auipc t6, 0xFFFFF // pc + -0x1000
mv \arg1, \arg0
ld \arg0, 0(t6)
ld t6, 8(t6)
jr t6
.endm
#define ARG0 a0
#define ARG1 a1
#define ARG2 a2
#define SARG0 ARG1
#define SARG1 ARG2
#elif defined(__ARM_ARCH_ISA_A64)
////////////////////////////////////////////////////////////////////////////////
// AArch64 (ARM64) trampoline
////////////////////////////////////////////////////////////////////////////////
.macro trampoline arg0, arg1
adr x17, #-4096
mov \arg1, \arg0
ldp \arg0, x17, [x17]
br x17
.endm
#define ARG0 x0
#define ARG1 x1
#define SARG0 x0
#define SARG1 x1
#elif __arm__
////////////////////////////////////////////////////////////////////////////////
// AArch32 (ARM) trampoline
////////////////////////////////////////////////////////////////////////////////
# if (__ARM_ARCH_ISA_THUMB == 2)
// If we're on a target that supports Thumb 2, then we need slightly more
// instructions to support Thumb/ARM code for the IMP and so we need to make
// the trampolines thumb to be able to fit them in 16 bytes (they fit exactly
// when assembled as Thumb-2).
.thumb
.macro trampoline arg0, arg1
sub r12, pc, #4095
mov \arg1, \arg0 // Move self over _cmd
ldr \arg0, [r12, #-5] // Load the block pointer over self
ldr r12, [r12, #-1] // Jump to the block function
bx r12
.endm
# else
.macro trampoline arg0, arg1
sub r12, pc, #4096
mov \arg1, \arg0 // Move self over _cmd
ldr \arg0, [r12, #-8] // Load the block pointer over self
ldr pc, [r12, #-4] // Jump to the block function
.endm
# endif // (__ARM_ARCH_ISA_THUMB == 2)
#define ARG0 r0
#define ARG1 r1
#define SARG0 r1
#define SARG1 r2
#else
#warning imp_implementationWithBlock() not implemented for your architecture
.macro trampoline arg0, arg1
.endm
#define ARG0 0
#define ARG1 0
#define SARG0 0
#define SARG1 0
#endif
.globl CDECL(__objc_block_trampoline)
CDECL(__objc_block_trampoline):
trampoline ARG0, ARG1
.globl CDECL(__objc_block_trampoline_end)
CDECL(__objc_block_trampoline_end):
.globl CDECL(__objc_block_trampoline_sret)
CDECL(__objc_block_trampoline_sret):
trampoline SARG0, SARG1
.globl CDECL(__objc_block_trampoline_end_sret)
CDECL(__objc_block_trampoline_end_sret):
#ifdef __ELF__
.section .note.GNU-stack,"",%progbits
#endif