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はクラスの後、型名の前に付ける)

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>