2010-08-12 12 views
8

Se si desidera chiamare una funzione C/C++ da assembly inline, si può fare qualcosa di simile:Chiamata diretta tramite l'assembly inline di GCC

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

GCC sarà poi emettere il codice che assomiglia a questo:

movl $callee, %eax 
call *%eax 

Questo può essere problematico poiché la chiamata indiretta distruggerà la pipeline su CPU meno recenti.

Poiché l'indirizzo di callee è infine una costante, si può immaginare che sarebbe possibile utilizzare il vincolo i. Citando dal GCC linea docs:

`i'

An immediate integer operand (one with constant value) is allowed. This includes symbolic constants whose values will be known only at assembly time or later.

Se cerco di usare in questo modo:

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

ricevo il seguente errore dal assembler:

Error: suffix or operands invalid for `call'

Questo perché GCC emette il codice

call $callee 

Invece di

call callee 

Quindi la mia domanda è se sia possibile fare uscita GCC il corretto call.

+1

Sei sicuro che la chiamata indiretta distrugge la pipeline? Hai un benchmark? La mia comprensione era che nei vecchi tempi su x86 (pre-i686), le chiamate indirette erano pessime (ricordo che sono un buon 10-100 volte più lento sul mio K6), ma al giorno d'oggi i cpus sono più intelligenti e riescono a gestirli bene . Quindi fai qualche prova prima di saltare alle conclusioni! –

+0

@R ..: hai ragione: se lo confronto su una CPU reale, non fa alcuna differenza. Sto eseguendo il mio codice in qemu, tuttavia, e sembra fare la differenza lì (circa il 20% in più di cicli/chiamata). – Job

+0

Quindi mi limiterò a seguire il modo in cui lo stai facendo, con la chiamata indiretta. Ciò consentirà a gcc di generare il codice corretto per le librerie/eseguibili PIC/PIE senza dover inserire degli hack speciali per gestire queste cose. –

risposta

10

ho avuto la answer dalla mailing list di GCC:

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

Ora ho solo bisogno di scoprire che cosa significa in realtà %P0 perché sembra essere una caratteristica non documentata ...

Edit: Dopo aver esaminato il codice sorgente GCC, non è esattamente chiaro cosa significhi il codice P davanti a un vincolo. Ma, tra le altre cose, impedisce a GCC di mettere uno $ di fronte a valori costanti. Quale è esattamente ciò di cui ho bisogno in questo caso.

+0

Vedere anche https://stackoverflow.com/questions/37639993/is-this-assembly-function-call-safe-completo ** per i pericoli dell'utilizzo di 'call' da in linea asm **. Fondamentalmente è impossibile fare affidamento su System V (Linux) x86-64, perché non si può dire al compilatore di voler clobare la zona rossa. In genere è una cattiva idea. Vedi https://stackoverflow.com/questions/7984209/calling-a-function-in-gcc-inline-assembly. –

0

Come circa.

asm volatile ("call $callee"); 

poiché si conosce il nome del simbolo, comunque.

Modifica: Il punto che volevo fare è che non devi seguire le convenzioni di gcc per gli argomenti asm qui, ma devi solo eseguire l'elaborazione del testo per eseguire il lavoro.

+0

Bene, questa sarebbe la mia ultima risorsa (dovrebbe essere 'asm (" call callee ");', comunque). Il problema è che avresti bisogno del nome del simbolo mutilato. Questo non è un grosso problema in C ma un PITA in C++. – Job

+0

Questo dovrebbe essere 'asm (" call callee ");', ma in C++ questo sarebbe qualcosa come 'asm (" call _ZL4callv ");' –

+4

Questo non funziona, per diversi motivi. Uno, diversi sistemi operativi (sullo stesso hardware) hanno diverse convenzioni su come mappare i nomi dei simboli (la stranezza più comune è la preposizione di un trattino basso). E due, a meno che gcc non sia in qualche modo informato che l'ASM usa la funzione, potrebbe benissimo non omettere mai una versione autonoma della funzione callable. Ad esempio, può essere in linea ovunque sia utilizzato, oppure può ometterlo interamente se non viene mai chiamato dal codice C. Tre, ci sono probabilmente problemi con il codice PIC e PIE ... –

0

Il trucco è la concatenazione letterale stringa. Prima di GCC inizia cercando di ottenere qualsiasi significato reale dal codice sarà concatenare stringhe letterali adiacenti, quindi, anche se il montaggio stringhe non sono le stesse di altre stringhe che si utilizzano nel programma dovrebbero essere concatenati se lo fai:

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


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

dal momento che si sta utilizzando GCC si potrebbe anche fare

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

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

Se non si conosce già si dovrebbe essere consapevoli del fatto che chiamare le cose dal montaggio in linea è molto difficile. Quando il compilatore genera le proprie chiamate ad altre funzioni, include il codice per impostare e ripristinare le cose prima e dopo la chiamata. Non sa che dovrebbe fare nulla per la tua chiamata, però. Dovrai o includerlo tu stesso (molto difficile da ottenere e potrebbe interrompere l'aggiornamento del compilatore o i flag di compilazione) o assicurarti che la tua funzione sia scritta in modo tale da non sembrare che abbia cambiato alcun registro o condizione del stack (o variabile su di esso).

modifica questo funzionerà solo per i nomi di funzioni C - non C++ in quanto vengono manchiati.

+1

No, questo non funziona affatto.Primo, il '$' è totalmente sbagliato per la chiamata, in secondo luogo non vuole "my_function" nella stringa asm, ma il ** nome storpiato che C++ genera **. –

+0

Non ho visto che questo fosse anche per il C++. In questo caso è probabile che questo non funzioni se il nome della funzione è sovraccarico senza molti cerchi. Ho appena copiato il "$" da un altro post dal momento che non ricordavo la sintassi asm x86. Correzione del fatto che ... – nategoose

0

Se si sta generando codice a 32 bit (ad esempio -m32 gcc opzione), la seguente linea asm emette una chiamata diretta:

asm ("call %0" :: "m" (callee)); 
1

Forse mi manca qualcosa qui, ma

extern "C" void callee(void) 
{ 

} 

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

dovrebbe funzionare bene. È necessaria la "C" extern in modo che il nome non venga decorato in base alle regole di mangling dei nomi C++.

Problemi correlati