在内联汇编中使用C++引用与GCC

Jay*_*y D 0 c++ gcc x86-64 inline-assembly

我有一个带有xchg指令的自旋锁.C++函数接受要锁定的资源.

以下是代码

void SpinLock::lock( u32& resource )
 { 
     __asm__ __volatile__
       (
            "mov     ebx, %0\n\t" 
"InUseLoop:\n\t"
            "mov     eax, 0x01\n\t"        /* 1=In Use*/
            "xchg    eax, [ebx]\n\t"
            "cmp     eax, 0x01\n\t"
            "je      InUseLoop\n\t"
            :"=r"(resource)
            :"r"(resource)
            :"eax","ebx"
        ); 
}

void SpinLock::unlock(u32& resource ) 
{ 
    __asm__ __volatile__
        (
                /* "mov DWORD PTR ds:[%0],0x00\n\t" */
                "mov ebx, %0\n\t"
                "mov DWORD PTR [ebx], 0x00\n\t"
                :"=r"(resource)
                :"r"(resource)
                : "ebx"               
        );      
}
Run Code Online (Sandbox Code Playgroud)

此代码在64位intel机器上使用 gcc 4.5.2 编译 -masm=intel.

objdump 用于上述功能产生以下组件.

0000000000490968 <_ZN8SpinLock4lockERj>:
  490968:       55                      push   %rbp
  490969:       48 89 e5                mov    %rsp,%rbp
  49096c:       53                      push   %rbx
  49096d:       48 89 7d f0             mov    %rdi,-0x10(%rbp)
  490971:       48 8b 45 f0             mov    -0x10(%rbp),%rax
  490975:       8b 10                   mov    (%rax),%edx
  490977:       89 d3                   mov    %edx,%ebx

0000000000490979 <InUseLoop>:
  490979:       b8 01 00 00 00          mov    $0x1,%eax
  49097e:       67 87 03                addr32 xchg %eax,(%ebx)
  490981:       83 f8 01                cmp    $0x1,%eax
  490984:       74 f3                   je     490979 <InUseLoop>
  490986:       48 8b 45 f0             mov    -0x10(%rbp),%rax
  49098a:       89 10                   mov    %edx,(%rax)
  49098c:       5b                      pop    %rbx
  49098d:       c9                      leaveq
  49098e:       c3                      retq
  49098f:       90                      nop


0000000000490990 <_ZN8SpinLock6unlockERj>:
  490990:       55                      push   %rbp
  490991:       48 89 e5                mov    %rsp,%rbp
  490994:       53                      push   %rbx
  490995:       48 89 7d f0             mov    %rdi,-0x10(%rbp)
  490999:       48 8b 45 f0             mov    -0x10(%rbp),%rax
  49099d:       8b 00                   mov    (%rax),%eax
  49099f:       89 d3                   mov    %edx,%ebx
  4909a1:       67 c7 03 00 00 00 00    addr32 movl $0x0,(%ebx)
  4909a8:       48 8b 45 f0             mov    -0x10(%rbp),%rax
  4909ac:       89 10                   mov    %edx,(%rax)
  4909ae:       5b                      pop    %rbx
  4909af:       c9                      leaveq
  4909b0:       c3                      retq
  4909b1:       90                      nop
Run Code Online (Sandbox Code Playgroud)

执行锁定操作时代码转储核心.

这里有什么严重错误吗?

问候,-J

Ale*_*nze 7

首先,为什么在汇编代码中使用截断的32位地址,而程序的其余部分编译为在64位模式下执行并使用64位地址/指针操作?我指的是ebx.为什么不rbx呢?

其次,为什么要尝试从汇编代码中返回一个值"=r"(resource)?你的功能改变与内存中的价值xchg eax, [ebx]mov DWORD PTR [ebx], 0x00和回报void.删除"=r"(resource).

最后,如果仔细观察一下拆解SpinLock::lock(),你能不能看到一些奇怪的东西ebx?:

mov    %rdi,-0x10(%rbp)
mov    -0x10(%rbp),%rax
mov    (%rax),%edx
mov    %edx,%ebx
<InUseLoop>:
mov    $0x1,%eax
addr32 xchg %eax,(%ebx)
Run Code Online (Sandbox Code Playgroud)

在这段代码中,ebx作为地址/指针的值不直接来自函数的参数(rdi),参数首先被解除引用mov (%rax),%edx,但为什么呢?如果丢弃所有令人困惑的C++引用内容,从技术上讲,函数会接收指针u32,而不是指向指针的指针u32,因此无需在任何地方进行额外的解引用.

问题出在这里:"r"(resource).一定是"r"(&resource).

一个小的32位测试应用程序演示了这个问题:

#include <iostream>

using namespace std;

void unlock1(unsigned& resource) 
{ 
    __asm__ __volatile__
    (
        /* "mov DWORD PTR ds:[%0],0x00\n\t" */
        "movl %0, %%ebx\n\t"
        "movl $0, (%%ebx)\n\t"
        :
        :"r"(resource)
        :"ebx"               
    );      
}

void unlock2(unsigned& resource) 
{ 
    __asm__ __volatile__
    (
        /* "mov DWORD PTR ds:[%0],0x00\n\t" */
        "movl %0, %%ebx\n\t"
        "movl $0, (%%ebx)\n\t"
        :
        :"r"(&resource)
        :"ebx"               
    );      
}

unsigned blah;

int main(void)
{
  blah = 3456789012u;
  cout << "before unlock2() blah=" << blah << endl;
  unlock2(blah);
  cout << "after unlock2() blah=" << blah << endl;

  blah = 3456789012u;
  cout << "before unlock1() blah=" << blah << endl;
  unlock1(blah); // may crash here, but if it doesn't, it won't change blah
  cout << "after unlock1() blah=" << blah << endl;
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出:

before unlock2() blah=3456789012
after unlock2() blah=0
before unlock1() blah=3456789012
Exiting due to signal SIGSEGV
General Protection Fault at eip=000015eb
eax=ce0a6a14 ...
Run Code Online (Sandbox Code Playgroud)