x86_64 выравнивает стек и восстанавливает без сохранения регистров - PullRequest
5 голосов
/ 07 марта 2012

Я пишу процедуры обработки прерываний для x86_64. ABI указывает, что перед вызовом функции C я должен выровнять стек по 16 байтов. ISA x86_64 указывает, что при входе в ISR мой стек выравнивается на 8 байт. Поэтому мне нужно выровнять мой указатель стека до 16 байт. Проблема заключается в том, что по возвращении из моей функции C я должен восстановить (потенциально) невыровненный указатель стека, чтобы я мог правильно вернуться из своего прерывания.

Интересно, есть ли способ сделать это без использования регистра общего назначения?

Ответы [ 4 ]

9 голосов
/ 07 марта 2012

Вот мое решение поставленного вопроса:

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

Два толчка выходят из стека с тем же выравниванием, которое было у него изначально, и с копией оригинала %rsp в (%rsp) и 8(%rsp). Затем andq выравнивает стек - если он уже был выровнен на 16 байтов, то ничего не меняется, если он был выровнен на 8 байтов, то он вычитает 8 из %rsp, что означает, что исходный %rsp теперь находится в 8(%rsp) и 16(%rsp). Таким образом, мы можем безоговорочно восстановить его из 8(%rsp).

4 голосов
/ 07 марта 2012

Нет способа сделать это без дополнительного регистра, потому что операция выравнивания разрушительна для регистра rsp. Вы должны сделать что-то вместе

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
1 голос
/ 07 марта 2012

Возможно, медленнее, чем использование% ebp, как описали другие, но как насчет:

    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

Это использует тот факт, что стек уже выровнен по 8 байтов, и что инструкция push может читатьзначение, которое нужно вытолкнуть из памяти.

0 голосов
/ 07 марта 2012

Я подозреваю, что единственный способ сделать это без использования дополнительного регистра требует дополнительной записи и чтения в память, что было бы единственной точкой отказа от использования дополнительного регистра.

Я предложу текущее решение, которое у меня есть. Я храню rbp, чтобы использовать его для временного хранения, а затем восстановить его до вызова функции. Это похоже на ответ Дрирша

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
...