mirror of
https://github.com/apache/nuttx-apps.git
synced 2026-08-01 20:29:00 +00:00
testing/sig_sp_test: add test for SP context restore on signal return
Add a test that verifies modifying SP (REG_R13) in the saved register context is honored on signal return. The test pushes values onto the stack, triggers SIGALRM, and in the handler advances SP to skip a value, then verifies the correct value is popped after signal return. This exercises the SP relocation fix in arm_sigdeliver.c for ARMv7-M and ARMv8-M architectures. Signed-off-by: Andrew Au <cshung@gmail.com>
This commit is contained in:
parent
98631ad7a5
commit
29a125f641
5 changed files with 288 additions and 0 deletions
20
testing/sig_sp_test/CMakeLists.txt
Normal file
20
testing/sig_sp_test/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
# ##############################################################################
|
||||
# apps/testing/sig_sp_test/CMakeLists.txt
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# ##############################################################################
|
||||
|
||||
if(CONFIG_TESTING_SIG_SP_TEST)
|
||||
nuttx_add_application(
|
||||
NAME
|
||||
${CONFIG_TESTING_SIG_SP_TEST_PROGNAME}
|
||||
PRIORITY
|
||||
${CONFIG_TESTING_SIG_SP_TEST_PRIORITY}
|
||||
STACKSIZE
|
||||
${CONFIG_TESTING_SIG_SP_TEST_STACKSIZE}
|
||||
MODULE
|
||||
${CONFIG_TESTING_SIG_SP_TEST}
|
||||
SRCS
|
||||
sig_sp_test_main.c)
|
||||
endif()
|
||||
31
testing/sig_sp_test/Kconfig
Normal file
31
testing/sig_sp_test/Kconfig
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
#
|
||||
# For a description of the syntax of this configuration file,
|
||||
# see the file kconfig-language.txt in the NuttX tools repository.
|
||||
#
|
||||
|
||||
config TESTING_SIG_SP_TEST
|
||||
tristate "Signal SP restore test"
|
||||
default n
|
||||
depends on BUILD_FLAT && ARCH_ARM
|
||||
---help---
|
||||
Test that modifying SP (REG_R13) in saved register context
|
||||
during a signal handler is honored on exception return.
|
||||
|
||||
This verifies the SP context relocation in arm_sigdeliver.
|
||||
Only available for flat builds on ARM.
|
||||
|
||||
if TESTING_SIG_SP_TEST
|
||||
|
||||
config TESTING_SIG_SP_TEST_PROGNAME
|
||||
string "Program name"
|
||||
default "sig_sp_test"
|
||||
|
||||
config TESTING_SIG_SP_TEST_PRIORITY
|
||||
int "Task priority"
|
||||
default 100
|
||||
|
||||
config TESTING_SIG_SP_TEST_STACKSIZE
|
||||
int "Stack size"
|
||||
default 4096
|
||||
|
||||
endif
|
||||
10
testing/sig_sp_test/Make.defs
Normal file
10
testing/sig_sp_test/Make.defs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
############################################################################
|
||||
# apps/testing/sig_sp_test/Make.defs
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
############################################################################
|
||||
|
||||
ifneq ($(CONFIG_TESTING_SIG_SP_TEST),)
|
||||
CONFIGURED_APPS += $(APPDIR)/testing/sig_sp_test
|
||||
endif
|
||||
17
testing/sig_sp_test/Makefile
Normal file
17
testing/sig_sp_test/Makefile
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
############################################################################
|
||||
# apps/testing/sig_sp_test/Makefile
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
############################################################################
|
||||
|
||||
include $(APPDIR)/Make.defs
|
||||
|
||||
PROGNAME = $(CONFIG_TESTING_SIG_SP_TEST_PROGNAME)
|
||||
PRIORITY = $(CONFIG_TESTING_SIG_SP_TEST_PRIORITY)
|
||||
STACKSIZE = $(CONFIG_TESTING_SIG_SP_TEST_STACKSIZE)
|
||||
MODULE = $(CONFIG_TESTING_SIG_SP_TEST)
|
||||
|
||||
MAINSRC = sig_sp_test_main.c
|
||||
|
||||
include $(APPDIR)/Application.mk
|
||||
210
testing/sig_sp_test/sig_sp_test_main.c
Normal file
210
testing/sig_sp_test/sig_sp_test_main.c
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
/****************************************************************************
|
||||
* apps/testing/sig_sp_test/sig_sp_test_main.c
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Test: Verify that modifying SP (REG_R13) in the saved register context
|
||||
* is honored on context restore (exception return path).
|
||||
*
|
||||
* Scenario (simulates runtime unwinding past a trampoline):
|
||||
* 1. Push values 1 and 2 onto the stack (simulating a trampoline push)
|
||||
* 2. Busy-wait for a timer signal (SIGALRM)
|
||||
* 3. In the handler, "emulate a pop" by advancing SP by 4
|
||||
* (simulating a runtime deciding the top frame was a stub)
|
||||
* 4. Redirect PC to resume_after_signal
|
||||
* 5. After signal return, pop a value — it should be 1
|
||||
*
|
||||
* This exercises:
|
||||
* - The SP relocation fix in arm_exception.S
|
||||
* - The backward sliding copy (new SP > old SP, stack shrinks)
|
||||
* - A realistic use case: unwinding past a trampoline frame
|
||||
*
|
||||
* Requirements:
|
||||
* - Flat build only (accesses nxsched_self() for saved_regs)
|
||||
* - ARM architecture (uses ARMv7-M register layout)
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <nuttx/arch.h>
|
||||
#include <nuttx/sched.h>
|
||||
#include <arch/irq.h>
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
****************************************************************************/
|
||||
|
||||
static volatile int g_result = -1;
|
||||
static volatile int g_ready = 0;
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: verify_result
|
||||
*
|
||||
* Description:
|
||||
* Called with the popped value in r0. Verifies it equals 1.
|
||||
****************************************************************************/
|
||||
|
||||
void __attribute__((noinline, used)) verify_result(uint32_t value)
|
||||
{
|
||||
printf("sig_sp_test: popped value = %lu (expected 1)\n",
|
||||
(unsigned long)value);
|
||||
|
||||
if (value == 1)
|
||||
{
|
||||
printf("sig_sp_test: PASS\n");
|
||||
g_result = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("sig_sp_test: FAIL - expected 1, got %lu\n",
|
||||
(unsigned long)value);
|
||||
g_result = 1;
|
||||
}
|
||||
|
||||
exit(g_result);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: resume_after_signal
|
||||
*
|
||||
* Description:
|
||||
* We land here after the signal handler adjusts SP and PC.
|
||||
* Stack now has [SP] = 1 (the '2' was skipped by SP += 4).
|
||||
* Pop one value and verify it equals 1.
|
||||
****************************************************************************/
|
||||
|
||||
static void __attribute__((naked, used)) resume_after_signal(void)
|
||||
{
|
||||
__asm__ __volatile__(
|
||||
"pop {r0}\n\t"
|
||||
"b verify_result\n\t"
|
||||
);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: sigalrm_handler
|
||||
*
|
||||
* Description:
|
||||
* Called from the timer interrupt path (async signal delivery).
|
||||
* Accesses saved_regs via nxsched_self() to modify the context
|
||||
* that will be restored after signal return.
|
||||
****************************************************************************/
|
||||
|
||||
static void sigalrm_handler(int signo, siginfo_t *info, void *ucontext)
|
||||
{
|
||||
struct tcb_s *rtcb = nxsched_self();
|
||||
uint32_t *regs;
|
||||
|
||||
(void)signo;
|
||||
(void)info;
|
||||
(void)ucontext;
|
||||
|
||||
regs = rtcb->xcp.saved_regs;
|
||||
if (regs == NULL)
|
||||
{
|
||||
printf("sig_sp_test: ERROR - saved_regs is NULL\n");
|
||||
exit(2);
|
||||
}
|
||||
|
||||
printf("sig_sp_test: handler - PC=0x%08lx SP=0x%08lx\n",
|
||||
(unsigned long)regs[REG_R15],
|
||||
(unsigned long)regs[REG_R13]);
|
||||
|
||||
/* Only act when the main code is ready (in the asm loop) */
|
||||
|
||||
if (!g_ready)
|
||||
{
|
||||
alarm(1);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Emulate a pop: advance SP by 4 (skip top-of-stack value '2') */
|
||||
|
||||
regs[REG_R13] += 4;
|
||||
|
||||
/* Redirect execution to resume_after_signal */
|
||||
|
||||
regs[REG_R15] = ((uint32_t)(uintptr_t)resume_after_signal) & ~1u;
|
||||
regs[REG_XPSR] |= (1 << 24);
|
||||
|
||||
printf("sig_sp_test: handler - new SP=0x%08lx PC=0x%08lx\n",
|
||||
(unsigned long)regs[REG_R13],
|
||||
(unsigned long)regs[REG_R15]);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: wait_with_values_on_stack
|
||||
*
|
||||
* Description:
|
||||
* Push 1 and 2 on the stack, set g_ready, then loop forever.
|
||||
* The alarm handler will redirect us out of the loop.
|
||||
* Stack after pushes: [SP] = 2, [SP+4] = 1
|
||||
****************************************************************************/
|
||||
|
||||
static void __attribute__((naked, used)) wait_with_values_on_stack(void)
|
||||
{
|
||||
__asm__ __volatile__(
|
||||
"mov r0, #1\n\t"
|
||||
"push {r0}\n\t"
|
||||
"mov r0, #2\n\t"
|
||||
"push {r0}\n\t"
|
||||
"ldr r0, =g_ready\n\t"
|
||||
"mov r1, #1\n\t"
|
||||
"str r1, [r0]\n\t"
|
||||
"1: nop\n\t"
|
||||
"b 1b\n\t"
|
||||
);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* sig_sp_test_main
|
||||
****************************************************************************/
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct sigaction sa;
|
||||
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
printf("sig_sp_test: Signal SP restore test\n");
|
||||
printf("sig_sp_test: push 1, push 2, alarm, handler SP+=4, pop => 1\n");
|
||||
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sa_sigaction = sigalrm_handler;
|
||||
sa.sa_flags = SA_SIGINFO;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
|
||||
if (sigaction(SIGALRM, &sa, NULL) < 0)
|
||||
{
|
||||
printf("sig_sp_test: ERROR sigaction failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
alarm(1);
|
||||
wait_with_values_on_stack();
|
||||
|
||||
/* Should never reach here */
|
||||
|
||||
printf("sig_sp_test: ERROR - returned from wait\n");
|
||||
return 1;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue