Как получить стек вызовов из исключения прерывания SoftWare

Я пытаюсь отладить программу, работающую на платформе ARM с голым железом, используя gdb, В какой-то момент SWI (Программное прерывание) генерируется исключение. Однако обратная трассировка не показывает, что породило исключение, как вы можете видеть:

(GDB) C

Continuing. 
^C 
Program received signal SIGTRAP, Trace/breakpoint trap. 0xffff0008 in ?? ()
(gdb) bt
#0  0xffff0008 in ?? ()

Программа, запущенная на ARM, была скомпилирована с arm-none-linux-gnueabi-gcc -O2 -c -ggdbЯ также пытался использовать -O0 с тем же результатом.

Как я могу получить значимый стек вызовов? Есть ли другой способ найти то, что генерирует это исключение?

1 ответ

Решение

Чтобы понять проблему gdb с производством трассировки стека, вы должны сначала понять, как gdb производит трассировку стека. Компилятор использует стандартный пролог и эпилог, когда создает C функция, это код ассемблера при входе и выходе из функции. Часть этого, чтобы сохранить lr в стеке зарезервируйте место для локальных переменных и свяжите указатель предыдущего кадра или fp, Эти стековые фреймы предоставляют тип связанного списка, который коренится с fp и обычно заканчивается нулем. Это зависит от ABI (см. -Mabi). Специфика немного отличается для каждого типа ARM ABI, но концепции похожи.

Итак, когда SWI (или происходит любое исключение), оно полностью прерывает поток C Скомпилированный код и список указателей фреймов могут быть трудны для декодирования, особенно в случае повреждения стека. Также, gdb не декодирует контекст программы. Некоторые системы могут изменить указатель кадра и немедленно переключиться с exception mode в system/supervisor mode, sp исключения могут даже использоваться в качестве чистых регистров. Когда SWI часть работы обработчика будет заключаться в сохранении user регистры (r0-r12). В случае многозадачной операционной системы это может привести к полному изменению user стек, от одной задачи к другой.

Вы всегда можете определить ошибочную / вызывающую инструкцию, изучив lr в режиме исключения. Это указано в ARM Architechure, и то же самое для любого процессора ARM. 0xfff0008 по умолчанию SWI адрес обработчика (при использовании таблицы векторов с высокой памятью). Исключения из SWI в ARM ARM (справочное руководство по архитектуре) следует,


A2.6.4 Исключение программного прерывания

Команда программного прерывания (SWI) переходит в режим супервизора для запроса определенной функции супервизора (операционной системы). Когда выполняется SWI, выполняются следующие действия:

   R14_svc    = address of next instruction after the SWI instruction
   SPSR_svc   = CPSR
   CPSR[4:0]  = 0b10011                 /* Enter Supervisor mode */
   CPSR[5]    = 0                       /* Execute in ARM state */
                                        /* CPSR[6] is unchanged */
   CPSR[7]    = 1                       /* Disable normal interrupts */
                                        /* CPSR[8] is unchanged */
   CPSR[9]    = CP15_reg1_EEbit         /* Endianness on exception entry */
   if high vectors configured then
       PC     = 0xFFFF0008
   else
       PC     = 0x00000008

Чтобы вернуться после выполнения операции SWI, используйте следующую инструкцию для восстановления ПК (из R14_svc) и CPSR (из SPSR_svc) и возврата к инструкции, следующей за SWI:

       MOVS PC,R14

Как вы видете, R14_svc какой lr в режиме супервизора устанавливается на SWI Инструкция по эксплуатации +4. Это так, чтобы нормальный возврат перезапустил следующую инструкцию. Изучая, где lr вы можете определить, где SWI произошло. Если пользовательский стек не поврежден, вы можете связать supervisor укладывать в user стек с использованием ABI, который компилятор использует через fp, Если вы сделаете это, то gdb может дать трассировку стека. Однако в обсуждаемой системе нет SWI код поддержки.

Вы могли бы также написать gdb макросы для трассировки стека в этой ситуации. Тем не менее, я надеюсь, что ясно, что gdb будет трудная задача сделать это в общем.

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