Cでバイトコード文字列を実行する

投稿者: | 2014年5月18日

自分用のメモです。

特にこれといって意味がないものですが、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 に対する中間コードのように聞こえて語弊があるかもしれません。この記事で扱っているのは、あくまでもコンパイル時にコンパイル済みのバイトコードが埋め込まれるようにしているだけであって、実行時評価はされませんし、プラットフォームに依存しています。

コメントを残す

メールアドレスが公開されることはありません。