Как настроить фрейм стека ARM, чтобы GDB мог его пройти?
Я делаю небольшой проект по использованию стандартной библиотеки C Linux для ARM на голом железе (без ОС). Я использую qemu-system-arm в качестве платформы выполнения и GDB для отладки. Я написал небольшой обработчик системных вызовов для обработки вызовов SVC, которые выполняет библиотека C, но меня смущает тот факт, что моя необработанная функция syscall не может переместить стек обратно к вызывающей стороне, даже если обработчик SVC может. Код обработчика:
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}
Если я установлю точку останова на __in_syscall, я смогу увидеть кадр стека очень хорошо. Если я ввожу Unhandled_SVC либо через ветвь, либо косвенно через указатель в SVC_Table, GDB запутывается, отображая кадр стека, даже если программа выполняется правильно.
Что мне не хватает?
Это часть моего проекта встроенного компилятора ELLCC, полный исходный код которого находится здесь.
1 ответ
tl; dr - вероятно, вы не можете делать то, что вы хотите для вашего случая использования системного вызова.
Тем не менее, следующее полезно для отслеживания ассемблера ARM, который не включает переключение режимов.
Существует несколько ассемблеров GNU или псевдо-операций, которые используются для отслеживания стека в ассемблере. Тем не менее, вы всегда можете создать ассемблер, который выходит за рамки обычной процедуры; например, планировщик с переключением контекста и т. д.
.fnstart
- запуск функции (текстовый диапазон).fnend
- конец функции (текстовый диапазон).setfp
- местоположение фрейма стека APCS..save
- список сохраненных регистров в стеке..pad
- другое зарезервированное пространство в стеке.movsp
увеличил стек..cantunwind
- не пытайтесь раскрутить эту функцию; когда ты вышел за рамки нормального.
Ваша текущая рутина опускаетpc
зарегистрироваться (дляSVC_Handler
) рутина и не обновляет fp
, Это нормально, но вы должны указать отладчику не смотреть на аргументы, которые могут быть в незарезервированных регистрах. Особенно полезным является учебник по размотке газа .
Ваш пользователь sp
и система sp
разные. Поэтому, когда вы отследите, он будет только в одном стеке. GDB не будет знать режимы прыжка и / или стека. Один механизм состоит в том, чтобы обнулить указатель кадра в записи системного вызова так, чтобы он был завершением трассировки кадра. Затем вам нужно установить реальный стек стека в Unhandled_SVC
, Вам нужно написать макрос GDB для извлечения информации о вызовах SVC ядра и перехода к освобожденному стеку, если вы хотите продолжить трассировку.
См. ARM Link и указатель кадра для получения дополнительной информации о кадре стека ARM.