失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > 深入理解计算机系统读书笔记(四)

深入理解计算机系统读书笔记(四)

时间:2023-05-18 20:34:05

相关推荐

深入理解计算机系统读书笔记(四)

深入理解计算机系统读书笔记(四)

x86-64位寄存器含有16组64位通用目的寄存器,%rax,%bx %cx %dx %rsi %rdi %rbp %rsp %r8 %r9 …%r15,其中特殊的%rsp存储栈指针的值,%rax为返回值。

汇编有两种架构,现在大多数编译器如GCC等使用的都是ATT架构的,另外微软与Intel的编译器使用的是Intel的架构,两者有细微的区别。

汇编指令后的为操作数,一遍指令都有一个或者多个操作数,操作数的值有三种来源,一种是立即数,一种是来自寄存器,一种是来自内存。

立即数的书写是$后直接跟正常的整数,如movb $0x45, %bl 操作数类型为立即数与寄存器

寄存器类型的操作数只能是1、2、4、8字节,这样是为了向后兼容,如8字节为64位的操作数对应64位寻址能力的系统,4字节则对应32位系统等等。如 movq %rax,%rdx 的操作数类型均为寄存器

对于操作数为内存类型的,有多种不同的寻址模式:

立即数寻址—$0x54寄存器寻址—%rax绝对寻址-----0x56间接寻址-----(%rax)基址+偏移量寻址—8(%rax)变址寻址—(%rax,%rbx)变址寻址—7(%rax,%rbx)比例变址寻址—(,%rax,4)比例变址寻址—7(,%rax,8)比例变址寻址—(%rax,%rbx,1)比例变址寻址—7(%rbx,%rax,2)比例值只能为1,2,4,8

数据传送指令:movb movw movl movq分别对应1,2,4,8字节的传送,有两个操作数,一个源操作数一个目的地址,如movq (%rbx),%rax 表示从寄存器%rbx中读取全部64位对应的内存地址的值存到寄存器%rax中,再比如movl $0x5495868383930205,-56(%rax),表示将32立即数$0x5495868383930205 存到寄存器%rax中的值表示的内存地址-56字节的位置上。

movzbw movzbl movzwl movzbq movzwq,在较小的源值复制到较大的目的时运用零扩展使用这些指令,注意到这里少了movzlq,即双字扩展到4字,这是因为该方法可以通过movl自动实现,因为在写寄存器的时候凡是产生大于32位的值其寄存器高危都将被置于0,所以可以用movl提带movzlq,

movsbw movsbl movswl movsbq movswq movslq ctlq 在较小的源值复制到较大的目的时运用字符扩展使用这些指令,注意到这里多了一个ctlq与movslq,上面说了0扩展填充0刚好与32位置零吻合所以可以提到,这里使用符号扩展扩展的是最高位,可能为1,所以movslq不能省略。ctlq 专指将%eax中的内容移动到%rax中,与movslq %eaq,%rax等效

上面两组指令的目的操作数只能为寄存器地址,源操作数可以来自寄存器,立即数,内存。

这里有一个c语言编写的函数:

long exchange(long *xp, long y)

{

long x = *xp;

*xp = y

return x

}

调用long 1 = 5; exchange(&a,8),最后返回的值为5,这是一个怎样的过程呢,该段代码经过编译器编译后变成

exchange:

movq (%rdi),%rax

movq %rsi, (%rdi)

ret

解释:%rdi 存储 xp %rsi 存储 y,首先调用xp的值对应的内存地址中的值赋值给x也就是5,然后将8赋值为xp对应的内存地址,也就是a指向的内存地址。最后返回%rax的值也就是x=5,。更有这个是有a=8,发生了数据交换。

一道练习题,通过mov指令完成类型转换:操作数地址分别存在%rdi 与%rsi中

char–>int:

movsbl (%rdi),%rax

movl %eax,(%rsi)

char–> unsigned int

movsbl (%rdi),%rax

movl %eax,(%rsi)

unsigned char --> long

movzbl (%rdi),%rax

movq %rax,(%rsi)

压栈与弹栈:栈是向下增长的,较低的栈地址在下面,所以压栈就是对栈地址做减法,弹栈就是做加法。栈遵循的是后进先出的原则即 FILO,压栈使用指令pushq pushl pushw pushb,弹栈使用指令popq popl popw popb。压栈的时候首先将栈顶下移然后讲数据存储进去,如pushb $0x56 ,如果此时的栈顶位置是0x101,那么首先将栈顶向下移动1字节得到栈顶位置0x100,然后讲1字节的数据$0x56放进去,完成压栈操作。这个过程可以拆分成两步即 subn $1,%rsp movb $0x56,(%rsp),不过这样拆分将导致更大的运算量所以不被采用。弹栈的过程一样,直接将栈顶位置上移即可。即popb %rax 相当于 movb (%rsp), %rax addb $1,%rsp

加载有效地址:leaq leal leaw leab指令 只加载地址不加载值

如leaq (%rdi,%rsi,4) %rax 表示讲%rdi+4*%rsi计算出的内存地址赋值给寄存器%rax,而不是获取内存地址对应的值。

其和movq (%rdi,%rsi,4) %rax 的结果明显不同,使用mov或获取到内存地址中的值赋值而寄存器%rax。

利用这个特性可以用来进行简答的加法与乘法运算,如有这样一个C函数

long scale(long x, long y, longz){

long t = x+4y+12z;

return t

}

编译器编译后将变成:

scale:

leaq (%rdi, %rsi, 4), %rax # x+4y

leaq (%rdx, %rdx, 2), %rdx # z+2z = 3z

leaq (%rax, %rdx, 4), %rax # x+4y+4*(3z)=x+4y*12z

ret

简直不要太神奇。。。

如果觉得《深入理解计算机系统读书笔记(四)》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。