一年ほど前にGDBを使ったコードカバレッジツールを作っていましたが、
mcommit.hatenadiary.com
最近少し時間ができたのでIntelPinという動的バイナリ計装エンジン(Dynamic Binary Instrumentation Engine)を使う形で改めて作ってみました。
IntelのPinプラグインを書くのは初めてだったので使い方やバイナリ計装について調べたので諸々を記事に書いておきたいと思います。
Intel Pin とは何か?
Intel Pin は文字通りIntelが公開している動的バイナリ計装エンジンです。ユーザアプリケーションを実行時に計装くれて、実行した命令やメモリアクセス、関数呼び出し時のレジスタの値など、様々な情報を収集することができます。プラットフォームはLinuxとWindowsに対応しているようです。
正式名称は Pin のようなので以降はPinで統一して記載します。
Pin本体はDBI(Dynamic Binary Instrumentation)のエンジンであり、Pinの利用者はPinが提供するAPI(C++)を使って実行時の情報を取得するコードを実装することができます。Pinの様々な機能(API)を利用したツールやサンプルのソースコードは公開されていますが、Pinの本体のソースコードは公開されておらず実行モジュールとして提供されています。
DBI は何をしているのか?
DBIの利用者は実行中のプログラムから様々な情報を取得するコードを実装することになります。
DBIエンジンはターゲットプログラムの関数を解析し、DBIのユーザが実装したコードを埋め込んだ関数を生成します。これは計装・パッチングと呼ばれます。
そして、ターゲットとなるプログラムの初回の関数呼び出しをフックし、JITコンパイルにより、計装された関数を呼び出すような変更が行われます。
ちなみに、実行時ではなく静的にバイナリ計装する研究もあるそうですが、静的なバイナリ計装だと得られる情報が少ないため適切な計装ができないため、動的バイナリ計装の方がよく使われているそうです。
Pinの使い方
Pinの使い方はそこまで難しくありません。PinはPin kitと呼ばれる実行モジュールとツール等を含んだ形で配布されています。
まずはPinのサイトからPin kitをダウンロードします。
tar.gzで圧縮されているので解凍します。
# 解凍例 $tar xf pin-3.27-98718-gbeaa5d51e-gcc-linux.tar.gz
APIの利用例やツール類は sources/tools以下にソースコードがあります。
ツール一式もここにあるので make を実行すればビルドされます。
$ cd sources/tools $ make
Pinの実行方法
pinコマンドはDBIエンジン本体になります。利用者は共有ライブラリとして実装したツールを読み込んで実行します。
実行する際は以下のような形でコマンドを実行することになります。
cd <pin_kit_path> ./pin -t ./<tool_module>.so -- <target_module_path> <target_args...>
ここでの
Pin のAPIの利用法
PinのAPIは Pin kit に含まれるpin.Hをインクルードすることで利用できます。
詳細はユーザガイドやAPIのリファレンス、sources/tools以下の実装例を参考にして頂ければと思います。
PinのAPIを利用する実装の大まかな流れは、
- PIN_Initにより初期化を行う
- IMG_AddInstrumentFunction,INS_AddInstrumentFunctionで必要に応じた計装処理を登録する
- PIN_AddFiniFunctionで実行終了時の処理(解析結果の出力やリソースの開放など)を登録する
- PIN_StartProgramでPinの実行を開始する
といった流れになります。
Makefileについて
Pinのツールを自作する場合、sources/tools/MyPinTool をひな型として利用するようです。
MyPinToolをコピーして、フォルダ名やファイル名を適切に変更していくことで自作のツールが実装できます。
ツールのフォルダをPin kitの外に配置する場合、
make PIN_ROOT=../pin-3.27-98718-gbeaa5d51e-gcc-linux
のようにPin kitのフォルダパスを指定してmakeコマンドを実行する必要があります。
Pinを使ってみたきっかけ
Pin 以外のDBIエンジンとしては、
といった実装があるそうです。
QDBIについてはこちらのYouTubeの動画で詳細が説明されています。
計装の仕組みとして、LLVM IRを利用することで計装を実現しているそうです。
www.youtube.com
私がPinを使ったコードカバレッジツールを書いてみたのは偶然こちらの動画を見たことがきっかけでした。
上記の動画中で、
プログラムの動的解析には
- デバッガを使う
- 動的バイナリ計装を使う
方法があるがデバッガを使う方法はめっちゃ遅い
ということが解説されています。
実際、私が以前作ったデバッガ(GDB)を使うカバレッジツールは規模の大きなプログラムのカバレッジを取るのにはかなり時間がかかっています。マイコンを使うような組み込み開発でカバレッジを取りたくて作ったものなのでx64のアプリケーションのトレースに時間がかかることはそこまで気にしていませんでしたが、もっとパフォーマンスのよいものが作れるならつくってみたいなぁと何となく思っていたところこのDBIのことを知り、Pinを使えばできそうということで作ってみたというところです。
作ってみたカバレッジツールについて
以下のレポジトリで公開しています。
github.com
具体的な使い方についてはREADMEの方に書いておりますのでこちらでは詳しくは触れませんが、
00_setup.sh, 01_run_example.sh の簡単な動作確認用のスクリプトを実行して頂ければ雰囲気や使い方を理解して頂けるかと思います。
00_setup.sh
でpinのダウンロードとカバレッジツールのビルドを行います。
01_run_example.sh
で同梱している簡単なCのプログラムのカバレッジを取ります。
機能も増やそうと思ってコードを触っている途中ですが、Pinツールを自作する際の参考になればと思います。
参考書籍・参考サイト
Pin を使ってみるにあたって、実践バイナリ解析という本を参考にさせて頂きました。動的バイナリ計装の仕組みの解説されいておすすめです。
Pin の利用例としてプロファイリングツールの実装例が取り上げられています。
pinの使い方については以下のサイトを参考にさせて頂きました。
inaz2.hatenablog.com
感想
PinにはいろんなAPIが提供されていて、DWARFの行番号情報もAPIを使って取得することができました。*1
提供されているAPIをうまく使うと面白いことができそうです。もう少し調べてコードカバレッジツールに便利な機能を増やしていきたいと思います。
*1:ただしDWARF4