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

Convention d'appel de fonction C et EBX

3 réponses
Avatar
ludovicd
Bonjour =E0 tous,

La convention d'appel des fonctions en C stipule que le contenu du
registre EBX ne doit pas =EAtre modifi=E9 par la fonction appell=E9 (for=E7=
ant
un push ebx, pusha de sa part) Qu'est sens=E9 contenir le registre EBX
dans ce cas?

=20
Merci de vos r=E9ponses

=20
Ludovic

3 réponses

Avatar
Eric Levenez
Le 23/02/07 0:43, dans
,
«  » a écrit :

La convention d'appel des fonctions en C stipule que le contenu du
registre EBX ne doit pas être modifié par la fonction appellé (forçant
un push ebx, pusha de sa part)


Le C ne connaît absolument pas la notion de registre EBX ou autre. Cela est
totalement lié à une implémentation donnée sur un système d'exploitation
donné. Avec un même CPU on peut avoir plusieurs implémentations différentes
et donc plusieurs conventions d'utilisation des registres. Regarde dans la
doc de ton compilateur sur ton système d'exploitation pour en savoir plus.

Par exemple en page 50 du document suivant, tu trouveras ce que Mac OS X met
dans le fameux EBX sur IA-32 qui te tient tant à coeur :

<http://developer.apple.com/documentation/developertools/Conceptual/LowLevel
ABI/LowLevelABI.pdf>

--
Éric Lévénez -- <http://www.levenez.com/>
Unix is not only an OS, it's a way of life.

Avatar
Pierre Maurette
Bonjour à tous,

La convention d'appel des fonctions en C stipule que le contenu du
registre EBX ne doit pas être modifié par la fonction appellé (forçant
un push ebx, pusha de sa part) Qu'est sensé contenir le registre EBX
dans ce cas?


Le domaine de votre question est x86-32 convention d'appel cdecl (et
dérivées).
Dans cette convention, les registres EBX, ESI, EDI et EBP ne doivent
pas dans l'appelant être modifiés par l'appel de la fonction. La
"préservation" de ESP est une autre question.
EBP est impliqué dans la gestion de la pile et donc l'accès aux
arguments et autres variables locales, et il serait hasardeux
d'improviser. Dans une fonction, il contiendra une copie pertinemment
décalée de la valeur de ESP à l'entrée de la fonction (il pointe vers
la base du cadre de pile de la fonction).
L'utilisation de EBX, EDI et ESI et de leur pérennité est à la
discrétion de l'appelant. Mais si par exemple la fonction est appelée
au sein d'une boucle gérée par des instructions de type "string" (je
préférerais le terme "array") l'utilisation de ESI et EDI se trouve
imposée. En revanche je ne vois aucun usage particulier pour EBX, si ce
n'est qu'il sera dicté par justement sa persistance.
Tout ça, c'est vu de l'appelant.

Si vous écrivez du code assembleur dans la fonction (ou si vous êtes en
charge d'écrire le compilateur qui génère ce code), vous avez tout à
fait le droit d'utiliser EBX, ESI et EDI (heureusement !) le tout est
de les rendre "dans l'état où vous les avez trouvés en entrant" ;-)
dans la fonction. Si vous êtes en assembleur ou en C avec modificateur
"naked", vous vous démerdez ;-) ;-) . Si vous utilisez simplement de
l'assembleur en ligne, vous regardez la documentation de votre
compilateur à ce sujet. Pour Microsoft (VC8) par exemple, il est
indiqué clairement que si vous utilisez EBX, ESI ou EDI dans un bloc
__asm, le compilateur génèrera le code de prologue et d'épilogue qui va
bien. En gcc / AT&T Alakon ecxtended, on pourrait supposer que ce
serait pareil, en déclarant EBX clobbered. En fait, les tests semblent
montrer que le résultat peut dépendre du niveau d'optimisation.
Toujours est-il qu'il ne faut pas compter sur EBX pour passer en douce
quelque chose à une fonction, même si ça semble fonctionner "souvent".
Exceptionnellement, les documentations font état d'une utilisation de
EBX par le compilateur qui en interdirait l'utilisation.
<URL:http://msdn2.microsoft.com/en-us/library/k1a8ss06(VS.80).aspx>

Pour tester avec MS VC8:

#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>

uint32_t _cdecl tropDebil(void)
{
__asm mov eax, ebx
__asm xor ebx, ebx
__asm inc ebx
}

volatile uint32_t _cdecl justeDebil(void)
{
uint32_t dumi;
__asm
{
mov dword ptr [dumi], ebx
xor ebx, ebx
inc ebx
}
return dumi;
}


int main(void)
{
__asm mov ebx, 1234
printf("%"PRIu32"n", tropDebil());
printf("%"PRIu32"n", tropDebil());
__asm mov ebx, 5678
printf("%"PRIu32"n", tropDebil());
printf("%"PRIu32"n", tropDebil());

__asm mov ebx, 1234
printf("%"PRIu32"n", justeDebil());
printf("%"PRIu32"n", justeDebil());
__asm mov ebx, 5678
printf("%"PRIu32"n", justeDebil());
printf("%"PRIu32"n", justeDebil());
return 0;
}


Pour tester avec gcc - x86-32 (Windows et Linux)

#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>

uint32_t debil(void)
{
uint32_t dumi = 0;
__asm__ __volatile__(
"movl %%ebx, %0n"
"xorl %%ebx, %%ebxn"
"incl %%ebxn"
: "=r"(dumi)
:
: "%ebx"
);
return dumi;
}

int main(void)
{
__asm__ __volatile__("movl $1234, %ebxn");
printf("%"PRIu32"n", debil());
printf("%"PRIu32"n", debil());
__asm__ __volatile__("movl $5678, %ebxn");
printf("%"PRIu32"n", debil());
printf("%"PRIu32"n", debil());
return 0;
}

--
Pierre Maurette

Avatar
Antoine Leca
écrivit dans
news::
La convention d'appel des fonctions en C


... dans ton cas (x86-32) très particulier ...

stipule que le contenu du
registre EBX ne doit pas être modifié par la fonction appellé (forçant
un push ebx, pusha de sa part)


Bouh : le push ebx est probablement inutile...

Qu'est sensé contenir le registre EBX dans ce cas?


Comme son nom l'indique, ce qu'est censé contenir EBX n'intéresse pas la
fonction appelée.

Si tu veux les détails croustillants, le contenu dépend du contexte d'appel
et des options d'optimisation du compilateur. Le cas le plus connu c'est les
fichiers relogeables sur Linux et compatibles, où EBX contient un pointeur
sur la TGD. D'autres compilateurs (de mémoire, Delphi le fait) peuvent
utiliser EBX comme un registre « gratuit » supplémentaire.


Antoine