如何计算诸如@DATA 或 OFFSET/ADDR VarA 之类的段的地址?
有2种情况:
a) 汇编器自己生成平面二进制文件或可执行文件,不涉及链接器
b) 汇编器正在生成一个目标文件,稍后发送到链接器
请注意,您可以混合使用。例如,在某些汇编程序(例如,NASM)中,有用于创建临时段(例如absolute
)的关键字,并且通过内部使用临时段来支持结构(结构中的字段是从地址零开始的临时段的偏移量) .
对于这两种情况;汇编器将源代码转换为某种内部表示(例如,可能是“指令数据、操作数 1 数据、操作数 2 数据……”的东西),其中“ jmp foo
”和“ mov eax,bar/5+33
”等指令的内部表示也可以简化很多并且需要在符号表中包含一些对符号的引用。
对于符号表本身,每个条目都有一个符号名称(例如“foo”)、它在哪个节中、该节中可能的最低偏移量和该节中可能的最高偏移量。当最低可能偏移量和最高可能偏移量匹配,并且该部分具有已知地址时,汇编器可以用实际值替换内部表示中对该符号的引用。
请注意,在某些情况下,您直到稍后才能知道指令的大小(例如,对于 80x86;"jmp foo
" 如果目标地址接近,则可能是 2 字节指令,但如果目标地址不接近,则可能需要是 3 字节指令或 5 字节指令,并且在您了解有关该值的某些信息之前,您无法决定“foo”将有);当您不知道指令有多大时,您将无法知道同一部分稍后出现的任何符号的偏移量。这就是为什么您最终希望符号都具有最低可能的偏移量和最大可能的偏移量 - 这样即使您不知道符号的实际偏移量,您仍然可以知道偏移量是否足够小或太大,并且仍然可以确定指令的大小(并获得更好地了解该部分中后面符号的值)。
进一步来说; 在组装时,您想要进行多次传递,其中每次传递尝试将每条指令的中间表示转换为更具体/完整的版本,并尝试改善符号的最低可能偏移量和最高可能偏移量值(以便您拥有更多/更好下一次可以使用的信息)。
当您完成“多次传递”并且汇编程序正在生成平面二进制文件并且不涉及链接器时;一切都将是已知的(包括节的地址和节内所有符号的偏移量,并将所有指令转换为实际字节),您可以生成最终文件。
当您完成“多次通过”并且汇编程序正在生成目标文件时;有些事情是未知的(段的地址),有些事情是已知的(段内所有符号的偏移量,所有指令的大小);并且目标文件格式将为您提供一种方法来提供您不知道/不知道的事物的详细信息(例如,需要修复的事物列表,以及链接器可以用来修复它们的信息),您可以从中提供指令的中间表示和符号表还剩下什么。
Note that there can be cases that are too complex for an object file format to support (e.g. probably the "mov eax,bar/5+33
" from earlier), where an instruction that can be assembled without any problem (if the assembler is generating a flat binary) has to be treated as an error (if the assembler is generating an object file). You will discover these cases (and generate appropriate error messages) when trying to create the object file.
Note that this all fits into a nice "3 phases" arrangement, where the "front-end" converts the "plain text" input into the intermediate representation, the "middle-end" (the multiple passes) refines the intermediate representation as much as possible, and the "back-end" generates a file. Only the back-end needs to care what the target file format is.