1
- .text
2
-
3
- // __morestack
4
- //
5
- // LLVM generates a call to this to allocate more stack space in a function
6
- // prolog when we run out.
1
+ /*
2
+ __morestack
3
+
4
+ This function implements stack growth using the mechanism
5
+ devised by Ian Lance Taylor for gccgo, described here:
6
+
7
+ http://gcc.gnu.org/wiki/SplitStacks
8
+
9
+ The Rust stack is composed of a linked list of stack segments,
10
+ and each stack segment contains two parts: the work area,
11
+ where Rust functions are allowed to execute; and the red zone,
12
+ where no Rust code can execute, but where short runtime
13
+ functions (including __morestack), the dynamic linker, signal
14
+ handlers, and the unwinder can run.
15
+
16
+ Each Rust function contains an LLVM-generated prologue that
17
+ compares the stack space required for the current function to
18
+ the space space remaining in the current stack segment,
19
+ maintained in a platform-specific TLS slot. The stack limit
20
+ is strategically maintained by the Rust runtime so that it is
21
+ always in place whenever a Rust function is running.
22
+
23
+ When there is not enough room to run the function, the function
24
+ prologue makes a call to __morestack to allocate a new stack
25
+ segment, copy any stack-based arguments to it, switch stacks,
26
+ then resume execution of the original function.
27
+
28
+ -- The __morestack calling convention --
29
+
30
+ For reasons of efficiency the __morestack calling convention
31
+ is bizarre. The calling function does not attempt to align the
32
+ stack for the call, and on x86_64 the arguments to __morestack
33
+ are passed in scratch registers in order to preserve the
34
+ original function's arguments.
35
+
36
+ Once __morestack has switched to the new stack, instead of
37
+ returning, it then calls into the original function, resuming
38
+ execution at the instruction following the call to
39
+ __morestack. Thus, when the original function returns it
40
+ actually returns to __morestack, which then deallocates the
41
+ stack and returns again to the original function's caller.
42
+
43
+ -- Unwinding --
44
+
45
+ All this trickery causes hell when it comes time for the
46
+ unwinder to navigate it's way through this function. What
47
+ will happen is the original function will be unwound first
48
+ without any special effort, then the unwinder encounters
49
+ the __morestack frame, which is sitting just above a
50
+ tiny fraction of a frame (containing just a return pointer
51
+ and, on 32-bit, the arguments to __morestack).
52
+
53
+ We deal with this by claiming that that little bit of stack
54
+ is actually part of the __morestack frame, encoded as
55
+ DWARF call frame instructions (CFI) by .cfi assembler
56
+ pseudo-ops.
57
+
58
+ One final complication (that took me a week to figure out)
59
+ is that OS X 10.6+ uses its own 'compact unwind info',
60
+ an undocumented format generated by the linker from
61
+ the DWARF CFI. This compact unwind info doesn't correctly
62
+ capture the nuance of the __morestack frame, and as a
63
+ result all of our linking on OS X uses the -no_compact_unwind
64
+ flag.
65
+ */
66
+
67
+ .text
7
68
8
69
#if defined(__APPLE__)
9
70
#define RUST_GET_TASK L_rust_get_task$stub
@@ -51,13 +112,31 @@ MORESTACK:
51
112
.cfi_startproc
52
113
#endif
53
114
115
+ // This base pointer setup differs from most in that we are
116
+ // telling the unwinder to consider the Canonical Frame
117
+ // Address (CFA) for this frame to be the value of the stack
118
+ // pointer prior to entry to the original function, whereas
119
+ // the CFA would typically be the the value of the stack
120
+ // pointer prior to entry to this function. This will allow
121
+ // the unwinder to understand how to skip the tiny partial
122
+ // frame that the original function created by calling
123
+ // __morestack.
124
+
125
+ // In practical terms, our CFA is 12 bytes greater than it
126
+ // would normally be, accounting for the two arguments to
127
+ // __morestack, and an extra return address.
128
+
54
129
pushl %ebp
55
130
#if defined(__linux__) || defined(__APPLE__)
131
+ // The CFA is 20 bytes above the register that it is
132
+ // associated with for this frame (which will be %ebp)
56
133
.cfi_def_cfa_offset 20
134
+ // %ebp is -20 bytes from the CFA
57
135
.cfi_offset %ebp , -20
58
136
#endif
59
137
movl %esp , %ebp
60
138
#if defined(__linux__) || defined(__APPLE__)
139
+ // Calculate the CFA as an offset from %ebp
61
140
.cfi_def_cfa_register %ebp
62
141
#endif
63
142
@@ -81,17 +160,25 @@ MORESTACK:
81
160
82
161
// Save the the correct %esp value for our grandparent frame,
83
162
// for the unwinder
163
+ // FIXME: This isn't used
84
164
leal 20 (%ebp ), %eax
85
165
movl %eax , -4 (%ebp )
86
166
87
- // The arguments to rust_new_stack2
88
- movl 56 (%esp ),%eax // Size of stack arguments
167
+ // The arguments to upcall_new_stack
168
+
169
+ // The size of the stack arguments to copy to the new stack,
170
+ // ane of the the arguments to __morestack
171
+ movl 56 (%esp ),%eax
89
172
movl %eax ,20 (%esp )
90
- leal 64 (%esp ),%eax // Address of stack arguments
173
+ // The address of the stack arguments to the original function
174
+ leal 64 (%esp ),%eax
91
175
movl %eax ,16 (%esp )
176
+ // The amount of stack needed for the original function,
177
+ // the other argument to __morestack
92
178
movl 52 (%esp ),%eax // The amount of stack needed
93
179
movl %eax ,12 (%esp )
94
- movl $0 , 8 (%esp ) // Out pointer
180
+ // Out pointer to the new stack
181
+ movl $0 , 8 (%esp )
95
182
96
183
#ifdef __APPLE__
97
184
call 1f
@@ -106,18 +193,22 @@ MORESTACK:
106
193
movl %eax ,(%esp )
107
194
call UPCALL_CALL_C
108
195
109
- movl 48 (%esp ),%eax // Grab the return pointer.
110
- inc %eax // Skip past the ret instruction in the parent fn
196
+ // Grab the __morestack return pointer
197
+ movl 48 (%esp ),%eax
198
+ // Skip past the ret instruction in the parent fn
199
+ inc %eax
111
200
112
- // Restore fastcc arguments
201
+ // Restore the fastcc arguments to the original function
113
202
movl 28 (%esp ), %ecx
114
203
movl 24 (%esp ), %edx
115
204
116
- movl 8 (%esp ),%esp // Switch stacks.
117
- call *%eax // Re-enter the function that called us.
205
+ // Switch stacks
206
+ movl 8 (%esp ),%esp
207
+ // Re-enter the function that called us
208
+ call *%eax
118
209
119
- // Now the function that called us has returned, so we need to delete the
120
- // old stack space.
210
+ // Now the function that called us has returned, so we need to
211
+ // delete the old stack space
121
212
122
213
// Switch back to the rust stack
123
214
movl %ebp , %esp
@@ -127,8 +218,8 @@ MORESTACK:
127
218
subl $4 , %esp
128
219
129
220
// Now that we're on the return path we want to avoid
130
- // stomping on %eax. FIXME: Need to save and restore
131
- // eax to actually preserve it across the call to delete the stack
221
+ // stomping on %eax. FIXME: Need to save and restore %eax to
222
+ // actually preserve it across the call to delete the stack
132
223
#ifdef __APPLE__
133
224
call 1f
134
225
1: popl %ecx
@@ -144,8 +235,14 @@ MORESTACK:
144
235
addl $12 ,%esp
145
236
146
237
popl %ebp
238
+
239
+ // FIXME: I don't think these rules are necessary
240
+ // since the unwinder should never encounter an instruction
241
+ // pointer pointing here.
147
242
#if defined(__linux__) || defined(__APPLE__)
243
+ // Restore the rule for how to find %ebp
148
244
.cfi_restore %ebp
245
+ // Tell the unwinder how to find the CFA in terms of %esp
149
246
.cfi_def_cfa %esp , 16
150
247
#endif
151
248
retl $8
0 commit comments