OVH Cloud OVH Cloud

Passage de paramètres de C++ à Assembleur

13 réponses
Avatar
berry_2623
Bonjour,

J'aimerais savoir comment utiliser mes paramètres (recevoir et
retourner)
dans mon sous-programmme. Pour le moment, j'ai une erreur de
segmentation fault. Le sous-programme(EtablirASCII) en Assembleur
reçoit 3 paramètres (int iTranscode, char cRepere, int iP) et retourne
un int. Puisque je n'arrive pas à recevoir correctement les
paramètres, EtablirASCII ne fait pas de traitement.
Pourriez-vous m'indiquer comment retourner, par exemple, mon paramètre
iTranscode? Merci.


/*Extrait du programme en C++*/
extern "C" int EtablirASCII(int iTranscode, char cRepere, int iP);
void main()
{
char cRepere;
int iDernierBit=0;
int iPosition = iNB_BITS;
int iP;
int iLigne;
int iTranscode=0;
int x;

//Appel de la fonction de lecture pour lire la première ligne
iLigne = LireLigne();

//Extraire le repère et le p
cRepere = DecodeRepere(iLigne);//Décoder le repère
iP = ~iLigne & 7;//Décoder le P

while(!cin.eof())
{
iLigne = LireLigne();
ExtraireTranscode(iLigne, &iDernierBit,&iPosition,&iTranscode);
while(iPosition > iNB_BITS)
{
x = EtablirASCII(iTranscode,cRepere,iPosition);
cerr<<x;
}
x=EtablirASCII(iTranscode,cRepere,iPosition);
cerr<<x;
}
cout<<"\nFin du programme"<<endl;
}

/* ASCII(Établir ASCII): Sous-programme qui met le code en ASCII */

.global EtablirASCII
SP = 0
ADR = SP + 64
REG = ADR + 4
PARAM = REG + 24
TRAVAIL = PARAM + 0
DYNAMIQUE = TRAVAIL + 0
LOCAL = DYNAMIQUE + 0
FP = LOCAL + (10 * 4)
TAILLE = ((FP/8) + 1 ) * 8

.section ".text" ! section d'instructions

EtablirASCII:
save %sp,-TAILLE,%sp ! sauvegarde...
st %i0,[%fp-4] ! recuperation des parametres
st %i1,[%fp-8] ! et sauvegarde dans le bloc du
sous-programme
st %i2,[%fp-12]
mov -12,%l0

ld [%fp+%l0],%l3


/*Fin du sous-programme*/
ASCIIEnd:
ld [%fp+ADR], %l0 ! Adresse de la structure de retour
st %l3,[%l0] ! retour de la somme */
jmp %i7 ! retour à l'appelant
restore ! ramène l'environnement

10 réponses

1 2
Avatar
Florent 'flure' C.
Le Sat, 31 Jul 2004 22:29:40 -0700, Line a écrit :

Quel processeur ? Quel environnement ? Quelle convention d'appel pour les
fonctions ? Où est la question sur le C++ ?

Voir sur un newsgroup plus approprié (vraisemblablement un newsgroup
traitant du langage assembleur du processeur sur lequel vous travaillez).

Bon courage.

--
Florent "flure" C.
Décrypter l'@ pour répondre
Coders don't die, they just JMP without RET !
Avatar
heinquoi
"Florent 'flure' C." a écrit dans le
message de news:

Quel processeur ? Quel environnement ? Quelle convention d'appel pour les
fonctions ? Où est la question sur le C++ ?

Voir sur un newsgroup plus approprié (vraisemblablement un newsgroup
traitant du langage assembleur du processeur sur lequel vous travaillez).

Bon courage.


ca manque d'info en effet.

cela dit il vient de soulever une question théorique sur la call frame et
la stack frame. Comment sont organisé le passage des paramètres de fonction
.
dite moi si je me troupe, pour windows avec les compilo microsoft ou borland
c'est :
sont mis ds la pile en premier l'adresse de retour de l'appel
le dernier argument d'appel
...
le 1er argument
puis il initialise le 'pointeur de base' ( registre BP) sur le début de la
structure d'appel ( notre call frame)

1er question ( con ) le début c'est l'adresse la + basse ou la plus haute ?
pointe sur retour ou sur arg 1 ?
2eme question est ce que c'est pareil pour tout les compilos c++ ( gcc, ...)
sur tout les os (sun,...)
en gros est ce que c'est normé ? ( j'ai la iso14882 mais j'ai pas trouvé
dedans)
3eme question est ce que le mot clef extern ou extern "C" modifie cela (
l'ordre des paramètres je crois)
4eme question n'y a t il pas d'autre modificateur de l'organisation du
passage des paramètres ( ex: extern "FORTRAN" , extern "C++" <= celui la
existe pour microsoft, ...)
5eme question lors d'un linkage mixing C++ et autre langage, les données
globales sont elles accesibles ( avec un soulignement devant )
6 on suppose que pour le retour c'est la même structure de call frame. A t
on tord de la supposer ? Est ce que ce serait pas plutôt un pointeur dans
eax sur la valeur de retour ?
7 qui fait le menage dans les registres a la fin de l'appelé: l'appelant ou
l'appelé ?
8 dans la pile lors de l'appel de la fonction mettons directement les
données ou des pointeurs ?

voila quelques questions...

--
Cordialement,
Heinquoi

Avatar
Pierre Maurette
Line écriva:
Bonjour,
[...]

restore ! ramène l'environnement


Vous n'êtes pas sur le bon groupe. Il me semble que le moins inadapté sera
un groupe sur l'assembleur. En anglais, vous trouverez sans doute votre
bonheur, adapté à votre environnement (processeur et/ou assembleur) que je
n'identifie pas. En français, c'est plus chaud. Rien à ma connaissance sur
usenet. Essayez le forum "assembleur" de www.developpez.com:
http://asm.developpez.com/

Sinon, voici quelques pistes que j'utiliserais face à ce genre de problème:
- Tout part bien entendu de la définition de la convention d'appel dans la
documentation de votre compilateur: modification du nom de la fonction,
passage des arguments (et dans quel ordre), qui prend en charge la
maintenance de la pile (s'il y a lieu), quelles ressources (registres?)
peut-on modifier, etc ...
- Confirmer tout ça en traçant dans le code machine.
- Vous pouvez tracer ou désassembler ou demander un listing asm d'une
version (simplifiée) C++, extern, de votre fonction.
- Le dernier point est d'identifier ce que génère votre outil d'assemblage
(assembleur, macro-assembleur, compilateur C ou C++...) comme code de
prologue et d'épilogue. Vous avez généralement le choix, par l'utilisation
de directives puissantes du macro-assembleur (PROC/INVOKE et la suite sous
MASM par exemple), voire de modificateurs genre "naked" en C ou C++. LE tout
est de bien vérifier pour ne pas écrire de code redondant. Ici encore,
documentation, traçage, etc.

Par curiosité, quelle plateforme, quel processeur, quels outils ?
--
Pierre

Avatar
adebaene
"heinquoi" <nospam* wrote in message news:<410ebf40$0$12543$...
"Florent 'flure' C." a écrit dans le
message de news:

Quel processeur ? Quel environnement ? Quelle convention d'appel pour les
fonctions ? Où est la question sur le C++ ?

Voir sur un newsgroup plus approprié (vraisemblablement un newsgroup
traitant du langage assembleur du processeur sur lequel vous travaillez).

Bon courage.


ca manque d'info en effet.

cela dit il vient de soulever une question théorique sur la call frame et
la stack frame. Comment sont organisé le passage des paramètres de fonction
.


A ma connaissance, rien n'est normé la dedans, et chacun fait comme il
veut.
Par exemple, pour les différents "standards" sous Windows, voire :
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclang/html/_core_Calling_Conventions_Topics.asp?frame=true

Arnaud


Avatar
Jean-Marc Bourguet
"heinquoi" <nospam* writes:

cela dit il vient de soulever une question théorique sur la call
frame et la stack frame. Comment sont organisé le passage des
paramètres de fonction . dite moi si je me troupe, pour windows
avec les compilo microsoft ou borland c'est : sont mis ds la pile en
premier l'adresse de retour de l'appel le dernier argument d'appel
... le 1er argument puis il initialise le 'pointeur de base' (
registre BP) sur le début de la structure d'appel ( notre call
frame)


Ils ne font toujours pas du passage par registre? La derniere fois
que j'ai regarde un compilo de Borland de pres (oh, c'est loin) il y
avait deja l'option pour.

1er question ( con ) le début c'est l'adresse la + basse ou la plus
haute ? pointe sur retour ou sur arg 1 ?


Aucune idee.

2eme question est ce que c'est pareil pour tout les compilos c++ ( gcc, ...)
sur tout les os (sun,...)


A coup sur, non. (Sur sparc par exemple, ne pas proposer de passer
quelque chose par registre serait une preuve d'incompetance absolue).

en gros est ce que c'est normé ? ( j'ai la iso14882 mais j'ai pas trouvé
dedans)


Non parce que ca depend a la fois du processeur (si j'ai bonne memoire
il y a des processeurs ayant un registre de frame impose pouvant
n'etre indexe que dans un sens), de l'OS (regarde Irix qui a eu -- a
toujours? -- 2 conventions simultanees: n32 et o32), du langage (qui
peut avoir des exigences, par exemple Ada ou Pascal permettent des
sous-routines locales), et du compilateur et de ses options (j'ai cite
Borland qui avait des options pour pouvoir faire du passage par
registre).

3eme question est ce que le mot clef extern ou extern "C" modifie
cela ( l'ordre des paramètres je crois)


Possiblement. On peut vouloir utiliser des conventions differentes
pour C et C++ pour un tas de raisons (les conventions C sont
historiques et on a pu vouloir corriger des erreurs et mieux profiter
du processeurs et des contraintes inexistantes en C K&R, les
exceptions et ce que ca impose, ...).

4eme question n'y a t il pas d'autre modificateur de l'organisation du
passage des paramètres ( ex: extern "FORTRAN" , extern "C++" <= celui la
existe pour microsoft, ...)


Rien de normalise mais c'est prevu pour.

5eme question lors d'un linkage mixing C++ et autre langage, les données
globales sont elles accesibles ( avec un soulignement devant )


Tout depend aussi de l'autre langage.

6 on suppose que pour le retour c'est la même structure de call frame. A t
on tord de la supposer ? Est ce que ce serait pas plutôt un pointeur dans
eax sur la valeur de retour ?


Pas compris.

7 qui fait le menage dans les registres a la fin de l'appelé: l'appelant ou
l'appelé ?


Depend du processeur (voir sparc), de l'OS, du langage du compilateur
et de ses options. Souvent on divise les registres et certains sont a
charge de l'appelant, d'autre de l'appele.

8 dans la pile lors de l'appel de la fonction mettons directement les
données ou des pointeurs ?


Depend du processeur (voir sparc), de l'OS, du langage du compilateur
et de ses options et des donnees en question.

Les normes pour les langages ne definissent qu'un comportement
observable tant qu'on reste dans leur cadre. Tous les moyens sont
valides pour optenir ces comportements, certains sont simplement plus
probables mais d'autres sont plus adaptes a des cas particuliers.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org

Avatar
Jean-Marc Bourguet
"heinquoi" <nospam* writes:

2eme question est ce que c'est pareil pour tout les compilos c++ ( gcc, ...)
sur tout les os (sun,...)
en gros est ce que c'est normé ? ( j'ai la iso14882 mais j'ai pas trouvé
dedans)


J'oublie, il y a une proposition d'ABI multi-vendeurs pour le C++ avec
des parties dependantes du processeur -- et je suppose de l'OS -- et
des parties independantes. Si j'ai bonne memoire elle est referencee
dans la FAQ (quelque part sur le site de codesourcery).

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org

Avatar
Gabriel Dos Reis
Jean-Marc Bourguet writes:

| "heinquoi" <nospam* writes:
|
| > 2eme question est ce que c'est pareil pour tout les compilos c++ ( gcc, ...)
| > sur tout les os (sun,...)
| > en gros est ce que c'est normé ? ( j'ai la iso14882 mais j'ai pas trouvé
| > dedans)
|
| J'oublie, il y a une proposition d'ABI multi-vendeurs pour le C++ avec
| des parties dependantes du processeur -- et je suppose de l'OS -- et
| des parties independantes. Si j'ai bonne memoire elle est referencee
| dans la FAQ (quelque part sur le site de codesourcery).

http://www.codesourcery.com/cxx-abi/

-- Gaby
Avatar
Florent 'flure' C.
Le Tue, 03 Aug 2004 09:52:12 +0200, Jean-Marc Bourguet a écrit :

Ils ne font toujours pas du passage par registre? La derniere fois
que j'ai regarde un compilo de Borland de pres (oh, c'est loin) il y
avait deja l'option pour.


He si, c'est faisable, cela dépend de la convention d'appel, de mémoire
il y a stdcall, fastcall et cdecl ...
Je crois que c'est fastcall qui passe les paramètres par registres,
autant que possible.

3eme question est ce que le mot clef extern ou extern "C" modifie
cela ( l'ordre des paramètres je crois)


Possiblement. On peut vouloir utiliser des conventions differentes
pour C et C++ pour un tas de raisons (les conventions C sont
historiques et on a pu vouloir corriger des erreurs et mieux profiter
du processeurs et des contraintes inexistantes en C K&R, les
exceptions et ce que ca impose, ...).


Je crois que extern "C" ne fait que dire au compilateur d'adapter la
décoration des noms des fonctions, pour qu'elle corresponde à celle du
C++, non ?

6 on suppose que pour le retour c'est la même structure de call frame. A t
on tord de la supposer ? Est ce que ce serait pas plutôt un pointeur dans
eax sur la valeur de retour ?


Pas compris.


Sur Intel, on met tout ce qui est de type entier dans le registre EAX pour
le retour, ou un pointeur ... Pour les flottants et les structures, je ne
sais pas.

7 qui fait le menage dans les registres a la fin de l'appelé: l'appelant ou
l'appelé ?


Depend du processeur (voir sparc), de l'OS, du langage du compilateur
et de ses options. Souvent on divise les registres et certains sont a
charge de l'appelant, d'autre de l'appele.


Sur Intel, il n'y a pas de ménage à faire, quelques registres peuvent
être modifiés sans ménagement, les autres doivent être restaurés (par
push et pop).

--
Florent "flure" C.
Décrypter l'@ pour répondre
Coders don't die, they just JMP without RET !


Avatar
Jean-Marc Bourguet
"Florent 'flure' C." writes:

Le Tue, 03 Aug 2004 09:52:12 +0200, Jean-Marc Bourguet a écrit :

Je crois que extern "C" ne fait que dire au compilateur d'adapter la
décoration des noms des fonctions, pour qu'elle corresponde à celle
du C++, non ?


extern "C" fait tout ce qui est necessaire pour appeler du C, cela
comprend la decoration du nom mais aussi toutes les conventions
d'appel, dont le passage de parametre.

6 on suppose que pour le retour c'est la même structure de call
frame. A t on tord de la supposer ? Est ce que ce serait pas
plutôt un pointeur dans eax sur la valeur de retour ?


Pas compris.


Sur Intel, on met tout ce qui est de type entier dans le registre
EAX pour le retour, ou un pointeur ... Pour les flottants et les
structures, je ne sais pas.


Pour les flottants, je suppose qu'on utilise le sommet de la pile
flottante. Pour les structures une technique possible (je ne sais pas
ce qui est employe) est de passer un parametre supplementaire qui est
un pointeur vers l'emplacement a utiliser.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org



Avatar
kanze
"heinquoi" <nospam* wrote in message
news:<410ebf40$0$12543$...

cela dit il vient de soulever une question théorique sur la call frame
et la stack frame. Comment sont organisé le passage des paramètres de
fonction.


Comme le veut l'implémentation. C'est très spécifique à chaque
implémentation.

dite moi si je me troupe, pour windows avec les compilo
microsoft ou borland c'est :

sont mis ds la pile en premier l'adresse de retour de l'appel
le dernier argument d'appel
...
le 1er argument
puis il initialise le 'pointeur de base' ( registre BP) sur le début
de la structure d'appel ( notre call frame)


C'est curieux. Ça ressemble à ce qu'on faisait il y a quinze ans, sur
8086. Je m'attendrais à ce qu'un compilateur moderne fasse mieux
(certains paramètres dans des régistres).

En fait, c'est un peu plus compliqué que ça. En gros, tu as, dans
l'ordre ascendant : les variables locales, les régistres sauvegardés (au
moins EBP), l'adresse de rétour, les paramètres qui ne passe pas dans
les régistres. Avec EBP qui pointe à l'adresse où est sauvegardé
l'ancien EBP -- on utilise un offset négatif pour accéder aux variables
locales.

Cette organisation est assez courante sur des machines où la pile croît
vers le bas (Intel, Motorola, NSC, Sparc...). C'est en effet ce qu'il y
a la plus facile à implémenter : l'appelant pousse les paramètres dans
l'ordre inverse (dernier paramètre d'abord), puis appelle la fonction.
L'en-tête de la fonction pousse les régistres, copie le pointeur de pile
dans le pointeur de frame, et soustrait ce qu'il faut du pointeur de
pile pour allouer les variables locales, par exemple, sous Intel :

function:
push EBP
mov EBP, ESP
sub ESP, n

1er question ( con ) le début c'est l'adresse la + basse ou la plus
haute ? pointe sur retour ou sur arg 1 ?


Sur Intel, la pile croît vers le bas. ESP désigne donc l'adresse la plus
basse dans la pile. À l'intérieur d'une fonction, la frame courante sur
la pile ressemble à peu près ceci :

+-------------------+
| param n |
+-------------------+
| ... |
+-------------------+
| param 1 |
+-------------------+
| adresse de retour |
+-------------------+
| ancien EBP | <--- EBP
+-------------------+
| variable locale |
+-------------------+
| ... | <--- ESP
+-------------------+

L"adresse du paramètre 1 serait donc EBP+8.

2eme question est ce que c'est pareil pour tout les compilos c++ (
gcc, ...) sur tout les os (sun,...)


Pas du tout. Comment est-ce qu'il pourrait être pareil, étant donné la
variété dans le hardware. Sur un Sparc, les cinq premier paramètres sont
passés dans les régistres -- quand on appelle une fonction, on décale la
fenêtre des régistres, de façon à ce que les 8 premiers régistres de
l'appelant devient les 8 derniers de l'appelé. Sur d'autres processeurs,
on n'a même pas de pile, ou la pile croît vers le haut.

en gros est ce que c'est normé ? ( j'ai la iso14882 mais j'ai pas
trouvé dedans)


Comment normer quelque chose de pareil en C++, quand le hardware n'est
pas normé.

3eme question est ce que le mot clef extern ou extern "C" modifie cela
( l'ordre des paramètres je crois)


Ça dépend de l'implémentation. Typiquement, non, mais il peut modifier
certains aspects de l'appel. Donc, sur l'ancien compilateur Zortech,
l'appelé enlevait les paramètres de la pile, au moyen d'une instruction
ret n. Avec « extern "C" », c'était à l'appelant de les enlever ;
l'appelant ne faisait qu'un simple ret. (Le C de l'époque n'avait pas de
prototypes, et c'était légal de passer plus de paramètres que prévus. Du
coup, l'appelé ne savait pas combien de paramètres à enlever.)

4eme question n'y a t il pas d'autre modificateur de l'organisation du
passage des paramètres ( ex: extern "FORTRAN" , extern "C++" <= celui
la existe pour microsoft, ...)


La norme garantit au moins "C" et "C++". D'autres sont à la liberté de
l'implémentation -- je m'attendrais à trouver au moins "Fortran" et
"Ada", par exemple. Voire même, aujourd'hui, Java (qui implémenterait
l'interface JNI, de façon plus ou moins transparent). Mais je ne sais
pas si mes attentes soient réaliste.

5eme question lors d'un linkage mixing C++ et autre langage, les
données globales sont elles accesibles ( avec un soulignement devant )


Selon les implémentations. Normalement, les données globales de types de
base sont accessibles de n'importe quel langage. Une globale de type
std::string serait théoriquement accessible du C aussi, mais il n'y a
pas grand chose qu'on pourrait en faire en C.

Typiquement, je crois que les noms des données sont décorés de façon
identique en tous les langages, sauf peut-être assembleur. Il ne doit
pas y avoir besoin d'ajouter un souligné. Mais là aussi, en fait,
l'implémentation fait ce qu'elle veut.

6 on suppose que pour le retour c'est la même structure de call frame.
A t on tord de la supposer ?


Je ne comprends pas trop la question. Lors d'un retour, le compilateur
génère un appel aux destructeurs des variables locales, et peut-être aux
destructeurs des paramètres, puis réstaure le pointeur de pile, puis
execute une instruction de retour. Sur un Intel :

; appel des destructeurs...
mov ESP, EBP
pop EBP
ret

Selon l'implémentation, les destructeurs des paramètres seront appelés
soit dans la fonction qui retourne, soit après le rétour, dans
l'appelant. Dans le cas de Intel, aussi, si on appelle les destructeurs
dans la fonction, on peut replacer le « ret » par « ret n », afin de
desallouer la mémoire.

Est ce que ce serait pas plutôt un
pointeur dans eax sur la valeur de retour ?


Comme toute la reste, où et comment on renvoie une valeur dépend de
l'implémentation. Sur Intel, typiquement, on renvoie une type de base
dans EAX, directement, ou dans EDX::EAX, s'il est trop grand pour EAX
seul. Je ne sais pas, mais j'imagine qu'on renvoie les valeurs
flottantes en haut de la pile flottante. Pour des types plus complexe
(classes, etc.), une solution assez fréquente, c'est de passer un
paramètre caché en tête avec l'adresse de la mémoire où l'appelé doit le
construire.

Mais ici, aussi, c'est très propre à l'implémentation. La norme précise
la sémantique voulue, non comment y arriver.

7 qui fait le menage dans les registres a la fin de l'appelé:
l'appelant ou l'appelé ?


Ça dépend de l'implémentation. Les conventions varient énormement. Avec
l'ancien compilateur Zortech (ma dernière expérience sur Intel), la
convention était que seulement BP (et DS, dans le cas de modèle large)
était sauvegardé -- si l'appelant avait besoin d'autres régistres,
c'était à lui de les sauvegarder. Sur Sparc, évidemment, c'est le
hardware qui sauve 16 régistres automatiquement, quand on déplace la
fenêtre de régistres.

8 dans la pile lors de l'appel de la fonction mettons directement les
données ou des pointeurs ?


Encore, la norme ne dit rien, mais dans ce cas-ci, tout est fait pour
que les paramètres soient passer directement, sans pointeur
supplémentaire intermédiaire. À moins que le paramètre soit une
référence.

--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

1 2