ここにIT工学の学生。コンテキストの切り替えを試してみるように求められましたが、ある特定の割り当てでは、かなり大雑把な試行/スローシステムを実装しました。これが私たちが書いてきたコードです:
struct ctx_s {
int esp;
int ebp;
};
struct ctx_s * pctx;
typedef int (func_t)(int); /* a function that returns an int from an int */
int try(func_t *f, int arg)
{
/* saving context by storing values of %esp and %ebp */
asm ("movl %%esp, %0"
: "=r"((*pctx).esp)
:
);
asm ("movl %%ebp, %0"
: "=r"((*pctx).ebp)
:
);
/* calling the function sent to try(), returning whatever it returns */
return f(arg);
}
int throw(int r)
{
printf("MAGIC PRINT\n");
static int my_return = 0;
/* ^ to avoid "an element from initialisation is not a constant" */
my_return = r;
/* restituting context saved in try() */
asm ("movl %0, %%esp"
:
: "r"((*pctx).esp)
);
asm ("movl %0, %%ebp"
:
: "r"((*pctx).ebp)
);
/* this return will go back to main() since we've restored try()'s context
so the return address is whatever called try... */
/* my_return is static (=> stored in the heap) so it's not been corrupted,
unlike r which is now the second parameter received from try()'s context,
and who knows what that might be */
return my_return;
}
pctxは、2つのintを保持する単純な構造体へのグローバルポインタです。fは、throw()を呼び出して42に#defineされたリターンコードを送信する関数です。main()は、基本的にpctxを割り当て、result = try(f、0)を実行します。結果を出力します。結果は42になると予想しています。
これで、throw()でMAGICPRINTを見つけたかもしれません。完全に明確ではない理由でここにあります; 基本的に、ほとんどの(すべてではない)学生はthrow()内でsegfaultingしていました。この関数内でprintf()を呼び出すと、プログラムは一見正しく機能するように見え、教師はシステムコールも同様に機能すると考えています。
あまり説明がつかなかったので、gcc -Sで生成されたアセンブリコードを両方のバージョン(printf()ありとなし)で比較してみましたが、あまり理解できませんでした。throw()の開始中括弧(33行目)にブレークポイントを設定し、gdbで逆アセンブルすると、次のようになります。
printf()なし:
Breakpoint 1, throw (r=42) at main4.c:38
(gdb) disass
Dump of assembler code for function throw:
0x0804845a <throw+0>: push %ebp
0x0804845b <throw+1>: mov %esp,%ebp
0x0804845d <throw+3>: mov 0x8(%ebp),%eax
0x08048460 <throw+6>: mov %eax,0x8049720
0x08048465 <throw+11>: mov 0x8049724,%eax
0x0804846a <throw+16>: mov (%eax),%eax
0x0804846c <throw+18>: mov %eax,%esp
0x0804846e <throw+20>: mov 0x8049724,%eax
0x08048473 <throw+25>: mov 0x4(%eax),%eax
0x08048476 <throw+28>: mov %eax,%ebp
0x08048478 <throw+30>: mov 0x8049720,%eax
0x0804847d <throw+35>: pop %ebp
0x0804847e <throw+36>: ret
End of assembler dump.
(gdb) c
Continuing.
Program received signal SIGSEGV, Segmentation fault.
0xb7e846c0 in ?? ()
printf()を使用する場合:
Breakpoint 1, throw (r=42) at main4.c:34
(gdb) disassemble
Dump of assembler code for function throw:
0x0804845a <throw+0>: push %ebp
0x0804845b <throw+1>: mov %esp,%ebp
0x0804845d <throw+3>: sub $0x18,%esp
0x08048460 <throw+6>: movl $0x80485f0,(%esp)
0x08048467 <throw+13>: call 0x8048364 <puts@plt>
0x0804846c <throw+18>: mov 0x8(%ebp),%eax
0x0804846f <throw+21>: mov %eax,0x804973c
0x08048474 <throw+26>: mov 0x8049740,%eax
0x08048479 <throw+31>: mov (%eax),%eax
0x0804847b <throw+33>: mov %eax,%esp
0x0804847d <throw+35>: mov 0x8049740,%eax
0x08048482 <throw+40>: mov 0x4(%eax),%eax
0x08048485 <throw+43>: mov %eax,%ebp
0x08048487 <throw+45>: mov 0x804973c,%eax
0x0804848c <throw+50>: leave
0x0804848d <throw+51>: ret
End of assembler dump.
(gdb) c
Continuing.
MAGIC PRINT
result = 42
Program exited normally.
どうしたらいいのかよくわかりません。明らかに状況は異なりますが、どちらの場合も何が起こっているのかを理解するのは非常に難しいと感じています...したがって、私の質問は、本質的に、printfを呼び出すとsegfaultではなくthrowを作成する方法です。