У меня следующая трассировка стека. Можно ли из этого разобрать что-нибудь полезное для отладки?
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 и сам найти ответ, поэтому я не публикую здесь код. Извиняюсь.
-fno-omit-frame-pointer
? Кроме того, для повреждения памятиvalgrind
может быть более подходящим инструментом, если он вам подходит.Ответы:
Эти фиктивные адреса (0x00000002 и т.п.) на самом деле являются значениями ПК, а не значениями SP. Теперь, когда вы получаете такой тип SEGV с поддельным (очень маленьким) адресом ПК, в 99% случаев это происходит из-за вызова через поддельный указатель на функцию. Обратите внимание, что виртуальные вызовы в C ++ реализуются через указатели функций, поэтому любая проблема с виртуальным вызовом может проявляться таким же образом.
Косвенная команда вызова просто толкает компьютер после вызова в стек , а затем устанавливает ПК к целевому значению (фиктивным в данном случае), так что если это является то , что произошло, вы можете легко отменить его вручную выскакивают ПК из стека . В 32-битном коде x86 вы просто делаете:
С 64-битным кодом x86 вам понадобится
Затем вы сможете сделать
bt
и выяснить, где на самом деле находится код.В остальном 1% случаев ошибка возникает из-за перезаписи стека, обычно из-за переполнения массива, хранящегося в стеке. В этом случае вы можете прояснить ситуацию с помощью такого инструмента, как valgrind
источник
gdb executable corefile
откроет gdb с исполняемым файлом и файлом ядра, после чего вы можете сделатьbt
(или приведенные выше команды, за которыми следуетbt
) ...sp
, а неesp
илиrsp
, а его инструкция вызова сохраняет адрес возврата вlr
регистре, а не в стеке. Итак, для ARM вам действительно нужно отменить вызовset $pc = $lr
. Если$lr
он недействителен, вам гораздо сложнее расслабиться.Если ситуация довольно проста, ответ Криса Додда будет лучшим. Похоже, что он перескочил через указатель NULL.
Однако возможно, что программа выстрелила себе в ногу, колено, шею и глаз, прежде чем вылетела - перезаписала стек, испортила указатель кадра и другие беды. Если это так, то при распутывании хеша вы вряд ли увидите картошку и мясо.
Более эффективным решением будет запуск программы под отладчиком и переход по функциям до тех пор, пока программа не выйдет из строя. Как только функция, вызывающая сбой, определена, запустите ее заново, войдите в эту функцию и определите, какая функция вызывает сбой. Повторяйте, пока не найдете единственную строку кода, вызывающую нарушение. В 75% случаев исправление будет очевидным.
В остальных 25% ситуаций так называемая критическая строка кода - отвлекающий маневр. Он будет реагировать на (недопустимые) условия, заданные на много строк раньше - может быть, на тысячи строк раньше. В таком случае выбор лучшего курса зависит от многих факторов: в основном от вашего понимания кода и опыта работы с ним:
printf
для критических переменных приведет к необходимому A ha!Удачи!
источник
Предполагая, что указатель стека действителен ...
Может быть невозможно точно узнать, где происходит SEGV из обратной трассировки - я думаю, что первые два кадра стека полностью перезаписаны. 0xbffff284 кажется действительным адресом, но следующие два нет. Чтобы поближе взглянуть на стек, вы можете попробовать следующее:
gdb $ x / 32ga $ rsp
или вариант (замените 32 другим числом). Это распечатает некоторое количество слов (32), начиная с указателя стека гигантского размера (g), отформатированных как адреса (a). Введите "help x" для получения дополнительной информации о формате.
В этом случае неплохой идеей может стать оснащение вашего кода некоторыми контрольными printf.
источник
info symbol
того, как это сделать в gdb.x/256wa $sp
=)Посмотрите на некоторые из ваших других регистров, чтобы увидеть, есть ли в одном из них кэшированный указатель стека. Оттуда вы можете получить стек. Кроме того, если он встроен, довольно часто стек определяется по очень конкретному адресу. Используя это, вы также можете иногда получить приличный стек. Все это предполагает, что, когда вы прыгнули в гиперпространство, ваша программа не рвала всю память на этом пути ...
источник
Если это перезапись стека, значения вполне могут соответствовать чему-то узнаваемому из программы.
Например, я просто смотрю на стек
и
0x342d
13357, который оказался идентификатором узла, когда я нашел его в журналах приложений. Это сразу помогло сузить количество сайтов-кандидатов, где могла произойти перезапись стека.источник