@Soroush、舞台裏で何が起こっているのかをよりよく理解するのに役立つかもしれない例を次に示します。
#include <stdio.h>
int main(void)
{
printf("begin\n");
printf("loop\n");
// declare a function pointer
int (*loopPtr)();
// set the function pointer to the current function
loopPtr = main;
// skip over the first printf();
loopPtr += 22;
// call the new location
loopPtr();
}
私にとっては、でコンパイルするとx86_64で動作しclang -O0
ます(これは無限再帰であり、各関数呼び出しがスタックスペースをかみ砕くため、スタックが使い果たされるまで動作します)。
オフセット22は、コンパイルしてから逆アセンブルしmain()
、2番目のアドレスから開始のアドレスを減算することによって決定しましたprintf()
。
まず、私はそれをコンパイルしました:
clang -O0 test.c
次にそれを分解しました:
otool -tv a.out
...この出力を生成しました:
[...]
_main:
0000000100000ee0 pushq %rbp
0000000100000ee1 movq %rsp,%rbp
0000000100000ee4 subq $0x20,%rsp
0000000100000ee8 leaq 0x00000073(%rip),%rdi
0000000100000eef movb $0x00,%al
0000000100000ef1 callq 0x100000f40
0000000100000ef6 leaq 0x0000006c(%rip),%rdi
0000000100000efd movl %eax,0xf4(%rbp)
0000000100000f00 movb $0x00,%al
0000000100000f02 callq 0x100000f40
[...]
_main:
関数のエントリポイントを示しますmain()
。最初のアドレスは0x100000ee0です。最初のcallq
命令はprintf()
スキップしたい最初の呼び出しに対応しているので、その直後のアドレス0x100000ef6を選択しました。0x100000ef6から0x100000ee0を引いた値は小数点以下22桁です。