Как настроить фрейм стека 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 или псевдо-операций, которые используются для отслеживания стека в ассемблере. Тем не менее, вы всегда можете создать ассемблер, который выходит за рамки обычной процедуры; например, планировщик с переключением контекста и т. д.

  1. .fnstart- запуск функции (текстовый диапазон)
  2. .fnend- конец функции (текстовый диапазон)
  3. .setfp- местоположение фрейма стека APCS.
  4. .save- список сохраненных регистров в стеке.
  5. .pad- другое зарезервированное пространство в стеке
  6. .movspувеличил стек.
  7. .cantunwind- не пытайтесь раскрутить эту функцию; когда ты вышел за рамки нормального.

Ваша текущая рутина опускаетpcзарегистрироваться (дляSVC_Handler) рутина и не обновляет fp, Это нормально, но вы должны указать отладчику не смотреть на аргументы, которые могут быть в незарезервированных регистрах. Особенно полезным является учебник по размотке газа .

Ваш пользователь sp и система sp разные. Поэтому, когда вы отследите, он будет только в одном стеке. GDB не будет знать режимы прыжка и / или стека. Один механизм состоит в том, чтобы обнулить указатель кадра в записи системного вызова так, чтобы он был завершением трассировки кадра. Затем вам нужно установить реальный стек стека в Unhandled_SVC, Вам нужно написать макрос GDB для извлечения информации о вызовах SVC ядра и перехода к освобожденному стеку, если вы хотите продолжить трассировку.

См. ARM Link и указатель кадра для получения дополнительной информации о кадре стека ARM.

Другие вопросы по тегам