2

ARM Cortex-M3 的 IAR 编译器提供内联汇编。如何将特定函数的地址存储到堆栈上的某个位置?

C代码会这样

void tick();

void bar()
{
    int x;
    // modify a value on stack
    (&x)[4] = &tick;
}

虽然这通常有效,但编译器在发布版本中对其进行了优化。我试图用内联汇编对其进行编码

void bar()
{
    int x;
    asm("ldr r0,%0" : : "i" (&tick));
    asm("str r0,[sp+#0x10];
}

ldrIAR 编译器不接受该指令。问题是这条指令需要一个带有寄存器和偏移量的寻址模式。函数的实际地址tick存储在函数后面,ldr指令仅保存到保存实际地址的内存位置的偏移量。反汇编类似这样:

    ldr r0,??tick_address
    str r0,[sp+#0x10]
    bx lr ; return
??tick_address dd tick

如何tick立即获取寄存器的地址以将其用于堆栈操作?

4

2 回答 2

2

GNU GCC 内联汇编可以通过伪空语句进行简单的赋值asm(),例如:

asm("" : "=r"(foo) : "0"(tick));

这告诉编译器:

  1. 该变量foo将从内联汇编块之后的寄存器中获取
  2. 变量tick将被传入 - 在同一个寄存器中(参数零)

使用哪个寄存器的实际选择完全留给编译器。

这里的技巧是输出输入约束——我们只是将(一个且唯一的)输入别名到输出,编译器将自行选择合适的寄存器,并生成加载/存储相应变量所需的指令在“实际”内联汇编代码之前/之后。你甚至可以这样做:

asm("" : "=r"(foo1), "=r"(foo2) : "0"(tick1) , "1"(tick2));

在单个内联汇编语句中执行两个“分配”。

即使实际的内联程序集为空(如此处),编译器生成的“设置输入,检索输出”代码生成也会发生。

另一个例子:假设你想读取当前的程序计数器——PC寄存器。您可以通过两个不同的内联汇编语句在 ARM 上执行此操作:

asm("" : "=pc"(foo));
asm("mov %0, PC" : "=r"(foo));

这不是 100% 相同的;在第二种情况下,编译器知道它想foo之后看到的任何寄存器asm,它都会在那里找到它。在前者中,编译器知道要在语句foo 之后使用它,它需要从PC. 两者之间的区别在于,如果您这样做:

uintptr_t *val;
uintptr_t foo;

asm("" : "=pc"(foo));
*val = foo;

在这种情况下,编译器可能会识别出 this 可以转换为单个str [R...], PC,因为它知道foopcasm 之后。你是通过以下方式写的吗

asm("mov %0, PC" : "=r"(foo));
*val = foo;

编译器将被迫创建(假设它选择R0/ R1for foo/ val):

MOV R0, PC
STR [R1], R0

此行为的文档主要位于 GCC 手册的“扩展 ASM”部分中,请参阅人为combine指令的示例。

于 2013-03-27T17:14:24.657 回答
0

您的代码中没有分配变量x,因此它的值是未定义的,并且foo不需要设置为未定义的值来更改foo

您需要将值分配给变量,而不是您假设编译器用来实现它的某个内存位置。

于 2013-03-26T20:16:09.507 回答