| /* |
| __morestack |
| |
| This function implements stack growth using the mechanism |
| devised by Ian Lance Taylor for gccgo, described here: |
| |
| http://gcc.gnu.org/wiki/SplitStacks |
| |
| The Rust stack is composed of a linked list of stack segments, |
| and each stack segment contains two parts: the work area, |
| where Rust functions are allowed to execute; and the red zone, |
| where no Rust code can execute, but where short runtime |
| functions (including __morestack), the dynamic linker, signal |
| handlers, and the unwinder can run. |
| |
| Each Rust function contains an LLVM-generated prologue that |
| compares the stack space required for the current function to |
| the space 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. |
| |
| When there is not enough room to run the function, the function |
| prologue makes a call to __morestack to allocate a new stack |
| segment, copy any stack-based arguments to it, switch stacks, |
| then resume execution of the original function. |
| |
| -- 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 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, and as a |
| result all of our linking on OS X uses the -no_compact_unwind |
| flag. |
| */ |
| |
| .text |
| |
| #if defined(__APPLE__) |
| #define RUST_GET_TASK L_rust_get_task$stub |
| #define UPCALL_NEW_STACK L_upcall_new_stack$stub |
| #define UPCALL_DEL_STACK L_upcall_del_stack$stub |
| #define MORESTACK ___morestack |
| #else |
| #if defined(__linux__) || defined(__FreeBSD__) |
| #define UPCALL_NEW_STACK upcall_new_stack |
| #define UPCALL_DEL_STACK upcall_del_stack |
| #define RUST_GET_TASK rust_get_task |
| #define MORESTACK __morestack |
| #else |
| #define UPCALL_NEW_STACK _upcall_new_stack |
| #define UPCALL_DEL_STACK _upcall_del_stack |
| #define RUST_GET_TASK _rust_get_task |
| #define MORESTACK ___morestack |
| #endif |
| #endif |
| |
| #ifndef __APPLE__ |
| .globl UPCALL_NEW_STACK |
| .globl UPCALL_DEL_STACK |
| .globl RUST_GET_TASK |
| #endif |
| .globl MORESTACK |
| |
| // 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: |
| #if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) |
| .cfi_startproc |
| #endif |
| |
| // 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 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. |
| |
| pushl %ebp |
| #if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) |
| // 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 |
| #endif |
| movl %esp, %ebp |
| #if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) |
| // Calculate the CFA as an offset from %ebp |
| .cfi_def_cfa_register %ebp |
| #endif |
| |
| // NB: This can be called with the fastcc convention so we |
| // have to preserve any argument registers |
| |
| // NB: __morestack is called misaligned by 4 bytes, i.e. |
| // subl $4, %esp would get us to a normal alignment |
| |
| subl $28,%esp |
| |
| // Save fastcc arguments |
| movl %ecx, 16(%esp) |
| movl %edx, 12(%esp) |
| |
| // FIXME (1388): it's possible we also need to save/restore some |
| // SSE2 registers here, if floats-go-in-regs on x86+SSE2. Unclear. |
| |
| // FIXME (1226): main is compiled with the split-stack prologue, |
| // causing it to call __morestack, so we have to jump back out |
| calll RUST_GET_TASK |
| testl %eax,%eax |
| jz .L$bail |
| |
| // The arguments to upcall_new_stack |
| |
| // The size of the stack arguments to copy to the new stack, |
| // ane of the the arguments to __morestack |
| movl 40(%esp),%eax |
| movl %eax,8(%esp) |
| // The address of the stack arguments to the original function |
| leal 48(%esp),%eax |
| movl %eax,4(%esp) |
| // The amount of stack needed for the original function, |
| // the other argument to __morestack |
| movl 36(%esp),%eax // The amount of stack needed |
| movl %eax,(%esp) |
| |
| call UPCALL_NEW_STACK |
| |
| // Save the address of the new stack |
| movl %eax, (%esp) |
| |
| // Grab the __morestack return pointer |
| movl 32(%esp),%eax |
| // Skip past the ret instruction in the parent fn |
| inc %eax |
| |
| // Restore the fastcc arguments to the original function |
| movl 16(%esp), %ecx |
| movl 12(%esp), %edx |
| |
| // Switch stacks |
| movl (%esp),%esp |
| // Re-enter the function that called us |
| call *%eax |
| |
| // Now the function that called us has returned, so we need to |
| // delete the old stack space |
| |
| // Switch back to the rust stack |
| movl %ebp, %esp |
| |
| // Realign stack - remember that __morestack was called misaligned |
| subl $12, %esp |
| |
| // Save the return value of the function we allocated space for |
| movl %eax, (%esp) |
| |
| call UPCALL_DEL_STACK |
| |
| // And restore it |
| movl (%esp), %eax |
| |
| addl $12,%esp |
| |
| popl %ebp |
| |
| retl $8 |
| |
| .L$bail: |
| movl 32(%esp),%eax |
| inc %eax |
| |
| addl $44, %esp |
| popl %ebp |
| addl $4+8,%esp |
| |
| jmpl *%eax |
| |
| #if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) |
| .cfi_endproc |
| #endif |
| |
| #ifdef __APPLE__ |
| |
| .section __IMPORT,__jump_table,symbol_stubs,pure_instructions+self_modifying_code,5 |
| |
| // Linker will replace the hlts (the ascii) with jmp |
| L_rust_get_task$stub: |
| .indirect_symbol _rust_get_task |
| .ascii "\364\364\364\364\364" |
| |
| L_upcall_new_stack$stub: |
| .indirect_symbol _upcall_new_stack |
| .ascii "\364\364\364\364\364" |
| |
| L_upcall_del_stack$stub: |
| .indirect_symbol _upcall_del_stack |
| .ascii "\364\364\364\364\364" |
| |
| .subsections_via_symbols |
| #endif |
| |