mirror of
https://github.com/rust-lang/rust.git
synced 2026-05-17 05:25:37 +03:00
9a738fd61d
When unwinding through __morestack the stack limit in the TLS is invalidated and must be reset. Instead of actually landing at __morestack we're just going to make all our Rust landing pads call upcall_reset_stack_limit, which will find the stack segment that corresponds to the current stack pointer and put the limit in the TLS. Also massively expand the stack segment red zone to make more room for the dynamic linker. Will fix in the future.
301 lines
8.4 KiB
C++
301 lines
8.4 KiB
C++
#include "rust_cc.h"
|
|
#include "rust_gc.h"
|
|
#include "rust_internal.h"
|
|
#include "rust_scheduler.h"
|
|
#include "rust_unwind.h"
|
|
#include "rust_upcall.h"
|
|
#include <stdint.h>
|
|
|
|
// Upcalls.
|
|
|
|
#if defined(__i386__) || defined(__x86_64__) || defined(_M_X64)
|
|
void
|
|
check_stack(rust_task *task) {
|
|
void *esp;
|
|
# ifdef __i386__
|
|
asm volatile("movl %%esp,%0" : "=r" (esp));
|
|
# else
|
|
asm volatile("mov %%rsp,%0" : "=r" (esp));
|
|
# endif
|
|
if (esp < task->stk->data)
|
|
task->kernel->fatal("Out of stack space, sorry");
|
|
}
|
|
#else
|
|
#warning "Stack checks are not supported on this architecture"
|
|
void
|
|
check_stack(rust_task *task) {
|
|
// TODO
|
|
}
|
|
#endif
|
|
|
|
// Copy elements from one vector to another,
|
|
// dealing with reference counts
|
|
static inline void
|
|
copy_elements(rust_task *task, type_desc *elem_t,
|
|
void *pdst, void *psrc, size_t n) {
|
|
char *dst = (char *)pdst, *src = (char *)psrc;
|
|
memmove(dst, src, n);
|
|
|
|
// increment the refcount of each element of the vector
|
|
if (elem_t->take_glue) {
|
|
glue_fn *take_glue = elem_t->take_glue;
|
|
size_t elem_size = elem_t->size;
|
|
const type_desc **tydescs = elem_t->first_param;
|
|
for (char *p = dst; p < dst+n; p += elem_size) {
|
|
take_glue(NULL, NULL, tydescs, p);
|
|
}
|
|
}
|
|
}
|
|
|
|
extern "C" CDECL void
|
|
upcall_fail(char const *expr,
|
|
char const *file,
|
|
size_t line) {
|
|
rust_task *task = rust_scheduler::get_task();
|
|
LOG_UPCALL_ENTRY(task);
|
|
LOG_ERR(task, upcall, "upcall fail '%s', %s:%" PRIdPTR, expr, file, line);
|
|
task->fail();
|
|
}
|
|
|
|
extern "C" CDECL uintptr_t
|
|
upcall_malloc(size_t nbytes, type_desc *td) {
|
|
rust_task *task = rust_scheduler::get_task();
|
|
LOG_UPCALL_ENTRY(task);
|
|
|
|
LOG(task, mem,
|
|
"upcall malloc(%" PRIdPTR ", 0x%" PRIxPTR ")",
|
|
nbytes, td);
|
|
|
|
gc::maybe_gc(task);
|
|
cc::maybe_cc(task);
|
|
|
|
// TODO: Maybe use dladdr here to find a more useful name for the
|
|
// type_desc.
|
|
|
|
void *p = task->malloc(nbytes, "tdesc", td);
|
|
memset(p, '\0', nbytes);
|
|
|
|
task->local_allocs[p] = td;
|
|
debug::maybe_track_origin(task, p);
|
|
|
|
LOG(task, mem,
|
|
"upcall malloc(%" PRIdPTR ", 0x%" PRIxPTR ") = 0x%" PRIxPTR,
|
|
nbytes, td, (uintptr_t)p);
|
|
return (uintptr_t) p;
|
|
}
|
|
|
|
/**
|
|
* Called whenever an object's ref count drops to zero.
|
|
*/
|
|
extern "C" CDECL void
|
|
upcall_free(void* ptr, uintptr_t is_gc) {
|
|
rust_task *task = rust_scheduler::get_task();
|
|
LOG_UPCALL_ENTRY(task);
|
|
|
|
rust_scheduler *sched = task->sched;
|
|
DLOG(sched, mem,
|
|
"upcall free(0x%" PRIxPTR ", is_gc=%" PRIdPTR ")",
|
|
(uintptr_t)ptr, is_gc);
|
|
|
|
task->local_allocs.erase(ptr);
|
|
debug::maybe_untrack_origin(task, ptr);
|
|
|
|
task->free(ptr, (bool) is_gc);
|
|
}
|
|
|
|
extern "C" CDECL uintptr_t
|
|
upcall_shared_malloc(size_t nbytes, type_desc *td) {
|
|
rust_task *task = rust_scheduler::get_task();
|
|
LOG_UPCALL_ENTRY(task);
|
|
|
|
LOG(task, mem,
|
|
"upcall shared_malloc(%" PRIdPTR ", 0x%" PRIxPTR ")",
|
|
nbytes, td);
|
|
void *p = task->kernel->malloc(nbytes, "shared malloc");
|
|
memset(p, '\0', nbytes);
|
|
LOG(task, mem,
|
|
"upcall shared_malloc(%" PRIdPTR ", 0x%" PRIxPTR
|
|
") = 0x%" PRIxPTR,
|
|
nbytes, td, (uintptr_t)p);
|
|
return (uintptr_t) p;
|
|
}
|
|
|
|
/**
|
|
* Called whenever an object's ref count drops to zero.
|
|
*/
|
|
extern "C" CDECL void
|
|
upcall_shared_free(void* ptr) {
|
|
rust_task *task = rust_scheduler::get_task();
|
|
LOG_UPCALL_ENTRY(task);
|
|
|
|
rust_scheduler *sched = task->sched;
|
|
DLOG(sched, mem,
|
|
"upcall shared_free(0x%" PRIxPTR")",
|
|
(uintptr_t)ptr);
|
|
task->kernel->free(ptr);
|
|
}
|
|
|
|
extern "C" CDECL type_desc *
|
|
upcall_get_type_desc(void *curr_crate, // ignored, legacy compat.
|
|
size_t size,
|
|
size_t align,
|
|
size_t n_descs,
|
|
type_desc const **descs,
|
|
uintptr_t n_obj_params) {
|
|
rust_task *task = rust_scheduler::get_task();
|
|
check_stack(task);
|
|
LOG_UPCALL_ENTRY(task);
|
|
|
|
LOG(task, cache, "upcall get_type_desc with size=%" PRIdPTR
|
|
", align=%" PRIdPTR ", %" PRIdPTR " descs", size, align,
|
|
n_descs);
|
|
rust_crate_cache *cache = task->get_crate_cache();
|
|
type_desc *td = cache->get_type_desc(size, align, n_descs, descs,
|
|
n_obj_params);
|
|
LOG(task, cache, "returning tydesc 0x%" PRIxPTR, td);
|
|
return td;
|
|
}
|
|
|
|
extern "C" CDECL void
|
|
upcall_vec_grow(rust_vec** vp, size_t new_sz) {
|
|
rust_task *task = rust_scheduler::get_task();
|
|
LOG_UPCALL_ENTRY(task);
|
|
reserve_vec(task, vp, new_sz);
|
|
(*vp)->fill = new_sz;
|
|
}
|
|
|
|
extern "C" CDECL void
|
|
upcall_vec_push(rust_vec** vp, type_desc* elt_ty, void* elt) {
|
|
rust_task *task = rust_scheduler::get_task();
|
|
LOG_UPCALL_ENTRY(task);
|
|
size_t new_sz = (*vp)->fill + elt_ty->size;
|
|
reserve_vec(task, vp, new_sz);
|
|
rust_vec* v = *vp;
|
|
copy_elements(task, elt_ty, &v->data[0] + v->fill, elt, elt_ty->size);
|
|
v->fill += elt_ty->size;
|
|
}
|
|
|
|
/**
|
|
* Returns a token that can be used to deallocate all of the allocated space
|
|
* space in the dynamic stack.
|
|
*/
|
|
extern "C" CDECL void *
|
|
upcall_dynastack_mark() {
|
|
return rust_scheduler::get_task()->dynastack.mark();
|
|
}
|
|
|
|
/**
|
|
* Allocates space in the dynamic stack and returns it.
|
|
*
|
|
* FIXME: Deprecated since dynamic stacks need to be self-describing for GC.
|
|
*/
|
|
extern "C" CDECL void *
|
|
upcall_dynastack_alloc(size_t sz) {
|
|
return sz ? rust_scheduler::get_task()->dynastack.alloc(sz, NULL) : NULL;
|
|
}
|
|
|
|
/**
|
|
* Allocates space associated with a type descriptor in the dynamic stack and
|
|
* returns it.
|
|
*/
|
|
extern "C" CDECL void *
|
|
upcall_dynastack_alloc_2(size_t sz, type_desc *ty) {
|
|
return sz ? rust_scheduler::get_task()->dynastack.alloc(sz, ty) : NULL;
|
|
}
|
|
|
|
/** Frees space in the dynamic stack. */
|
|
extern "C" CDECL void
|
|
upcall_dynastack_free(void *ptr) {
|
|
return rust_scheduler::get_task()->dynastack.free(ptr);
|
|
}
|
|
|
|
extern "C" void record_sp(void *limit);
|
|
|
|
/**
|
|
* Switch to the C stack and call the given function, passing a single pointer
|
|
* argument.
|
|
*/
|
|
extern "C" CDECL void
|
|
upcall_call_shim_on_c_stack(void *args, void *fn_ptr) {
|
|
rust_task *task = rust_scheduler::get_task();
|
|
|
|
// FIXME (1226) - The shim functions generated by rustc contain the
|
|
// morestack prologue, so we need to let them know they have enough
|
|
// stack.
|
|
record_sp(0);
|
|
|
|
rust_scheduler *sched = task->sched;
|
|
try {
|
|
sched->c_context.call_shim_on_c_stack(args, fn_ptr);
|
|
} catch (...) {
|
|
task = rust_scheduler::get_task();
|
|
task->record_stack_limit();
|
|
throw;
|
|
}
|
|
task = rust_scheduler::get_task();
|
|
task->record_stack_limit();
|
|
}
|
|
|
|
struct rust_new_stack2_args {
|
|
void *new_stack;
|
|
size_t stk_sz;
|
|
void *args_addr;
|
|
size_t args_sz;
|
|
};
|
|
|
|
// A new stack function suitable for calling through
|
|
// upcall_call_shim_on_c_stack
|
|
extern "C" CDECL void
|
|
upcall_new_stack(struct rust_new_stack2_args *args) {
|
|
rust_task *task = rust_scheduler::get_task();
|
|
args->new_stack = task->new_stack(args->stk_sz,
|
|
args->args_addr,
|
|
args->args_sz);
|
|
}
|
|
|
|
extern "C" CDECL void
|
|
upcall_del_stack() {
|
|
rust_task *task = rust_scheduler::get_task();
|
|
task->del_stack();
|
|
}
|
|
|
|
// Landing pads need to call this to insert the
|
|
// correct limit into TLS.
|
|
// NB: This must be called on the Rust stack
|
|
extern "C" CDECL void
|
|
upcall_reset_stack_limit() {
|
|
rust_task *task = rust_scheduler::get_task();
|
|
task->reset_stack_limit();
|
|
}
|
|
|
|
extern "C" _Unwind_Reason_Code
|
|
__gxx_personality_v0(int version,
|
|
_Unwind_Action actions,
|
|
uint64_t exception_class,
|
|
_Unwind_Exception *ue_header,
|
|
_Unwind_Context *context);
|
|
|
|
extern "C" _Unwind_Reason_Code
|
|
upcall_rust_personality(int version,
|
|
_Unwind_Action actions,
|
|
uint64_t exception_class,
|
|
_Unwind_Exception *ue_header,
|
|
_Unwind_Context *context) {
|
|
return __gxx_personality_v0(version,
|
|
actions,
|
|
exception_class,
|
|
ue_header,
|
|
context);
|
|
}
|
|
|
|
//
|
|
// Local Variables:
|
|
// mode: C++
|
|
// fill-column: 78;
|
|
// indent-tabs-mode: nil
|
|
// c-basic-offset: 4
|
|
// buffer-file-coding-system: utf-8-unix
|
|
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
|
// End:
|
|
//
|