2016-05-21 1 views
2

Problème

Je veux exécuter l'appel système de sortie dans ARM en utilisant l'assembleur en ligne sur un appareil Android Linux, et je veux la valeur de sortie à lire à partir d'un emplacement en mémoire.ARM en ligne asm: appel système de sortie avec la valeur lue dans la mémoire

Exemple

Sans donner cet argument supplémentaire, une macro pour l'appel ressemble à:

#define ASM_EXIT() __asm__("mov  %r0, #1\n\t" \ 
          "mov  %r7, #1\n\t" \ 
          "swi  #0") 

Cela fonctionne bien. Pour accepter un argument, je l'adapter à:

#define ASM_EXIT(var) __asm__("mov  %r0, %0\n\t" \ 
           "mov  %r7, #1\n\t" \ 
           "swi  #0"   \ 
           :      \ 
           : "r"(var)) 

et je l'appelle à l'aide:

#define GET_STATUS() (*(int*)(some_address)) //gets an integer from an address 

ASM_EXIT(GET_STATUS()); 

Erreur

invalide 'asm': numéro d'opérande hors de portée

Je ne peux pas expliquer pourquoi je reçois cette erreur, car j'utilise une variable d'entrée i n l'extrait ci-dessus (% 0/var). En outre, j'ai essayé avec une variable régulière, et toujours eu la même erreur.

+0

Est-ce que 'GET_STATUS' a vraiment un' // 'dessus ou l'avez-vous ajouté pour clarifier la question? Que se passe-t-il si vous le supprimez? Aussi, que diriez-vous d'essayer ASM_EXIT (1)? Enfin, il pourrait être instructif d'essayer de compiler ceci avec '-E'. –

+1

'% r7' - bien, vous n'avez pas 8 opérandes à l'extension ASM, n'est-ce pas? Dans le cas de l'asm simple (où le compilateur n'essaye pas d'analyser le contenu), il arrive que l'assembleur ignore apparemment la fausse syntaxe et assemble ce que vous pensez. – Notlikethat

+1

Pour GCC ARM asm, vous n'avez pas besoin du préfixe% pour enregistrer les noms. – Jeremy

Répondre

5

Extended-asm syntax nécessite d'écrire %% pour obtenir un seul % dans la sortie asm. par exemple. pour x86:

asm("inc %eax")    // bad: undeclared clobber 
asm("inc %%eax" ::: "eax"); // safe but still useless :P 

%r7 est de traiter r7 comme numéro d'opérande. Comme les commentateurs l'ont souligné, il suffit d'omettre les %, car vous n'en avez pas besoin pour ARM, même avec GNU as.


Malheureusement, il doesn't seem to be a way to request input operands in specific registers on ARM, la façon dont vous pouvez pour x86. (par exemple "a" signifie contrainte eax spécifiquement).

Vous pouvez utiliser register int var asm ("r7") pour forcer un var à utiliser un registre spécifique, puis utiliser une contrainte "r" et supposer qu'il sera dans ce registre. Je ne suis pas sûr que ce soit toujours sûr, ou une bonne idée, mais il semble fonctionner même après l'inline. @Jeremy commente que cette technique a été recommandée par l'équipe du GCC.

J'ai obtenu un code efficace généré, ce qui évite de perdre une instruction sur un mouvement reg-reg:

See it on the Godbolt Compiler Explorer:

__attribute__((noreturn)) static inline void ASM_EXIT(int status) 
{ 
    register int status_r0 asm ("r0") = status; 
    register int callno_r7 asm ("r7") = 1; 
    asm volatile("swi #0\n" 
     : 
     : "r" (status_r0), "r" (callno_r7) 
); 
} 

#define GET_STATUS() (*(int*)(some_address)) //gets an integer from an address 

void foo(void) { ASM_EXIT(12); } 
    push {r7} @   # gcc is still saving r7 before use, even though it sees the "noreturn" and doesn't generate a return 
    movs r0, #12 @ stat_r0, 
    movs r7, #1 @ callno, 
    swi #0 
    # yes, it literally ends here, after the inlined noreturn 

void bar(int status) { ASM_EXIT(status); } 
    push {r7} @ 
    movs r7, #1 @ callno, 
    swi #0     # doesn't touch r0: already there as bar()'s first arg. 

Puisque vous voulez toujours la valeur lue de la mémoire, vous pouvez utiliser une contrainte "m" et inclure un ldr dans votre asm inline. Ensuite, vous n'avez pas besoin de l'astuce register int var asm("r0") pour éviter un mov perdu pour cet opérande. Le mov r7, #1 peut ne pas toujours être nécessaire non plus, c'est pourquoi j'ai aussi utilisé la syntaxe register asm() pour cela.Si gcc veut une constante 1 dans un registre ailleurs dans une fonction, il peut le faire en r7 alors c'est déjà là pour l'ASM_EXIT.


Chaque fois que la première ou les dernières instructions d'une déclaration en ligne asm GNU C sont mov instructions, il y a probablement un moyen de les éliminer avec de meilleures contraintes.

+1

"Vous pouvez utiliser register int var asm (" r7 ") pour forcer un var à utiliser un registre spécifique" - autant que je sache, c'est la seule façon de le faire (sans le mov supplémentaire) sur ARM et m'a été recommandé par l'équipe du GCC. Je ne sais pas pourquoi vous ne pouvez pas donner le nom du registre comme vous pouvez sur x86. – Jeremy