はじめに#
こんにちは!今回はaddr2lineコマンドについて解説します。
addr2lineはバイナリファイルのメモリアドレスをソースコードの行番号に変換するコマンドです。プログラムがクラッシュしたときのスタックトレースを読みやすくしたい時に使う、デバッガー向けのコマンドですね。
開発者なら知ってて損はない便利ツール。バグ追跡に大活躍します!
addr2lineコマンドとは#
addr2lineは、ELFバイナリファイルのメモリアドレスをソースコードのファイル名と行番号に変換する外部コマンドです。
デバッグシンボル情報を利用して、実行中のメモリアドレスがソースコードのどの部分に対応するか教えてくれます。スタックトレースやコアダンプの解析に重要です。GDBなどのデバッガーでも使われています。
基本構文#
1
|
addr2line [オプション] address [address ...] -e binary_file
|
address: 変換したいメモリアドレス(16進数)
binary_file: 対象のバイナリファイル(-eオプションで指定)
主なオプション#
| オプション |
説明 |
-e file |
対象バイナリファイルを指定(必須) |
-f |
関数名も表示 |
-s |
シンボルテーブルのアドレスを表示 |
-C |
C++の関数名をデマングル |
-p |
出力形式を調整 |
-j section |
特定のセクションを指定 |
使用例#
例1: 基本的な使い方#
1
|
addr2line -e ./my_program 0x400450
|
実行結果:
1
|
/path/to/source/main.c:42
|
バイナリmy_programのアドレス0x400450に対応するソースコード位置を表示します。
例2: 関数名を含めて表示#
1
|
addr2line -f -e ./my_program 0x400450
|
実行結果:
1
2
|
main
/path/to/source/main.c:42
|
関数名も一緒に表示されます。
例3: 複数のアドレスを変換#
1
|
addr2line -e ./my_program 0x400450 0x400500 0x400550
|
実行結果:
1
2
3
|
/path/to/source/main.c:42
/path/to/source/main.c:85
/path/to/source/utils.c:120
|
複数アドレスを一度に処理できます。
例4: 標準入力からアドレスを読み込み#
1
|
echo "0x400450" | addr2line -e ./my_program
|
実行結果:
1
|
/path/to/source/main.c:42
|
パイプで複数アドレスを処理するのに便利です。
例5: スタックトレースの解析#
1
2
3
4
5
6
7
8
|
gdb ./my_program
(gdb) run
Segmentation fault (core dumped)
(gdb) bt
#0 0x0000555555554650 in main () at main.c:42
(gdb) quit
addr2line -f -e ./my_program 0x0000555555554650
|
実行結果:
1
2
|
main
/path/to/source/main.c:42
|
GDBのスタックトレースから取得したアドレスを解析します。
例6: C++プログラムでのマングル解除#
1
|
addr2line -C -f -e ./cpp_program 0x400450
|
実行結果:
1
2
|
std::vector<int>::push_back(int const&)
/path/to/source/main.cpp:35
|
C++の関数名を人間が読める形に変換します。
例7: コアダンプの解析#
1
2
3
4
5
6
7
|
# コアダンプから情報を抽出
gdb ./my_program core.dump
(gdb) info stack
(gdb) quit
# addr2lineで変換
addr2line -f -e ./my_program 0x400450 0x400500
|
実行結果:
1
2
3
4
|
crash_function
/path/to/source/error_handler.c:156
segfault_handler
/path/to/source/signal.c:89
|
コアダンプの解析に活用します。
例8: ログファイル内のアドレスを一括処理#
1
|
grep "0x" error.log | awk '{print $1}' | addr2line -e ./my_program
|
実行結果:
1
2
3
|
/path/to/source/main.c:42
/path/to/source/utils.c:120
/path/to/source/error.c:55
|
ログファイルからアドレスを抽出して一括処理します。
Tips・注意点#
デバッグシンボル情報が必須#
addr2lineが機能するにはバイナリにデバッグシンボルが含まれている必要があります。
1
2
|
# gccでコンパイルするときは-gオプション
gcc -g -o my_program main.c
|
stripコマンドでシンボル削除に注意#
本番環境ではstripでシンボル情報を削除することがありますが、その場合addr2lineは使えなくなります。
1
2
3
|
# シンボル削除前のバイナリを保存
cp my_program my_program.debug
strip my_program
|
64ビット環境でのアドレス#
64ビットシステムでは長いアドレスが出現します。16進数のまま処理してください。
objdumpとの併用#
addr2lineとobjdumpを組み合わせるとさらに詳しい情報が得られます。
1
2
|
objdump -d ./my_program | grep 400450
addr2line -e ./my_program 0x400450
|
実践的な使い方#
スタックトレースの自動解析スクリプト#
1
2
3
4
5
6
7
8
9
10
|
#!/bin/bash
BINARY="./my_program"
while IFS= read -r line; do
if [[ $line =~ 0x[0-9a-f]+ ]]; then
addr=$(echo "$line" | grep -oE '0x[0-9a-f]+' | head -1)
echo "Address: $addr"
addr2line -f -e "$BINARY" "$addr"
fi
done
|
スタックトレースファイルの全アドレスを自動的に解析します。
リモートシステムでのデバッグ#
1
2
3
|
# リモートから取得したコアダンプを解析
scp user@remote:/tmp/core.dump .
addr2line -f -e ./remote_program 0x400450
|
リモートシステムのクラッシュを手元で分析できます。
Pythonでの自動化#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
#!/usr/bin/env python3
import subprocess
import re
def get_source_line(binary, address):
cmd = ['addr2line', '-e', binary, address]
result = subprocess.run(cmd, capture_output=True, text=True)
return result.stdout.strip()
binary = "./my_program"
addresses = ["0x400450", "0x400500", "0x400550"]
for addr in addresses:
print(f"{addr}: {get_source_line(binary, addr)}")
|
Pythonスクリプトで複数アドレスを処理します。
まとめ#
addr2lineコマンドのポイント:
- メモリアドレスをソースコード位置に変換するコマンド
- デバッグシンボル: バイナリに-gでコンパイル必須
- -e: 対象バイナリファイルを指定
- -f: 関数名も表示(便利!)
- -C: C++のマングル解除
- よく使う組み合わせ:
addr2line -f -e binary address
スタックトレースの謎が解けます。デバッグが捗りますよ!