1

I was working on an exception handler in C++. It will be used in a messaging protocol for maximum reliability - I want to get an answer back from a process that received a message at all times - even if the process catches a segfault. I know C++ doesn't raise exceptions for null pointer dereferencing, or division by zero, but Java, which is written in C does. That got me thinking and looking around. Eventually I found this little library:

https://code.google.com/p/segvcatch/source/browse/trunk/lib/

My question is in regards to file x86_64-signal.h. I don't completely understand the details of HANDLE_DIVIDE_OVERFLOW. REX byte? What is that? What does RESTORE2 do? What is _Jv_catch_segv and _Jv_catch_fpe? I understand the basic meaning of *catch*, but where are they defined?

Ideally if someone could go through the file top to bottom and address the key points that would be great. Thanks.

4

1 回答 1

2

我不是这方面的专家,但我会尽力回答。

从 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;   
      }                                                  

0x80000000UL0x8000000000000000UL分别是 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

而且我认为这涵盖了您的所有问题,但是如果有任何不清楚的地方或者您希望我在任何特定方面进行扩展,请在评论中告诉我。

于 2013-05-27T22:40:03.800 回答