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
|
// Mark stack as non-executable
#if defined(__linux__) && defined(__ELF__)
.section .note.GNU-stack, "", @progbits
#endif
/*
__morestack
This function is normally used to implement stack growth using the
mechanism devised by Ian Lance Taylor for gccgo, described here:
http://gcc.gnu.org/wiki/SplitStacks
Each Rust function contains an LLVM-generated prologue that compares the
stack space required for the current function to the space remaining in
the current stack segment, maintained in a platform-specific TLS slot.
The stack limit is strategically maintained by the Rust runtime so that
it is always in place whenever a Rust function is running.
In Rust, however, we currently do not use __morestack for stack growth
purposes. Rather each task has one large stack segment. When this
__morestack function is run, we interpret this as a "stack overflow"
event rather than an event requiring an allocation of a new stack.
In the early days, this implementation did indeed have all of the fiddly
bits in order to manage split stacks in the sense of always growing
stacks. For posterity, the implementation can be found at commit
c8e77d5586aed50821e0b9361b2e24c96ade816c if we ever need to refer back
to it.
-- The __morestack calling convention --
For reasons of efficiency the __morestack calling convention
is bizarre. The calling function does not attempt to align the
stack for the call, and on x86_64 the arguments to __morestack
are passed in scratch registers in order to preserve the
original function's arguments.
Once __morestack has switched to the new stack, instead of
returning, it then calls into the original function, resuming
execution at the instruction following the call to
__morestack. Thus, when the original function returns it
actually returns to __morestack, which then deallocates the
stack and returns again to the original function's caller.
-- Unwinding --
All this trickery causes hell when it comes time for the
unwinder to navigate it's way through this function. What
will happen is the original function will be unwound first
without any special effort, then the unwinder encounters
the __morestack frame, which is sitting just above a
tiny fraction of a frame (containing just a return pointer
and, on 32-bit, the arguments to __morestack).
We deal with this by claiming that little bit of stack
is actually part of the __morestack frame, encoded as
DWARF call frame instructions (CFI) by .cfi assembler
pseudo-ops.
One final complication (that took me a week to figure out)
is that OS X 10.6+ uses its own 'compact unwind info',
an undocumented format generated by the linker from
the DWARF CFI. This compact unwind info doesn't correctly
capture the nuance of the __morestack frame, so we need to
prevent the linker from attempting to convert its DWARF unwind
information.
*/
.text
#if defined(__APPLE__)
#define MORESTACK ___morestack
#define EXHAUSTED _rust_stack_exhausted
#else
#if defined(__linux__) || defined(__FreeBSD__)
#define MORESTACK __morestack
#define EXHAUSTED rust_stack_exhausted
#else
#define MORESTACK ___morestack
#define EXHAUSTED _rust_stack_exhausted
#endif
#endif
.globl MORESTACK
.globl EXHAUSTED
// FIXME: What about __WIN32__?
#if defined(__linux__) || defined(__FreeBSD__)
.hidden MORESTACK
#else
#if defined(__APPLE__)
.private_extern MORESTACK
#endif
#endif
#ifdef __ELF__
.type MORESTACK,@function
#endif
MORESTACK:
.cfi_startproc
// This base pointer setup differs from most in that we are
// telling the unwinder to consider the Canonical Frame
// Address (CFA) for this frame to be the value of the stack
// pointer prior to entry to the original function, whereas
// the CFA would typically be the value of the stack
// pointer prior to entry to this function. This will allow
// the unwinder to understand how to skip the tiny partial
// frame that the original function created by calling
// __morestack.
// In practical terms, our CFA is 12 bytes greater than it
// would normally be, accounting for the two arguments to
// __morestack, and an extra return address.
// FIXME(#9854) these cfi directives don't work on windows.
pushl %ebp
#if defined(__APPLE__)
// The pattern of the return address being saved twice to the same location
// tells the OS X linker that it should not attempt to convert the DWARF
// unwind information to the compact format.
.cfi_offset %eip, -4
.cfi_offset %eip, -4
#endif
// The CFA is 20 bytes above the register that it is
// associated with for this frame (which will be %ebp)
.cfi_def_cfa_offset 20
// %ebp is -20 bytes from the CFA
.cfi_offset %ebp, -20
movl %esp, %ebp
// Calculate the CFA as an offset from %ebp
.cfi_def_cfa_register %ebp
// re-align the stack
subl $12,%esp
calll EXHAUSTED
// the exhaustion function guarantees that it can't return
.cfi_endproc
|