GDB поврежденный кадр стека - Как отлаживать? - PullRequest
109 голосов
/ 21 марта 2012

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

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 ]

150 голосов
/ 21 марта 2012

Эти фиктивные адреса (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

42 голосов
/ 21 марта 2012

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

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

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

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

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

Удачи!

25 голосов
/ 21 марта 2012

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

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

gdb $ x / 32ga $ rsp

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

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

6 голосов
/ 21 марта 2012

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

2 голосов
/ 13 апреля 2018

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

Например, я просто нашел себя в стеке

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...