私はこの問題の専門家ではありませんが、できる限りお答えします。
REX バイト テストから始めます。
if ((_rip[0] & 0xf0) == 0x40) /* REX byte present. */
{
unsigned char _rex = _rip[0] & 0x0f;
_is_64_bit = (_rex & 0x08) != 0;
_rip++;
}
REX バイトは、64 ビット モードで使用される命令プレフィックスです。命令の最初のバイトの上位 4 ビットが一致0x40
する場合、REX プレフィックス バイトがあることがわかります。また、ビット 3 (W フィールド) が 1 に設定されている場合は、オペランド サイズが 64 ビットであることを意味します。_rip++
プレフィックスをスキップするだけです。
if (_rip[0] == 0xf7)
F7
これは、ある種の整数除算命令であることがわかります。
{
bool _min_value_dividend = false;
unsigned char _modrm = _rip[1];
次のバイトは ModR/M バイトで、通常はオペランドの詳細を示しますが、この場合は除算命令のタイプも決定します。
if (((_modrm >> 3) & 7) == 7)
ModR/M バイトの REG フィールド (ビット 3 ~ 5) は通常レジスタを表しますが、ここでは命令のオペコードの拡張です。7 の場合、これは符号付き除算であることを意味します。
{
if (_is_64_bit)
_min_value_dividend =
_gregs[REG_RAX] == (greg_t)0x8000000000000000UL;
else
_min_value_dividend =
(_gregs[REG_RAX] & 0xffffffff) == (greg_t)0x80000000UL;
}
0x80000000UL
と0x8000000000000000UL
は、それぞれ 32 ビットと 64 ビットで可能な最小の負の数です。eax レジスタ (被除数) がその値と一致する場合、それは最小の可能な被除数があることを意味します。
if (_min_value_dividend)
{
unsigned char _rm = _modrm & 7;
_gregs[REG_RDX] = 0; /* the remainder is zero */
可能な限り最小の被除数がある場合、剰余 (edx) がゼロに設定され、被除数が結果として eax に残されます。
switch (_modrm >> 6)
{
case 0: /* register indirect */
if (_rm == 5) /* 32-bit displacement */
_rip += 4;
if (_rm == 4) /* A SIB byte follows the ModR/M byte */
_rip += 1;
break;
case 1: /* register indirect + 8-bit displacement */
_rip += 1;
if (_rm == 4) /* A SIB byte follows the ModR/M byte */
_rip += 1;
break;
case 2: /* register indirect + 32-bit displacement */
_rip += 4;
if (_rm == 4) /* A SIB byte follows the ModR/M byte */
_rip += 1;
break;
case 3:
break;
}
_rip += 2;
_gregs[REG_RIP] = (greg_t)_rip;
return;
}
コードの残りの部分は、ModR/M バイトを調べて、除数オペランドで使用されるバイト数を判断し、命令ポインターを次の命令に進めることができるようにするだけです。
基本的に、これはコメントに記載されていることを正確に実行しています。被除数が最大の負の整数である場合、結果は被除数と等しくなり、例外は発生しません。
_Jv_catch_segv
とについて_Jv_catch_segv
は、segvpatch.cpp で定義されています。
SIGNAL_HANDLER(catch_segv)
{
unblock_signal(SIGSEGV);
MAKE_THROW_FRAME(nullp);
handle_segv();
}
SIGNAL_HANDLER(catch_fpe)
{
unblock_signal(SIGFPE);
#ifdef HANDLE_DIVIDE_OVERFLOW
HANDLE_DIVIDE_OVERFLOW;
#else
MAKE_THROW_FRAME(arithexception);
#endif
handle_fpe();
}
マクロは x86_64-signal.h で定義され、次のSIGNAL_HANDLER
ように展開されます。
static void _Jv_catch_segv (int, siginfo_t *, void *_p __attribute__ ((__unused__)))
最後に、RESTORE2
基本的に から呼び出されるマクロは、次のRESTORE (restore_rt, __NR_rt_sigreturn)
ように展開されます。
asm
(
".text\n"
".byte 0 # Yes, this really is necessary\n"
".align 16\n"
"__restore_rt:\n"
" movq $__NR_rt_sigreturn, %rax\n"
" syscall\n"
);
これにより、シグナルハンドラから戻るために使用される sigreturn syscall が作成されます。これは、次のrestore_rt
行で関数に変換されます。
void restore_rt (void) asm ("__restore_rt")
これは、このコードで復元関数ポインターとして設定されます。
act.k_sa_restorer = restore_rt;
INIT_SEGV
これは、との 2 つのシグナル ハンドラを初期化するときに使用されますINIT_FPE
。
これですべての質問がカバーされると思いますが、不明な点がある場合、または特定の側面について詳しく説明してほしい場合は、コメントでお知らせください。