为了生成一个目标文件,汇编器需要做两件事:将每个指令助记符转换为指令字节,例如将 add eax, ds:[eax] 转换为 0x0000。其中有一些很容易处理,因为所有信息都包含在指令中。但是,有些指令会引用指令之外的元素,例如: 跳转,它们有一个跳转目标 数据访问中指向内存位置的指针 如果跳转目标或内存位置已经出现过,则没有问题。但是,汇编不像C语言一样限制您只能访问已经出现的位置(例如C语言仅允许您使用此前在文件中定义的标识符),所以可能存在标识符尚未知晓的情况。例如: cmp eax, 0 jz skip_this add eax, edx skip_this: mov ecx, eax 在这个例子中,当遇到jz时,skip_this没有被识别,所以汇编器不知道要把它放在哪个地址。为了构建目标文件,汇编器逐行处理汇编文件。它翻译它所能理解的内容,并跟踪它仍然不知道的内容。在这个阶段结束时,所有的标识符都已经被遇到了。当汇编器完成这个阶段后,它会再次处理汇编文件并填补空缺。现在,为了回答你的问题,处理整个源文件从头到尾的每个阶段都称为一个pass。
“Pass”的意思是准确的——从源代码中“通过”。在打孔卡片时代,你需要将整个批次的打孔卡片按照汇编器/编译器所需的次数进行通行证。许多旧的汇编器是“双通行证”的,这意味着“批处理”被馈送一次以计算代码偏移量,第二次用于生成代码(当然,这些代码被打在卡片上)。当然,对于更现代化的设置,如果一个汇编器有10个通过证(除了性能差),你可能都不会注意到,因为所有工作都是通过磁盘完成的,在每个通过之间没有用户交互。还有“阶段”,编译器(比汇编器更多)可以是“两阶段”、“三阶段”或更多,这取决于从源代码到最终对象输出过程中代码经历了多少次转换。