I'm doing a little project to use a Linux standard C library for ARM on bare metal (no OS). I'm using qemu-system-arm as the execution platform and GDB to debug. I have written a little system call handler to handle the SVC calls that the C library makes but am confused by the fact that my unhandled syscall function can't traverse the stack back to the caller even though the SVC handler can. The handler code is:
SVC_Handler:
srsfd sp!, #Mode_SYS // Save LR_svc and SPSR_svc on the sys stack.
cpsid i, #Mode_SYS // Switch to sys mode.
push {r4-r12, lr} // Save registers.
// In a system call.
// r7 is the call number.
__in_syscall: // The stack frame is valid here.
cmp r7, #512
blhs Unhandled_SVC // Jump if too big for a syscall.
adr r8, SVC_Table // Get the system call table.
str r7, SysCall // Save call number for error reporting.
ldr r7, [r8, r7, lsl #2]// Get the stystem call entry.
blx r7 // Dispatch. Return value is in r0/r1
goback:
pop {r4-r12, lr} // Restore registers.
rfeia sp! // And return.
SysCall:
.word 0
// Unhandled system calls.
Unhandled_SVC:
stmfd sp!, {r12, lr}
push {r2-r5} // Push extra arguments.
mov r3, r1
mov r2, r0
ldr r1, SysCall // And the system call number.
ldr r0, stringPtr // Get the format string.
bl printf
add sp, #16 // clean up the stack.
mov r0, #-ENOSYS
ldmfd sp!, {r12, pc}
If I set a breakpoint at __in_syscall, I can see the stack frame just fine. If I enter Unhandled_SVC either via the branch or indirectly via a pointer in SVC_Table, GDB gets confused displaying the stack frame, even though the program executes correctly.
What am I missing?
This is part of my ELLCC embedded compiler project and the complete source is here.