diff --git a/lib/libc/powerpc64/gen/_ctx_start.S b/lib/libc/powerpc64/gen/_ctx_start.S index 4d5f106c96cb..250a9da3837f 100644 --- a/lib/libc/powerpc64/gen/_ctx_start.S +++ b/lib/libc/powerpc64/gen/_ctx_start.S @@ -37,11 +37,30 @@ ld %r2,8(%r14) ld %r14,0(%r14) #else + mflr %r0 + /* + * ABI hack: + * + * We are using an unusual calling convention here. + * Our function is being called in the context of the start + * function, as there may be stack parameters we need to pass + * along. + * + * So we actually want to store things in the *parent* frame. + */ + ld %r12, 0(%r1) + std %r0, 16(%r12) + std %r2, 24(%r12) /* Load global entry point */ mr %r12,%r14 #endif mtlr %r14 blrl /* branch to start function */ + + /* Return to parent frame and restore TOC. */ + ld %r1, 0(%r1) + ld %r2, 24(%r1) + mr %r3,%r15 /* pass pointer to ucontext as argument */ nop bl CNAME(_ctx_done) /* branch to ctxt completion func */ diff --git a/lib/libc/powerpc64/gen/makecontext.c b/lib/libc/powerpc64/gen/makecontext.c index f5ecf3fa9ee6..5b1da36d5374 100644 --- a/lib/libc/powerpc64/gen/makecontext.c +++ b/lib/libc/powerpc64/gen/makecontext.c @@ -61,9 +61,9 @@ void __makecontext(ucontext_t *ucp, void (*start)(void), int argc, ...) { mcontext_t *mc; - char *sp; + uintptr_t sp, backchain; va_list ap; - int i, regargs, stackargs; + int i, regargs, stackargs, stackheader; /* Sanity checks */ if ((ucp == NULL) || (argc < 0) @@ -75,26 +75,41 @@ __makecontext(ucontext_t *ucp, void (*start)(void), int argc, ...) } /* - * Since we are setting up a stack frame for an arbitrary function, - * we *must* allocate a Parameter Save Area on top of the normal - * stack frame header, as the callee may try and use it. - * - * The ELFv1 stack frame header is 6 dwords (48 bytes) and the - * ELFv2 stack frame header is 4 dwords (32 bytes). - * - * Immediately following this is the Parameter Save Area, which - * must be a minimum of 8 dwords when it exists. + * Start by setting up an initial stack frame. + * This frame will be used by _ctx_start() itself. */ - stackargs = (argc > 8) ? argc - 8 : 0; #if !defined(_CALL_ELF) || _CALL_ELF == 1 - sp = (char *) ucp->uc_stack.ss_sp + ucp->uc_stack.ss_size - - sizeof(uintptr_t)*(6 + 8 + stackargs); + stackheader = 6; /* 48 bytes */ #else - sp = (char *) ucp->uc_stack.ss_sp + ucp->uc_stack.ss_size - - sizeof(uintptr_t)*(4 + 8 + stackargs); + stackheader = 4; /* 32 bytes */ #endif - /* Ensure stack alignment. */ - sp = (char *)((uintptr_t)sp & ~0x1f); + sp = ((uintptr_t) ucp->uc_stack.ss_sp + ucp->uc_stack.ss_size - + sizeof(uintptr_t)*stackheader) & ~0x0f; + + /* Initialize backchain */ + *(uint64_t *)sp = 0x0; + backchain = sp; + + /* + * Set up the stack frame for the call to start. + * + * Despite the fact that we're actually branching to _ctx_start, + * this stack frame is needed so we can pass stack arguments to + * start. + * + * After _ctx_start does the call, it will discard this frame + * and continue running on the initial stack frame. + * + * We are always allocating a Parameter Save Area here because + * we do not know if start is varargs or not. + */ + stackargs = (argc > 8) ? argc - 8 : 0; + + /* header + 8 dwords for the Parameter Save Area. */ + sp = sp - sizeof(uintptr_t)*(stackheader + stackargs + 8); + + /* Link to previous stack frame. */ + *(uint64_t *)sp = backchain; mc = &ucp->uc_mcontext; @@ -123,11 +138,7 @@ __makecontext(ucontext_t *ucp, void (*start)(void), int argc, ...) * space for the callee to use. Skip an additional 8 dwords * past the end of the header to land on the correct slot. */ -#if !defined(_CALL_ELF) || _CALL_ELF == 1 - argp = (uint64_t *)sp + 6 + 8; -#else - argp = (uint64_t *)sp + 4 + 8; -#endif + argp = (uint64_t *)sp + stackheader + 8; for (i = 0; i < stackargs; i++) *argp++ = va_arg(ap, uint64_t); @@ -143,6 +154,8 @@ __makecontext(ucontext_t *ucp, void (*start)(void), int argc, ...) mc->mc_srr0 = *(uintptr_t *)_ctx_start; #else mc->mc_srr0 = (uintptr_t) _ctx_start; + /* ELFv2 ABI requirement. */ + mc->mc_gpr[12] = (uintptr_t) _ctx_start; #endif mc->mc_gpr[1] = (uintptr_t) sp; /* new stack pointer */ mc->mc_gpr[14] = (uintptr_t) start; /* r14 <- start */