[home]
[log in]


crunchgenとは

*BSDには、複数のプログラム(コンポーネント)を結合して、一つの実行ファイルにするcrunchgenというプログラムがあります。

それぞれのプログラムを別々の実行ファイルにするよりも、トータルのサイズを小さくする事ができます。

要は、Linuxでのbusyboxのような物です。

crunchgenのキモは、各コンポーネントに含まれるシンボルが重ならないように、シンボルの書き換えを行っている点です。

結合されたプログラムの動作概要

例として、catとcpとchmodという3つのコンポーネントを一つの実行ファイルallprogにまとめたとします。 (allprogという名前は、任意ですので、別の名前にする事もできます)。

ファイルシステム上では、cat、cp、chmodから、allprogへリンク(シンボリック、ハードどちらでも可)を張っておきます。 こうすると、cat, cp, chmodのどれを実行しても、allprogが実行される事になります。

allprogのmain()は、

という動作をします。

crunchgenが作るファイルの説明

crunchgenは、allprog.confファイルを読み込んで、allprog.mkファイルとallprog.cファイルを作成します。

allprog.confには、

などの設定を書いておきます。

allprog.mk: 各コンポーネントのビルドをし、その後一つの実行ファイルを作るためのmakefile

allprog.c: main()。ここからargv[0]を見て、各のコンポーネントのmainを呼ぶ。

使い方:

$ crunchgen allprog.conf (allprog.mkとallprog.cの作成)
$ make -f allprog.mk

allprogのmain()の説明

crunchgenは、allprog.cを自動生成します。 allprog.cの中身は、以下のようになってします(一部省略しています)。

struct stub {
    char *name;
    int (*f)();
};

extern struct stub entry_points[];

int main(int argc, char **argv, char **envp)
{
    char *slash, *basename;
    struct stub *ep;

    if(argv[0] == NULL || *argv[0] == '\0')
        crunched_usage();

    for(ep=entry_points; ep->name != NULL; ep++)
        if(!strcmp(argv[0], ep->name)) break;

    if(ep->name)
        return ep->f(argc, argv, envp);
    else {
        fprintf(stderr, "%s: %s not compiled in\n", EXECNAME, basename);
        crunched_usage();
    }
}

extern int _crunched_cat_stub();
extern int _crunched_chmod_stub();
extern int _crunched_cp_stub();
extern int _crunched_dd_stub();
extern int _crunched_df_stub();
extern int _crunched_ed_stub();

struct stub entry_points[] = {
        { "cat", _crunched_cat_stub },
        { "chmod", _crunched_chmod_stub },
        { "cp", _crunched_cp_stub },
        { "dd", _crunched_dd_stub },
        { "df", _crunched_df_stub },
        { "ed", _crunched_ed_stub },
        { NULL, NULL }
};

各コンポーネントのmain()は、_crunched_xxx_stub()という名称に変わっています。 これは、mainというシンボル名が重ならないようにするためです。

allprog.mkの中身

  1. 各コンポーネントをコンパイルして、xxx.roを作る。
  2. xxx.roのシンボルを書き換えて、xxx.croを作る。
  3. xxx.croとallprog.oをリンクして、allprogを作る。

という動作をします。

xxx.croは、各コンポーネントのシンボルが重ならないように、シンボル書き換え済みのオブジェクトファイルです。

xxx.croの作り方

catを例にとります。

まず、catを作るのに必要なファイルをコンパイルして、cat.roを作ります。 cat.roから、cat.croを作るのは、allprog.mkの以下の部分です。

cat.cro: cat .WAIT ${cat_OBJPATHS}
    ${NM} -ng cat.ro | awk '/^ *U / { next };\
/^[0-9a-fA-F]+ C/ { next };\
/ main$$/ { print "main _crunched_cat_stub"; next };\
{ print $$3 " " $$3 "$$$$from$$$$cat" }' > cat.cro.syms
    ${OBJCOPY} --redefine-syms cat.cro.syms cat.ro cat.cro

まず、nm -ngで、cat.roの外部シンボルのみを取り出します。

bash-3.00$ nm -ng cat.ro
         U __fstat30
         U __sF
         U __setlocale_mb_len_max_32
         U __srget
         U __swbuf
         U _ctype_
         U close
         U err
         U exit
         U fclose
         U fcntl
         U fopen
         U fprintf
         U fputs
         U getopt
         U malloc
         U open
         U optind
         U read
         U setbuf
         U setprogname
         U strcmp
         U warn
         U warnx
         U write
00000000 T raw_cat
000000f8 T raw_args
0000023c T cook_buf
00000408 B vflag
0000040c B tflag
00000410 B fflag
00000414 B rval
00000418 B filename
0000041c B eflag
00000420 B bflag
00000424 B lflag
00000428 B nflag
0000042c B sflag
0000048c T cook_args
00000558 T main

次にawkで、以下の処理をします。

この出力をcat.cro.symsに書き込み、cat.cro.symsが作られます。 このファイルは、シンボルの書き換え前の名前と書き換え後の名前のテーブルです。

bash-3.00$ cat cat.cro.syms
raw_cat raw_cat$$from$$cat
raw_args raw_args$$from$$cat
cook_buf cook_buf$$from$$cat
vflag vflag$$from$$cat
tflag tflag$$from$$cat
fflag fflag$$from$$cat
rval rval$$from$$cat
filename filename$$from$$cat
eflag eflag$$from$$cat
bflag bflag$$from$$cat
lflag lflag$$from$$cat
nflag nflag$$from$$cat
sflag sflag$$from$$cat
cook_args cook_args$$from$$cat
main _crunched_cat_stub

シンボルの書き換えは、objcopyの--redefine-syms機能を使って行います。

cat.cro.symsをシンボルの書き換えテーブルとして指定する事で、cat.roのシンボルを書き換えたcat.croを作ります。

$ objcopy --redefine-syms cat.cro.syms cat.ro cat.cro

なぜ$$をシンボル名に入れるか

という事だと思います。

ただ、main()はallprog.cから呼ぶので、mainだけは$入りのシンボル名に変換する事はできません。



Last Modified: 2008-05-15 Thursday