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 и найти ответ сам, поэтому я не размещаю код здесь. Извинения.

Ответ 1

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

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

Ответ 2

Если ситуация довольно проста, ответ Криса Додда является лучшим. Кажется, он перепрыгнул через указатель NULL.

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

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

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

  • Возможно, установка точки наблюдения отладчика или вставка диагностического printf для критических переменных приведет к необходимости A ha!
  • Возможно, изменение условий тестирования с различными входами обеспечит более глубокое понимание, чем отладка.
  • Возможно, вторая пара глаз заставит вас проверить ваши предположения или собрать недооцененные доказательства.
  • Иногда, все, что требуется, собирается обедать и думать о собранных доказательствах.

Удачи!

Ответ 3

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

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

gdb $x/32ga $rsp

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

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

Ответ 4

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