Какие регистры должны быть сохранены функцией x86?
Я пишу функцию в сборке x86, которая должна вызываться из кода c, и мне интересно, какие регистры мне нужно восстановить, прежде чем я вернусь к вызывающей стороне.
В настоящее время я только восстанавливаю esp
а также ebp
в то время как возвращаемое значение находится в eax
,
Есть ли какие-либо другие регистры, о которых мне следует беспокоиться, или я могу оставить в них то, что мне нравится?
3 ответа
Используя 32-битный ABI Microsoft (cdecl
или же stdcall
или другие соглашения о вызовах), EAX
, EDX
а также ECX
являются скретч-регистрами (вызов закрыт). Другие целочисленные регистры общего назначения сохраняются при вызове.
Коды условий в EFLAGS являются замкнутыми. DF=0 требуется при звонке / возврате, чтобы вы могли использовать rep movsb
без cld
первый. Стек x87 должен быть пустым при вызове или при возврате из функции, которая не возвращает значение FP. (Возвращаемые значения FP входят в st0
, с незаполненным стеком x87.) XMM6 и 7 сохраняются при вызове, остальные - это незаполненные регистры.
За пределами Windows большинство 32-битных соглашений о вызовах (в том числе i386 System V в Linux) согласны с этим выбором EAX, EDX и ECX в качестве системного вызова, но все регистры xmm являются системным вызовом.
Для x64 под Windows вам нужно только восстановить RBX
, RBP
, RDI
, RSI
, R12
, R13
, R14
, а также R15
, XMM6..15 сохраняются при вызове. (И вам нужно зарезервировать 32 байта теневого пространства для использования вызываемой стороной, независимо от того, есть ли какие-либо аргументы, которые не помещаются в регистрах.) Xmm6..15 сохраняются вызовом.
См. https://en.wikipedia.org/wiki/X86_calling_conventions для получения дополнительной информации.
Другие операционные системы используют x86-64 System V ABI (см. Рисунок 3.4), где целочисленные регистры с сохранением вызовов RBP
, RBX
, RSP
, R12
, R13
, R14
, а также R15
, Все регистры XMM/YMM/ZMM являются замкнутыми.
EFLAGS и стек x87 такие же, как и в 32-разрядных соглашениях: DF=0, флаги условий помечены, а стек x87 пуст. (Соглашения x86-64 возвращают значения FP в XMM0, поэтому регистры стека x87 всегда должны быть пустыми при вызове / возврате.)
Ссылки на официальные документы по соглашениям о вызовах см. На https://stackru.com/tags/x86/info.
32-bit: EBX, ESI, EDI, EBP
64-bit Windows: RBX, RSI, RDI, RBP, R12-R15, XMM6-XMM15
64-bit Linux,BSD,Mac: RBX, RBP, R12-R15
Подробнее см. " Ресурсы по оптимизации программного обеспечения " от Agner Fog. Соглашения о вызовах описаны в этом файле PDF.
Если вы не уверены в ситуации с регистрами, приведенные ниже инструкции могут легко спасти день.
PUSHA/PUSHAD - Нажмите все общие регистры
POPA/POPAD - Pop все общие регистры
Эти инструкции выдвигают и выдвигают регистры общего назначения и регистры SI/ESI, DI/EDI в определенном порядке.
Порядок инструкций PUSHA/PUSHAD следующий.
Opcode Instruction Clocks Description
60 PUSHA 18 Push AX, CX, DX, BX, original SP, BP, SI, and DI
60 PUSHAD 18 Push EAX, ECX, EDX, EBX, original ESP, EBP ESI, and EDI
И порядок для инструкции POPA / POPAD выглядит следующим образом. (в обратном порядке)
Opcode Instruction Clocks Description
61 POPA 24 Pop DI, SI, BP, SP, BX, DX, CX, and AX
61 POPAD 24 Pop EDI, ESI, EBP, ESP(***),EBX, EDX, ECX, and EAX
*** Значение ESP сбрасывается, а не загружается в ESP.