Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

[Assembleur] cmpxchgq ?

20 réponses
Avatar
JKB
Bonjour à tous,

Pardonnez-moi cette question à la limite de la charte, mais j'ai un
petit problème avec un bout d'assembleur amd64 trivial (en notation
AT&T) :

.text
.global try_lock_amd64
.type try_lock_amd64, @function
.align 16

try_lock_amd64:
// Setup the local variables
pushq %rbp
movq %rsp, %rbp
movq 16(%rbp), %rcx /* %rcx = &m->holder */
movq 24(%rbp), %rdx /* %rdx = me */

// Test the lock :
// if (%rcx) == $0, write %rdx into (%rcx)
// if not, write (%rcx) into %rax.
movq $0, %rax
lock
cmpxchgq %rdx, (%rcx)
jz t1

// What is the result
movq 16(%rbp), %rax /* %rax = &m->holder */
movq (%rax), %rax /* %rax = m->holder */
cmpq 24(%rbp), %rax /* m->holder == me */
sete %al
andq $255, %rax

popq %rbp
ret

t1: pushq %rdx
movq (%rcx), %rdx
pushq %rdx
jmp _Z13dbg$backtracev

Cette fonction fait partie d'un bout d'OS que je passe en 64 bits.
En 32 bits, ça fonctionne parfaitement. Ce bout de code est appelé
depuis une fonction en C (équivalente à mutex_trylock() pour des
mutexes récursifs).

Mon problème est simple. Le 'cmpxchgq' ne semble pas faire ce qu'il
devrait (ou alors, je n'ai rien compris).

Je viens de lire et de relire la doc (origine AMD) et si j'ai bien
tout compris, CMPXCHGQ est censé comparer la valeur de %RAX à la
valeur (%RCX). Si ces deux valeurs sont égales, il copie la valeur
de %RDX dans %(RCX) et met le drapeau Z à 1.

Dans mon cas, le programme saute à ma routine de débug qui m'affiche
la pile. J'en déduis donc que ce drapeau est à 1 et que CMPXCHGQ
aurait dû copier %RDX dans (%RCX). Je vois bien que %RDX contient la
bonne valeur, mais (%RCX) reste nul comme le prouve la sortie :

SP -> [$0000000001049428]
+056: [$0000000001049430] -> $0000000000000000 <- pourquoi 0 ?!
+064: [$0000000001049438] -> $00000000010498D0 <- valeur correcte de %rcx
+072: [$0000000001049440] -> $0000000001049490
+080: [$0000000001049448] -> $0000000001001FDD
+088: [$0000000001049450] -> $0000003A00000001

Je suppose que j'ai raté quelque chose, mais je ne vois pas quoi.

Merci de toute lumière,

JKB

--
Si votre demande me parvient sur carte perforée, je titiouaillerai très
volontiers une réponse...
=> http://grincheux.de-charybde-en-scylla.fr

10 réponses

1 2
Avatar
JKB
Le Tue, 2 Nov 2010 15:51:04 +0000 (UTC),
JKB écrivait :
Bonjour à tous,



Attention, Xpost et Fu2. Le Xpost se fait sur des groupes où je suis
totalement hors charte mais où risquent de se trouver des personnes
pouvant m'aider à trouver une solution. Merci de me pardonner cette
pollution.

Quelques précisions :
- j'utilise qemu pour tester (émulation d'un processeur core2 mono
coeur).
- la machine hôte est une machine avec un core2duo et un système
Linux/debian.
- la fonctio en question fait partie de la root task d'un OS
tournant au-dessus d'un micronoyau L4 (64 bits).
- l'assembleur utilisé est GNU as. J'ai naturellement vérifié le
code objet en le désassemblant.

Pardonnez-moi cette question à la limite de la charte, mais j'ai un
petit problème avec un bout d'assembleur amd64 trivial (en notation
AT&T) :

.text
.global try_lock_amd64
.type try_lock_amd64, @function
.align 16

try_lock_amd64:
// Setup the local variables
pushq %rbp
movq %rsp, %rbp
movq 16(%rbp), %rcx /* %rcx = &m->holder */
movq 24(%rbp), %rdx /* %rdx = me */

// Test the lock :
// if (%rcx) == $0, write %rdx into (%rcx)
// if not, write (%rcx) into %rax.
movq $0, %rax
lock
cmpxchgq %rdx, (%rcx)
jz t1

// What is the result
movq 16(%rbp), %rax /* %rax = &m->holder */
movq (%rax), %rax /* %rax = m->holder */
cmpq 24(%rbp), %rax /* m->holder == me */
sete %al
andq $255, %rax

popq %rbp
ret

t1: pushq %rdx
movq (%rcx), %rdx
pushq %rdx
jmp _Z13dbg$backtracev

Cette fonction fait partie d'un bout d'OS que je passe en 64 bits.
En 32 bits, ça fonctionne parfaitement. Ce bout de code est appelé
depuis une fonction en C (équivalente à mutex_trylock() pour des
mutexes récursifs).

Mon problème est simple. Le 'cmpxchgq' ne semble pas faire ce qu'il
devrait (ou alors, je n'ai rien compris).

Je viens de lire et de relire la doc (origine AMD) et si j'ai bien
tout compris, CMPXCHGQ est censé comparer la valeur de %RAX à la
valeur (%RCX). Si ces deux valeurs sont égales, il copie la valeur
de %RDX dans %(RCX) et met le drapeau Z à 1.

Dans mon cas, le programme saute à ma routine de débug qui m'affiche
la pile. J'en déduis donc que ce drapeau est à 1 et que CMPXCHGQ
aurait dû copier %RDX dans (%RCX). Je vois bien que %RDX contient la
bonne valeur, mais (%RCX) reste nul comme le prouve la sortie :

SP -> [$0000000001049428]
+056: [$0000000001049430] -> $0000000000000000 <- pourquoi 0 ?!
+064: [$0000000001049438] -> $00000000010498D0 <- valeur correcte de %rcx
+072: [$0000000001049440] -> $0000000001049490
+080: [$0000000001049448] -> $0000000001001FDD
+088: [$0000000001049450] -> $0000003A00000001

Je suppose que j'ai raté quelque chose, mais je ne vois pas quoi.

Merci de toute lumière,

JKB





--
Si votre demande me parvient sur carte perforée, je titiouaillerai très
volontiers une réponse...
=> http://grincheux.de-charybde-en-scylla.fr
Avatar
Pierre Maurette
JKB, le 11/2/2010 a écrit :
Le Tue, 2 Nov 2010 15:51:04 +0000 (UTC),
JKB écrivait :
Bonjour à tous,



Attention, Xpost et Fu2. Le Xpost se fait sur des groupes où je suis
totalement hors charte mais où risquent de se trouver des personnes
pouvant m'aider à trouver une solution. Merci de me pardonner cette
pollution.

Quelques précisions :
- j'utilise qemu pour tester (émulation d'un processeur core2 mono
coeur).
- la machine hôte est une machine avec un core2duo et un système
Linux/debian.
- la fonctio en question fait partie de la root task d'un OS
tournant au-dessus d'un micronoyau L4 (64 bits).
- l'assembleur utilisé est GNU as. J'ai naturellement vérifié le
code objet en le désassemblant.

Pardonnez-moi cette question à la limite de la charte, mais j'ai un
petit problème avec un bout d'assembleur amd64 trivial (en notation
AT&T) :

.text
.global try_lock_amd64
.type try_lock_amd64, @function
.align 16

try_lock_amd64:
// Setup the local variables
pushq %rbp
movq %rsp, %rbp
movq 16(%rbp), %rcx /* %rcx = &m->holder */
movq 24(%rbp), %rdx /* %rdx = me */

// Test the lock :
// if (%rcx) == $0, write %rdx into (%rcx)
// if not, write (%rcx) into %rax.
movq $0, %rax
lock
cmpxchgq %rdx, (%rcx)
jz t1

// What is the result
movq 16(%rbp), %rax /* %rax = &m->holder */
movq (%rax), %rax /* %rax = m->holder */
cmpq 24(%rbp), %rax /* m->holder == me */
sete %al
andq $255, %rax

popq %rbp
ret

t1: pushq %rdx
movq (%rcx), %rdx
pushq %rdx
jmp _Z13dbg$backtracev

Cette fonction fait partie d'un bout d'OS que je passe en 64 bits.
En 32 bits, ça fonctionne parfaitement. Ce bout de code est appelé
depuis une fonction en C (équivalente à mutex_trylock() pour des
mutexes récursifs).

Mon problème est simple. Le 'cmpxchgq' ne semble pas faire ce qu'il
devrait (ou alors, je n'ai rien compris).

Je viens de lire et de relire la doc (origine AMD) et si j'ai bien
tout compris, CMPXCHGQ est censé comparer la valeur de %RAX à la
valeur (%RCX). Si ces deux valeurs sont égales, il copie la valeur
de %RDX dans %(RCX) et met le drapeau Z à 1.

Dans mon cas, le programme saute à ma routine de débug qui m'affiche
la pile. J'en déduis donc que ce drapeau est à 1 et que CMPXCHGQ
aurait dû copier %RDX dans (%RCX). Je vois bien que %RDX contient la
bonne valeur, mais (%RCX) reste nul comme le prouve la sortie :

SP -> [$0000000001049428]
+056: [$0000000001049430] -> $0000000000000000 <- pourquoi 0 ?!
+064: [$0000000001049438] -> $00000000010498D0 <- valeur correcte de %rcx





Disons valeur de %rdx après le cmpxchg, valeur /attendue/ dans (%rcx),
non ?

+072: [$0000000001049440] -> $0000000001049490
+080: [$0000000001049448] -> $0000000001001FDD
+088: [$0000000001049450] -> $0000003A00000001

Je suppose que j'ai raté quelque chose, mais je ne vois pas quoi.





Je suis de votre avis, c'est étrange. Il semble que vous ayez peu de
moyens d'investigation. Peut-être %rcx pointe vers un segment non
/writable/, et le #BG(0) est silencieux (et fait des choses utiles) ?

--
Pierre Maurette
Avatar
JKB
Le Tue, 02 Nov 2010 19:40:24 +0100,
Pierre Maurette écrivait :
Dans mon cas, le programme saute à ma routine de débug qui m'affiche
la pile. J'en déduis donc que ce drapeau est à 1 et que CMPXCHGQ
aurait dû copier %RDX dans (%RCX). Je vois bien que %RDX contient la
bonne valeur, mais (%RCX) reste nul comme le prouve la sortie :

SP -> [$0000000001049428]
+056: [$0000000001049430] -> $0000000000000000 <- pourquoi 0 ?!
+064: [$0000000001049438] -> $00000000010498D0 <- valeur correcte de %rcx





Disons valeur de %rdx après le cmpxchg, valeur /attendue/ dans (%rcx),
non ?



Je ne saisis pas votre remarque. Je devrais avoir le ThreadID de mon
processus et non 0, le ThreadID étant bien dans %rdx.

+072: [$0000000001049440] -> $0000000001049490
+080: [$0000000001049448] -> $0000000001001FDD
+088: [$0000000001049450] -> $0000003A00000001

Je suppose que j'ai raté quelque chose, mais je ne vois pas quoi.





Je suis de votre avis, c'est étrange. Il semble que vous ayez peu de
moyens d'investigation. Peut-être %rcx pointe vers un segment non
/writable/, et le #BG(0) est silencieux (et fait des choses utiles) ?



Ce segment est parfaitement writable, il correspond à une variable
statique déclarée dans la fonction appelante. S'il n'est pas
writable, le pager de L4 m'enverrait une bordée d'insultes bien
sentie et ce n'est pas le cas.

J'ajoute que je viens de tester avce virtualbox et le résultat est
le même. Il y a donc vraiment un problème dans ce programme...

Cordialement,

JKB

--
Si votre demande me parvient sur carte perforée, je titiouaillerai très
volontiers une réponse...
=> http://grincheux.de-charybde-en-scylla.fr
Avatar
Pierre Maurette
JKB, le 11/2/2010 a écrit :

[...]

Je viens de tout relire et de piger que vous écriviez le code. A ce
moment-là, il serait peut-être intéressant dans t1 d'empiler %rcx et
non (%rcx), juste pour voir. Et aussi de tenter:
movq magic, (%rcx)
movq (%rcx), %rdx
pushq %rdx

--
Pierre Maurette
Avatar
JKB
Le Tue, 02 Nov 2010 20:02:34 +0100,
Pierre Maurette écrivait :
JKB, le 11/2/2010 a écrit :

[...]

Je viens de tout relire et de piger que vous écriviez le code. A ce
moment-là, il serait peut-être intéressant dans t1 d'empiler %rcx et
non (%rcx), juste pour voir. Et aussi de tenter:



+048: [$0000000001049420] -> $0000000001049440
+056: [$0000000001049428] -> $0000000000000000 <- (%rcx)
+064: [$0000000001049430] -> $0000003A00000001 <- %rcx
+072: [$0000000001049438] -> $00000000010498D0 <- %rdx

%rcx contient $0000003A00000001 qui est la bonne valeur (celle de
mon pointeur passé en argument depuis ma fonction C).

movq magic, (%rcx)



lock/lock_mutex_amd64.S:63: Error: too many memory references for `movq'

Je ne connais pas cette syntaxe (et mon compilo non plus).

movq (%rcx), %rdx
pushq %rdx



Cordialement,

JKB

--
Si votre demande me parvient sur carte perforée, je titiouaillerai très
volontiers une réponse...
=> http://grincheux.de-charybde-en-scylla.fr
Avatar
Pierre Maurette
JKB, le 11/2/2010 a écrit :
Le Tue, 02 Nov 2010 20:02:34 +0100,
Pierre Maurette écrivait :



[...]

movq magic, (%rcx)



lock/lock_mutex_amd64.S:63: Error: too many memory references for `movq'

Je ne connais pas cette syntaxe (et mon compilo non plus).



Par /magic/, je voulais dire une valeur quelconque au hasard, pas 0 par
exemple.

--
Pierre Maurette
Avatar
JKB
Le Tue, 02 Nov 2010 20:19:22 +0100,
Pierre Maurette écrivait :
JKB, le 11/2/2010 a écrit :
Le Tue, 02 Nov 2010 20:02:34 +0100,
Pierre Maurette écrivait :



[...]

movq magic, (%rcx)



lock/lock_mutex_amd64.S:63: Error: too many memory references for `movq'

Je ne connais pas cette syntaxe (et mon compilo non plus).



Par /magic/, je voulais dire une valeur quelconque au hasard, pas 0 par
exemple.



Il y a un truc bizarre lorsque je remplace 0 par une valeur non
nulle. Je vais laisser tomber pour ce soir...

Cordialement,

JKB

--
Si votre demande me parvient sur carte perforée, je titiouaillerai très
volontiers une réponse...
=> http://grincheux.de-charybde-en-scylla.fr
Avatar
Jean-Christophe
On Nov 2, 5:48 pm, JKB

Gnî !

Bon, je me lance :

1. As-tu essayé de changer cmpxchgq par une suite
de codes équivalents pour comparer le résultat ?
Un truc de ce genre ( si j'ai bien tout suivi )
cmp eax,edx
bne skip
mov edx,ecx
skip ...

2. Il me semble que cmpxchgq compare le
RESTE d'un modulo 8, 16 ou 32 selon le cas.
Sachant que ton code tourne en 32 bits mais pas en 64,
y a t'il une chance que ce soit du à ce remainder ?

"CMPXCHG r/m8, r8 Compare AL with r/m8. If equal, ZF is set and r8
is loaded into r/m8. Else, clear ZF and load r/m8 into AL."
"CMPXCHG r/m16, r16 Compare AX with r/m16. If equal, ZF is set and
r16 is loaded into r/m16. Else, clear ZF and load r/m16 into AL"
"CMPXCHG r/m32, r32 Compare EAX with r/m32. If equal, ZF is set and
r32 is loaded into r/m32. Else, clear ZF and load r/m32 into AL"

HTH ...








Le Tue, 2 Nov 2010 15:51:04 +0000 (UTC),
JKB écrivait :

> Bonjour à tous,

Attention, Xpost et Fu2. Le Xpost se fait sur des groupes où je suis
totalement hors charte mais où risquent de se trouver des perso nnes
pouvant m'aider à trouver une solution. Merci de me pardonner c ette
pollution.

Quelques précisions :
- j'utilise qemu pour tester (émulation d'un processeur core2 m ono
coeur).
- la machine hôte est une machine avec un core2duo et un syst ème
Linux/debian.
- la fonctio en question fait partie de la root task d'un OS
tournant au-dessus d'un micronoyau L4 (64 bits).
- l'assembleur utilisé est GNU as. J'ai naturellement vérifi é le
code objet en le désassemblant.

> Pardonnez-moi cette question à la limite de la charte, mais j'ai u n
> petit problème avec un bout d'assembleur amd64 trivial (en notatio n
> AT&T) :

> .text
> .global try_lock_amd64
> .type try_lock_amd64, @function
> .align 16

> try_lock_amd64:
> // Setup the local variables
> pushq %rbp
> movq %rsp, %rbp
> movq 16(%rbp), %rcx /* %rcx = &m->holder */
> movq 24(%rbp), %rdx /* %rdx = me */

> // Test the lock :
> // if (%rcx) == $0, write %rdx into (%rcx)
> // if not, write (%rcx) into %rax.
> movq $0, %rax
> lock
> cmpxchgq %rdx, (%rcx)
> jz t1

> // What is the result
> movq 16(%rbp), %rax /* %rax = &m->holder */
> movq (%rax), %rax /* %rax = m->holder */
> cmpq 24(%rbp), %rax /* m->holder == me */
> sete %al
> andq $255, %rax

> popq %rbp
> ret

> t1: pushq %rdx
> movq (%rcx), %rdx
> pushq %rdx
> jmp _Z13dbg$backtracev

> Cette fonction fait partie d'un bout d'OS que je passe en 64 bits.
> En 32 bits, ça fonctionne parfaitement. Ce bout de code est appel é
> depuis une fonction en C (équivalente à mutex_trylock() pour des
> mutexes récursifs).
> Mon problème est simple. Le 'cmpxchgq' ne semble pas faire ce qu'i l
> devrait (ou alors, je n'ai rien compris).
> Je viens de lire et de relire la doc (origine AMD) et si j'ai bien
> tout compris, CMPXCHGQ est censé comparer la valeur de %RAX à la
> valeur (%RCX). Si ces deux valeurs sont égales, il copie la valeur
> de %RDX dans %(RCX) et met le drapeau Z à 1.
> Dans mon cas, le programme saute à ma routine de débug qui m'aff iche
> la pile. J'en déduis donc que ce drapeau est à 1 et que CMPXCHGQ
> aurait dû copier %RDX dans (%RCX). Je vois bien que %RDX contient la
> bonne valeur, mais (%RCX) reste nul comme le prouve la sortie :

> SP -> [$0000000001049428]
> +056: [$0000000001049430] -> $0000000000000000 <- pourquoi 0 ?!
> +064: [$0000000001049438] -> $00000000010498D0 <- valeur correcte de %rcx
> +072: [$0000000001049440] -> $0000000001049490
> +080: [$0000000001049448] -> $0000000001001FDD
> +088: [$0000000001049450] -> $0000003A00000001

> Je suppose que j'ai raté quelque chose, mais je ne vois pas quoi.
> Merci de toute lumière,
> JKB

--
Si votre demande me parvient sur carte perforée, je titiouaillerai tr ès
volontiers une réponse...
=>http://grincheux.de-charybde-en-scylla.fr
Avatar
Pierre Maurette
JKB, le 11/2/2010 a écrit :

[...]

Il y a un truc bizarre lorsque je remplace 0 par une valeur non
nulle. Je vais laisser tomber pour ce soir...



C'est également ce que j'ai fait hier soir. Mais je suis tombé du lit
ce matin. J'ai fait quelques tests, et cmpxchg fonctionne comme vous et
moi l'attendions:

Ubuntu Maverick64 VirtualBox, gcc 4.4.5, as 2.20.51
On peut entrer des valeurs > 0xFFFF, mais l'affichage est tronqué.

<test.c>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

uint64_t test(uint64_t* var, uint64_t rdx, uint64_t rax);
//cmpxchg rdx, (var)
//compare (var) et rax
//si égal (ZF == 1): rdx -> (var)
//sinon (ZF == 0): (var) -> rax

#define WORD(ad, n) (*((uint16_t*)(ad) + (n)))

int main(void) {
uint64_t val = 0x0;
uint64_t rax = 0x0;
uint64_t rdx = 0x5678;
printf("ZF:%d val:%04X rdx:%04X rax:%04Xn"
, val == rax , (uint16_t)val, (uint16_t)rdx, (uint16_t)rax);
uint64_t res = test(&val, rdx, rax);
printf("ZF:%d val:%04X rdx:%04X rax:%04X (val dans asm:%04X)n"
, !!WORD(&res, 1), (uint16_t)val
, WORD(&res, 2), WORD(&res, 3), WORD(&res, 0));
return EXIT_SUCCESS;
}
</test.c>

<test.asm>
.globl test
test:
push %rbp
mov %rsp, %rbp

mov %rdx, %rax
mov %rdi, %rcx
mov %rsi, %rdx
mov $-1, %rbx
lock
cmpxchg %rdx, (%rcx)
jz suite
mov $0, %rbx
suite:
shl $0x10, %rax
mov %dx, %ax
shl $0x10, %rax
mov %bx, %ax
shl $0x10, %rax
mov (%rcx), %rdx
mov %dx, %ax
//rax contient [ax][dx][zflag][var]
fin:
leaveq
retq
</test.asm>

Bon courage, bonne journée.

--
Pierre Maurette
Avatar
DF
1. As-tu essayé de changer cmpxchgq par une suite
de codes équivalents pour comparer le résultat ?
Un truc de ce genre ( si j'ai bien tout suivi )
cmp eax,edx
bne skip
mov edx,ecx

Juste une remarque :
- Il n'y a pas de code équivalent à cmpxchgq qui puisse fonctionner
correctement en environnement multi-core, même si le code équivalent marche
sur un mono.
1 2