Linuxのコンパイル結果が同等であることの確認方法

LINEで送る
Pocket

がじです。
今回はがじノートとしては異色のエントリです。

Linuxでソースコードをコンパイルして実行ファイルを作成する場合に、同じソースコードを使って同じバージョンのコンパイラでビルドを実行していても、ビルドサーバーが異なっていたりすると作成された実行ファイルのバイナリの比較結果が異なることがあります。

このことについて調べた結果の覚書です。

まず差分が発生する理由として考えられるのがコンパイラがビルドする際にリンカが使うためのシンボル情報を付加しているというのは常識。ということでstripコマンドを使ってみることにしました。

strip -s hogehoge

※stripコマンドはバイナリ自身を書き換えてしまいます。単体実行ファイルならstripを実行しても影響は少ないと思いますが、ライブラリ用のファイルなどにstripコマンドを実行してしまうと正常動作しなくなる可能性があるのでテンポラリディレクトリにコピーしたうえで実行してみてください。

このコマンドを実行してhogehogeバイナリからすべてのシンボル情報を削除してみました。
しかし、このコマンドでシンボル情報を削除したうえでサーバー1で作成したhogehogeバイナリとサーバー2で作成したhogehogeバイナリを比較するとなぜかまだ差分があります。

なんだコレと思ってバイナリエディタで差分を比較してみると十数バイトほど差分があることが判明しました。

いろいろ調べてみたところKernelのObject構造というページが見つかりました。

BSD系のページですが、ELFフォーマットについてはLinuxと変わりはありません。

このページに書かれている図を基にELFフォーマットを簡単に図解してみるとこんな感じ。
Binary

stripコマンドで削除されるのはこの図のシンボルの部分。シンボル部分が削除されれば一致しそうなのですが一致しない。

そこでELFヘッダ、プログラムヘッダを一つ一つ確認してみると、ヘッダ情報の中の比較してみるとヘッダの中身の文字列を見ていると

あるヘッダの中身で差分が見つかりました。
そのヘッダの中身がたとえば

huga\0*****

だとすると有効な文字列hugaは一致しているが、ヘッダのアラインメントを合わせるための領域*****の部分が一致していないのです。

私はコンパイラは専門外ですが、おそらくヘッダ情報を作成するためにメモリを確保してそこに値を書き込むのでしょうが、有効範囲外はメモリが確保された時の初期値のまま残ってしまうのではないでしょうか?(尚、コンパイラのソースコードを読もうとしたら10分で挫折・・・)

ということでバイナリ差分を取るためには

1.strip -s コマンドでヘッダ情報を削除する
2.objdump -x でヘッダ情報を表示してdiffで比較する
3.objdump -d で実行ファイルをアセンブルした結果をdiffで確認する

の3段階(差分比較はヘッダの比較とアセンブル結果の比較)で一致を取ればバイナリは同等と言えるのではないでしょうか?

1.のstrip -sコマンドをしておかないと、3.のアセンブル結果にシンボルが含まれて差分が発生する可能性があるので1.は必須です。

かなり個人の想像が含まれていますが、これでバイナリ一致を判断できるという結論に達しました。

・・・本当にこれでいいのか自信はありませんが、覚書として。(2015/2/15)

—以下広告

LINEで送る
Pocket