Get the address in ARM Inline assembly
The IAR compiler for ARM Cortex-M3 provides inline assembly. How can one store the address of a specific function to a location on the stack?
The C code would like this
void tick();
void bar()
{
int x;
// modify a value on stack
(&x)[4] = &tick;
}
While this works in general it is optimized away by the compiler in release build. I have tried to code it with inline assembly
void bar()
{
int x;
asm("ldr r0,%0" : : "i" (&tick));
asm("str r0,[sp+#0x10];
}
The ldr
instruction is not accepted by the IAR compiler. The problem is that this instruction requires an addressing mode with a register and offset. The actual address of the function tick
is store behind the function and the ldr
instruction holds only the offset to the memory location the holds the actual address. The disassembly is similar like this:
ldr r0,??tick_address
str r0,[sp+#0x10]
bx lr ; return
??tick_address dd tick
How do I get the address of tick
immediately to a register to use it for the stack manipulation?
GNU GCC inline assembly can do mere assignments via pseudo-empty asm()
statements, like:
asm("" : "=r"(foo) : "0"(tick));
This tells the compiler:
foo
is to be taken from a register after the inline assembly block tick
is to be passed in - in the same register (argument zero) The actual choice of which register is to be used is completely left to the compiler.
The trick here are the output and input constraints - we just alias the (one and only) input to the output, and the compiler will, on its own, choose a suitable register, and generate the instructions necessary to load / store the respective variables before / after the "actual" inline assembly code. You could even do:
asm("" : "=r"(foo1), "=r"(foo2) : "0"(tick1) , "1"(tick2));
to do two "assignments" in a single inline assembly statement.
This compiler-generated "set the inputs, retrieve the outputs" code generation happens even if the actual inline assembly is empty (as here).
Another example: Say you want to read the current program counter - the PC
register. You can do that on ARM via two different inline assembly statements:
asm("" : "=pc"(foo));
asm("mov %0, PC" : "=r"(foo));
This is not 100% identical; in the second case, the compiler knows that whatever register it wants to see foo
in after the asm
, it'll find it there. In the former, the compiler knows that were it to use foo
after the statement, it needs to retrieve it from PC
. The difference between the two would be if you did:
uintptr_t *val;
uintptr_t foo;
asm("" : "=pc"(foo));
*val = foo;
In this case, the compiler can possibly identify that this can be turned into a single str [R...], PC
because it knows foo
is in pc
after the asm. Were you to write this via
asm("mov %0, PC" : "=r"(foo));
*val = foo;
the compiler would be forced to create (assuming it chooses R0
/ R1
for foo
/ val
):
MOV R0, PC
STR [R1], R0
The documentation for this behaviour is largely in the "Extended ASM" section of the GCC manuals, see the example for the contrived combine
instruction.
There is no assignment the variable x
in your code, therefore it's value is undefined and setting foo
to an undefined value isn't required to change foo
.
You need to assign the value to the variable, not to some memory location you assume the compiler use to implement it.
链接地址: http://www.djcxy.com/p/87132.html上一篇: 操作指令?
下一篇: 在ARM Inline汇编中获取地址