2015-04-26 3 views
1

Так у меня есть проблема с моего учебника (Computer Systems: программист Перспектива Проблемы 3,64):x86 Распределение Procedure Call Памяти

Это дает примерно такой код:

typedef struct { 
int a; 
int *p; 
} str1; 

typedef struct { 
int sum; 
int diff; 
} str2; 

str2 word_sum(str1 s1) { 
str2 result; 
result.sum = s1.a + *s1.p; 
result.diff = s1.a - *s1.p; 
return result; 
} 

int prod(int x, int y) { 
str1 s1; 
str2 s2; 
s1.a = x; 
s1.p = &y; 
s2 = word_sum(s1); 
return s2.sum * s2.diff; 
} 

, а затем коду сборки для в прод & word_sum функции:

1 word_sum: 
2 pushl %ebp 
3 movl %esp, %ebp 
4 pushl %ebx 
5 movl 8(%ebp), %eax 
6 movl 12(%ebp), %ebx 
7 movl 16(%ebp), %edx 
8 movl (%edx), %edx 
9 movl %ebx, %ecx 
10 subl %edx, %ecx 
11 movl %ecx, 4(%eax) 
12 addl %ebx, %edx 
13 movl %edx, (%eax) 
14 popl %ebx 
15 popl %ebp 

1 prod: 
2 pushl %ebp 
3 movl %esp, %ebp 
4 subl $20, %esp 
5 leal 12(%ebp), %edx 
6 leal -8(%ebp), %ecx 
7 movl 8(%ebp), %eax 
8 movl %eax, 4(%esp) 
9 movl %edx, 8(%esp) 
10 movl %ecx, (%esp) 
11 call word_sum 
12 subl $4, %esp 
13 movl -4(%ebp), %eax 
14 imull -8(%ebp), %eax 
15 leave 
16 ret 

И спрашивает, почему прод выделяет 20 байт в стеке в сборочной линии кода 4.

Я вижу, что он будет выделять по 8 байт для str1 и str2, но я не знаю, что будет 5-м распределением памяти по 4 байта.

Кроме того, были ли у вас ребята какие-либо рекомендации (видео, статьи, сообщения в блоге) об обучении структуре кадра кадра x86 и процедурных вызовах? На данный момент я очень потерял курс «Компьютерная архитектура».

+1

Я думаю, что этот код немного оптимизирован. Что такое конвенция? Cdecl? –

+0

@CollinDauphinee это не говорит, но я полагаю, что это cdecl, потому что учебник сказал нам, что eax, ecx и edx являются вызывающими, и это то, что имеет cdecl. –

+0

Да, я знаю, что делает этот код, я просто пытаюсь найти лучший способ объяснить это. Это ужасная проблема, если вы не получили слово «word_sum» и не включили его в вопрос. –

ответ

2

Распределение составляет 8 байт для s1, 8 байтов для s2 и 4 байта для передачи word_sum адреса для сохранения результата.


Как я понял это?

Если мы посмотрим на вершине prod, мы видим:

5 leal 12(%ebp), %edx 
6 leal -8(%ebp), %ecx 
7 movl 8(%ebp), %eax 

линия 5 и 7 являются единственными инструкциями обращающейся стеки нашего абонента, поэтому они должны быть захватывая x и y. Мы знаем, что мы сохраняем указатель на y, а строка 5 - инструкция lea, поэтому мы можем предположить, что EDX содержит &y, а EAX - x. Это все еще оставляет ECX, который содержит указатель на что-то в нашем стеке.

Двигаясь дальше, мы видим, что это хранение EAX, EDX и ECX на нашем стеке, а затем вызвать word_sum:

8 movl %eax, 4(%esp) 
9 movl %edx, 8(%esp) 
10 movl %ecx, (%esp) 
11 call word_sum 

Мы знаем, что EAX и EDX хранятся значения, которые должны быть сохранены в s1 , Мы знаем, что s1 будет передан в word_sum, а аргументы передаются в верхней части стека. Строки 8 и 9 хранят EAX и EDX очень близко к вершине стека, поэтому мы можем предположить, что это s1.

Функции, возвращающие структуру , ожидают, что дополнительный указатель будет передан в верхней части стека. Это адрес, в котором он должен хранить возвращаемое значение. Единственное, что мы храним в верхней части стека, это ECX, и мы знаем, что мы сохраняем результат word_sum в s2, поэтому ECX должен быть указателем на s2.

Мы теперь предположили, что имеет место каждый регистр; EAX - x, EDX - &y, а ECX - &s2.

Если мы посмотрим ниже, мы можем подтвердить наши ожидания:

13 movl -4(%ebp), %eax 
14 imull -8(%ebp), %eax 

Мы знаем, что результат этой функции s2.sum * s2.diff. Есть инструкция imul, и мы умножаем s2.sum на s2.diff, поэтому EBP-8 должен указывать на s2.sum, а EBP-4 должен указывать на s2.diff.

Если мы вернемся к строке 6, мы увидим, что EBP-8 хранился в ECX, который мы правильно подозревали, был указателем на s2.


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

+0

Не могли бы вы объяснить 4 байта для сохранения ECX? Имеет ли это какое-то отношение к тому, чтобы быть спасенным? –

+0

Надеюсь, вы сможете это понять. Это трудно объяснить, потому что здесь есть несколько оптимизаций. –

+0

Благодарим вас за подробное редактирование. У меня было еще несколько вопросов, если вы не возражаете ответить: 1. Почему это только адрес s2, а не s1? 2. Откуда вы знаете, какие аргументы хранятся в регистрах? –

Смежные вопросы