Assembly 什么是PC相对寻址,如何在MASM中使用它?

Assembly 什么是PC相对寻址,如何在MASM中使用它?,assembly,x86,masm,68000,addressing,Assembly,X86,Masm,68000,Addressing,我正在学习Jack Crenshaw的编译器教程(如果你看我的个人资料,这就是我所有的问题都是关于lol的),它刚好达到了引入变量的程度。他评论说68k要求所有东西都是“位置独立的”,这意味着它是“PC相对的”。我知道PC是程序计数器,在x86上是EIP。但是他使用了类似于movex(PC),D0的语法,其中X是一个变量名。我在前面读了一点,后面没有提到在.data中声明变量。这是怎么回事?要在x86中实现这一点,我将用什么替换X(PC)呢 老实说,我甚至不确定它是否应该输出工作代码,但到目前为

我正在学习Jack Crenshaw的编译器教程(如果你看我的个人资料,这就是我所有的问题都是关于lol的),它刚好达到了引入变量的程度。他评论说68k要求所有东西都是“位置独立的”,这意味着它是“PC相对的”。我知道PC是程序计数器,在x86上是EIP。但是他使用了类似于
movex(PC),D0
的语法,其中X是一个变量名。我在前面读了一点,后面没有提到在.data中声明变量。这是怎么回事?要在x86中实现这一点,我将用什么替换X(PC)呢


老实说,我甚至不确定它是否应该输出工作代码,但到目前为止,它已经输出了,我已经向我的编译器添加了代码,添加了适当的头文件等,以及一个批处理文件来组装、链接和运行结果

许多处理器支持PC相对或绝对寻址

但是,在X86机器上存在以下限制:

  • 跳转和呼叫始终与PC相关(除非基于寄存器)
  • 其他地址总是绝对的(除非基于寄存器)
可以进行PC相对寻址的C编译器将通过以下方式实现:

  CALL x
x:
  ; Now address "x" is on the stack
  POP EDI
  ; Now EDI contains address of "x"
  ; Now we can do (pseudo-)PC-Relative addressing:
  MOV EAX,[EDI+1234]

如果在编译/链接期间内存中的代码地址未知(例如,对于Linux下的动态库(DLL)),因此变量的地址(此处位于地址“x+1234”)未知,则使用此选项。

这里简要概述了静态分配的全局变量(这就是这个问题的内容)真的是这样,该怎么办呢

什么是变量 对于机器来说,没有变量这样的东西。它从来没有听说过他们,从来没有关心过他们,只是对他们没有概念。它们只是一种约定,将一致的含义分配给RAM中的特定位置(在虚拟内存的情况下,是地址空间中的一个位置)

实际上,变量放在哪里取决于你自己,但这是合理的。如果要对它进行写入(很可能是这样),它最好位于一个可写位置,这意味着:该变量的地址应该位于一个已分配且可写的内存区域内。.data部分只是这方面的另一个约定。你不必这样称呼它,你甚至不需要一个单独的部分(如果你真的愿意,你可以让你的.text部分可写并在那里分配你的globals),你甚至可以使用操作系统函数,如
VirtualAllocEx
(或等效函数),在固定位置分配内存并使用它(但不要这样做)。这取决于你。但是.data部分是放置它们的方便地方

“分配”变量只是选择一个地址的问题,这样变量就不会与任何其他变量重叠。这并不难,只需按顺序排列它们:在要放置它们的地方的开始处启动一个指针
var_ptr
(因此.data部分的VA,或者如果使用链接器,则为0),然后针对每个变量
v

  • v
    的位置
    l
    align(变量ptr,四舍五入到2的功率(大小v))
  • var\u ptr
    设置为
    l+sizeof(v)
作为一个小的变化,您可以跳过对齐(大多数编译器教科书都会这样做,但在现实生活中您应该对齐)。x86通常可以让您侥幸逃脱

作为更大的变化,您可以尝试“填充路线留下的孔”。填充至少大多数孔的最简单方法是首先对变量进行排序(如果所有大小都是二的幂,则填充所有孔)。虽然这可能会节省一些空间(虽然不一定会节省空间,因为节本身是对齐的),但它不会节省很多空间。在通常的对齐规则下,“按顺序排列”算法在最坏的情况下会浪费它在孔上使用的近一半空间。导致这种情况的模式是最小类型和最大类型的交替序列。老实说,这不会真的发生——即使发生了,也没那么糟糕

然后,您必须确保.data段足够大,可以容纳所有变量,并且初始内容与初始化变量的内容匹配

但你甚至不必做这些。您可以在汇编代码中使用变量声明(您知道如何做到这一点),然后汇编器/链接器(它们通常都在其中扮演角色)将为您完成所有这一切(当然,它还将用变量地址替换变量名)

如何使用变量 视情况而定。如果您使用的是汇编器/链接器,只需参考给变量的标签即可。当然,标签不必与源代码中的名称匹配,它可以是任何合法的唯一名称(例如,您可以使用声明的AST节点ID,前面有下划线)

因此,加载变量可能如下所示:

mov eax, dword ptr [variablelabel]
或者,在x64上,可能是这样

mov eax, dword ptr [rel variablelabel]
它将发出一个rip相对地址。如果这样做,就不必关心RIP的当前值或变量的分配位置,汇编程序/链接器将负责处理它。在x64上,使用这样的RIP相对地址很常见,原因如下:

  • 它允许.data段位于不是第一个4GB(或2GB)地址空间的某个位置,只要它靠近.text段
  • 它比绝对64位地址的指令短
  • 只有两条指令甚至采用绝对64位地址,即
    mov-rax[imm64]
    mov[imm64],rax
  • 你可以免费获得重新安置
如果您没有使用汇编器和/或链接器,那么(至少在某种程度上)用分配给变量名的任何地址替换变量名(如果y