x86过程调用内存分配

时间:2015-04-26 08:20:13

标签: c assembly struct x86 att

所以我的教科书出了问题(计算机系统:程序员的透视问题3.64):

它提供了这样的代码:

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;
}

然后是prod&组件的汇编代码。 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

它询问为什么prod在汇编代码行4中的堆栈上分配20个字节。

我可以看到它会为str1和str2分配8个字节,但我不知道第5个4字节内存分配是什么。

另外,你们有没有关于学习x86堆栈框架结构和程序调用的任何建议(视频,文章,博客文章)?目前我的计算机体系结构课程很丢失。

1 个答案:

答案 0 :(得分:2)

分配为s1的8个字节,s2的8个字节,以及传递word_sum个地址的4个字节,用于存储它的结果。

我是怎么想出来的?

如果我们查看prod的顶部,我们会看到:

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

第5行和第7行是访问我们来电者堆栈框架的唯一说明,因此他们必须抓取xy。我们知道我们存储指向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

返回struct 的函数期望额外的指针在堆栈顶部传递。这是它应该存储它的返回值的地址。我们在堆栈顶部存储的唯一其他内容是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的指针。

一般来说,像这样的调试问题几乎完全是利用你对生成程序集的代码的知识进行有根据的猜测,然后使用消除过程来确认你的猜测是正确的。