首页 > 解决方案 > 如何在内联汇编中使用变量偏移量?

问题描述

我试图解决的总体问题是调用 printf,同时从原始缓冲区中获取其格式字符串和参数。到目前为止,似乎效果最好的解决方案是使用内联汇编作为将混合类型可变参数传递给函数的一种方式。

目前,我们的 chars 和 int 可以完美地工作,并且 floats/doubles 可以正常工作,直到我们需要将它们传递到堆栈上。(通过 xmm0 - xmm7 对我们来说完美无缺)。这里的目标是在 xmm0-xmm7 全部使用后将这些浮点值推送到堆栈。然后这些值将在随后的 printf 调用中使用。我们为 chars 和 int 处理这个问题的方法是通过简单地使用指令将它们推入堆栈push,对 printf 的调用可以很好地使用该指令,但是由于该指令不适用于我们拥有的浮点值使用以下方法手动将其“推送”到堆栈上。我意识到这很可能是处理此问题的错误方法,但我们一直无法找到解决方法。

目前,我们在堆栈上传递超过 8 个浮点值的解决方案要求我们知道传递给 printf 调用的参数的偏移量。在这种情况下,偏移量对应于 8 个字节的增量。第 9 个参数将被加载到(%rsp),第 10 个到0x8(%rsp)第 11 个到0x10(%rsp)第 12 个0x18(%rsp),其余参数继续这种趋势。

我对这个“可变偏移量”的目标是减少处理增加的偏移量的重复代码的数量。目前它只检查正在处理的参数,并跳转到硬编码的常量偏移量。但这导致了很多重复的代码,我希望能够清理这些代码。

下面是我们目前正在做的一个小片段,将其中一个参数移动到适当的位置,以便调用 printf 来访问参数。

double myDouble = 1.23;
asm volatile (
  "movsd %0, 0x8(%%rsp)" #The 0x8 is the offset we are hoping to pass in
:: "m" (myDouble)
);

我正在寻找一种方法来将此偏移量(0x8、0x10、0x18,...)存储在一个变量中,当我处理参数时,该变量可以递增 8,尽管我现在担心一旦我们开始混合,这会破坏更多压入堆栈的混合类型值。

任何指导将不胜感激!

标签: cgccassemblyx86-64inline-assembly

解决方案


使用具有恒定偏移量的指令是不可能的。要生成代码,需要在编译时知道偏移量,而不是可变的。您必须使用不同的指令,即带有基址寄存器和偏移量的间接加载:

int foo(int64_t offset, double value)
{
    asm volatile (
        "movsd %0, (%%rsp,%1)" :: "x" (value), "r" (offset)
        : "memory"
    );
}

您还可以让 CPU 通过使用缩放的偏移寻址模式来进行 8 的乘法运算:

int foo(int64_t offset, double value)
{
    asm volatile (
        "movsd %0, (%%rsp,%1,8)" :: "x" (value), "r" (offset)
        : "memory"
    );
}

或者,如果您想模拟push,那么sub $8, %%rsp/ movsd %0, (%%rsp),但是您不能在不破坏编译器生成的代码的情况下弄乱内联 asm 中的堆栈指针。


推荐阅读