2012-03-06 4 views
5

Sto scrivendo le routine di gestione degli interrupt per x86_64. L'ABI specifica che prima di chiamare una funzione C devo allineare lo stack a 16 byte. L'ISA x86_64 specifica che alla voce di un ISR, il mio stack è allineato a 8 byte. Ho quindi bisogno di allineare il mio puntatore dello stack a 16 byte. Il problema è che al ritorno dalla mia funzione C, devo recuperare il puntatore stack (potenzialmente) non allineato in modo che possa tornare correttamente dal mio interrupt.x86_64 allinea lo stack e ripristina senza salvare i registri

Mi chiedo se esiste un modo per farlo senza utilizzare un registro generale?

+0

Memorizzazione di SP nell'heap? –

+0

@ H2CO3 Sembra un'idea terribile. Inoltre, non ho la nozione di heap in questo contesto. – dschatz

+0

OK, era solo un'idea approssimativa. –

risposta

8

Ecco la mia soluzione per la questione put:

pushq %rsp 
pushq (%rsp) 
andq $-0x10, %rsp 
    call function 
movq 8(%rsp), %rsp 

I due spinte lasciare la pila con lo stesso allineamento che aveva in origine, e una copia dell'originale %rsp a (%rsp) e 8(%rsp). Lo andq allinea lo stack - se era già allineato a 16 byte non cambia nulla, se era allineato a 8 byte allora sottraeva 8 da %rsp, il che significa che l'originale %rsp è ora allo 8(%rsp) e 16(%rsp). Quindi possiamo ripristinarlo incondizionatamente da 8(%rsp).

4

Non c'è modo di farlo senza un registro aggiuntivo, perché l'operazione di allineamento è distruttiva per il registro rsp. Hai bisogno di fare qualcosa insieme

push %rbp   ;save rbp for stack pointer 
mov %rsp, %rbp ;move old sp to rbp 
and $-0x10, %rsp ;align stack 
...     
...    ;if you want to use %rbp here, save it on the stack before 
... 
mov %rbp, %rsp ;old stack pointer 
pop %rbp 
iret 
+0

Non mi hai convinto che non c'è modo, anche se sospetto che sia il caso. Solo perché l'operazione è distruttiva non significa che non posso fare qualcosa prima dell'allineamento per salvare il puntatore dello stack originale. – dschatz

+0

Bene, c'è un modo: puoi spingere il puntatore dello stack e poi un numero magico da impilare. Poiché non tutti gli indirizzi nello spazio degli indirizzi a 64 bit sono attualmente consentiti in x86_64, puoi scegliere una magia che non può essere un indirizzo di stack. Al ritorno, cerca la magia e trova il puntatore dello stack dietro di esso. Se il divario tra pila allineata e pila vecchia contiene la magia per caso, puoi identificare questo caso perché troverai la magia due volte. – hirschhornsalz

+0

L'uso di '% rbp' è una buona scelta qui, perché è un registro salvato da un callee (quindi la funzione C lo ripristinerà prima di tornare, se lo elimina). – caf

0

Ho il sospetto che l'unico modo per farlo senza l'utilizzo di un registro aggiuntivo richiede la scrittura e la lettura aggiuntiva alla memoria che sarebbe l'unico punto di non utilizzare un registro supplementare.

Offrirò la soluzione corrente che ho. Conservo rbp in modo da poterlo utilizzare per la memorizzazione temporanea e quindi ripristinarlo prima della chiamata alla funzione. Questo è simile al drhirsch di rispondere

movq %rbp, -24(%rsp) //store original rbp 3 words beyond the stack 
movq %rsp, %rbp //store original rsp 
subq $8, %rsp //buy a word on the stack 
andq $-0x10, %rsp //16 byte align the stack (growing downwards) 
//We now have 1 or 2 words free on the stack (depending on the original 
// alignment). This is why we put rbp 3 words beyond the stack 
movq %rbp, (%rsp) //store the original rsp right here 
movq -24(%rbp), %rbp //restore original rbp 
call foo 
movq (%rsp), %rsp //restore original rsp 
iretq 
+0

Questo non è sicuro contro interrupt annidati - se un interrupt con priorità più alta ti colpisce dopo aver archiviato in '-24 (% rsp)' e prima di aggiustare '% rsp', verrà memorizzato il tuo'% rbp' salvato. – caf

0

Probabilmente più lento rispetto all'utilizzo% ebp come altri hanno descritto, ma come circa:

push %rsp 
    test $0xf, %rsp 
    jz aligned 
    push (%rsp) // duplicate the top of the stack 
aligned: 
    // now have 16-byte alignment with the original stack pointer 
    // on the top of the stack, either once or twice 
     : 
    pop %rsp 
    iret 

questa sfrutta il fatto che lo stack è già 8 byte allineato e che un'istruzione push può leggere il valore da spingere dalla memoria.

+0

Ora è una spinta condizionale ma un pop incondizionato. Molto problematico –

+0

@R: perché? Il pop incondizionato modifica il puntatore allo stack in modo da annullare le cose indipendentemente da quale fosse la condizione. –

+0

Oh, ora capisco. Molto intelligente! Mi piace. –