web-dev-qa-db-ja.com

segfaultカーネルログメッセージの読み方

これは非常に簡単な質問です。次のsegfaultエラーをkern.logで生成するアプリケーションをデバッグしようとしています。

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

私の質問は次のとおりです。

  1. Segfaultのdiffエラー番号についてのドキュメントはありますか?この例ではエラー6ですが、エラー4、5を見ました

  2. 情報at bf794ef0 ip 0805130b sp bf794ef0 and myapp[8048000+24000]の意味は何ですか?

これまでのところ、シンボルを使用してコンパイルできましたが、x 0x8048000+24000を実行するとシンボルが返されますが、それは正しい方法ですか?これまでの私の仮定は次のとおりです。

  • sp =スタックポインター?
  • ip =命令ポインター
  • at = ????
  • myapp [8048000 + 24000] =シンボルのアドレス?
61
Sullenx

レポートが共有ライブラリーではなくプログラムを指す場合

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を参照してください。
40
Charles Duffy

私の限られた知識に基づいて、あなたの仮定は正しいです。

  • sp =スタックポインター
  • ip =命令ポインター
  • myapp[8048000+24000] =アドレス

問題をデバッグしている場合、コードを修正してコアダンプを生成するか、クラッシュ時に stack 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は、フォルトが命令フェッチであったことを意味します
36
jschmier

共有ライブラリの場合

残念ながら、あなたはうんざりしています。ライブラリが動的リンカーafter-the-factによってメモリ内のどこに配置されているかを知ることはできません

さて、バイナリからではなく、オブジェクトから情報を取得する可能性がまだあります。ただし、オブジェクトのベースアドレスが必要です。そして、この情報はまだlink_map構造のコアダンプ内にあります。

そのため、最初に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

gDBコマンドのセット内でlink_mapコンテンツ全体を印刷します。

それ自体は見劣りしないように見えるかもしれませんが、共有オブジェクトのbase_addrを使用すると、別のGDBインスタンスの関連する共有オブジェクトを直接デバッグすることで、アドレスからさらに多くの情報を取得できます。シンボルのアイデアを持つ最初のgdbを保持します。

注:スクリプトはかなり不完全です-add add-symbol-fileの2番目のパラメーターに、この値で合計を出力した可能性があります:

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

ここで、$ SO_PATHはadd-symbol-fileのfirst引数です

それが役に立てば幸い

6
scripthelps