CSAPP-Revision-ch03
Posted 给个HK.phd读
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CSAPP-Revision-ch03相关的知识,希望对你有一定的参考价值。
预计第三章会在第四次完全结束。
今天先跳过“栈帧结构”一节,因为发现作业题里好多关于“结构体 struct”和联合体“Union”部分的。
昨日循环复习
循环是很重要的一个部分,其已经相对是指令集合的一个使用了。而在这前面基本都是单条指令的学习和运用。
下面来看这样一道题:
和昨日的有异曲同工之妙,但刚好题型是反过来的,我们需要写汇编。
我们一开始让栈指针rsp减了0x10,就是十进制下的16。
其实就是刚好是4个int数据,4 × sizeof(int) = 4 * 4 = 16
所以接下来就是连续的四条指令。
把2放到%rsp
0放到%rsp + 4
1放到%rsp + 8
9放到%rsp + 12
据此可以完成前面的填空
接着继续往下看
从jl指令可以看出前边应该是一条比较的指令,那么一定是i < 4的汇编
再又有,%rdi就是存储sum指针的寄存器,要对应其内存取值才是*sum的值。
将%ecx拿来加说明了%ecx应该是拿来存放a[i]的。
我们可以那eax来当做i循环变量处理。
因此中间三条要做的操作就是,将当前arr[i]数转移到%ecx中,执行i++的操作,然后另i和4做一次比较
那么最后呢?
最后我们需要释放栈空间,只要让栈指针加回0x10就可以啦。
我的答案如下:
func:
subq $0x10, %rsp # 为arr数组开辟空间
movl $0x09, 12(%rsp) #
movl $0x1, 8(%rsp)
movl $0x0, 4(%rsp) #
movl $0x2, (%rsp) #
movl $0, %eax
movl $0, %ecx
.L1
movl (%rsp, %eax, 4), %ecx
addl $0x1, %eax
cmpl %eax, $0x4
jl .L1
addl %ecx, (%rdi) #
addq $0x10, %rsp
ret
SWITCH跳转表
或许大家在C语言中已经习惯性地用if elif else来替代switch case default。
但不可否认switch仍旧是比较重要的一种语法(其实现也是非常简单高效的),接下来就来看看我们的汇编是如何来实现switch语句的。
语言也比较难以描述这张表,所以利用图示来表示:
就是说从我们的汇编翻译回C的时候,我们就是把各种情况(即图中的各种Tar)视为了一个数组结构,我们把x当作下标索引跳转到要执行的代码段。
再来看更直观的一张图:
图中,如果x > 6就不会进入跳转表。
反之,会以L4(跳转表首地址)为偏置项,8为比例因子,x为偏移量进行跳转,跳到不同的代码段区域。
要注意的就是switch中一些特殊的情况,比如:
落空,就是case忘记带了break,那么会继续执行下一个case。
一个代码段对应多个标号,其实就是有几个case写了但都是空的,一步步递下来直到有代码段的地方。
目前没有很好的题……书上有例题到时候再看,主要先把作业搞完。
数组的访问
个人感觉这个比较容易的,不会过多讲,拿几个例子看一下。
如下图所示就是典型的数组的分配形式:
所以我们只要知道首地址,加上我们想要访问的下标,乘以比例因子就可以访问到我们想要的元素了。
但上图也说明了,指针数据在我们IA32指令集是4Byte的,但x86-64就是8Byte的,因为指针就是地址呀,地址当然就是和机器的位数相关咯。
还有一点要提醒自己的就是指针取数,我们就是“寄存器中取到一块地址->地址映射到内存空间,去相应的内存里取数”,所以千万别忘了加括号哈。
这块内容其实只要C语言的基础扎实,前面讲过的指令全明白了应该问题不大。
然后就是什么嵌套数组,多维数组的访问。无非也是数据结构里“广义表”的内容吧,我们要时刻牢记的就是“行优先”吧。
可以看前面两篇的有几个题。
结构体
下面这张图应该我们映像里的结构体:
sizeof(int) = 4Byte × 3 = 12Byte分配给a数组。
4Byte分配给i。
指针分配4Byte。(说明不是64位的机器)
对于结构体内元素:
如果有一个结构体实例比如 rec r,可以直接用r.进行索引。
如果我们拿到的是结构体指针,那么就需要用“->”进行索引。
可以先看一个ppt里较复杂的例子:
%rdi保存rec指针首地址,%rsi就是val值。
我们会先索引到结构体里的i值,以此作为下标索引到数组位置进行赋值,然后进行下一个rec的操作。
然后就可以看我们的汇编代码:
%edx既然就是r,即首地址,其加上12就是索引到我们的i值,让%eax保存。
然后要把val数据传输到,以%eax里的值为下标的数组中去(此数组的首地址和r的首地址相同的,所以基址仍旧用%edx来写),最后把一个新地址给到edx。
同时,如若新地址为空,即0x0,就跳出循环。
有了这个例子,再看一道作业题:
5 第三章 结构体+函数+控制
已知node 结构体定义如下struct node long a; struct node *next;
请对以下init函数进行逆向分析,写出其C代码
Init:
movl $12,$eax
jmp .TestExprStat
.Loop:
addq (%rdi),%rax
movq 8(%rdi),%rdi
.TestExprStat:
testq %rdi,%rdi
jne .Loop
ret
先设置%eax为12。
然后测试%rdi的值,可以猜测其为函数传入的一个参数。
只要这个不为空,就可以进入循环主体。
对于(%rdi),其就是一个取值操作,将这个值加到%rax中。
而后,又会将%rdi + 8取到的值给到rdi。
此时,要利用经验判断出,这个传入的参数就是指向node的一个指针。
那么对此指针首地址直接取值的操作实际上就是取到了long a。
而首地址加上8其实就是取到next指针,将这个指针取到的值覆盖原先的指针。
注意我们最后返回的是%rax,我们加指令是addq(4字,对应C语言中的long),况且为了不溢出还是得用long来设置ans。
很容易得到代码:
long Init(node* head)
long ans = 12;
while(head != NULL)
ans += head->a;
head = head->next;
return ans;
对齐
结构体还有一个知识点就是对齐的概念。
仍旧先看一张简单的图:
看起来这是一种浪费空间的行为,但实际上这会提高系统的性能。
在汇编中,使用.align关键字——例如“.align 4”表示4的倍数。
书上的解释也很通俗,如果把所有数据结构设置为8的倍数,那么我们可以通过一次读操作来完成全部读取。
否则的话,两个对象可能分别存储在2个8字节区域,需要多次读。
所以可以看这个题目:
6 第三章 结构体
struct
char a;;
char *b;;
short c;
int d;
请问在紧凑布局和对齐布局中a/b/c/d字段的偏移量各是多少?
紧凑布局就太简单啦。
a 是0
b 是0 + sizeof(char) = 1
c 是1 + sizeof(char *) = 9
d 是9 + sizeof(short) = 11
对齐布局怎么办呢?
首地址还是无所谓所以,a偏移还是0。
但b不能偏移1,应该是8的一个倍数,所以会填充7Byte的空,使得b偏移为8。
8 + 8 = 16是2的倍数,所以short是直接符合的,因此short偏移为16。
最后16 + 2 = 18,并非4的倍数,所以要多填充2Byte,因此int的偏移为20。
同时我们发现20 + 4是24是最长的8的倍数,因此int后面无需再填充空位。
否则的话需要补充到最长的数据的整数倍,这样在结构体数组时才会更加对齐,否则简单的对齐会失效。
所以答案:
紧凑:
0 1 9 11
对齐:
0 8 16 20
对齐布局浪费空间终归算是一个缺点,那么怎么改善呢。
我们可以先放入占用空间大的数据,如下图所示:
(此题指定K=4因此我们必须每个数据块都占用4的倍数)
联合Union
C还有一种数据结构是Union,称作“联合体”。
联合体是按照内部最大元素分配空间。
同样的例子:
会发现上面的Union只需要8Byte即可实现。
那么同样来看一道例题:
4 第三章 union+结构体
union a1
struct int * b1; char c1; long d1 str1;
double data[3];
请问按照默认的对齐方式,上述a1占用多少字节空间?
首先要对struct进行分析。
int* b1占用8Byte,c1偏移8是1的整数倍,因此b1后面不填充。
那么对于d1其偏移就是8 + 1 =9,显然不是long 8Byte的整数倍,所以c1后面要填充7,即d1的偏移会是 8 + 1 + 7 = 16Byte。
那么最后一个struct的总量就是16 + 8 = 24,是8的倍数。
同理,一个3个double类型的数组占用空间为,3 × sizeof(double) = 24Byte。
取两者的大值,(两者相同)为24。
所以最后的a1仍旧是占用24Byte。
联合体常可以用来做数据转换,如这张图所示:
都是一开始拿一类型的4Byte数填充,然后返回的另一类型的数。
即相同的二进制编码进行不同数据类型的转换。
其实就是ppt的问号答案,就是相当于做了一个强制性的隐式转换。
ppt后面有50页也不像是书上的内容,一点小提示之类的,自己过一遍就得了。
就酱,然后今晚要结束ch03。
就是把栈帧结构和缓冲区溢出一起看。
以上是关于CSAPP-Revision-ch03的主要内容,如果未能解决你的问题,请参考以下文章