ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

CSAPP(三)——程序的机器级表示

2022-05-05 14:03:40  阅读:291  来源: 互联网

标签:CSAPP 机器 代码 程序 long 64 内存 寄存器 rdi


x86-64中的一些处理器状态

  1. 程序计数器:下一条将要执行的指令在内存中的地址
  2. 整数寄存器文件:有16个命名的位置,每个都能存储64位的值,可以存储地址或整数数据,它们根据命名的不同,经常被用于处理不同的数据。
  3. 条件码寄存器:保存最近执行的算数或逻辑指令的状态信息,可以用来实现条件或分支控制结构。
  4. 一组向量寄存器:可以存放一个或多个整数或浮点数值。

一个简单的汇编例子

// filename: mstore.c
long mult2(long, long);

void multstore(long x, long y, long *dest) {
  long t = mult2(x, y);
  *dest = t;
}

这是一个简单的c语言程序,我们使用gcc -S通知gcc仅将它编译成文本形式的汇编代码(而不做后面的汇编和链接步骤),下图是一个c程序编译过程的简单示例,gcc -S编译后的.s文件处于下图中的第三个步骤。

gcc -Og -S mstore.c

下面是.s文件中的内容,以.xxx开头的行相当于是编译过程对后面汇编和链接过程的一些指导,你可以理解为它们只是注解。

如果你使用-c指令,就会生成.o文件。

gcc -Og -c mstore.c

.o文件已经是将文本形式的汇编代码转换成二进制的目标代码了,我们可以使用objdump工具来查看它。

objdump -d mstore.o

不同版本的gcc编译后的代码可能不尽相同,如果你在非x86_64指令集下编译可能得到相差更远的内容。而且目前我们也不太认识这些汇编代码,目前我们只需要知道如何生成.s.o文件以及它们在整个编译过程中扮演的角色即可。

目前,整个编译过程还差一个链接步骤来生成可执行文件,链接器需要我们的目标代码文件(.o)中包含main函数,创建main.c

// filename: main.c
#include <stdio.h>

void multstore(long, long, long *);

int
main()
{
  long d;
  multstore(2, 3, &d);
  printf("2 * 3 --> %ld\n", d);
  return 0;
}

long mult2(long a, long b) {
  long s = a * b;
  return s;
}

编译:

gcc -Og -o prog main.c mstore.c

编译后的文件疯狂变大,因为其中包含了两个文件以及依赖的库函数printf的链接,并且还有一些用于启动和中止程序的代码,以及与操作系统进行交互的代码。

使用objdump查看生成的可执行文件prog

objdump -d prog

该文件中多了好多和我们编写的代码无关的内容,并且可执行程序中,每行代码的地址已经被填入了,并且callq中的参数也有了一个要调用的程序的地址,也就是说编译后的可执行程序的内存布局已经确定。

数据格式

Intel由于历史原因,使用字(word) 代表16位数据,使用 双字(double words) 代表32位数据,64位则是 四字(quad words)

下面是一张表,代表了一些C中的声明映射到Intel上的数据类型,以及所使用的汇编代码后缀。所以,上面的指令集中总有什么callqretq这种指令,这代表你使用这些指令时使用的是四字操作指令。

访问信息

下图是16个通用寄存器以及它们的常见作用。

这些通用寄存器虽然可以保存64位数据,但是也可以使用它们的低位来单独保存32位、16位和8位的数据。%r寄存器名用来访问整个64位寄存器数据,%e寄存器名用来访问32个低位中保存的数据,%寄存器名用来访问低16位数据,还有一些可以访问寄存器中单个字节的名称。

操作数指示符

操作数经常会有一个或多个参数,这些参数可能是

  1. 立即数(Immediate):一个常数值,在ATT格式的汇编代码中,$前缀用来表示立即数,如$-577$0x1F
  2. 寄存器:表示取某个寄存器中的内容,下图中用\(r_a\)表示任意一个寄存器,\(R[r_a]\)表示该寄存器中的值。
  3. 内存引用:下面使用符号\(M_b[Addr]\)表示存储在内存中从地址\(Addr\)开始的\(b\)个字节的值,有时会省去下标\(b\)。
    下面使用的内存引用的形式\(Imm(r_b, r_i, s)\)表示一个立即数偏移量\(Imm\)加上一个基址寄存器\(r_b\),加上一个变址寄存器\(r_i\)和一个比例因子\(s\),s必须是1,2,4或8。有效地址会被计算为\(Imm+R[r_b]+R[r_i] * s\)。

说起来可能很难懂,看下面的表格就懂了。

练习题3.1

假设下面的值存放在指明的内存地址和寄存器中:

填写下表,给出操作数的值:

操作数
%rax 0x100
0x104 0xAB
$0x108 0x108
(%rax) 0xFF
4(%rax) 0xAB
9(%rax, %rdx) 0x11
260(%rcx, %rdx) 0x13(260转换成16进制是104)
0xFC(, %rcx, 4) 0xFF
(%rax, %rdx, 4) 0x11

数据传送指令

将数据从一个位置复制到另一个位置的指令。

MOV类

mov类命令具有两个操作数,第一个是源位置,第二个是目的位置,mov要把原位置的数据复制到目的位置。下面是一些mov类命令,它们的区别就是传送的数据大小不同。

  1. 源位置可以是立即值、存储在寄存器位置或内存位置,目的位置则可以是寄存器或内存位置
  2. 如果使用寄存器,命令的后缀必须和寄存器的大小相匹配
  3. 除了movl外,这些指令都只操作64位寄存器中它们需要的部分
  4. x86-64中新增了一条限制,原位置和目标位置不能都是内存地址,如果需要在内存地址间进行复制,考虑使用一个寄存器做中间变量,并使用两条指令来完成

MOVZ类

MOV类只会对寄存器中它们所需要的位进行操作,MOVZ则不同,如果你使用MOVZ向寄存器中复制数据并且这个数据没有占满64位,剩余的部分将被设置成0,这个操作叫零扩展

MOVS类

和MOVZ类似,MOVZ比较适用于操作无符号数,如果你操作基于补码的有符号数,MOVS可能更加好用。它会将源操作数中的最高位进行复制,以填满寄存器中剩余的高位(相当于保持符号)。

没有movzlq的原因是movl操作和movzlq的操作一样

练习题3.2

  1. movl —— 一次寄存器中双字到内存的复制
  2. movw —— 一次内存中到寄存器的单字复制
  3. movb —— 一次立即值到寄存器的单字节复制
  4. movb —— 一次内存值到寄存器的单字节复制
  5. movq —— 一次内存值到寄存器的四字复制
  6. movw —— 一次寄存器到内存的单字复制

练习题3.3

  1. 地址需要64位,所以不能从%ebx中取,它只有32位
  2. movl只能处理四字节,也就是两个字,%rax则是八个字节,也就是四个字
  3. 两个操作数都是内存引用
  4. 没有%sl寄存器
  5. 目的位置不能是立即值
  6. movl不能处理八字节四个字的%rdx
  7. %si长度是两个字节,与movb不符

注意,这里的所有答案都是基于x86-64的

数据传送示例

上面的代码会被编译成下面的汇编代码,其中xp被存在%rdi中,y在%rsi中。

所以指针可能会编译成保存在寄存器中的一个内存地址,函数中的局部变量一般也会存在寄存器中。*xp这种对指针进行取值操作的c代码和(%rdi)这种根据寄存器中的内存地址取内存值的操作相关。

习题3.4

src_t dest_t 指令
long long movq (%rdi), %rax
movq %rax, (%rsi)
char int movsbl (%rdi), %eax
movl %eax, (%rsi)
char unsigned movsbl (%rdi), %eax
movl %eax, (%rsi)
unsigned char long movzbl (%rdi), %eax
movq %eax, (%rsi)
int char movl (%rdi), %eax
movb %al, (%rsi)
unsigned unsigned char movl (%rdi), %eax
movb %al, (%rsi)
char short movsbw (%rdi), %ax
movw %ax, %(rsi)

可能的疑问:

  1. 当一个小的有符号数据类型转向大的时,需要使用movs保留符号位
  2. 当一个小的无符号数据类型转向大的时,需要使用movz进行零扩展
  3. 当大的数据类型转向小的时,不需要考虑什么符号位,直接截断。

习题3.5

压入和弹出栈数据

如下图,栈是这样一种数据,push操作会将数据压入到栈顶,pop操作会将栈顶的数据从栈中弹出,栈顶向下增长,所以栈顶的地址是越来越小的。

下面是pushq指令的效果,%rsp是栈指针,它记录的是当前栈顶的内存地址。4字即8字节,它会将栈指针寄存器中的值减8,表示栈向下延伸了8字节,并将数据塞入到这8字节中。popq差不多不说了。

算数和逻辑操作

未完...

标签:CSAPP,机器,代码,程序,long,64,内存,寄存器,rdi
来源: https://www.cnblogs.com/lilpig/p/16224453.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有