第二章
汇编
汇编入门
汇编示例
编译运行.s文件:
as 1005.s -o 1005.o
ld 1005.o -o 1005
./1005
编译调试.s文件:1005.o不可执行,但是链接后生成的1005可以执行
as -g 1005.s -o 1005.o
ld 1005.o -o 1005
gdb -q 1005
反汇编可执行文件:
objdump -d 1011
数据传输
movl movw movb 分别传送32位 16位 8位数据:
movl $4,%eax
movw $1,%ax
movb $0x65,%dh
如果只写mov,机器会根据寄存器名字补全l/w/b,若往%bx(16位寄存器 )传输了32位数据,机器会报错。
- 引入变量名:
movl %ecx,value1
e.g. value1为int型的整数1,此处将%ecx的内容传给value1所在的内存地址,即更新了value1 的值。
movl value1,%ecx
e.g. value1为int型的整数1,此处将从value1的地址开始从内存取4个字节的内容放到%ecx里面,即将value1的值赋值给%ecx。
movl $value1,%ecx
e.g. value1为int型的整数1,此处将value1的地址放到%ecx里面。 - 引入leal指令,专门用来传输地址
movl $value1,%ecx
==leal value1,%ecx
leal 5(%edx,%edx,2),%eax
e.g. %edx=x,计算 - 没有内存到内存的数据传输,原因有二:1. 既然不需要经过CPU,那么可以由内存自己进行 2. 对于如下的指令,89代表%ecx,0d代表mov指令,a4 90 04 08 代表地址,所以如果前后都是内存地址,指令长度不够
寻址方式
- 查看内存内容:
x/4bt 0x11223344
或者x/4bt $eax
注意是$符号
- 查看变量的值:
print value
查看变量在内存中的地址 :print &value
-
立即数寻址
movl $1,%eax
-
寄存器寻址
movl %ebx,%eax
-
绝对寻址
movl 0x08048054,%eax
这里的内存地址只是一个起始地址,具体传几位数据,由movl和%eax决定 -
间接寻址
movl (%ebx),%eax
从寄存器获取内存地址,作为绝对寻址的目标地址 -
基址偏移量寻址
movl 0x8(%ebx),%eax
先从寄存器获取内存地址,加上偏移量,作为绝对寻址的目标地址 -
变址寻址
movl (%ebx,%edx),%eax
访问数组 或者 循环结果中常用,e.g %ebx存储为2,%edx存储为0x11223344,则绝对寻址的目标地址为0x11223346 -
比例变址寻址
movl (%ebx,%ecx,0x2),%eax
e.g %ebx存储为0x08048056,%ecx存储为2,则绝对寻址的目标地址为0x0804505a
汇编进阶
栈操作指令
- 栈顶指针朝低地址方向增长
- gdb查看栈顶指针地址:
print $esp
- e.g. %esp首先在0x0bffff29c,
push %ebx
(%ebx = 0x12345678),则push后,%esp到了0x0bffff298(上移4字节),0x0x0bffff298-0x0x0bffff29b分别存放0x78,0x56,0x34,0x12(低地址放低位数据) - e.g.
push value
VSpush $value
前者从变量value存放的内存地址取值push入栈,后者把value的内存地址push入栈
算数逻辑操作指令
跳转指令
- %eip寄存器:存放下一条要执行的指令的地址,%eip不允许被代码直接修改,编译会报错
- 直接跳转指令:jump (无条件跳转)
- %eflags寄存器:
- 条件跳转指令:
汇编执行的思路:cmp x,y
根据y-x的结果,设置CF(进位标志),OF(溢出标志),SF(计算结果的最高位),ZF的各个位(存储在%eflags),再根据需求判断用到的位 ja
无符号数jg
有符号数
循环指令
可以用跳转指令实现,也可以用loop指令实现
期中考试
通用32位CPU 常用寄存器及其作用
32位CPU所含有的寄存器有:
4个数据寄存器(EAX 累加器、EBX 基址寄存器、ECX 计数寄存器和EDX 数据寄存器)
2个变址和指针寄存器(ESI和EDI)
2个指针寄存器(ESP和EBP)
6个段寄存器(ES、CS、SS、DS、FS和GS)
1个指令指针寄存器(EIP)
1个标志寄存器(EFlags)
杂乱知识点集锦
- leave指令,还原程序现场,还原%ebp,%esp
- ret指令从栈中弹出地址,并返回到call指令后的那条指令
- movzbl 指令,传输数据并用0扩展;movsbl 指令,传输数据并用1扩展
- 汇编过程:从源文件到可执行文件的执行过程是:预编译->编译->汇编->链接
- 间接寻址和寄存器寻址 注意区分
- 补码的非运算:
- CPU要访问的某一存储单元的实际地址称(物理地址),程序存储器是通过(虚拟地址 )来寻址,汇编器将指令存放在代码段中的地址称为(指令地址)
- 指令计数器 PC属于控制器
- 堆栈指针 %esp 的内容是栈顶单元地址
- 跳转指令 :above 无符号
- CF ZF SF OF 进位 零 符号位 溢出
对于Z = X + Y , (unsigned)z < (unsigned)x 表示无符号溢出,判断CF;(x < 0 == y < 0) && (z < 0 != x < 0) 表示有符号溢出,判断OF - 求指定位数的补码:先写绝对值,若是负数,则按位取反加一
- 浮点数
对阶码:小阶码调整到与大阶码相同的
阶码E用移码表示,尾数用原码表示
int向float转换:int型的有效位数是31,而float型小数域的有效位只有23位,也就是说int的二进制的有效位超过了24位,那么float型的小数域的精度就不够了。因此必须进行舍入。 - 函数栈调用
在过程P调用过程Q时,先将参数按反序压栈(即从最后一个参数开始压栈),然后返回地址压栈,最后再将过程P的%ebp值 压栈 - 汇编指令LOOPE/LOOPZ是指CX不为零且标志ZF=1时循环
- 若int len = strlen(s),如果s=“hell”,则len=( ),字符串后有结束位’\0’,所以长度为5。但我实际写代码的时候并没有多出一位长度。
- 寄存器%eax, %ecx, %edx被划分为由调用者保存的寄存器. 寄存器%ebx, %esi, %edi被划分为由被调用者保存的寄存器。
- 函数指针int (*f)(int a,int b) f是函数名称,a,b是参数
- C语言中有符号数和无符号数进行运算(包括逻辑运算和算术运算)默认会将有符号数看成无符号数进行运算,其中算术运算默认返回无符号数,逻辑运算当然是返回0或1了。
- 有符号正数的原码、反码、补码都一样
- 并发运行是指一个进程的指令和另一个进程的指令是交错执行的。
- 汇编器(as)将hello.s翻译成机器语言指令,把这些指令打包成一种叫做可重定位目标程序的格式,并将结果保存在目标文件hello.o中
- 程序寄存器组是唯一能被所有过程共享的资源
- 文件是对I/O设备的抽象,虚拟存储器是对主存和磁盘的抽象,进程是对处理器、主存和I/O设备的抽象