Remove stackoverflow error, have one CORE_INTERNAL error for catastrophic crash

This commit is contained in:
Toby Jaffey 2025-12-10 18:42:34 +00:00
parent 59f5d6449b
commit 508d41c8c5
10 changed files with 163 additions and 57 deletions

View file

@ -1,8 +1,9 @@
PREFIX:=riscv64-elf- PREFIX:=riscv64-elf-
OPT ?= -Os
CFLAGS+=-I${TOPDIR}/common CFLAGS+=-I${TOPDIR}/common
CFLAGS+=-fno-stack-protector CFLAGS+=-fno-stack-protector
CFLAGS+=-static-libgcc -fdata-sections -ffunction-sections CFLAGS+=-static-libgcc -fdata-sections -ffunction-sections
CFLAGS+=-g -Os -march=rv32im -mabi=ilp32 -static CFLAGS+=-g ${OPT} -march=rv32im -mabi=ilp32 -static
LDFLAGS:= -T ${TOPDIR}/apps/linker.ld -nostdlib -Wl,--gc-sections LDFLAGS:= -T ${TOPDIR}/apps/linker.ld -nostdlib -Wl,--gc-sections
LIBS:= -lgcc # needed for softfp LIBS:= -lgcc # needed for softfp
@ -21,8 +22,8 @@ else
endif endif
all_common: all_common:
${PREFIX}gcc -o ${PROJECT}.elf ${CFLAGS} ${LDFLAGS} ${SRCS} ${LIBS} @${PREFIX}gcc -o ${PROJECT}.elf ${CFLAGS} ${LDFLAGS} ${SRCS} ${LIBS}
$(PREFIX)objcopy ${PROJECT}.elf -O binary ${PROJECT}.bin @$(PREFIX)objcopy ${PROJECT}.elf -O binary ${PROJECT}.bin
disasm_common: all disasm_common: all
$(PREFIX)objdump -S -d -f ${PROJECT}.elf $(PREFIX)objdump -S -d -f ${PROJECT}.elf

View file

@ -1,5 +1,8 @@
all: all:
make -C example_1 @make -C example_1
@make -C stackoverflow
clean: clean:
make -C example_1 clean make -C example_1 clean
make -C stackoverflow clean

View file

@ -17,15 +17,15 @@ INC_DIRS=-I$(UNITY_ROOT)/src -I../../uvm32/ -I../../common -Irom
.PHONY: rom .PHONY: rom
default: $(SRC_FILES1) rom default: $(SRC_FILES1) rom
$(C_COMPILER) $(CFLAGS) $(INC_DIRS) $(SYMBOLS) $(SRC_FILES1) rom/rom-header.c -o $(TARGET1) @$(C_COMPILER) $(CFLAGS) $(INC_DIRS) $(SYMBOLS) $(SRC_FILES1) rom/rom-header.c -o $(TARGET1)
- ./$(TARGET1) @- ./$(TARGET1)
rom: rom:
(cd rom && make) @(cd rom && make)
test/test_runners/${SUITE_NAME}_Runner.c: test/${SUITE_NAME}.c test/test_runners/${SUITE_NAME}_Runner.c: test/${SUITE_NAME}.c
mkdir -p test/test_runners @mkdir -p test/test_runners
ruby $(UNITY_ROOT)/auto/generate_test_runner.rb test/${SUITE_NAME}.c test/test_runners/${SUITE_NAME}_Runner.c @ruby $(UNITY_ROOT)/auto/generate_test_runner.rb test/${SUITE_NAME}.c test/test_runners/${SUITE_NAME}_Runner.c
clean: clean:
rm -rf $(TARGET1) test/test_runners rm -rf $(TARGET1) test/test_runners

View file

@ -2,9 +2,9 @@ TOPDIR=../../../
PROJECT:=$(shell basename ${PWD}) PROJECT:=$(shell basename ${PWD})
SRCS=${PROJECT}.c ${TOPDIR}/apps/crt0.S SRCS=${PROJECT}.c ${TOPDIR}/apps/crt0.S
all: all_common all: all_common
# Convert ROM to C file and header @# Convert ROM to C file and header
xxd -i ${PROJECT}.bin > ${PROJECT}-header.c @xxd -i ${PROJECT}.bin > ${PROJECT}-header.c
echo "extern unsigned char ${PROJECT}_bin[]; extern int ${PROJECT}_bin_len;" > ${PROJECT}-header.h @echo "extern unsigned char ${PROJECT}_bin[]; extern int ${PROJECT}_bin_len;" > ${PROJECT}-header.h
test: test_common test: test_common
clean: clean_common clean: clean_common

View file

@ -0,0 +1,33 @@
C_COMPILER=gcc
UNITY_ROOT=../unity
CFLAGS=-std=c99
CFLAGS += -Wall
CFLAGS += -Werror
CFLAGS += -DUVM32_MEMORY_SIZE=512
SUITE_NAME=tests
TARGET_BASE1=test1
TARGET1 = $(TARGET_BASE1)
SRC_FILES1=$(UNITY_ROOT)/src/unity.c test/${SUITE_NAME}.c test/test_runners/${SUITE_NAME}_Runner.c ../../uvm32/uvm32.c
INC_DIRS=-I$(UNITY_ROOT)/src -I../../uvm32/ -I../../common -Irom
.PHONY: rom
default: $(SRC_FILES1) rom
@$(C_COMPILER) $(CFLAGS) $(INC_DIRS) $(SYMBOLS) $(SRC_FILES1) rom/rom-header.c -o $(TARGET1)
@- ./$(TARGET1)
rom:
@(cd rom && make)
test/test_runners/${SUITE_NAME}_Runner.c: test/${SUITE_NAME}.c
@mkdir -p test/test_runners
@ruby $(UNITY_ROOT)/auto/generate_test_runner.rb test/${SUITE_NAME}.c test/test_runners/${SUITE_NAME}_Runner.c
clean:
rm -rf $(TARGET1) test/test_runners
(cd rom && make clean)

View file

@ -0,0 +1,14 @@
TOPDIR=../../../
PROJECT:=$(shell basename ${PWD})
SRCS=${PROJECT}.c ${TOPDIR}/apps/crt0.S
OPT=-O0 # don't optimise
all: all_common
@# Convert ROM to C file and header
@xxd -i ${PROJECT}.bin > ${PROJECT}-header.c
@echo "extern unsigned char ${PROJECT}_bin[]; extern int ${PROJECT}_bin_len;" > ${PROJECT}-header.h
test: test_common
clean: clean_common
rm -f ${PROJECT}-header.h ${PROJECT}-header.c
include ${TOPDIR}/apps/makefile.common

View file

@ -0,0 +1,12 @@
#include "uvm32_target.h"
int foo(int a) {
yield(0);
printdec(a);
return foo(a+1);
}
void main(void) {
foo(1);
}

View file

@ -0,0 +1,34 @@
#include <string.h>
#include "unity.h"
#include "uvm32.h"
#include "../common/uvm32_common_custom.h"
#include "rom-header.h"
static uvm32_state_t vmst;
static uvm32_evt_t evt;
void setUp(void) {
// runs before each test
uvm32_init(&vmst);
uvm32_load(&vmst, rom_bin, rom_bin_len);
}
void tearDown(void) {
}
void test_stackoverflow(void) {
// run the vm
while(1) {
uvm32_run(&vmst, &evt, 100);
if (evt.typ == UVM32_EVT_SYSCALL) {
// ignore syscalls
} else {
TEST_ASSERT_EQUAL(UVM32_EVT_ERR, evt.typ);
TEST_ASSERT_EQUAL(UVM32_ERR_INTERNAL_CORE, evt.data.err.errcode);
break;
}
}
}

View file

@ -141,7 +141,7 @@ uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter)
uint32_t num_instr = 0; uint32_t num_instr = 0;
if (vmst->stack_canary != UVM32_NULL && *vmst->stack_canary != STACK_CANARY_VALUE) { if (vmst->stack_canary != UVM32_NULL && *vmst->stack_canary != STACK_CANARY_VALUE) {
setStatusErr(vmst, UVM32_ERR_STACKOVERFLOW); setStatusErr(vmst, UVM32_ERR_INTERNAL_CORE);
setup_err_evt(vmst, evt); setup_err_evt(vmst, evt);
return num_instr; return num_instr;
} }
@ -159,50 +159,58 @@ uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter)
uint64_t elapsedUs = 1; uint64_t elapsedUs = 1;
uint32_t ret; uint32_t ret;
ret = MiniRV32IMAStep(vmst, &vmst->core, vmst->memory, 0, elapsedUs, 1); ret = MiniRV32IMAStep(vmst, &vmst->core, vmst->memory, 0, elapsedUs, 1);
if (3 == ret) {
// Fetch registers used by syscall switch(ret) {
const uint32_t syscall = vmst->core.regs[17]; // a7 case 0: // ok
// on exception we should jump to mtvec, but we handle directly break;
// and skip over the ecall instruction case 12: { // ecall
vmst->core.pc += 4; // Fetch registers used by syscall
switch(syscall) { const uint32_t syscall = vmst->core.regs[17]; // a7
// inbuilt syscalls // on exception we should jump to mtvec, but we handle directly
case UVM32_SYSCALL_HALT: // and skip over the ecall instruction
setStatus(vmst, UVM32_STATUS_ENDED); vmst->core.pc += 4;
break; switch(syscall) {
case UVM32_SYSCALL_STACKPROTECT: { // inbuilt syscalls
// don't allow errant code to change it once set case UVM32_SYSCALL_HALT:
if (vmst->stack_canary == (uint8_t *)UVM32_NULL) { setStatus(vmst, UVM32_STATUS_ENDED);
uint32_t param0 = vmst->core.regs[10]; // a0 break;
uint32_t mem_offset = param0 - MINIRV32_RAM_IMAGE_OFFSET; case UVM32_SYSCALL_STACKPROTECT: {
// don't allow errant code to change it once set
if (vmst->stack_canary == (uint8_t *)UVM32_NULL) {
uint32_t param0 = vmst->core.regs[10]; // a0
uint32_t mem_offset = param0 - MINIRV32_RAM_IMAGE_OFFSET;
mem_offset &= ~0xF; // round up by 16 bytes
mem_offset += 16*4;
// check data fits in ram // check data fits in ram
if (mem_offset > UVM32_MEMORY_SIZE) { if (mem_offset > UVM32_MEMORY_SIZE) {
setStatusErr(vmst, UVM32_ERR_STACKOVERFLOW); setStatusErr(vmst, UVM32_ERR_INTERNAL_CORE);
setup_err_evt(vmst, evt); setup_err_evt(vmst, evt);
}
// check canary is inside valid memory
if (mem_offset < UVM32_MEMORY_SIZE) {
// set canary
vmst->stack_canary = &vmst->memory[mem_offset];
*vmst->stack_canary = STACK_CANARY_VALUE;
}
} }
// check canary is inside valid memory } break;
if (mem_offset < UVM32_MEMORY_SIZE) { default:
// set canary // user defined syscalls
vmst->stack_canary = &vmst->memory[mem_offset]; vmst->ioevt.typ = UVM32_EVT_SYSCALL;
*vmst->stack_canary = STACK_CANARY_VALUE; vmst->ioevt.data.syscall.code = syscall;
} vmst->ioevt.data.syscall.ret = &vmst->core.regs[12]; // a2
} vmst->ioevt.data.syscall.params[0] = &vmst->core.regs[10]; // a0
} break; vmst->ioevt.data.syscall.params[1] = &vmst->core.regs[11]; // a1
default: setStatus(vmst, UVM32_STATUS_PAUSED);
// user defined syscalls break;
vmst->ioevt.typ = UVM32_EVT_SYSCALL; } // end switch(syscall)
vmst->ioevt.data.syscall.code = syscall; } break; // end ecall
vmst->ioevt.data.syscall.ret = &vmst->core.regs[12]; // a2 default:
vmst->ioevt.data.syscall.params[0] = &vmst->core.regs[10]; // a0 // unhandled exception
vmst->ioevt.data.syscall.params[1] = &vmst->core.regs[11]; // a1 setStatusErr(vmst, UVM32_ERR_INTERNAL_CORE);
setStatus(vmst, UVM32_STATUS_PAUSED); setup_err_evt(vmst, evt);
break; break;
}
} else if (ret != 0) {
// unhandled exception
setStatusErr(vmst, UVM32_ERR_INTERNAL_CORE);
setup_err_evt(vmst, evt);
} }
num_instr++; num_instr++;
@ -213,8 +221,10 @@ uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter)
setup_err_evt(vmst, evt); setup_err_evt(vmst, evt);
return num_instr; return num_instr;
} }
} }
if (vmst->status == UVM32_STATUS_ENDED) { if (vmst->status == UVM32_STATUS_ENDED) {
evt->typ = UVM32_EVT_END; evt->typ = UVM32_EVT_END;
return num_instr; return num_instr;

View file

@ -20,7 +20,6 @@
X(UVM32_ERR_INTERNAL_CORE) \ X(UVM32_ERR_INTERNAL_CORE) \
X(UVM32_ERR_INTERNAL_STATE) \ X(UVM32_ERR_INTERNAL_STATE) \
X(UVM32_ERR_ARGS) \ X(UVM32_ERR_ARGS) \
X(UVM32_ERR_STACKOVERFLOW) \
#define X(name) name, #define X(name) name,
typedef enum { typedef enum {
@ -55,7 +54,7 @@ typedef struct {
#define MINIRV32_DECORATE static #define MINIRV32_DECORATE static
#define MINI_RV32_RAM_SIZE UVM32_MEMORY_SIZE #define MINI_RV32_RAM_SIZE UVM32_MEMORY_SIZE
#define MINIRV32_POSTEXEC(pc, ir, retval) {if (retval > 0) return 3;} #define MINIRV32_POSTEXEC(pc, ir, retval) {if (retval > 0) return retval;}
#ifndef MINIRV32_IMPLEMENTATION #ifndef MINIRV32_IMPLEMENTATION
#define MINIRV32_STEPPROTO #define MINIRV32_STEPPROTO
#endif #endif