gccでwrapオプションを利用すると、main関数を別の関数から実行することができます。
コンパイルの時にwrapオプションで指定した関数を実行する時、その前にwrap関数を割りこませる、という表現の方が実態に近いかもしれません。あと、これでwrapできるのはlibcでサポートしているシステムコール限定かもしれません。
ソースコードをコンパイルし、生成されたオブジェクトファイルをリンクしていく仕組み、プログラムが起動する仕組みをちゃんと勉強すればもう少しまともな説明ができそうな気もしますが、その辺は後々。
サンプルコード 其の一
test.c
#include<stdio.h>
#include<unistd.h>
// こが実際のmain関数らしい...
int __real_main(int argc, char *argv[]);
// コンパイルする時「wrap,main」を指定するとmain関数の前に__wrap_man関数が実行されます。
int __wrap_main(int argc, char *argv[])
{
printf("%s\n", __FUNCTION__);
return __real_main(argc, argv);
}
int main(int argc, char *argv[])
{
int cmdopt = 0;
printf("%s\n", __FUNCTION__);
printf("%d\n", argc);
while((cmdopt=getopt(argc, argv, "m:")) > 0)
{
switch(cmdopt) {
case 'm':
printf("%s\n", (char*)optarg);
break;
default:
printf("not support arg(%d %s)\n", cmdopt, (char*)optarg);
return(1);
}
}
return(0);
}
これを、
$ gcc -g -o test test.c -Wall -Wl,-wrap,main
または、
$ gcc -g -o test test.c -Wall -Wl,-wrap=main
でコンパイルすると、
$ ./test -m orz
__wrap_main
main <- __wrap_mainから実行されたmain関数
3 <- コマンド引数の数(コマンド名含む)
orz <- -mのオプション
のように、main関数実行前に__wrap_main関数が実行されます。
サンプルコード 其の二
其の一のサンプルだと、__wrap_mainはただの土管なので、わざわざラップするメリットがないのですが、これを利用するとプログラム内でmain関数に引数をセットして実行するテストコード(みたいな?)を書くことができます。
test2.c
/*
* デバッグコンパイル
* gcc -g -o test test2.c -Wall -Wl,-wrap,main -DDEBUG
* gcc -g -o test test2.c -Wall -Wl,-wrap=main -DDEBUG
* 通常コンパイル
* gcc -g -o test test2.c -Wall
*/
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<assert.h>
#ifdef DEBUG
int __real_main(int argc, char *argv[]);
int __wrap_main(int argc, char *argv[])
{
/* main関数の引数をセットする */
int wrap_argc = 3;
char *wrap_argv[] = {argv[0], "-m", "test message"};
/* main関数の戻り値を評価する */
assert(__real_main(wrap_argc, wrap_argv) != 0);
return(0);
}
#endif
int main(int argc, char *argv[])
{
int cmdopt = 0;
while((cmdopt=getopt(argc, argv, "m:")) > 0)
{
switch(cmdopt) {
case 'm':
printf("%s\n", (char*)optarg);
break;
default:
printf("not support option(%d %s)\n", cmdopt, (char*)optarg);
return(EXIT_FAILURE);
}
}
return(EXIT_SUCCESS);
}
ただ、下記のように__wrap_main関数を、main関数終了直後に、引数を変えてもう一度main関数を実行しても期待した動作が得られないので、テストコードとしては微妙な感じです。
int __wrap_main(int argc, char *argv[])
{
printf("%s\n", __FUNCTION__);
int wrap_argc1 = 3;
char *wrap_argv1[] = {argv[0], "-m", "test message"};
assert(__real_main(wrap_argc1, wrap_argv1) == 0);
int wrap_argc2 = 3;
char *wrap_argv2[] = {argv[0], "-M", "test message"};
/* ここでassert NGになることを期待していたのですがOKだった... */
assert(__real_main(wrap_argc2, wrap_argv2) == 0);
return(0);
}