Как вы читаете сообщение журнала ядра segfault
Это может быть очень простой вопрос, я пытаюсь отлаживать приложение, которое генерирует следующую ошибку segfault в kern.log
kernel: myapp[15514]: segfault at 794ef0 ip 080513b sp 794ef0 error 6 in myapp[8048000+24000]
Вот мои вопросы:
-
Есть ли какая-либо документация о том, какие числа ошибок ошибок в segfault, в этом случае это ошибка 6, но я видел ошибку 4, 5
-
В чем смысл информации at bf794ef0 ip 0805130b sp bf794ef0 and myapp[8048000+24000]
?
До сих пор мне удалось скомпилировать символы, и когда я делаю x 0x8048000+24000
, он возвращает символ, это правильный способ сделать это? До сих пор мои предположения следующие:
- sp = указатель стека?
- ip = указатель инструкции
- at =????
- myapp [8048000 + 24000] = адрес символа?
Ответы
Ответ 1
Когда отчет указывает на программу, а не на общую библиотеку
Запустите addr2line -e myapp 080513b
(и повторите для других значений указателя инструкции), чтобы увидеть, где происходит ошибка. Лучше, получить сборку отладки и воспроизвести проблему под отладчиком, например gdb.
Если это общая библиотека
В части libfoo.so[NNNNNN+YYYY]
NNNNNN
находится место, где была загружена библиотека. Вычтите это из указателя инструкции (ip
), и вы получите смещение в .so
инструкции о нарушении. Затем вы можете использовать objdump -DCgl libfoo.so
и искать инструкцию с этим смещением. Вы должны легко уяснить, какая функция это из меток asm. Если .so
не имеет оптимизаций, вы также можете попробовать использовать addr2line -e libfoo.so <offset>
.
Что означает ошибка
Здесь разбиение полей:
-
address
- место в памяти, к которой пытается получить код (вероятно, что 10
и 11
являются смещениями от указателя, который, как мы ожидаем, будет установлен в действительное значение, но который вместо этого указывает на 0
)
-
ip
- указатель инструкции, т.е. где код, который пытается сделать это, живет
-
sp
- указатель стека
-
error
- флаги, специфичные для архитектуры; см. arch/*/mm/fault.c
для вашей платформы.
Ответ 2
Основываясь на моих ограниченных знаниях, ваши предположения верны.
-
sp
= указатель стека
-
ip
= указатель инструкций
-
myapp[8048000+24000]
= адрес
Если бы я отлаживал проблему, я бы изменил код для создания дампа ядра или зарегистрировал backtrace стека при сбое. Вы также можете запустить программу под (или приложением) GDB.
Код ошибки - это всего лишь архитектурный код ошибки для ошибок страницы и, по-видимому, относится к архитектуре. Они часто документируются в arch/*/mm/fault.c
в источнике ядра. Моя копия Linux/arch/i386/mm/fault.c
имеет следующее определение для error_code:
- бит 0 == 0 означает, что страница не найдена, 1 означает ошибку защиты.
- бит 1 == 0 означает чтение, 1 означает запись
- бит 2 == 0 означает, что ядро, 1 означает пользовательский режим
Моя копия Linux/arch/x86_64/mm/fault.c
добавляет следующее:
- бит 3 == 1 означает, что ошибка была вызвана командой
Ответ 3
Если это общая библиотека
Ты, к сожалению, хочешь; невозможно узнать, где библиотеки были помещены в память динамическим компоновщиком после.
Ну, есть еще возможность извлекать информацию, а не из двоичного, а из объекта. Но вам нужен базовый адрес объекта. И эта информация все еще находится внутри coredump в структуре link_map.
Итак, сначала вы хотите импортировать struct link_map в GDB. Поэтому давайте скомпилируем программу с ней с помощью отладочного символа и добавим его в GDB.
link.c
#include <link.h>
toto(){struct link_map * s = 0x400;}
get_baseaddr_from_coredump.sh
#!/bin/bash
BINARY=$(which myapplication)
IsBinPIE ()
{
readelf -h $1|grep 'Type' |grep "EXEC">/dev/null || return 0
return 1
}
Hex2Decimal ()
{
export number="`echo "$1" | sed -e 's:^0[xX]::' | tr '[a-f]' '[A-F]'`"
export number=`echo "ibase=16; $number" | bc`
}
GetBinaryLength ()
{
if [ $# != 1 ]; then
echo "Error, no argument provided"
fi
IsBinPIE $1 || (echo "ET_EXEC file, need a base_address"; exit 0)
export totalsize=0
# Get PT_LOAD size segment out of Program Header Table (ELF format)
export sizes="$(readelf -l $1 |grep LOAD |awk '{print $6}'|tr '\n' ' ')"
for size in $sizes
do Hex2Decimal "$size"; export totalsize=$(expr $number + $totalsize); export totalsize=$(expr $number + $totalsize)
done
return $totalsize
}
if [ $# = 1 ]; then
echo "Using binary $1"
IsBinPIE $1 && (echo "NOT ET_EXEC, need a base_address..."; exit 0)
BINARY=$1
fi
gcc -g3 -fPIC -shared link.c -o link.so
GOTADDR=$(readelf -S $BINARY|grep -E '\.got.plt[ \t]'|awk '{print $4}')
echo "First do the following command :"
echo file $BINARY
echo add-symbol-file ./link.so 0x0
read
echo "Now copy/paste the following into your gdb session with attached coredump"
cat <<EOF
set \$linkmapaddr = *(0x$GOTADDR + 4)
set \$mylinkmap = (struct link_map *) \$linkmapaddr
while (\$mylinkmap != 0)
if (\$mylinkmap->l_addr)
printf "add-symbol-file .%s %#.08x\n", \$mylinkmap->l_name, \$mylinkmap->l_addr
end
set \$mylinkmap = \$mylinkmap->l_next
end
он выведет вам весь контент link_map в составе команды GDB.
Это может показаться unnesseray, но с base_addr общего объекта, о котором мы говорим, вы можете получить дополнительную информацию из адреса, отлаживая непосредственно задействованный совместно используемый объект в другом экземпляре GDB.
Сохраните первый gdb, чтобы иметь идеал символа.
ПРИМЕЧАНИЕ: script является довольно неполным, я подозреваю, что вы можете добавить во второй параметр файла add-symbol, напечатав сумму с этим значением:
readelf -S $SO_PATH|grep -E '\.text[ \t]'|awk '{print $5}'
где $SO_PATH - это первый аргумент файла-символа add-symbol
Надеюсь, что это поможет