如何在内联汇编中使用可变偏移量?

时间:2019-07-01 16:12:03

标签: c gcc assembly x86-64 inline-assembly

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

当前,我们的char和int可以正常工作,并且进行浮点/双精度运算,直到需要将它们传递到堆栈上为止。 (通过xmm0-xmm7可以完美地为我们工作)。目的是一旦使用完xmm0-xmm7,就将这些浮点值压入堆栈。这些值将在随后的printf调用中使用。我们处理char和int的方法是仅使用push指令将它们压入堆栈,对printf的调用可以很好地使用该指令,但是由于该指令不适用于浮点值,我们必须使用以下方法手动将其“推”到堆栈上。我意识到这很可能是处理此问题的错误方法,但是我们还没有办法解决这种问题。

当前,要在堆栈上传递八个以上的浮点值,我们需要知道传递给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,尽管我现在担心一旦我们开始该操作就会中断混合更多推入堆栈的混合类型值。

任何指导将不胜感激!

1 个答案:

答案 0 :(得分:1)

使用带有恒定偏移量的指令是不可能的。为了生成代码,需要在编译时知道偏移量,并且该偏移量不是可变的。您必须使用另一条指令,使用基址寄存器和偏移量进行间接加载:

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的堆栈指针。