【问题标题】:How can an assembly instruction of 4 byte encode a 4 byte memory address?4字节的汇编指令如何编码4字节的内存地址?
【发布时间】:2015-10-04 22:00:12
【问题描述】:

我没有特定的机器设置,但一般来说,我用 C++ 编写了以下内容:

int* x;
x = 0xFFFFFFFF;
int* y; 
y = 0x00000000;

*y = *x

我可以想办法在汇编中做到这一点:

// find &x (on the stack frame) using the frame register + an offset
// load value of x into register 0
load *(register 0) -> register 1   // load value of *x
load y -> register 2
store register 1 register 2

然而,上述指令都没有编码内存地址(对于 4GB 内存是 32 位),只有寄存器的地址被编码(如果我们有 32 个寄存器,则指令中需要 5 位)

TL;DR 我的问题是像“分支”(函数调用)这样的指令,它需要一个函数地址(或链接器给出的符号),它实际上是一个 32 位内存地址......它怎么可能(假设每个汇编指令也是4 个字节大)?如果“分支”也使用临时寄存器来存储地址(如我给出的示例),这是否意味着“分支”不是原子操作?

【问题讨论】:

    标签: assembly linker makefile


    【解决方案1】:

    我的问题是关于“分支”(函数调用)之类的指令,它 接受一个函数地址(或链接器给出的符号),它位于 实际上是一个 32 位的内存地址......它怎么可能(假设每个程序集 指令也是4个字节大)?如果“分支”也使用临时 寄存器来存储地址(就像我给出的例子),这样做 意思是“分支”不是原子操作?

    可以通过多种方式完成:

    • 通过使用 PC 相对寻址:目标地址不编码为绝对地址,而是相对于 PC 的当前值的带符号偏移。这利用了相关函数彼此接近的优势,因此它们之间的距离可以编码为 16 位有符号整数。

    • 通过使用 32 位寄存器作为操作数,而不是使用中间操作数。因此,一个 32 位寄存器预先加载了 CALL 或 JUMP 指令的目标地址,然后,CALL/JUMP 被执行到该寄存器指向的地址。这将 32 位指令中具有 32 位值的问题转移到 LOAD/STORE 指令,它们通常分两个阶段执行此操作,使用 LOADHI 和 LOADLOW 指令来加载 32 位寄存器的一半,或者只有一个LOADLOW 指令和使用 SHIFTLEFT 指令。

    【讨论】:

    • 对于第一种解决方案,假设链接器将函数 ADDress 存储在符号表中,那么函数分支应该有一个偏移量:(函数 ADDress - 函数调用的 PC),但是存储的 PC任何地方?我在 make 生成的汇编文件中没有看到它
    • PC 是指令本身的地址。
    • nvm,我想你可以在链接期间计算PC(行号),谢谢
    • 第一个选项不需要计算PC的实际值。只是从 CALL/JUMP 指令到例程开始的字节数,并将该值作为中间操作数。
    【解决方案2】:

    在许多架构上,控制传输使用范围小于 32 位的相对编码。在其他架构上,指令不固定为 32 位。您还可以通过寄存器或内存获得间接跳转。加载寄存器和跳转可能不是原子操作,但您通常不会在意,尤其是在您编写 C 代码时。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-01-10
      • 1970-01-01
      • 2016-09-07
      • 2015-10-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多