Как отладить ошибку сегментации, в то время как трассировка стека gdb заполнена "??"?
Мой исполняемый файл содержит таблицу символов. Но кажется, что трассировка стека перезаписана.
Как получить дополнительную информацию из этого ядра, пожалуйста? Например, есть ли способ проверить кучу? См. Примеры объектов, заполняющих кучу, чтобы получить некоторые подсказки. Как бы то ни было, любая идея ценится.
Ответы
Ответ 1
Я программист на С++ для жизни, и я столкнулся с этой проблемой больше раз, чем я хотел бы признать. Ваше приложение разбивает ОГРОМНУЮ часть стека. Скорее всего, функция, которая повреждает стек, также сбой при возврате. Причина в том, что адрес возврата был перезаписан, и именно поэтому трассировка стека GDB перепутана.
Вот как я отлаживаю эту проблему:
1) Шаг, хотя приложение до сбоя. (Ищите функцию, которая сбой при возврате).
2) После того, как вы определили функцию, объявите переменную в ОЧЕНЬ ПЕРВОЙ ЛИНИИ функции:
int canary=0;
(Причина, по которой это должна быть первая строка, заключается в том, что это значение должно находиться в самом верху стека. Эта "канарейка" будет перезаписана до адреса возврата функции.)
3) Положите переменный вахту на канарейку, шаг за шагом, а функция canary!= 0, то вы обнаружили переполнение буфера! Еще одна возможность поставить переменную точку останова для canary!= 0 и просто запустить программу в обычном режиме, это немного проще, но не все контрольные точки переменной IDE.
EDIT: Я разговаривал со старшим программистом в моем офисе и для того, чтобы понять, что ядро дало вам, чтобы решить, какие адреса памяти у него есть. Один из способов выяснить эти адреса - посмотреть файл MAP для двоичного кода, который является читаемым человеком. Ниже приведен пример создания файла MAP с использованием gcc:
gcc -o foo -Wl,-Map,foo.map foo.c
Это часть головоломки, но все равно будет очень сложно получить адрес функции, которая терпит крах. Если вы запускаете это приложение на современной платформе, ASLR, вероятно, сделает адреса в основной дампе бесполезными. Некоторая реализация ASLR будет рандомизировать адреса функций вашего двоичного кода, что делает основной сброс абсолютно бесполезным.
Ответ 2
- Вы должны использовать некоторый отладчик для обнаружения, valgrind в порядке
- пока вы компилируете свой код, убедитесь, что вы добавили параметр -Wall, он заставит компилятор сообщать вам, есть ли какие-то ошибки или нет (убедитесь, что у вас есть какие-либо предупреждения в вашем коде).
ex: gcc -Wall -g -c -o oke.o oke.c
3. Убедитесь, что у вас также есть опция -g для создания отладочной информации. Вы можете вызывать отладочную информацию с помощью некоторых макросов. Следующие макросы очень полезны для меня:
__LINE__
: указывает строку
__FILE__
: указывает исходный файл
__func__
: сообщает вам функцию
- Использование отладчика недостаточно, я думаю, вы должны привыкнуть к максимальной гибкости компилятора.
Надеюсь, это поможет
Ответ 3
TL; DR: в стеке выделяются чрезвычайно большие объявления локальных переменных в функциях, которые в некоторых комбинациях платформы и компилятора могут переполнять и повреждать стек.
Просто добавьте еще одну потенциальную причину этой проблемы. Я недавно отлаживал очень похожую проблему. Запуск gdb с файлом приложения и ядра приведет к результатам, например:
Core was generated by `myExecutable myArguments'.
Program terminated with signal 6, Aborted.
#0 0x00002b075174ba45 in ?? ()
(gdb)
Это было крайне бесполезно и неутешительно. После нескольких часов очистки Интернета я нашел форум, в котором говорилось о том, как конкретный компилятор, который мы использовали (компилятор Intel), имел меньший размер стека по умолчанию, чем другие компиляторы, и что большие локальные переменные могли переполнять и повреждать стек. Глядя на наш код, я нашел виновника:
void MyClass::MyMethod {
...
char charBuffer[MAX_BUFFER_SIZE];
...
}
Бинго! Я обнаружил, что для MAX_BUFFER_SIZE установлено значение 10000000, поэтому локальная переменная 10 МБ выделяется в стеке!. После изменения реализации для использования shared_ptr и создания буфера динамически, неожиданно программа начала работать отлично.
Ответ 4
Попробуйте запустить отладчик памяти Valgrind.
Ответ 5
Чтобы подтвердить, был ли ваш исполняемый файл скомпилирован в режиме деблокирования, т.е. никаких символов отладки.... что могло бы объяснить, почему там? Попробуйте перекомпилировать с помощью -g
switch, который включает в себя информацию об отладке и встраивает ее в исполняемый файл. Кроме этого, я не знаю, почему у вас есть "??"...
Ответ 6
Не совсем. Конечно, вы можете копаться в памяти и смотреть на вещи. Но без трассировки стека вы не знаете, как вы добрались до того, где вы находитесь, и каковы значения параметров.
Однако сам факт, что ваш стек поврежден, говорит вам, что вам нужно искать код, который записывается в стек.
- Перезапись массива стека. Это можно сделать очевидным путем или путем вызова функции или системного вызова с аргументами неправильного размера или указателями неправильного типа.
- Использование указателя или ссылки на переменные локального стека функции после возврата этой функции.
- Наведение указателя на значение стека указателем неправильного размера и его использование.
Если у вас есть система Unix, "valgrind" - хороший инструмент для поиска некоторых из этих проблем.
Ответ 7
Я предполагаю, что, поскольку вы говорите: "Мой исполняемый файл содержит таблицу символов", которую вы скомпилировали и связали с -g, и что ваш двоичный файл не был удален.
Мы можем просто подтвердить это:
strings -a | grep function_name_you_know_should_exist
Также попробуйте использовать pstack на ядре ans и посмотрите, лучше ли это сделать для вызова вызова. В этом случае это звучит так, как ваш gdb устарел по сравнению с вашей версией gcc/g++.
Ответ 8
Похоже, вы не используете идентичную версию glibc на своем компьютере, поскольку основной файл был тогда, когда он разбился о производстве. Получите файлы, выводимые с помощью "ldd./appname", и загрузите их на свой компьютер, затем скажите gdb, где искать;
set solib-absolute-prefix /path/to/libs