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
的地址输入寄存器以将其用于堆栈操作?
答案 0 :(得分:2)
GNU GCC内联汇编只能通过伪空asm()
语句进行赋值,例如:
asm("" : "=r"(foo) : "0"(tick));
这告诉编译器:
foo
将在内联汇编块tick
将传入 - 相同的寄存器(参数零)使用哪个寄存器的实际选择完全留给编译器。
这里的技巧是输出和输入约束 - 我们只是别名输出的(唯一的)输入,以及编译器将自己选择一个合适的寄存器,并生成在“实际”内联汇编代码之前/之后加载/存储相应变量所需的指令。你甚至可以这样做:
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
。
您需要将值赋给变量,而不是您假定编译器用来实现它的某个内存位置。