GDB поврежденный кадр стека - как отлаживать?


у меня есть следующая трассировка стека. Можно ли из этого сделать что-нибудь полезное для отладки?

Program received signal SIGSEGV, Segmentation fault.
0x00000002 in ?? ()
(gdb) bt
#0  0x00000002 in ?? ()
#1  0x00000001 in ?? ()
#2  0xbffff284 in ?? ()
Backtrace stopped: previous frame inner to this frame (corrupt stack?)
(gdb) 

С чего начать смотреть на код, когда мы получаем Segmentation fault, а трассировка стека не так полезна?

Примечание: Если я опубликую код, то эксперты SO дадут мне ответ. Я хочу взять руководство от SO и найти ответ сам, поэтому я не публикую код здесь. Извинения.

5 103

5 ответов:

эти фиктивные адреса (0x00000002 и т. п.) На самом деле являются значениями ПК, а не значениями SP. Теперь, когда вы получаете этот вид SEGV, с фиктивным (очень маленьким) адресом ПК, 99% времени это связано с вызовом через фиктивный указатель функции. Обратите внимание, что виртуальные вызовы в C++ реализуются через указатели функций, поэтому любая проблема с виртуальным вызовом может проявляться таким же образом.

инструкция косвенного вызова просто толкает ПК после вызова в стек, а затем устанавливает ПК на целевое значение (фиктивное в данном случае), так что если это и что произошло, вы можете легко отменить его, вручную вытащив компьютер из стека. В 32-битном x86 коде вы просто делаете:

(gdb) set $pc = *(void **)$esp
(gdb) set $esp = $esp + 4

С 64-битным x86 кодом вам нужно

(gdb) set $pc = *(void **)$rsp
(gdb) set $rsp = $rsp + 8

затем, вы должны быть в состоянии сделать bt и выяснить, где код на самом деле.

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

если ситуация достаточно проста, ответ Криса Додда - Это лучший. Похоже, что он прыгнул через нулевой указатель.

однако, возможно, программа выстрелила себе в ногу, колено, шею и глаз перед сбоем-переписала стек, испортила указатель кадра и другие беды. Если это так, то распутывание окрошки вряд ли покажет вам картофель и мясо.

более эффективным решением будет запуск программы под отладчик, и шаг над функциями, пока программа не выйдет из строя. Как только функция сбоя идентифицирована, начните снова и шагните в эту функцию и определите, какая функция, которую она вызывает, вызывает сбой. Повторяйте, пока не найдете единственную оскорбительную строку кода. В 75% случаев исправление будет очевидным.

в остальных 25% ситуаций так называемая оскорбительная строка кода является отвлекающим маневром. Он будет реагировать на (недопустимые) условия, настроенные на много строк раньше-возможно, тысячи линии раньше. Если это так, то лучше конечно зависит от многих факторов: в основном, ваше понимание кода и опыт работы с ним:

  • возможно установка точки наблюдения отладчика или вставка диагностики printf ' s по критическим переменным приведет к необходимому ха!
  • возможно, изменение условий тестирования с различными входами обеспечит больше понимания, чем отладка.
  • может быть, вторая пара глаз заставит вас Проверьте свои предположения или соберите упущенные доказательства.
  • иногда все, что нужно, это пойти на ужин и подумать о собранных доказательствах.

удачи!

предполагая, что указатель стека является допустимым...

может быть невозможно точно знать, где происходит SEGV из backtrace - я думаю, что первые два кадра стека полностью перезаписаны. 0xbffff284 выглядит как действительный адрес, а следующие две-нет. Для более близкого взгляда на стек, вы можете попробовать следующее:

gdb$ x / 32ga $rsp

или вариант (заменить 32 на другое число). Это выведет некоторое количество слов (32) начиная из указателя стека гигантского (g) размера, отформатированного как адреса (a). Помочь типа " Х " для получения дополнительной информации о формате.

в этом случае инструментирование вашего кода с помощью некоторых sentinel 'printf' может быть неплохой идеей.

посмотрите на некоторые из ваших других регистров, чтобы увидеть, если один из них имеет указатель стека кэшируется в них. Оттуда вы можете получить стек. Кроме того, если это встроено, довольно часто стек определяется по очень конкретному адресу. Используя это, вы также можете иногда получить приличный стек. Все это предполагает, что когда вы прыгнули в гиперпространство, ваша программа не блевала по всей памяти по пути...

Если это перезапись стека, значения вполне могут соответствовать чему-то узнаваемому из программы.

например, я просто обнаружил, что смотрю на стек

(gdb) bt
#0  0x0000000000000000 in ?? ()
#1  0x000000000000342d in ?? ()
#2  0x0000000000000000 in ?? ()

и 0x342d - это 13357, который оказался идентификатором узла, когда я захватил журналы приложений для него. Это сразу же помогло сузить сайты-кандидаты, где могла произойти перезапись стека.