前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >CSAPP学习笔记 - 程序的机器级表示

CSAPP学习笔记 - 程序的机器级表示

原创
作者头像
claudeliang
修改2020-02-10 15:29:33
9220
修改2020-02-10 15:29:33
举报
文章被收录于专栏:奔跑的老肥羊

程序的机器级表示

  • 所有以.开头的行都是指导汇编器和链接器工作的伪指令,通常可以忽略

数据格式

数据类型

汇编代码后缀

大小(字节)

字节

b

1

w

2

双字

l

4

四字

q

8

通用目的寄存器

名字

用途

%rax

返回值

%rbx

被调用者保存

%rcx

第4个参数

%rdx

第3个参数

%rsi

第2个参数

%rdi

第1个参数

%rbp

被调用者保存

%rsp

栈指针

%r8

第5个参数

%r9

第6个参数

%r10

调用者保存

%r11

调用者保存

%r12

被调用者保存

%r13

被调用者保存

%r14

被调用者保存

%r15

被调用者保存

寻址模式

类型

格式

操作数值

名称

立即数

$Imm

Imm

立即数寻址:直接取常数的值

寄存器

ra

Rra

寄存器寻址:取寄存器存储的内容

存储器

Imm

MImm

绝对寻址:取内存中某有效地址处的内容

存储器

(ra)

MRra

间接寻址

存储器

Imm(rb)

MImm+Rrb

(基址+偏移量)寻址

存储器

Imm(rb,ri)

MImm+Rrb+Rri

变址寻址

存储器

Imm(rb,ri,s)

MImm+Rrb+Rri*s

比例变址寻址

数据传送指令

  • 第一个是源操作数,第二个是目的操作数

指令

效果

描述

MOV S, D

S -> D

传送

MOVZ S, R

零扩展(S) -> R

以零扩展进行传送

MOVS S, R

符号扩展(S) ->R

传送符号扩展的字节(把源操作数的最高位进行复制)

cltq

符号扩展(%eax) -> %rax

把%eax符号扩展到%rax

  • 源操作数的值是一个立即数,存储在寄存器中或者内存中
  • 目的操作数指定一个位置,寄存器或者内存地址
  • x86-64限制传送指令的两个操作数不能都指向内存位置,讲一个内存位置复制到另一个内存位置需要两条指令:
  • 第一条指令将源值加载到寄存器中
  • 第二条将该寄存器值写入目的位置

压入和弹出栈数据

指令

效果

描述

push S

R%rsp <- R%rsp - 8; MR%rsp <- S

将四字压入栈

pops D

D <- MR%rsp; R%rsp <- R%rsp + 8

将四字弹出栈

算数和逻辑操作

指令

效果

描述

leaq S, D

D <- &S

加载有效地址,目的操作数为寄存器

INC D

D <- D + 1

加1

DEC D

D <- D - 1

减1

NEG D

D <- -D

取负

NOT D

D <- ~D

取补

ADD S, D

D <- D + S

SUB S, D

D <- D - S

IMUL S, D

D <- D * S

XOR S, D

D <- D ^ S

异或

OR S, D

D <- D | S

AND S, D

D <- D & S

SAL k, D

D <- D << k

左移

SHL k, D

D <- D << k

左移(等同于SAL)

SAR k, D

D <- D >> K

算数右移

SHR k, D

D <- D >> k

逻辑右移

  • 逻辑右移SHR是将各位依次右移若干位,低位移出部分舍弃,左补0
  • 算数右移SAR左侧用原符号位补齐

控制

条件码

标志

CF

进位标志:最近的操作使最高位产生了进位

ZF

零标志:最近的操作得出的结果为0

SF

符号标志:最近的操作得到的结果为负数

OF

溢出标志:最近的操作导致一个补码溢出

比较和测试指令

指令

基于

描述

CMP S1, S2

S2 - S1(这里和操作数顺序相反)

比较:行为同SUB,但是只设置条件码而不更新目的寄存器

TEST S1, S2

S1 & S2

测试:行为同AND,但是只设置条件码而不更新目的寄存器

跳转指令

指令

跳转条件

描述

jmp Label

1

直接跳转

amp *Operand

1

间接跳转

用条件控制来实现条件分支
代码语言:txt
复制
long absdiff_se(long x, long y)
x in %rdi, y in %rsi

 1 absdiff_se:
 2   cmpq    %rsi, %rdi        Compare x:y
 3   jge     .L2               If >= goto x_ge_y
 4   addq    $1, lt_cnt(%rip)  lt_cnt++            (这里的%rip是什么意思?)
 5   movq    %rsi, %rax
 6   subq    %rdi, %rax        result = y - x
 7   ret                       Return
 8 .L2:
 9   addq    $1, ge_cnt(%rip)  ge_cnt++
10   movq    %rdi, %rax
11   subq    %rsi, %rax        result = x - y
12   ret                       Return
用条件传送来实现条件分支
  • 当机器遇到条件跳转时,只有当分支条件求值完成之后,才能决定分支往那边走。处理器采用分支预测逻辑来猜测每条指令是否会执行
  • 现代处理器设计试图达到90%以上的预测成功率,但一个错误预测会招致严重的惩罚,浪费大约15~30个时钟周期,导致程序性能严重下降
代码语言:txt
复制
long absdiff(long x, long y)
x in %rdi, y in %rsi

1 absdiff:
2   movq   %rsi, %rax
3   subq   %rdi, %rax          rval = y - x
4   movq   %rdi, %rdx
5   subq   %rsi, %rdx          eval = x - y
6   cmpq   %rsi, %rdi          Compare x:y
7   cmovge %rdx, %rax          If >=, rval = eval
8   ret                        Return

指令

传送条件

描述

cmove S, R

ZF

相等/零

cmovge S, R

~(SF ^ OF)

大于或等于( 有符号>= )

循环

  • 汇编中没有循环指令的存在,用条件测试和跳转组合起来实现循环的效果

switch语句

  • 通过跳转表数据结构使实现变得更加高效,跳转表是个数组,表项i是一个代码段的地址
  • 代码段实现当开关索引值等于i时程序应该采取的动作
代码语言:txt
复制
1     .section     .rodata     只读数据代码段
2     .align 8                 Align address to multiple of 8
3 .L4:
4     .quad    .L3             Case 100: loc_A
5     .quad    .L8             Case 101: loc_def
6     .quad    .L5             Case 102: loc_B
...

过程

  • 传递控制
  • 传递数据
  • 分配和释放内存

栈和寄存器存放着传递控制和数据、分配内存所需要的信息

栈帧
  • 当过程P调用过程Q时,会把返回地址压入栈中,指明当Q返回时,要从P程序的哪个位置继续执行
  • 返回地址当作P的栈帧的一部分,因为它存放的是与P相关的状态
  • 通过寄存器,过程P可以传递最多6个整数值,但如果Q需要更多的参数,P可以在调用Q之前在自己的栈帧里存储好这些参数
转移控制

指令

描述

call Label

过程调用

call *Operand

过程调用

ret

从过程调用中返回

数据传送

1

2

3

4

5

6

%rdi

%rsi

%rdx

%rcx

%r8

%r9

  • 如果参数大于6个,那么P分配的栈帧需要能容纳7~n号参数的存储空间,切参数7位于栈顶
  • 通过栈传递参数时,所有的数据大小都向8的倍数对齐
栈上的局部存储

有些时候,局部数据必须存放在内存中:

  • 寄存器不足够存放所有的本地数据
  • 对一个局部变量使用地址运算符&,因此必须能够为它产生一个地址
  • 某些局部变量时数组或结构,因此必须能够通过数组或结构引用被访问到
代码语言:txt
复制
long call_proc()
{
  	long x1 = 1; int x2 = 2;
  	short x3 = 3; char x4 = 4;
  	proc(x1, &x1, x2, &x2, x3, &x3, x4, &x4);
  	return (x1 + x2) * (x3 * x4);
}

函数call_proc的栈帧,包含局部变量和两个要传递给函数proc的参数

代码语言:txt
复制
+------------------------------------------------+
|                     返回地址                  32|
+------------------------------------------------+
|                        x1                    24|
+------------------------------------------------+
|            x2         20|    x3   18|x4 17|  16|
+------------------------------------------------+
|                    参数8 = &x4                 8|
+------------------------------------------------+
|                                           | 4 0| <==参数7
+------------------------------------------------+
寄存器中的局部存储空间
  • 寄存器%rbx、%rbp和%r12~%r15被划分为被调用者保存寄存器
  • 其他的寄存器,除了栈指针%rsp,都分类为调用者保存寄存器,任何函数都可以修改它们

结构

  • 结构的所有组成部分都存放在内存的一段连续的区域内,而指向结构的指针就是结构第一个字节的地址
代码语言:txt
复制
struct {
  	int i;
  	int j;
  	int a[2];
  	int *p;
};

偏移

0

4

8

16

内容

i

j

a0

a1

p

数据对齐
  • 对齐限制简化了形成处理器和内存系统之间接口的硬件设计
  • 对齐原则是任何K字节的基本对象的地址必须是K的倍数
  • 对于包含结构的代码,编译器可能需要在字段的分配中插入间隙,以保证每个结构元素都满足它的对齐要求
  • 此外,编译器必须保证任何struct S1 *类型的指针p都满足4字节对齐
代码语言:txt
复制
struct S1 {
  	int i;
  	char c;
  	int j;
};

偏移

0

4

5

8

内容

i

c

j

  • 任何针对x86-64处理器的编译器和运行时系统,都必须保证分配用来保存可能会被SSE寄存器读或写的数据结构的内存,都必须满足16字节对齐,这导致:
  • 任何内存分配函数(如malloccallocrealloc等)生成的块的起始地址都必须是16的倍数
  • 大多数函数的栈帧的边界都必须是16字节的倍数

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 程序的机器级表示
    • 数据格式
      • 通用目的寄存器
        • 寻址模式
          • 数据传送指令
            • 压入和弹出栈数据
              • 算数和逻辑操作
                • 控制
                  • 比较和测试指令
                  • 跳转指令
                  • 用条件控制来实现条件分支
                  • 用条件传送来实现条件分支
                • 循环
                  • switch语句
                    • 过程
                      • 栈帧
                      • 转移控制
                      • 数据传送
                      • 栈上的局部存储
                      • 寄存器中的局部存储空间
                    • 结构
                      • 数据对齐
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档