Как вы читаете сообщение журнала ядра Segfault? - PullRequest
61 голосов
/ 01 февраля 2010

Это может быть очень простой вопрос, я пытаюсь отладить приложение, которое генерирует следующую ошибку segfault в kern.log

kernel: myapp[15514]: segfault at 794ef0 ip 080513b sp 794ef0 error 6 in myapp[8048000+24000]

Вот мои вопросы:

  1. Есть ли какая-либо документация относительно того, каковы номера ошибок diff на segfault, в данном случае это ошибка 6, но я видел ошибку 4, 5

  2. Что означает информация at bf794ef0 ip 0805130b sp bf794ef0 and myapp[8048000+24000]?

До сих пор я был в состоянии скомпилировать с символами, и когда я делаю x 0x8048000+24000, он возвращает символ, это правильный путь? Пока что мои предположения следующие:

  • sp = указатель стека?
  • ip = указатель инструкции
  • at = ????
  • myapp [8048000 + 24000] = адрес символа?

Ответы [ 3 ]

40 голосов
/ 31 марта 2010

Когда отчет указывает на программу, а не на общую библиотеку

Запустите 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 для вашей платформы.
36 голосов
/ 01 февраля 2010

Исходя из моих ограниченных знаний, ваши предположения верны.

  • sp = указатель стека
  • ip = указатель инструкции
  • myapp[8048000+24000] = адрес

Если бы я отлаживал проблему, я бы изменил код, чтобы создать дамп ядра, или записал бы возвращение стека в случае сбоя. Вы также можете запустить программу под (или прикрепить) 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 означает, что ошибка была извлечением инструкции
6 голосов
/ 06 июня 2012

Если это общая библиотека

Вы, к сожалению, отыгрались; невозможно узнать, где библиотеки были помещены в память динамическим компоновщиком после факта .

Ну, есть возможность получить информацию не из двоичного файла, а из объекта. Но вам нужен базовый адрес объекта. И эта информация все еще находится в 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's 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.

Само по себе это может показаться непонятным, но с base_addr общего объекта, о котором мы говорим, вы можете получить дополнительную информацию из адреса, отлаживая непосредственно связанный общий объект в другом экземпляре GDB. Оставьте первый GDB, у которого есть идея символа.

ПРИМЕЧАНИЕ: скрипт довольно неполный, я подозреваю, что вы можете добавить ко второму параметру add-symbol-file и вывести сумму с этим значением:

readelf -S $SO_PATH|grep -E '\.text[ \t]'|awk '{print $5}'

где $ SO_PATH - это первый аргумент файла добавления символа

Надеюсь, это поможет

...