2012-11-12 10 views
6

Desidero incrementare una variabile TLS nell'assembly ma genera un errore di segmentazione nel codice assembly. Non voglio permettere al compilatore di cambiare nessun altro registro o memoria. C'è un modo per farlo senza usare la sintassi di input e output di gcc?Archiviazione locale del thread nell'assieme

__thread unsigned val; 
int main() { 
    val = 0; 
    asm("incl %gs:val"); 
    return 0; 
} 
+0

1) Perché non è possibile scrivere "val + = 1;" invece? 2) Scriverlo, compilarlo con '-O2 -S' ed esaminare l'output dell'assembly; scoprirai che ti stai sbagliando su come accedere alle variabili di '__thread'. – zwol

+1

@Zack puoi scrivere una risposta a riguardo? – 0x90

+0

val ++ si traduce in movl $ 0x1,% gs: 0xfffffffc, ma quando eseguo asm ("movl $ 1,% gs: val") manualmente si traduce in movl $ 0x1,% gs: 0x8049f14. Come ottenere l'indirizzo 0xfffffffc nel mio programma. – Yogi

risposta

14

Se davvero davvero bisogno di essere in grado di fare questo per qualche motivo, si dovrebbe accedere a una variabile thread-locale dal linguaggio assembly per il precaricamento il suo indirizzo in C, in questo modo:

__thread unsigned val; 
void incval(void) 
{ 
    unsigned *vp = &val; 
    asm ("incl\t%0" : "+m" (*vp)); 
} 

Questo perché la sequenza di codice richiesta per accedere a una variabile locale del thread è diversa per quasi ogni combinazione di SO e CPU supportata da GCC e varia anche se si sta compilando per una libreria condivisa anziché un eseguibile (ovvero con -fPIC). Il costrutto sopra permette al compilatore di emettere la sequenza di codice corretta per te. Nei casi in cui è possibile accedere alla variabile locale del thread senza istruzioni aggiuntive, la generazione dell'indirizzo verrà piegata nell'operazione di assemblaggio. A titolo di esempio, ecco come gcc 4.7 per x86/Linux compila il sopra in diversi modi possibili diversi (ho spogliato fuori un mucchio di direttive assembler in tutti i casi, per chiarezza) ...

# -S -O2 -m32 -fomit-frame-pointer 
incval: 
     incl %gs:[email protected] 
     ret 

# -S -O2 -m64 
incval: 
     incl %fs:[email protected] 
     ret 

# -S -O2 -m32 -fomit-frame-pointer -fpic 
incval: 
     pushl %ebx 
     call __x86.get_pc_thunk.bx 
     addl $_GLOBAL_OFFSET_TABLE_, %ebx 
     leal [email protected](,%ebx,1), %eax 
     call [email protected] 
     incl (%eax) 
     popl %ebx 
     ret 

# -S -O2 -m64 -fpic 
incval: 
     .byte 0x66 
     leaq [email protected](%rip), %rdi 
     .value 0x6666 
     rex64 
     call [email protected] 
     incl (%rax) 
     ret 

Rendetevi conto che i tutti e quattro gli esempi sarebbero diversi se avessi compilato per x86/OSX e diverso ancora per x86/Windows.

+0

Bella risposta. I prefissi extra per l'allineamento nell'ultimo codice? – Jester

+2

@Jester Devono dare al linker spazio extra, in modo che possa sostituire le istruzioni che si vedono con una sequenza più efficiente (ma che consiste in istruzioni più lunghe) se è possibile. Vedi http://people.redhat.com/drepper/tls.pdf e http://www.x86-64.org/pipermail/discuss/2002-Settembre/002829.html per i dettagli cruenti. – zwol

Problemi correlati