ltraceで関数呼び出しをトレース

2012-05-16 00:00:00 +0000

ltraceを使って、実行中の関数の呼び出しを簡単にトレースする方法を紹介します。

ltraceとは

ltraceは共有ライブラリの関数呼び出しをトレースするコマンドです。

特にソースコードを変更せずに、コマンドで指定するだけなので気軽に使えます。

今回はltraceを使って関数の呼び出しをトレースする方法を紹介します。

下ごしらえ

まずは呼び出される共有ライブラリを作ります。

foo.cとして以下のコードを保存します。

#include <stdio.h>
#include "foo.h"

void foo()
{
  printf("foo called!\n");
}

今回はfoo.hとしてヘッダーファイルも用意します。

void foo();

ひとまずここまででコンパイルして共有ライブラリを作成します。

$ gcc -shared -fPIC -o foo.so foo.c

次に呼び出し元のコードをmain.cとして保存します。

#include "foo.h"

int main(int argc, char *argv[])
{
  foo();
  return 0;
}

コンパイルします。

$ gcc main.c foo.so

ちなみにmain.cを先に指定しないとリンクエラーになります。

参照元が先に指定されないとリンクエラーになるので注意が必要です。

リンクに成功したら実行してみましょう。

$ ./a.out 
./a.out: error while loading shared libraries: foo.so: cannot open shared object file: No such file or directory

あれれ、エラーが出てますね。

私の環境ではLD_LIBRARY_PATHにカレントディレクトリが含まれていないのでfoo.soを実行時に見つけることができません。

LD_LIBRARY_PATHを指定して実行します。

$ LD_LIBRARY_PATH=. ./a.out 
foo called!

無事に実行されました。

ltraceを使う

それでは準備ができましたので本題のltraceを使ってみましょう。

ltraceの引数として実行形式を指定します。

$ LD_LIBRARY_PATH=. ltrace ./a.out 
__libc_start_main(0x40067c, 1, 0x7fffd110d578, 0x4006a0, 0x400730 <unfinished ...>
foo(1, 0x7fffd110d578, 0x7fffd110d588, 0x4006a0, 0x400730foo called!
) = 12
+++ exited (status 0) +++

実行されてトレースの情報が表示されていますが、なにやらプログラムの出力と混ざってしまったようです。

プログラムの出力を/dev/nullにつっこみます。

$ LD_LIBRARY_PATH=. ltrace ./a.out > /dev/null
__libc_start_main(0x4005b4, 1, 0x7fffa905a7d8, 0x4005e0, 0x4005d0 <unfinished ...>
foo(1, 0x7fffa905a7d8, 0x7fffa905a7e8, 0, 0x3abd38b300)                                          = 12
+++ exited (status 0) +++

余計なものがなくなりました。

ちなみに’= 12’はfooを呼び出したあとの戻り値用のレジスタの値が表示されているようです。

fooの戻り値の型はvoidなので戻り値はないのですが、printfの戻り値が、12バイト出力したので12で、 それが戻り値用のレジスタにはいったままなのでfooの戻り値が12と認識されています。

それでは試しにインラインアセンブラで戻り値用のレジスタに値’3’を入れてみましょう。

私の環境では戻り値用のレジスタは\%raxなので次のように変更します。

#include <stdio.h>

void foo()
{
  printf("foo called!\n");
  __asm__ ("mov $3, %rax");
}

コンパイルして実行してみましょう。

$ gcc -shared -fPIC -o foo.so foo.c
$ LD_LIBRARY_PATH=. ltrace ./a.out > /dev/null
__libc_start_main(0x4005b4, 1, 0x7fff57970d68, 0x4005e0, 0x4005d0 <unfinished ...>
foo(1, 0x7fff57970d68, 0x7fff57970d78, 0, 0x3abd38b300)                                          = 3
+++ exited (status 0) +++

3になっていることがわかります。

まとめ

今回はltraceを使って関数の呼び出しをトレースする方法を紹介しました。

バイナリがどの共有ライブラリのどの関数を呼び出して、その戻り値がどうなっているのか簡単に確認できるため、 ソースのないプログラムでも何をやっているのかを知る手がかりとなります。

ご質問等、何かありましたらこちらへ。

関連する記事

将棋DB2
プロ棋士からコンピューター将棋の棋譜まで観られる将棋の棋譜サービス
将棋DB iOS
将棋DB2のiOSアプリ
将棋DB Android
将棋DB2のAndroidアプリ
碁DB
プロ棋士からコンピューター囲碁の棋譜まで観られる囲碁の棋譜サービス