在ARM Inline汇编中获取地址

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

ldr指令不被IAR编译器接受。 问题是这条指令需要一个带寄存器和偏移量的寻址模式。 函数tick的实际地址存储在函数后面, ldr指令只保存实际地址所在的存储单元的偏移量。 反汇编是这样的:

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

我怎样才能立即得到tick的地址给寄存器使用它来进行堆栈操作?


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%相同; 在第二种情况下,编译器知道无论希望在asm之后看到foo寄存器,它都会在那里找到它。 在前者中,编译器知道如果在语句后使用foo ,它需要从PC检索它。 两者之间的区别是,如果你这样做了:

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

    在这种情况下,编译器可能会识别出它可以变成单个str [R...], PC因为它知道foo在asm之后的pc 。 你是通过写这个吗?

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

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

    MOV R0, PC
    STR [R1], R0
    

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


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

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

    链接地址: http://www.djcxy.com/p/87131.html

    上一篇: Get the address in ARM Inline assembly

    下一篇: Stack resident buffer overflow on 64