2012-03-06 10 views
5

Piszę procedury obsługi przerwań dla x86_64. ABI określa, że ​​przed wywołaniem funkcji C muszę wyrównać stos do 16 bajtów. ISA x86_64 określa, że ​​przy wejściu do ISR mój stos jest wyrównany do 8 bajtów. Potrzebuję wyrównać wskaźnik stosu do 16 bajtów. Problem polega na tym, że po powrocie z mojej funkcji C, muszę odzyskać (potencjalnie) niewyrównany wskaźnik stosu, aby móc poprawnie wrócić z mojego przerwania.x86_64 wyrównaj stos i odzyskaj bez zapisywania rejestrów

Zastanawiam się, czy istnieje sposób, aby to zrobić bez korzystania z rejestru ogólnego przeznaczenia?

+0

Przechowywanie SP na stercie? –

+0

@ H2CO3 To brzmi jak okropny pomysł. W tym kontekście również nie mam pojęcia kupy. – dschatz

+0

OK, to był tylko pomysł na qiick. –

Odpowiedz

8

Oto moje rozwiązanie kwestii jak umieścić:

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

Dwa popycha opuścić stos z tym samym wyrównanie to pierwotnie, oraz kopię oryginalnego %rsp na (%rsp) i 8(%rsp). andq następnie wyrównuje stos - jeśli był już wyrównany do 16 bajtów, nic się nie zmienia, jeśli był wyrównany do 8 bajtów, to odejmuje 8 od %rsp, co oznacza, że ​​oryginalny %rsp jest teraz pod 8(%rsp) i 16(%rsp). Możemy więc bezwarunkowo przywrócić go z 8(%rsp).

4

Nie ma możliwości zrobienia tego bez dodatkowego rejestru, ponieważ operacja wyrównania jest destrukcyjna do rejestru rsp. Trzeba coś zrobić wzdłuż

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

Nie przekonałeś mnie, że nie ma sposobu, choć podejrzewam, że tak jest. To, że operacja jest destrukcyjna, nie oznacza, że ​​nie mogę zrobić czegoś przed wyrównaniem, aby zapisać oryginalny wskaźnik stosu. – dschatz

+0

Cóż, jest jeden sposób: Możesz przesuwać wskaźnik stosu, a następnie magiczną liczbę do ułożenia. Ponieważ nie wszystkie adresy w 64-bitowej przestrzeni adresowej są obecnie dozwolone w x86_64, możesz wybrać magię, która nie może być adresem stosu. Po powrocie wyszukujesz magię i znajdujesz za nią wskaźnik stosu. Jeśli luka pomiędzy wyrównanym stosem a starym stosem zawiera magię przypadkową szansą, możesz zidentyfikować tę sprawę, ponieważ znajdziesz magię dwa razy. – hirschhornsalz

+0

Używanie '% rbp' jest dobrym wyborem tutaj, ponieważ jest to rejestr zapisany przez callee (więc funkcja C przywróci ją przed powrotem, jeśli ją zablokuje). – caf

0

Podejrzewam, że jedynym sposobem, aby to zrobić bez użycia dodatkowego rejestr wymaga dodatkowego zapisu i odczytu do pamięci, który byłby jedynym punktem nie używając dodatkowego rejestr.

Oferuję bieżące rozwiązanie, które mam. Przechowuję plik rbp, aby móc go użyć do tymczasowego przechowywania, a następnie przywrócić go przed wywołaniem funkcji. Jest to podobne do drhirsch na odpowiedź

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

To nie jest bezpieczne przed zagnieżdżonymi przerwaniami - jeśli przerwanie o wyższym priorytecie trafi cię po zapisaniu do '-24 (% rsp)', a przed wyregulowaniem '% rsp ', to odbije twoje zapisane'% rbp'. – caf

0

Prawdopodobnie wolniejsze niż przy użyciu% ebp jak inni opisane, ale jak o:

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 

ta wykorzystuje fakt, że stos jest już 8-bajty , a instrukcja push może odczytać wartość, która ma być wypchnięta z pamięci.

+0

Teraz jest to presja warunkowa, ale bezwarunkowa. Bardzo problematyczne. –

+0

@R: dlaczego? Bezwarunkowy pop modyfikuje wskaźnik stosu, więc cofa rzeczy bez względu na warunek. –

+0

Och, teraz to rozumiem. Bardzo mądry! Lubię to. –