はじめに

こんにちは!今回は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との併用

addr2lineobjdumpを組み合わせるとさらに詳しい情報が得られます。

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

スタックトレースの謎が解けます。デバッグが捗りますよ!