Использование ссылки C ++ во встроенной сборке с GCC - PullRequest
0 голосов
/ 17 декабря 2011

У меня есть спин-блокировка с инструкцией 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"               
        );      
}

Этот код скомпилирован с gcc 4.5.2 -masm=intel на 64-битной машине 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

1 Ответ

7 голосов
/ 17 декабря 2011

Во-первых, почему вы используете усеченные 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"(&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 ...
...