自分用のメモです。

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