我不是这方面的专家,但我会尽力回答。
从 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();
}
该SIGNAL_HANDLER
宏在 x86_64-signal.h 中定义并扩展为如下内容:
static void _Jv_catch_segv (int, siginfo_t *, void *_p __attribute__ ((__unused__)))
最后,RESTORE2
本质上调用 from 的宏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 系统调用,用于从信号处理程序返回。这变成了restore_rt
这一行的函数:
void restore_rt (void) asm ("__restore_rt")
在此代码中设置为恢复器函数指针:
act.k_sa_restorer = restore_rt;
它在初始化 和 中的两个信号处理程序时INIT_SEGV
使用INIT_FPE
。
而且我认为这涵盖了您的所有问题,但是如果有任何不清楚的地方或者您希望我在任何特定方面进行扩展,请在评论中告诉我。