所以我的教科书上有一个问题(计算机系统:程序员的观点问题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个字节,但是我不知道第五个4字节的内存分配是什么。
另外,你们对学习x86堆栈框架结构和过程调用有什么建议(视频,文章,博客文章)吗?目前,我的计算机体系结构课程非常迷茫。
分配用于的8个字节,用于的s1
8个字节s2
和用于传递word_sum
地址以存储其结果的4个字节。
我是怎么知道的?
如果我们查看的顶部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,该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
in的结果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
。
通常,这样的调试问题几乎完全是利用您对生成程序集的代码的了解来进行有根据的猜测,然后使用消除过程来确认您的猜测是正确的。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句