Question

Si vous voulez appeler une fonction de C / C de l'assembleur en ligne, vous pouvez faire quelque chose comme ceci:

void callee() {}
void caller()
{
    asm("call *%0" : : "r"(callee));
}

GCC émet alors le code qui ressemble à ceci:

movl $callee, %eax
call *%eax

Cela peut être problématique puisque l'appel indirect détruira le pipeline sur les processeurs plus anciens.

Depuis l'adresse de callee est finalement une constante, on peut imaginer qu'il serait possible d'utiliser la contrainte de i. Je cite le CCG en ligne docs :

  

`i '

     

Un opérande entier immédiatement (une valeur constante avec) est autorisée. Cette   comprend des constantes symboliques dont   Les valeurs ne seront connus que lors de l'assemblée   temps ou plus tard.

Si je tente de l'utiliser comme ceci:

asm("call %0" : : "i"(callee));

je reçois l'erreur suivante de l'assembleur:

  

Erreur: suffixe ou opérandes non valide pour `appel '

Ceci est parce que GCC émet le code

call $callee

Au lieu de

call callee

Alors, ma question est de savoir s'il est possible de faire la sortie de la GCC call correcte.

Était-ce utile?

La solution

Je suis réponse de la liste de diffusion de GCC :

asm("call %P0" : : "i"(callee));

Maintenant, je juste besoin de savoir ce qui en fait des moyens %P0 parce qu'il semble être une fonctionnalité non documentée ...

Modifier : Après avoir regardé le code source de GCC, il est clair pas exactement ce que le code P devant un moyen de contrainte. Mais, entre autres, il empêche GCC de mettre un $ devant des valeurs constantes. Ce qui est exactement ce que je dois dans ce cas.

Autres conseils

Peut-être que je manque quelque chose ici, mais

extern "C" void callee(void) 
{

}

void caller(void)
{
  asm("call callee\n");
}

devrait fonctionner correctement. Vous avez besoin extern « C » pour que le nom ne sera pas décoré basé sur C ++ règles de nommage mangling.

L'astuce est une chaîne concaténation littérale. Avant GCC commence à essayer d'obtenir un réel sens de votre code, il concaténera littéraux de chaîne adjacents, de sorte que même si l'assemblage des chaînes ne sont pas les mêmes que d'autres chaînes que vous utilisez dans votre programme, ils devraient être concaténées si vous faites:

#define ASM_CALL(X) asm("\t call  " X "\n")


int main(void) {
    ASM_CALL( "my_function" );
    return 0;
}

Puisque vous utilisez GCC, vous pouvez également faire

#define ASM_CALL(X) asm("\t call  " #X "\n")

int main(void) {
   ASM_CALL(my_function);
   return 0;
}

Si vous ne connaissez pas déjà, vous devriez savoir que appeler les choses est très délicat de montage en ligne. Lorsque le compilateur génère ses propres appels à d'autres fonctions, il inclut le code pour mettre en place et de restaurer les choses avant et après l'appel. Il ne sait pas qu'il devrait faire tout cela pour votre appel, cependant. Vous devrez soit inclure vous-même (très difficile à obtenir le droit et peut rompre avec une mise à jour du compilateur ou des drapeaux de compilation) ou vous assurer que votre fonction est écrite de telle manière qu'il ne semble pas avoir modifié les registres ou l'état des pile (ou variable dessus).

modifier cela ne fonctionnera que pour les noms de fonction C -. Pas C ++ comme ils sont mutilées

Si vous générez du code 32 bits (par exemple -m32 gcc option), la ligne asm suivante émet un appel direct:

asm ("call %0" :: "m" (callee));
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top