Ruby じゃない GEM

http://www.atdot.net/~ko1/diary/200807.html#d17

こういうのはえーと GEM とかあったような(無論名前とか覚えてなかったけど)…ということで遊んでみました。 GEM って何かっていうと、まぁ GCC のそこらこちらに適当にフックがかけられるようになってるとかいう感じかと思います。

http://www.ecsl.cs.sunysb.edu/gem/

ビルド

% tar -xvzf ~/arch/gem-1.7.tar.gz
% cd src/gem-1.7
% tar -xvjf ~/arch/gcc-core-4.1.0.tar.bz2  #
% mkdir gcc-4.1.0/bin                      #
% cd gcc-4.1.0                             #
% patch -p2 < ../patch/gem-4.1.0.patch     #
% cd ..                                    #
% vi Makefile  # GCC_RELEASE=4.1.0
% make

途中 # が並んでる 5行は省略可。省略すると勝手にダウンロードとかしてくれる。

適当に代入文を hook するコードを書く。

assign_hook.c:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tree.h"
#include "c-tree.h"

#include "gem.h"

static tree get_fndecl(char* name) {
  tree fndecl;

  fndecl=get_identifier(name);

  if (fndecl!=NULL_TREE) {
      fndecl=lookup_name(fndecl);
  }

  if (fndecl==NULL_TREE) {
      printf("unable to find %s\n", name);
      abort();
  }

  return fndecl;
}

void assign_hook(tree* t) {
    if (TREE_CODE(*t) != MODIFY_EXPR) {
        return;
    }
    puts("assign_hook");
    //dump_node(*t, 0, stdout);

    tree type = TREE_TYPE(*t);
    tree lhs = TREE_OPERAND(*t, 0);
    tree rhs = TREE_OPERAND(*t, 1);
    // We only hook integral type assignments.
    if (!INTEGRAL_TYPE_P(TREE_TYPE(lhs)) || !INTEGRAL_TYPE_P(TREE_TYPE(lhs))) {
        return;
    }

    // Ignore artificial assignments.
    if (!DECL_NAME(lhs)) {
        return;
    }

    tree arglist = build_tree_list(NULL_TREE, rhs);
    *t = build2(MODIFY_EXPR, type, lhs,
                build_function_call_expr(get_fndecl("assign_hook"), arglist));
}

void gem_init() {
    puts("gem_init");
    gem_expand_expr_pre = assign_hook;
}

void gem_destroy() {
    puts("gem_destroy");
}

MODIFY_EXPR とかいうのが代入文とからしいので、それを見かけたら

  • 両辺が int 型で
  • 左辺に名前があれば (この条件ないと return 文とかも MODIFY_EXPR になるみたいだったので)
  • lhs = assign_hook(rhs); の形に

ってようなことをやってます。ちなみにこれで完全にフックできてるかとか、余計なものフックしないかとかは全く調べてないので、よく知らない。

で assign_hook 関数を定義しておく。

assign_hook_lib.c:

#include <stdio.h>

int assign_hook(int i) {
    printf("assignment hooked!: %d\n", i);
    return i;
}

あとテスト用のコード。

assign_hook_test.c:

#include <stdio.h>

int assign_hook(int i);

int main() {
    int i = 0;
    int s = 0;
    for (i = 0; i < 5; i++) {
        s += i;
    }
    printf("%d\n", s);
    return s;
}

このファイル群を gem-1.7/examples とかに放り込んでやって、 Makefile

assign_hook_obj = assign_hook.o

assign_hook: $(assign_hook_obj)
	gcc -shared -Wl,-soname,test.so -o $(bindir)/assign_hook.gem $(assign_hook_obj) $(LDFLAGS)

assign_hook_test: assign_hook_test.c assign_hook_lib.c assign_hook
	$(GCC_BASE)/bin/bin/gcc assign_hook_lib.c -c -o assign_hook_lib.o
	$(GCC_BASE)/bin/bin/gcc -fextension-module=$(bindir)/assign_hook.gem assign_hook_test.c -c -o assign_hook_test.o
	$(GCC_BASE)/bin/bin/gcc assign_hook_test.o assign_hook_lib.o -o $@

とか書いてやれば、

> make assign_hook_test
gcc -shared -Wl,-soname,test.so -o ../gcc-4.1.0/bin/bin/assign_hook.gem assign_hook.o -ldl -lc
../gcc-4.1.0/bin/bin/gcc assign_hook_lib.c -c -o assign_hook_lib.o
../gcc-4.1.0/bin/bin/gcc -fextension-module=../gcc-4.1.0/bin/bin/assign_hook.gem assign_hook_test.c -c -o assign_hook_test.o
gem_init
assign_hook
assign_hook
assign_hook
assign_hook
assign_hook
assign_hook
gem_destroy
../gcc-4.1.0/bin/bin/gcc assign_hook_test.o assign_hook_lib.o -o assign_hook_test
> ./assign_hook_test
assignment hooked!: 0
assignment hooked!: 0
assignment hooked!: 0
assignment hooked!: 0
assignment hooked!: 1
assignment hooked!: 1
assignment hooked!: 2
assignment hooked!: 3
assignment hooked!: 3
assignment hooked!: 6
assignment hooked!: 4
assignment hooked!: 10
assignment hooked!: 5
10

なんかフックできてますね。おしまい。

なにかあれば下記メールアドレスへ。
shinichiro.hamaji _at_ gmail.com
shinichiro.h