获取ARM Inline程序集中的地址

时间:2013-03-25 10:13:53

标签: inline-assembly iar

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];
}

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

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

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

2 个答案:

答案 0 :(得分:2)

GNU GCC内联汇编只能通过伪空asm()语句进行赋值,例如:

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

这告诉编译器:

  1. 变量foo将在内联汇编块
  2. 之后从寄存器中获取
  3. 变量tick将传入 - 相同的寄存器(参数零)
  4. 使用哪个寄存器的实际选择完全留给编译器。

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

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

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

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

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

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

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

    PC

    在这种情况下,编译器可能会识别出这可以转换为单个uintptr_t *val; uintptr_t foo; asm("" : "=pc"(foo)); *val = foo; ,因为它知道{4}之后str [R...], PC位于asm之后。你是通过

    写的吗?
    foo

    编译器将被强制创建(假设它为pc / asm("mov %0, PC" : "=r"(foo)); *val = foo; 选择R0 / R1):

    foo

    此行为的文档主要是in the "Extended ASM" section of the GCC manuals,请参阅设计val指令的示例。

答案 1 :(得分:0)

您的代码中没有对变量x进行赋值,因此它的值未定义,并且不需要将foo设置为未定义的值来更改foo

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