Шахматы с ABI в стековом фрейме Windows x64
Итак, я делаю игрушечный компилятор для сборки, и моя программа дает сбой на границе моего сгенерированного кода и кода C. Вот как выглядит сборка:
(Pseudo-code from my compiler)
def test(x: int) -> int:
return magic_func(magic_func(x, x), magic_func(x, x))
(Actual output assembly:)
test:
push rcx
mov rdx, rcx
call magic_func
mov rdx, rax
pop rcx
push rdx
mov rdx, rcx
call magic_func
mov rcx, rax
pop rdx
call magic_func
ret
Код C выглядит следующим образом:
int magic_func(int x, int y) {
printf("magic_func(%d, %d)\n", x, y);
return x + y * 2;
}
extern int test(int);
int main() {
printf("%d\n", test(44));
return 0;
}
Иmagic_func
функция конкретно такая: (из objdump)
push rbp
mov rbp,rsp
sub rsp,0x20
mov DWORD PTR [rbp+0x10],ecx
mov DWORD PTR [rbp+0x18],edx
mov eax,DWORD PTR [rbp+0x18]
mov r8d,eax
mov edx,DWORD PTR [rbp+0x10]
lea rcx,[rip+0x0] # 1e <magic_func+0x1e>
call 23 <magic_func+0x23>
mov eax,DWORD PTR [rbp+0x18]
lea edx,[rax+rax*1]
mov eax,DWORD PTR [rbp+0x10]
add eax,edx
add rsp,0x20
pop rbp
ret
Теперь с помощью GDB я обнаружил, что ошибка сегментации возникает вret
инструкция моей функции (test
), но всегда при третьем (последнем) вызове. Адрес возврата, который должен был быть сохранен в стеке, перезаписывается с каким-то мусором. Я уверен, что это какие-то махинации со стеком Windows с выравниванием или какими-то красными зонами, или я не выделяю достаточно места, но я не могу в этом разобраться. Кто-нибудь, имеющий опыт работы с соглашениями о вызовах Windows, может дать мне совет?