it-roy-ru.com

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

108
Sangeeth Saravanaraj

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

148
Chris Dodd

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

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

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

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

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

Удачи!

42
wallyk

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

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

gDB $ х/32га $ RSP

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

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

25
manabear

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

6
Michael Dorgan

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

Например, я просто смотрю на стек

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

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

2
Craig Ringer