【问题标题】:Using a C++ reference in inline assembly with GCC在 GCC 的内联汇编中使用 C++ 引用
【发布时间】:2011-12-17 08:15:55
【问题描述】:

我有一个带有 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"               
        );      
}

此代码在 64 位英特尔机器上使用 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

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

这里有什么严重的错误吗?

问候, -J

【问题讨论】:

  • gdb 让您能够获得准确的出错机器指令。
  • 尝试print $rip获取当前指令

标签: c++ gcc x86-64 inline-assembly


【解决方案1】:

首先,为什么在汇编代码中使用截断的 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)

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

问题在这里:"r"(resource)。必须是"r"(&amp;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;
}

输出:

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 ...

【讨论】:

  • 感谢您的回答。一个更正是对于 64 位 movl 需要用 movq 代替。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-01-07
  • 2012-10-20
  • 1970-01-01
  • 2012-03-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多