《程序是怎样跑起来的》读书笔记——第十章 通过汇编语言了解程序的实际构成

1 汇编语言和本地代码是一一对应的

  • 1⃣️计算机 CPU 能直接解释运行的只有本地代码(机器语言)程序。
  • 2⃣️高级语言等编写的源代码,需要通过各自的编译器编译后,转换成本地代码。
  • 3⃣️通过调查本地代码的内容,可以了解程序最终是以何种形式来运行的。
  • 4⃣️如果直接打开本地代码来看的话,只能看到数值的罗列。
  • 5⃣️因而 就产生了这样一种想法,那就是在各本地代码中,附带上表示其功能的英语单词缩写。例如,在加法运算的本地代码中加上 add(addition 的缩写)、在比较运算的本地代码中加上 cmp(compare 的缩写)等。这 些缩写称为助记符,使用助记符的编程语言称为 汇编语言。
  • 6⃣️不过,即使是用汇编语言编写的源代码,最终也必须要转换成本 地代码才能运行。
  • 7⃣️负责转换工作的程序称为 汇编器转换这一处理本身称为 汇编
  • 8⃣️本地代码也可以反过来转换成汇编语言的源代码。持有该功能的逆变换程序称为 反汇编程序逆变换这一处理本身称为 反汇编

2 通过编译器输出汇编语言的源代码

除了将本地代码进行反汇编这一方法外,通过其他方式也可以获 取汇编语言的源代码。大部分C语言编译器,都可以把利用C语言编写的源代码转换成汇编语言的源代码,而不是本地代码。


编译 生成的汇编语言源代码(一部分做了省略,彩色部分是转换成注 释的 C 语言源代码)

3 不会转换成本地代码的伪指令

汇编语言的源代码,是由转换成本地代码的指令(后面讲述的操作码)和针对汇编器的 伪指令构成的。伪指令负责把程序的构造及汇编的方法指示给汇编器(转换程序)。不过伪指令本身是无法汇编转换成本地代码的。

这里我们把代码清单 10-2 中用到的伪指令部分摘出


由伪指令 segment 和 ends 围起来的部分,是给构成程序的命令和 数据的集合体加上一个名字而得到的,称为 段定义

  • _TEXT 是指令的段定义,
  • _DATA 是被初始化(有初始值) 的数据的段定义,
  • _BSS 是尚未初始化的数据的段定义。
  • group 这一伪指令,表示的是把 _BSS_DATA 这两个段定义汇总为名为 DGROUP 的组。
  • _AddNum proc_AddNum endp 围起来的部分,以及 _MyFuncprocMyFunc endp 围起来的部分,分别表示 AddNum 函数和 MyFunc 函数的范围。
  • 伪指令 procendp 围起来的部分,表示的是过程(procedure)的范围。

4 汇编语言的语法是“操作码 + 操作数”

在汇编语言中,1 行表示对 CPU 的一个指令。汇编语言指令的语法结构是操作码 + 操作数(也存在只有操作码没有操作数的指令)。
操作码表示的是指令动作操作数表示的是指令对象。


本地代码加载到内存后才能运行。内存中存储着构成本地代码的 指令和数据。程序运行时,CPU 会从内存中把指令和数据读出,然后 再将其存储在 CPU 内部的寄存器中进行处理


寄存器是 CPU 中的存储区域。不过,寄存器并不仅仅具有存储指令和数据的功能,也有运算功能。寄存器的名称会通过汇编语言的源代码指定给操作数。内存中的存储区域是用地址编号来区分的。CPU 内的寄存器 是用 eax 及 ebx 这些名称来区分的。

5 最常用的 mov 指令

指令中最常使用的是对寄存器和内存进行数据存储的 mov指令。 mov 指令的两个操作数,分别用来指定数据的存储地和读出源。

操作数中可以指定寄存器、常数、标签(附加在地址前),以及用方括号[]围起来的这些内容。如果指定了没有用方括号围起来的内容, 就表示对该值进行处理;如果指定了用方括号围起来的内容,方括号 中的值则会被解释为内存地址,然后就会对该内存地址对应的值进行 读写操作。

mov ebp,esp中,esp寄存器中的值被直接存储在了ebp寄存器中。 esp寄存器的值是100时ebp寄存器的值也是100。而在mov eax,dword ptr [ebp+8]的情况下,ebp寄存器的值加8后得到的值会被解释为内存 地址。如果 ebp 寄存器的值是 100 的话,那么 eax 寄存器中存储的就是 100 + 8 = 108 地址的数据。dword ptr(double word pointer)表示的是从 指定内存地址读出 4 字节的数据。

6 对栈进行 push 和 pop

程序运行时,会在内存上申请分配一个称为栈的数据空间。如该名称所表示的那样,数据 在存储时是从内存的下层(大的地址编号)逐渐往上层(小的地址编号)累积,读出时则是按照从上往下的顺利进行(图 10-3)的。

是存储临时数据的区域,它的特点是通过 push 指令和 pop 指令 进行数据的存储和读出。往栈中存储数据称为“入栈”,从栈中读出数 据称为“出栈”。32 位 x86 系列的 CPU 中,进行 1 次 push 或 pop,即 可处理 32 位(4 字节)的数据。

对栈进行读写的内存地址是由 esp 寄存 器(栈指针)进行管理的。push 指令和 pop 指令运行后,esp 寄存器的 值会自动进行更新(push 指令是 -4,pop 命令是 +4),因而程序员就没 有必要指定内存地址了。

7 函数调用机制


8 函数内部的处理


“ 函数 的参数是通过栈来传递,返回值是通过寄存器来返回的”

9 始终确保全局变量用的内存空间

C 语言中, 在函数外部定义的变量称为 全局变量,在函数内部定义的变量称为 局部变量





10 临时确保局部变量用的内存空间


11 循环处理的实现方法

12 条件分支的实现方法



posted @ 2017-02-12 15:09  佳佳牛  阅读(519)  评论(0编辑  收藏  举报