自分用のメモです。
特にこれといって意味がないものですが、Cでバイトコード文字列を実行します。
ソースコード
仮引数 v の中身を直に書き替えているので、あまり行儀の良いコードではないのです。 本来、関数ポインタを渡す qsort()
の第4引数にコンパイルした関数のバイト列を文字列(つまりchar*
)として記述し、そのバイト列を const void*, const void*
を引数にとって int
型を返す関数ポインタにキャストして、qsort()
内からコールバックされます。
インライン・アセンブラならぬ、インライン・バイトコードといったところでしょうか。ハードコーディングされた文字列も、ポインタとして抽象的な解釈がなされるため、よく理にかなった動きをしていると言えます。
このコードを実行すると、引数に与えられた文字列をソートして印字します。
また、上記のようなバイトコード文字列を即時実行させるには、
int n = 10;
int m = 20;
printf("%d\n",
((int(*)(int,int))
"\x55\x89\xe5\x8b\x55\x08\x8b\x45\x0c\x39\xd0\x7d\x02\x89\xd0\x5d\xc3"
)(n, m));
という記述で、関数名(つまり関数ポインタ)に関数呼び出しであることを示す()をつければ、通常の関数と同様に呼び出すことができます。 上のコードは、渡された n, m
を評価し、大きい方を返す関数のバイトコード文字列を実行しています。
バイトコード文字列は、
int max(int a, int b)
{
return a > b ? a : b;
}
という内容のmax.c
があった場合、
gcc -c max.c
objdump -D max.o
して得られる、関数 max() の部分から得られます。
00000000 <max>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 8b 55 08 mov 0x8(%ebp),%edx
6: 8b 45 0c mov 0xc(%ebp),%eax
9: 39 d0 cmp %edx,%eax
b: 7d 02 jge f <max+0xf>
d: 89 d0 mov %edx,%eax
f: 5d pop %ebp
10: c3 ret
バイトコードという表現だと、Java や Ruby の VM に対する中間コードのように聞こえて語弊があるかもしれません。この記事で扱っているのは、あくまでもコンパイル時にコンパイル済みのバイトコードが埋め込まれるようにしているだけであって、実行時評価はされませんし、プラットフォームに依存しています。