69

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

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

ここに私の質問があります:

  1. segfault の差分エラー番号に関するドキュメントはありますか。この例ではエラー 6 ですが、エラー 4、5 を見たことがあります。

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

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

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

3 に答える 3

52

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

実行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お使いのプラットフォームについては、を参照してください。
于 2010-03-31T00:21:11.093 に答える
37

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

  • 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は、障害が命令フェッチであったことを意味します
于 2010-02-01T19:33:23.960 に答える
7

共有ライブラリの場合

残念ながら、あなたはうんざりしています。動的リンカ 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-symbol-file の2番目のパラメーターに追加して、この値で合計を出力した可能性があると思います:

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

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

それが役に立てば幸い

于 2012-06-06T17:14:26.600 に答える