2011-12-17 1 views
0

J'ai un verrou de rotation avec l'instruction xchg. La fonction C++ prend en compte la ressource à verrouiller.Utilisation d'une référence C++ dans l'assemblage en ligne avec GCC

Voici le code

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"    
     );  
} 

Ce code est compilé avec gcc 4.5.2 -masm=intel sur une machine à processeur Intel 64 bits. Le objdump produit l'assemblage suivant pour les fonctions ci-dessus.

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 

Le code décharges noyau lors de l'exécution de l'opération de verrouillage.

Y a-t-il quelque chose qui cloche ici?

Cordialement, -J

+0

gdb vous donne la possibilité d'obtenir l'instruction de la machine précise qui est formation de failles. –

+0

essayez 'print $ rip' pour obtenir l'instruction en cours – user685684

Répondre

7

D'abord, pourquoi vous utilisez des adresses 32 bits tronqués dans votre code d'assemblage tandis que le reste du programme est compilé pour exécuter en mode 64 bits et de fonctionner avec des adresses 64 bits/pointeurs? Je parle de ebx. Pourquoi n'est-ce pas rbx? Deuxièmement, pourquoi essayez-vous de renvoyer une valeur du code d'assemblage avec "=r"(resource)? Vos fonctions modifient la valeur en mémoire avec xchg eax, [ebx] et mov DWORD PTR [ebx], 0x00 et renvoient void. Enlever "=r"(resource).

Enfin, si vous regardez attentivement le démontage de SpinLock::lock(), vous ne pouvez pas voir quelque chose d'étrange ebx:

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

Dans ce code, la valeur ebx, qui est une adresse/pointeur, ne ne vient pas directement du paramètre de la fonction (rdi), le paramètre est déréférencé en premier avec mov (%rax),%edx, mais pourquoi? Si vous jetez tout le matériel de référence C++ prêtant à confusion, techniquement, la fonction reçoit un pointeur vers u32, pas un pointeur vers un pointeur vers u32, et n'a donc besoin d'aucun déréférencement supplémentaire.

Le problème est ici: "r"(resource). Ce doit être "r"(&resource).

Une petite application de test 32 bits montre ce problème:

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

Sortie:

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

Merci pour votre réponse. Une correction est pour 64 bits, le movl doit être remplacé par movq. –

+0

Vous avez raison, movl devrait également être changé en movq. –