about summary refs log tree commit diff
path: root/src/rt/arch/x86_64/morestack.S
blob: 9cd37d656ac661c35f177c16b7d59a084731241a (plain)
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
/*
	__morestack

	See i386/morestack.S for the lengthy, general explanation.
*/

.text

#if defined(__APPLE__) || defined(_WIN32)
#define UPCALL_NEW_STACK        _upcall_new_stack
#define UPCALL_DEL_STACK        _upcall_del_stack
#define UPCALL_CALL_C           _upcall_call_shim_on_c_stack
#define MORESTACK               ___morestack
#else
#define UPCALL_NEW_STACK        upcall_new_stack
#define UPCALL_DEL_STACK        upcall_del_stack
#define UPCALL_CALL_C           upcall_call_shim_on_c_stack
#define MORESTACK               __morestack
#endif

.globl UPCALL_NEW_STACK
.globl UPCALL_DEL_STACK
.globl UPCALL_CALL_C
.globl MORESTACK

#if defined(__linux__)
	.hidden MORESTACK
#else
#if defined(__APPLE__)
	.private_extern MORESTACK
#endif
#endif

#ifdef __ELF__
	.type MORESTACK,@function
#endif


#if defined(__linux__) || defined(__APPLE__)
MORESTACK:
	.cfi_startproc

	pushq %rbp
	// The CFA is 24 bytes above the register that it will
	// be associated with for this frame (%rbp). That is 8
	// bytes greater than a normal frame, to allow the unwinder
	// to skip the partial frame of the original function.
	.cfi_def_cfa_offset 24
	// %rbp is -24 bytes from the CFA
	.cfi_offset %rbp, -24
	movq %rsp, %rbp
	// Calculate the CFA as on offset from %ebp
	.cfi_def_cfa_register %rbp

	// Save the grandparent stack pointer for the unwinder
	// FIXME: This isn't used
	leaq 24(%rbp), %rax
	pushq %rax

	// FIXME: libgcc also saves rax. not sure if we need to

	// Save argument registers of the original function
	pushq	%rdi
	pushq	%rsi
	pushq	%rdx
	pushq	%rcx
	pushq	%r8
	pushq	%r9

	// Calculate the address of the stack arguments.
	// We have the base pointer, __morestack's return address,
	// and __morestack's caller's return address to skip
	movq %rbp, %rcx
	addq $24, %rcx  // Base pointer, return address x2

	// The arguments to __morestack are passed in %r10 & %r11

	pushq %r11 // Size of stack arguments
	pushq %rcx // Address of stack arguments
	pushq %r10 // The amount of stack needed
	pushq $0   // Out pointer

	movq UPCALL_NEW_STACK@GOTPCREL(%rip), %rsi
	movq %rsp, %rdi
#ifdef __APPLE__
	call UPCALL_CALL_C
#endif
#ifdef __linux__
	call UPCALL_CALL_C@PLT
#endif

	// Pop the new_stack_args struct
	popq %rax
	addq $24, %rsp

	// Pop the saved arguments
	popq %r9
	popq %r8
	popq %rcx
	popq %rdx
	popq %rsi
	popq %rdi

	// Pop the unwinding %rsp
	addq $8, %rsp

        movq 8(%rbp),%r10       // Grab the return pointer.
        incq %r10               // Skip past the `ret` in our parent frame
        movq %rax,%rsp          // Switch to the new stack.

        call *%r10              // Reenter the caller function

	// Switch back to the rust stack
	movq %rbp, %rsp

	// Align the stack again
	pushq $0

	// FIXME: Should preserve %rax here
	movq UPCALL_DEL_STACK@GOTPCREL(%rip), %rsi
	movq $0, %rdi
#ifdef __APPLE__
	call UPCALL_CALL_C
#endif
#ifdef __linux__
	call UPCALL_CALL_C@PLT
#endif

	addq $8, %rsp
	popq %rbp
	// FIXME: I don't think these rules are necessary
	// since the unwinder should never encounter an instruction
	// pointer pointing here.
	.cfi_restore %rbp
	.cfi_def_cfa %rsp, 16
	ret
	
	.cfi_endproc

#else
MORESTACK:
	ret
#endif