これは非常に簡単な質問です。次のsegfaultエラーをkern.log
で生成するアプリケーションをデバッグしようとしています。
kernel: myapp[15514]: segfault at 794ef0 ip 080513b sp 794ef0 error 6 in myapp[8048000+24000]
私の質問は次のとおりです。
Segfaultのdiffエラー番号についてのドキュメントはありますか?この例ではエラー6ですが、エラー4、5を見ました
情報at bf794ef0 ip 0805130b sp bf794ef0 and myapp[8048000+24000]
の意味は何ですか?
これまでのところ、シンボルを使用してコンパイルできましたが、x 0x8048000+24000
を実行するとシンボルが返されますが、それは正しい方法ですか?これまでの私の仮定は次のとおりです。
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
を参照してください。私の限られた知識に基づいて、あなたの仮定は正しいです。
sp
=スタックポインターip
=命令ポインターmyapp[8048000+24000]
=アドレス問題をデバッグしている場合、コードを修正してコアダンプを生成するか、クラッシュ時に stack backtrace を記録します。 GDBの下でプログラムを実行(またはアタッチ)することもできます。
エラーコードはページフォールトのアーキテクチャエラーコードにすぎず、アーキテクチャ固有のものと思われます。多くの場合、カーネルソースのArch/*/mm/fault.c
に文書化されています。 Linux/Arch/i386/mm/fault.c
のコピーには、error_codeの次の定義があります。
Linux/Arch/x86_64/mm/fault.c
の私のコピーは以下を追加します:
共有ライブラリの場合
残念ながら、あなたはうんざりしています。ライブラリが動的リンカー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引数です
それが役に立てば幸い