Как настроить рамку стека ARM так, чтобы GDB могла ее пересечь?


Я делаю небольшой проект по использованию стандартной библиотеки Linux C для 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}

Если я установлю точку останова в _ _ в _ syscall, я смогу увидеть стек кадр просто отлично. Если я ввожу Unhandled_SVC либо через ветвь, либо косвенно через указатель в SVC_Table, GDB запутывается, отображая кадр стека, даже если программа выполняется правильно.

Что я упускаю?

Это часть моей ELLCC встроенного компилятора и полной источник здесь.

1 4

1 ответ:

Tl; dr - Вероятно, вы не можете сделать то, что хотите для вашего случая использования системного вызова.

Однако, следующее полезно для трассировки ARM ассемблера, который не включает переключение режимов.


Существует несколько GNU assembler или gas псевдо-ops, которые используются для трассировки стека в ассемблере. Тем не менее, вы всегда можете создать ассемблер, который выходит за рамки обычной процедуры; например, планировщик с переключением контекста, и т.д.

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

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

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

См.: соединения рукоятки и рамка указателя для некоторых информация в стеке структуры руки.