Я компилирую следующий код в Visual Studio в 64-битном формате:
int func1(int a, int b,int c, int d) {
int res = 0;
return res;
}
int main() {
int p = 0;
p = func1(1,2,3,4);
return 0;
}
и открываю его в x64dbg:
sub rsp,38
mov dword ptr ss:[rsp+20],0
mov r9d,4
mov r8d,3
mov edx,2
mov ecx,1
call <prolog_epilog_64bit.int __cdecl func1(int, int, int, int)>
mov dword ptr ss:[rsp+20],eax
xor eax,eax
add rsp,38
ret
Я понимаю, почему 38:
return add
---------------
8 байт
---------------
4 * 8 = 32=20h для регистра (быстрый вызов)
---------------
4 байта для int
---------------
12 (Выравнивание стека в x64)
8+20+4+c=38
Теперь у меня есть этот код:
int func2(int a, int b,int c, int d,int e) {
int res = 0;
return res;
}
int main() {
int p = 0;
p = func2(1,2,3,4,5);
return 0;
}
и открываю его в x64dbg:
sub rsp,48
mov dword ptr ss:[rsp+30],0
mov dword ptr ss:[rsp+20],5
mov r9d,4
mov r8d,3
mov edx,2
mov ecx,1
call <prolog_epilog_64bit.int __cdecl func2(int, int, int, int, int)>
mov dword ptr ss:[rsp+30],eax
xor eax,eax
add rsp,48
ret
return add
---------------
8 байт
---------------
4 * 8 = 32=20h для регистра (быстрый вызов)
---------------
8 байт для параметров стека
----------------------
4 байта для локальной переменной int
---------------
4 байта (Выравнивание стека в x64)
8 + 20 + 8 + 4 + 4=48
Почему 48?
Должны ли быть пробелы между локальными переменными и параметрами, передаваемыми по стеку? Если мы увеличим количество параметров функции MAIN (2 параметра), это не изменится. (48)
Я ожидал увидеть здесь значение 38, но этого не произошло.
Почему RSP+30 и RSP+20?
Ответ
В вашем вопросе вы поднимаете несколько интересных аспектов касаемо стека в x64 и организации вызовов функций. Давайте разберем код и ответим на ваши вопросы по шагам.
1. Почему 48?
Когда вы вызываете func2(int a, int b, int c, int d, int e)
, у вас есть следующие параметры:
- A, B, C, D передаются через регистры:
rcx
,rdx
,r8
,r9
(всего 4 параметра). - Пятый параметр (E) передается через стек, так как в x64 существует ограничение на количество параметров, которые можно передать через регистры – максимум это 4.
Для того чтобы подготовить стек для вызова функции, происходит следующее:
- Выравнивание стека: Вызовы функций в x64 требуют, чтобы стек был выровнен на границу 16 байт.
- Хранение локальных переменных и переданных параметров: Стек для
func2
выглядит следующим образом:- 8 байт для сохранения адреса возврата.
- 32 байта (4 параметра по 8 байт) переданных параметров в регистрах.
- 8 байт для пятого параметра, который передан через стек.
- 4 байта для локальной переменной.
- 4 байт для выравнивания стека (всегда дополнительно надо учитывать, чтобы поддерживать выравнивание на границу 16 байт).
Таким образом, замедляя шаги, мы получаем:
- 8 (адрес возврата) + 32 (4 параметра) + 8 (пятый параметр через стек) + 4 (локальная переменная) + 4 (выравнивание стека) = 48 байт.
2. Почему RSP+20 и RSP+30?
RSP+20
и RSP+30
используются для хранения параметров и локальных переменных на стеке.
RSP+20
– это адрес, по которому хранится пятый параметр (E). Таким образом, он сохраняется на стеке.RSP+30
– это адрес локальной переменной (res), которую вы объявили в функции.
Поэтому, просмотрев эти значения можно понять, что они указывают на разные места в стеке, в которых хранятся передаваемые параметры и локальные переменные.
3. Добавление параметров в main
Если вы добавите параметры в main
(например, два параметра), это изменение не повлияет на структуру стека для вызова func2()
, так как параметры для func2
по-прежнему решаются независимо от параметров main
. Стек для функции main
просто станет больше, если мы примем их в расчет, но параметры для других функций, таких как func2
, останутся неизменными.
Итог
Стек в x64 архитектуре организован так, чтобы обеспечивать эффективность передачи параметров и хранение локальных переменных с учетом строгих требований к выравниванию. Установка правильного выравнивания и организация передачи через регистры/стек — это основа работы функций.
Как вы видите, в случае функции func2
, она занимает 48 байт на стеке для корректного выполнения с учетом всех параметров, локальных переменных и правил выравнивания.