Skip to content

Commit 2edf4fe

Browse files
libc/powerpc64: Fix swapcontext(3)
On PowerPC platforms a valid link to the Table of Contents (TOC) is required for PLT lookups to function. This TOC pointer is stored in a dedicated register, and is used along with the stack pointer by both C prologue and PLT lookup code. When calling swapcontext() with uc_link != NULL, a PLT lookup to setcontext(3) is attempted from within the _ctx_done context. The exiting process has usually trashed both r1 and r2 at this point, leading to a crash within the PLT lookup before setcontext(2) is reached to restore the linked context. Save and restore r1 and r2, using r16 as a scratch register to bypass the prologue trampling r2. This ensures the subsequent PLT lookup to setcontext(3) succeeds. Signed-off-by: Timothy Pearson <[email protected]>
1 parent 88c8cba commit 2edf4fe

File tree

4 files changed

+75
-2
lines changed

4 files changed

+75
-2
lines changed

lib/libc/powerpc64/gen/_ctx_start.S

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,22 @@
3434
ld %r2,8(%r14)
3535
ld %r14,0(%r14)
3636
#else
37+
/* Create minimal stack frame and save TOC */
38+
stdu %r1,-32(%r1)
39+
std %r2,24(%r1)
40+
3741
/* Load global entry point */
3842
mr %r12,%r14
3943
#endif
4044
mtlr %r14
4145
blrl /* branch to start function */
4246
mr %r3,%r15 /* pass pointer to ucontext as argument */
4347
nop
48+
#if defined(_CALL_ELF) && _CALL_ELF != 1
49+
/* Restore TOC and pop stack frame */
50+
ld %r2,24(%r1)
51+
ld %r1,0(%r1)
52+
#endif
4453
bl CNAME(_ctx_done) /* branch to ctxt completion func */
4554
/*
4655
* we should never return from the

lib/libc/powerpc64/gen/makecontext.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ __makecontext(ucontext_t *ucp, void (*start)(void), int argc, ...)
119119
mc->mc_srr0 = *(uintptr_t *)_ctx_start;
120120
#else
121121
mc->mc_srr0 = (uintptr_t) _ctx_start;
122+
mc->mc_gpr[12] = (uintptr_t) _ctx_start;
122123
#endif
123124
mc->mc_gpr[1] = (uintptr_t) sp; /* new stack pointer */
124125
mc->mc_gpr[14] = (uintptr_t) start; /* r14 <- start */

lib/libc/tests/sys/Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ ATF_TESTS_C+= brk_test
77
.endif
88
ATF_TESTS_C+= cpuset_test
99
ATF_TESTS_C+= errno_test
10+
ATF_TESTS_C+= swapcontext_test
1011
ATF_TESTS_C+= queue_test
1112
ATF_TESTS_C+= sendfile_test
1213

13-
# TODO: clone, lwp_create, lwp_ctl, posix_fadvise, recvmmsg,
14-
# swapcontext
14+
# TODO: clone, lwp_create, lwp_ctl, posix_fadvise, recvmmsg
1515
NETBSD_ATF_TESTS_C+= access_test
1616
NETBSD_ATF_TESTS_C+= bind_test
1717
NETBSD_ATF_TESTS_C+= chroot_test
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*-
2+
* Copyright (c) 2025 Raptor Computing Systems, LLC
3+
*
4+
* SPDX-License-Identifier: BSD-2-Clause
5+
*/
6+
7+
#include <stdio.h>
8+
#include <stdlib.h>
9+
#include <ucontext.h>
10+
#include <errno.h>
11+
12+
#include <atf-c.h>
13+
14+
#define STACK_SIZE (64ull << 10)
15+
16+
static volatile int callback_reached = 0;
17+
18+
static ucontext_t uctx_save, uctx_switch;
19+
20+
static void swapcontext_callback()
21+
{
22+
// Increment callback reached variable
23+
// If this is called multiple times, we will fail the test
24+
// If this is not called at all, we will fail the test
25+
callback_reached++;
26+
}
27+
28+
ATF_TC(swapcontext_basic);
29+
ATF_TC_HEAD(swapcontext_basic, tc)
30+
{
31+
atf_tc_set_md_var(tc, "descr",
32+
"Verify basic functionality of swapcontext");
33+
}
34+
35+
ATF_TC_BODY(swapcontext_basic, tc)
36+
{
37+
char *stack;
38+
int res;
39+
40+
stack = malloc(STACK_SIZE);
41+
ATF_REQUIRE_MSG(stack != NULL, "malloc failed: %s", strerror(errno));
42+
res = getcontext(&uctx_switch);
43+
ATF_REQUIRE_MSG(res == 0, "getcontext failed: %s", strerror(errno));
44+
45+
uctx_switch.uc_stack.ss_sp = stack;
46+
uctx_switch.uc_stack.ss_size = STACK_SIZE;
47+
uctx_switch.uc_link = &uctx_save;
48+
makecontext(&uctx_switch, swapcontext_callback, 0);
49+
50+
res = swapcontext(&uctx_save, &uctx_switch);
51+
52+
ATF_REQUIRE_MSG(res == 0, "swapcontext failed: %s", strerror(errno));
53+
ATF_REQUIRE_MSG(callback_reached == 1,
54+
"callback failed, reached %d times", callback_reached);
55+
}
56+
57+
ATF_TP_ADD_TCS(tp)
58+
{
59+
ATF_TP_ADD_TC(tp, swapcontext_basic);
60+
61+
return (atf_no_error());
62+
}
63+

0 commit comments

Comments
 (0)