程序的机器级表示

CSAPP

image

机器级表示

汇编

  • 两个抽象

    • 硬件 指令集 ISA 的抽象
    • 虚拟内存 大数组
  • 汇编与高级语言的区别

    • 暴露的可操作

      • 程序计数器 rip
      • 条件寄存器
      • 整数寄存器

  • ATT 汇编与 intel 汇编区别

    • intel 省略了 指示大小的后缀
    • Intel省略了 寄存器前的 %
    • Intel 有很多不同的方法 描述内存的位置
    • 多个操作数 Intel 列出的操作数 是相反的

访问信息

  • 不同类型操作数

    • 寄存器
    • 立即数
    • 内存引用
  • 数据传送

    • mov S , D
  • 入栈出栈

    • push

      • sub %rsp; mov xxx (%rsp)
    • pop

      • mov %rsp xxx , add %rsp
    • pic

  • lea

    • load effective addr

      • 加载有效地址
    • lea s , d -> d=&s

控制

  • 条件码寄存器

    • CF 进位标志
    • ZF 零标志
    • SF 符号标志
    • OF 溢出标志
  • 访问条件码

    • 可以依据条件码的某种组合,将一个字节设置成0或1

      • SET 指令

        • setne D -> D=~ZF
    • 可以条件跳转到程序的其他部分

    • 可以有条件的传送数据
  • 跳转指令

    • 无条件跳转

      • jmp

        • 直接跳转

          • 跳转目标是作为指令的一部分编码
        • 间接跳转

          • 跳转目标是从寄存器或内存位置读出
    • 有条件跳转

      • 例子

        • jnz
        • jz
        • jne
      • 条件跳转只能 直接跳转

    • 跳转指令的编码

      • 跳转指令有几种不同的编码,最常用的是 PC相对的

        • PC-relative

          • 地址偏移量编码为 1 2 4字节

            • 指令编码很简洁
            • 目标代码可以不做改变就移到内存不同的位置
      • 绝对地址

        • 4个字节直接指定目标
  • 分支、循环、switch

过程

  • 概念

    • 用指定的一组参数和可选的返回值实现某种功能
  • 转移控制

    • 过程

      • 压栈,设置%rip ; 弹出地址,设置%rip

    • 跳转

      • 直接跳转
      • 间接跳转
  • 传递数据

    • 寄存器传参

      • rdi rsi rdx rcx r8 r9
    • 超过6个参数

      • 栈上传参
  • 分配和回收内存

    • 栈上的局部存储

      • 变量取地址
      • 结构
    • 寄存器局部存储

      • 寄存器保存惯例

        • 被调用者保存寄存器

          • rbp rbx r12~r15

            • %rbp 是栈帧指针,用于标识当前栈帧的起始位置
          • leave 指令来实现两条命令

            • movq %rbp, %rsp
            • popq %rbp
  • 实现基础

    • 运行时栈

      • 过程需要的存储空间超出寄存器大小,在栈上分配的数据称栈帧

结构与数组

  • 数组

    • movl (%rdx , %rcx , 4) , %rax
  • 结构

    • 通过首地址的相对偏移

过程的详细解释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
void proc(long a1, long*a1p , int a2 , int* a2p, short a3, short* a3p , char a4, char* a4p  )
{
*a1p += a1;
*a2p += a2;
*a2p += a3;
*a4p += a4;
}


int main()
{
long a1 = 0x12345678;
long*a1p = &a1;

int a2 = 0x66666666;
int* a2p = &a2;

short a3 = 0x3333;
short* a3p = &a3;

char a4 = 0xFE;
char* a4p = &a4;

proc( a1 ,a1p , a2, a2p, a3, a3p,a4 , a4p );

a1 = 0x11112222;
a2 = 0x66669999;

return 0;
}

栈描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
int main()
{
400565: 55 push %rbp
400566: 48 89 e5 mov %rsp,%rbp
400569: 48 83 ec 40 sub $0x40,%rsp
long a1 = 0x12345678;
40056d: 48 c7 45 d8 78 56 34 movq $0x12345678,-0x28(%rbp)
400574: 12
long*a1p = &a1;
400575: 48 8d 45 d8 lea -0x28(%rbp),%rax // 先构造了一些 局部变量
400579: 48 89 45 f8 mov %rax,-0x8(%rbp)

int a2 = 0x66666666;
40057d: c7 45 d4 66 66 66 66 movl $0x66666666,-0x2c(%rbp)
int* a2p = &a2;
400584: 48 8d 45 d4 lea -0x2c(%rbp),%rax
400588: 48 89 45 f0 mov %rax,-0x10(%rbp)

short a3 = 0x3333;
40058c: 66 c7 45 d2 33 33 movw $0x3333,-0x2e(%rbp)
short* a3p = &a3;
400592: 48 8d 45 d2 lea -0x2e(%rbp),%rax
400596: 48 89 45 e8 mov %rax,-0x18(%rbp)

char a4 = 0xFE;
40059a: c6 45 d1 fe movb $0xfe,-0x2f(%rbp)
char* a4p = &a4;
40059e: 48 8d 45 d1 lea -0x2f(%rbp),%rax
4005a2: 48 89 45 e0 mov %rax,-0x20(%rbp)

proc( a1 ,a1p , a2, a2p, a3, a3p,a4 , a4p );
4005a6: 0f b6 45 d1 movzbl -0x2f(%rbp),%eax // 通过寄存器和栈 传参
4005aa: 0f be f8 movsbl %al,%edi
4005ad: 0f b7 45 d2 movzwl -0x2e(%rbp),%eax
4005b1: 44 0f bf d0 movswl %ax,%r10d
4005b5: 8b 55 d4 mov -0x2c(%rbp),%edx
4005b8: 48 8b 45 d8 mov -0x28(%rbp),%rax
4005bc: 4c 8b 4d e8 mov -0x18(%rbp),%r9
4005c0: 48 8b 4d f0 mov -0x10(%rbp),%rcx
4005c4: 48 8b 75 f8 mov -0x8(%rbp),%rsi
4005c8: 4c 8b 45 e0 mov -0x20(%rbp),%r8
4005cc: 4c 89 44 24 08 mov %r8,0x8(%rsp)
4005d1: 89 3c 24 mov %edi,(%rsp)
4005d4: 45 89 d0 mov %r10d,%r8d
4005d7: 48 89 c7 mov %rax,%rdi
4005da: e8 11 ff ff ff callq 4004f0 <proc>

gdb 调试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
void proc(long a1, long*a1p , int a2 , int* a2p, short a3, short* a3p , char a4, char* a4p  )
{
4004f0: 55 push %rbp
4004f1: 48 89 e5 mov %rsp,%rbp
4004f4: 48 89 7d f8 mov %rdi,-0x8(%rbp)
4004f8: 48 89 75 f0 mov %rsi,-0x10(%rbp)
4004fc: 89 55 ec mov %edx,-0x14(%rbp)
4004ff: 48 89 4d e0 mov %rcx,-0x20(%rbp)
400503: 44 89 c2 mov %r8d,%edx
400506: 4c 89 4d d8 mov %r9,-0x28(%rbp)
40050a: 8b 45 10 mov 0x10(%rbp),%eax
40050d: 66 89 55 e8 mov %dx,-0x18(%rbp)
400511: 88 45 d4 mov %al,-0x2c(%rbp)
*a1p += a1;
400514: 48 8b 45 f0 mov -0x10(%rbp),%rax // 寄存器 能传递6个参数
400518: 48 8b 10 mov (%rax),%rdx
40051b: 48 8b 45 f8 mov -0x8(%rbp),%rax // 其余的参数可以相对 rbp 获取
40051f: 48 01 c2 add %rax,%rdx
400522: 48 8b 45 f0 mov -0x10(%rbp),%rax
400526: 48 89 10 mov %rdx,(%rax)
*a2p += a2;
400529: 48 8b 45 e0 mov -0x20(%rbp),%rax
40052d: 8b 10 mov (%rax),%edx
40052f: 8b 45 ec mov -0x14(%rbp),%eax
400532: 01 c2 add %eax,%edx
400534: 48 8b 45 e0 mov -0x20(%rbp),%rax
400538: 89 10 mov %edx,(%rax)

call proc后, 这个过程会push main的调用地址的下一处,在proc里面也会push rbp, 通过打印内存的值,可以看到 rsp上 存储的变量信息, 选用的数字比较有规则,比如 0x12345678 , 0x 66666666 如下图:

-->