Cでバイトコード文字列を実行する
自分用のメモです。
特にこれといって意味がないものですが、Cでバイトコード文字列を実行します。
ソースコード
run-bytes-code-as-char-array.c (Gist)
#include <stdio.h>
#include <stdlib.h>
int main(int c, char** v)
{
int i;
qsort(v+1, c-1, sizeof(char*),
(int(*)(const void*, const void*))
"\x55\x89\xe5\x56\x8b\x75\x08\x53\x8b\x5d\x0c\xeb\x0f\x84\xc0\x75\x04\x31"
"\xd2\xeb\x1b\x8d\x41\x01\x89\x06\xff\x03\x8b\x0e\x8b\x03\x8a\x11\x8a\x00"
"\x38\xc2\x74\xe5\x0f\xbe\xd2\x0f\xbe\xd2\x0f\xbe\xc0\x29\xc2\x5b\x89\xd0"
"\x5e\x5d\xc3");
for(i=1; i<c; i++) {
puts(v[i]);
}
return 0;
}
仮引数 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 に対する中間コードのように聞こえて語弊があるかもしれません。この記事で扱っているのは、あくまでもコンパイル時にコンパイル済みのバイトコードが埋め込まれるようにしているだけであって、実行時評価はされませんし、プラットフォームに依存しています。