BINARY HACKS #23-#29

registerの参照
以下のようにすることで、stack_pointerとframe_pointer registerを参照できる。

 register void* stack_pointer asm ("%esp");
 register void* frame_pointer asm ("%ebp");

なんでespとebpなのだろう?

インラインアセンブラ

 __asm__ ("アセンブラテンプレート":出力オペランド指定:入力オペランド指定:この操作によって変わってしまうもの);
 __asm__ __volatile__ ("アセンブラテンプレート":出力オペランド指定:入力オペランド指定:この操作によって変わってしまうもの);

出力オペランド指定では、以下のような制約の表記をする。=は
“=&S” (d0)
出力オペランドで指定した変数は、アセンブラテンプレートで順に%0, %1などと参照可能。
また、制約は”0″などと表記できる。
入力オペランド指定では、
“0″ (src)
などとする。これは変数srcに”0″という制約を付ける。”0″は%esiレジスタに割り当てられているので、srcを%esiレジスタに設定するようになっている。
やっていることとしては、

入力オペランドで、C言語の入力される変数をレジスタに割り当て
アセンブラテンプレートを処理する
出力オペランドで、レジスタの値を出力の変数に代入する

操作によって変わってしまうもの、および__volatile__はC言語の最適化回避。

ビルトイン関数
例えば、strlenに文字列リテラルを渡すと、最適化の段階でstrlenを呼ばずに即値に変換してくれる。
このような機能は数学関連、文字列関連の関数でしばしば実装されている。
先頭の0bitを数える__builtin_clz(unsigned int x)などがハッカーに好んで使われると言われているが、何に使うのか皆目わからない。
なお、gccでは以下の1行目は文字列リテラルと同値に扱われない。2行目のようにしなければならない。

const char* str = "hoge";
const char* const str = "hoge";

glibcを使わないプログラミング
libcが大量にリンクされているために、プログラムのコード単純なHello Worldでも5kbを越える。この無駄を排除したい。
これを解決するために、システムコールのみを使うことが解決策としてあげられる。
しかし、C言語のwrite関数などは、アーキテクチャ依存性を排除するための単なるglibcの関数である。
つまり、アーキテクチャに特化したシステムコールを探してこなければならない。
そこで、_syscallNを探しだし、exit, write関数となるべきマクロをコピペする。すると、マクロによって関数を定義することができて、writeコマンドをCPUの命令で実行できる。
これを

 gcc -Os -fno-builtin -fomit-frame-pointer -no-ident -c hello.c
 strip -s hello
 strip -R .data hello #dataセクションはreadelf -S曰く、0バイトだからいらない

TLSの利用
スレッド固有の変数を簡便に利用するためのシステムを使う方法。
TLSはthread local storageといい、グローバル変数に__threadキーワードをつけることで実現できる。
まともな使い道としては、errnoの保持などに利用できる。

pthread_key
TLSはポータブルではなく、gccに特有の機能である。
ポータブルに実装するならば、以下のようにpthreadの機能を用いる。

 pthread_key_t key; 
 pthread_key_create(&key, destructor);

 void* value;
 pthread_thread_setspecific(key, value);
 value = pthread_thread_setspecific(key);

 pthread_key_delete(key);

#27を一回飛ばします。

weekを用いて、リンクされているライブラリによって挙動を変化させる
weekはリンク時にundefinedであればそれを0に設定する、というキーワードである。
つまり、例えばlibmがあるかどうかを判別するためのプログラムを以下のように実装することができる。

extern double sqrt(double x) __attribute__ ((week));

void foo {
    if (sqrt) 
        cout << "found" << endl;
    else 
        cout << "not found" << endl;
}

バージョンスクリプトでライブラリ外に出るシンボルを限定する
別のファイルにあるfooとbarが存在し、fooがbarに依存するが、barはライブラリの外部に漏らしたくない、という状況を考える。
この時、以下のバージョンスクリプトlibfoo.mapと、動的ライブラリ作成のためのリンクで-Wl,–version-script,libfoo.mapオプションをつけることで、外部に公開するシンボルを限定できる。

{
    global: foo;
    local: *;
};

C++の場合は、デマングルするためにextern “C++”が必要であることと、デマングルしたときに引数の違いによって関数名の後に後続する文字列があるので、以下のようにする。

{
    global:
        extern "C++" {
            foo::hoge*;
        };
    local *;
};

gcc拡張のvisualityを用いて関数の公開性を制御する
上のversion scriptによる方法は単に隠しているだけなので、PICでコンパイルするとhiddenな関数もPLTを通り、低速である。
gccでは__attribute__ ((visuality(“default”)))を用いることで、より簡便かつ高速な公開非公開の制御ができる。

 #define EXPORT __attribute__ ((visuality("default")))

このオプションをつけて、さらにコンパイル時に、以下をつけることで、defaultと明示的に指定していないシンボルが全てhiddenになる。

-fvisuality=hidden

(attributeはクラスの後、型名の前に付ける)

BINARY HACKS #20-#22

PICを付ける意味
PICは.rel.dynamicを.textに押し付けるオプションである。
PICをつけなくとも、共有ライブラリを正常に動かすことは可能である。しかし、速度面からPICを付ける必要がある。
PICをつけ{ずに, て}コンパイルしたファイルのELFを見る。TEXTRELはテキストの再配置が必要であること、RELCOUNTは再配置の数を表す。
readelf -d fpic-no-pic.so | egrep ‘TEXTREL|RELCOUNT’
readelf -d fpic-pic.so | egrep ‘TEXTREL|RELCOUNT’
とすると、下ではTEXTRELが見つかり、RELCOUNTが多い。
(テキストの再配置って何?RELCOUNTって何?.rel.dynamicって何?)

statifier
動的リンクが必要な実行ファイルを、必要のない実行ファイルに変換する。
statifiere /usr/bin/php php2
実行してみないと動的リンク先が決められないライブラリを利用している場合は、–set=LD_PRELOAD=”a.so b.so”などとする。
これが必要な状況は、/lib/libnss*.soと、/usr/lib/gconv/*.soである。

gccのGNU拡張
(1) ビルトイン関数
文字列リテラルを出力するだけのprintfはputsに置き換えられるなど。
LD_PRELOADのオーバライドで問題が起きることがあるので、これの状況を知るための関数が用意されている。
(2) アトリビュート
関数やデータの性質を記述し、特別な意味を付加したり、呼び出しの最適化を期待する。
宣言時に
int foo(void) __attribute__((つけたいアトリビュート));
などとする。
(3) ラベルの参照
ラベルを&&をつけることで、関数ポインタとして扱うことが出来る。なお、関数ポインタなので減算加算が可能である。
void *label = &&error;
goto *label;
error:

(これは実際に使ってみないと、何が使えるのかよくわからなそう)

BINARY HACKS #14-#19

c++filtについて
nm cpp.o
とすると、cppの名前マングリングによって、_Z3fooiのような読みづらいシンボルが得られる。
これは、
nm cpp.o | c++filt
nm –demangle cpp.o
によって解決できる。

addr2line(needs -g)
ポインタからソースコードの行を特定するためのコマンド。
addr2line -e ./a.out 0×8048764
-fオプションをつけることで、関数名もわかるようになる。
addr2line -e ./a.out 0×8048764

strip
ユーザのプログラムをstripして、開発者のところにしていないバイナリを残すと、コアファイルをコピーすることで開発環境でデバッグ可能。
strip ./a.out
-dオプションは、だいたい-gによる部分だけを削除する。すると、完全にデバッグできなくなる状況を避けられる。
strip -d ./a.out
.o, .aをstripするとシンボルが全部消えてしまう。

ar
作成(r: 新規挿入既存置換、c: 警告抑止, u: 新規のみ置換, s: ranlib)、閲覧、展開
ar rcus libhoge.a 1.o 2.o 3.o …
ar tv libhoge.a
ar xv libhoge.a

C++からCを呼び出す
一点のみ注意が必要である。Cのheaderで以下のようにすればよい。
#ifdef __cplusplus
extern “C” {
/*Cのコードいっぱい*/
}
#endif
extern “C”をつけると、C++の名前マングルの対象外となる。C++ではリンクが通れば型のミスはないと思われるが、Cでは間違ったプロトタイプ宣言が可能。extern “C”をつけた部分では、型の安全性がCレベルまで低下する。でも、普通に考えたらヘッダファイルが存在するはずなので、CからでもC++からでも使えるプロトタイプ宣言を作っておけば問題ない。
参考: http://hakobe932.hatenablog.com/entry/20090104/1231073299

CからC++を呼び出す
C++コードをg++でコンパイル、Cコードをgccでコンパイル、g++でリンクすることを考える。
4点注意が必要である。(1)(2)は「C++からCを呼び出す」の理由、(3)はCのコードにC++の例外が通るとプログラムが落ちることによる。
(1) C++のheaderをifdef, extern “C”でくくる。
#ifdef __cplusplus
extern “C” {
/*Cのコードいっぱい*/
}
#endif
(2) C++のソースをextern “C”でくくる
extern “C” {
int cpp_func(void) {return 0;}
}
(3) C++のソースが例外を投げるならば、例えば以下のようにする。
extern “C” {
int cpp_func(void) try {throw -1;} catch(…) {return -1}
}
(4) 関数ポインタを引数に取るCの関数は-fexceptionsでコンパイルしなければならない。このオプションをつけると、全ての関数についてフレーム解放処理を挿入する。
なお、glibcのqsort, bsearchなどは、このオプションをつけてコンパイルされている。

警告の出ない名前衝突–C言語
gccでたまに経験する、名前衝突しているのに何故か警告も出ずに、実行時に予想していないほうのシンボルが呼ばれるアレ。
これの原因は
(1) 静的ライブラリを作成する時のarコマンドは、ただのアーカイバなので、シンボル名とか見てない。
(2) 動的リンクのSONAMEとNEEDEDの対応付けも、一番初めに見つかった方を利用している。
逆に、回避可能な条件は以下である。
(1) 動的リンクを利用しておらず
(2) .oを全てまとめてリンクして実行ファイルを作成するとき

警告の出ない名前衝突–C++言語
C++ではもう一つ罠が増える。.oを全てまとめてリンクしていても期待したシンボルが参照されない可能性がある。
原因は、
(3) inlineで定義するweekシンボルがnon-weekシンボルの存在により無視されるから。これは.hの中に定義が入っていたとしたら、それが複数の.cppにincludeされると重複して定義されてしまうため、後で解決してやろうという気持ちによる。
回避方法は、
(3) inlineで定義するシンボルを作らない、もしくは、無名namespaceでリンケージリークを防ぐ。無名namespaceによってmoduleにシンボルを閉じ込めれば、当然モジュール内でしか利用できなくなるが、今回のような大域領域での名前衝突は解決できる。