.seh_stackalloc и выравнивание стека

Я пишу сборку x64 для ассемблера GNU. Я пытался читать о директивах .seh_*, но не нашел о них много информации. В документах gas они вообще не упоминаются.

Но насколько я понимаю, если мой код может оказаться в стеке во время операции раскрутки SEH, я должен использовать их. И поскольку мой код выполняет манипуляции со стеком и вызывает другие функции, SEH возможен, поэтому я должен использовать их.

В основном я думаю, что я понял это правильно:

.seh_proc FCT
FCT:

push %rbp
.seh_pushreg    %rbp

mov  %rsp, %rbp
.seh_setframe   %rbp, 0

push %r14
.seh_pushreg %r14

lea -(iOffset + iBytes)(%rsp), %rsp
.seh_stackalloc iOffset + iBytes

andq $-16, %rsp <---- But what about this?

.seh_endprologue

etc...

Но есть один момент, который не ясен. У меня есть такая инструкция:

andq $-16, %rsp

Как мне сообщить SEH, что я выполняю выравнивание стека? Это может скорректировать стек от 15 байтов (очень маловероятно) до 8 байтов (очень вероятно) и до 0 байтов (конечно возможно). Поскольку реальная сумма не может быть определена до времени выполнения, я застрял.

Я полагаю, что могу пропустить инструкцию .seh, но если там зарезервировано 8 байтов стека, я, вероятно, испортил раскрутку, не так ли? Разве это не противоречит всей цели?

В качестве альтернативы я могу опустить выравнивание. Но если я вызову другие функции (скажем, memcpy), разве я не должен выравнивать стек? Согласно MS:

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

Может быть, я могу «рассуждать» об этом? Если парень, который мне позвонил, сделал все правильно (если...), то стек был выровнен, когда он сделал call, так что теперь я отстаю на 8 байт (обратный адрес) плюс все, что я делаю в своем прологе. Могу ли я зависеть от этого? Кажется хрупким.

Я пытался смотреть на другой код, но я не уверен, что доверяю тому, что вижу. Я сомневаюсь, что gas сообщает об ошибках из-за неправильного использования .seh_*. Вы, вероятно, увидите проблему только во время фактического исключения (и, возможно, не всегда даже тогда).

Если я собираюсь сделать это, я хотел бы сделать это правильно. Кажется, что выравнивание стека будет обычным делом, поэтому у кого-то должно быть решение. Я просто не вижу этого.




Ответы (1)


Глядя на код, выводимый gcc, я думаю, что знаю ответ. Я был на правильном пути с моим «причинным» подходом.

Когда вызывается функция, стек временно становится невыровненным (из-за call), но почти сразу же повторно выравнивается через pushq %rbp. После этого корректировки стека (для локальных переменных или пространства стека для параметров вызываемых функций и т. д.) всегда выполняются с использованием кратных 16. Таким образом, к концу пролога стек всегда снова правильно выровнен и остается таким. до следующего call.

Это означает, что, хотя andq $-16, %rsp можно использовать для выравнивания стека, мне это не нужно, если я правильно напишу свой пролог.

ПРЕДУПРЕЖДЕНИЕ. Конечные функции (т. е. функции, которые не вызывают другие функции) не должны выравнивать стек (https://msdn.microsoft.com/en-us/library/67fa79wz.aspx).

person David Wohlferd    schedule 04.07.2016