《深入理解计算机系统》读书笔记(三)程序的机器级表示

前言

继续阅读《深入理解计算机系统》这本经典书籍
本节是第三章
程序的机器级表示

1 、程序编码

对机器级编程来说,两种抽象极为重要

  • 指令集体系结构(ISA):定义处理器的状态、指令的格式,以及每条指令对状态的影响
  • 内存地址是虚拟地址

一个例子
《深入理解计算机系统》读书笔记(三)程序的机器级表示_第1张图片
其汇编和十六进制如下
《深入理解计算机系统》读书笔记(三)程序的机器级表示_第2张图片

2、数据格式

当初学C的时候,挺好奇为啥有short、long这种
这是历史留下的痕迹,随着CPU位数扩展
《深入理解计算机系统》读书笔记(三)程序的机器级表示_第3张图片

3、访问信息

操作数

  • 立即数:表示常数值
  • 寄存器:寄存器内容
  • 内存引用:根据有效地址访问某个内存位置

《深入理解计算机系统》读书笔记(三)程序的机器级表示_第4张图片

数据传送指令mov

有多种不同
《深入理解计算机系统》读书笔记(三)程序的机器级表示_第5张图片
一个例子
《深入理解计算机系统》读书笔记(三)程序的机器级表示_第6张图片

《深入理解计算机系统》读书笔记(三)程序的机器级表示_第7张图片
效果如下
《深入理解计算机系统》读书笔记(三)程序的机器级表示_第8张图片

4、算术和逻辑

《深入理解计算机系统》读书笔记(三)程序的机器级表示_第9张图片

5、控制

条件码

  • CF:进位标志
  • ZF:零标志
  • SF:符号标志
  • OF:溢出标志

具体如下

  • 如果两个数相加,在最高位还需要进位(也就是溢出了),那么 CF 标识位就会被设置
  • 如果 t 等于 0,那么 ZF 标识位会被设置
  • 如果 t 小于 0,那么 SF 标识位会被设置
  • 如果 2’s complement 溢出,那么 OF 标识位会被设置为 1(溢出的情况是 (a>0 && b > 0 && t <0) || (a<0 && b<0 && t>=0))

比较和测试指令
《深入理解计算机系统》读书笔记(三)程序的机器级表示_第10张图片
跳转指令

《深入理解计算机系统》读书笔记(三)程序的机器级表示_第11张图片

6、过程


实际上一块内存区域,这个区域的数据进出满足先进后出的原则
《深入理解计算机系统》读书笔记(三)程序的机器级表示_第12张图片
一个例子

0000000000400540 :
    # x 在 %rdi 中,y 在 %rsi 中,dest 在 %rdx 中
    400540: push    %rbx            # 通过压栈保存 %rbx
    400541: mov     %rdx, %rbx      # 保存 dest
    400544: callq   400550   # 调用 mult2(x, y)
    # t 在 %rax 中
    400549: mov     %rax, (%rbx)    # 结果保存到 dest 中
    40054c: pop     %rbx            # 通过出栈恢复原来的 %rbx
    40054d: retq                    # 返回

0000000000400550 :
    # a 在 %rdi 中,b 在 %rsi 中
    400550: mov     %rdi, %rax      # 得到 a 的值
    400553: imul    %rsi, %rax      # a * b
    # s 在 %rax 中
    400557: retq                    # 返回

《深入理解计算机系统》读书笔记(三)程序的机器级表示_第13张图片

  • 在执行到 400544 那一行的时候 %rsp 指向栈顶(存储着栈顶的地址),%rip 指向当前要执行的指令(也就是 400544)
  • 在上一步操作完成之后,因为跳转的关系,%rip 指向 mult2 函数开始的地方(也就是 400550),之前的压栈操作也使得栈顶改变(返回值的位置),于是 %rsp 对应进行改变
  • 接着执行到了 retq 那句,这个时候要做的就是从栈中取出栈顶位置(这样就可以从跳转处继续了),然后对寄存器做对应的修改
  • 最后恢复到原来的 multstore 函数中继续执行

7、数组分配

对于多维的数组,基本形式是 T A[R][C],R 是行,C 是列,如果类型 T 占 K 个字节的话,那么数组所需要的内存是 RCK 字节。具体在内存里的排列方式如下:

《深入理解计算机系统》读书笔记(三)程序的机器级表示_第14张图片

8、数据结构

对于一个结构体来说,所占据的内存空间必须是最大的类型所需字节的倍数,所以可能需要占据更多的空间

主要是数据对齐:如果数据类型需要 K 个字节,那么地址都必须是 K 的倍数

例子

struct S1
{
    char c;
    int i[2];
    double v;
} *p;

《深入理解计算机系统》读书笔记(三)程序的机器级表示_第15张图片

9、控制和数据结合

关键是注意缓冲区溢出

例子

typedef struct 
{
    int a[2];
    double d;
} struct_t;

double fun(int i)
{
    volatile struct_t s;
    s.d = 3.14;
    s.a[i] = 1073741824; // 可能会越界
    return s.d;
}

《深入理解计算机系统》读书笔记(三)程序的机器级表示_第16张图片
产生原因:访问内存的时候跨过了数组本身的界限修改了 d 的值

对抗方法

  • 好好写代码,尽量不让缓冲区异常,如用fgets, strncpy 等等
  • 程序容易出问题,那么提供系统层级的保护:栈的位置不确定,让缓冲区溢出没办法影响到,并且每次位置都不一样,就不怕被暴力破解。并且也可以把一段内存标记为只读,那么就避免因为缓冲区溢出而导致的重写
  • 编译器也可以来个认证(stack canaries):在超出缓冲区的位置加一个特殊的值,如果发现这个值变化了,那么就知道出问题了

结语

主要是通过汇编进行理解

你可能感兴趣的:(system,系统,汇编,程序)