'How do I access local C variable in arm inline assembly?

I want to access local variable declared in C in inline arm Assembly. How do I do that?

Global variables can be accessed like this,

int temp = 0;
Function(){
    __asm(
       ".global temp\n\t"           
        "LDR R2, =temp\n\t"                                                     
        "LDR R2, [R2, #0]\n\t"
    );
}       

But how do I access local variables? I tried changing ".global" to ".local" for local variables, but it generated error (undefined reference to `temp'). The IDE I am using is KEIL.

Any thoughts? Thanks in Advance.



Solution 1:[1]

According to GCC docs: 6.45.2.3 Output Operands

You can pass the values like this:

#include <stdio.h>

int main(int argc, char *argv[]) {

  int src = 1;
  int dst;   

  asm ("mov %1, %0\n\t add $1, %0" : "=r" (dst) : "r" (src));

  printf("0x%X\n", dst);

  return 0;
}

After your asm code you put the ':' character and the values you want to pass like this: "(=|+)(r|m)" (variable). Use '=' when overriding the value and '+' when reading or overriding the value, then use the 'r' letter if the value resides in a register or 'm' if it resides in memory.

Solution 2:[2]

r minimal runnable example

main.c

#include <assert.h>
#include <inttypes.h>

int main(void) {
    uint64_t in0 = 1, in1 = 2, out;
    __asm__ (
        "add %[out], %[in0], %[in1];"
        : [out] "=r" (out)
        : [in0] "r"  (in0),
          [in1] "r"  (in1)
    );
    assert(in0 == 1);
    assert(in1 == 2);
    assert(out == 3);
}

GitHub upstream.

Compile and run:

sudo apt-get install qemu-user gcc-aarch64-linux-gnu
aarch64-linux-gnu-gcc -std=c99 -ggdb3 -march=armv8-a -pedantic -Wall -Wextra -o main.out main.c
qemu-aarch64 -L /usr/aarch64-linux-gnu -E LD_BIND_NOW=1 main.out

Disassembly:

gdb-multiarch -nh -batch -ex 'disassemble/rs main' add.out

Output excerpt:

Dump of assembler code for function main:
add.c:
6   int main(void) {
   0x0000000000000764 <+0>: fd 7b bd a9 stp x29, x30, [sp, #-48]!
   0x0000000000000768 <+4>: fd 03 00 91 mov x29, sp

7       uint64_t in0 = 1, in1 = 2, out;
   0x000000000000076c <+8>: 20 00 80 d2 mov x0, #0x1                    // #1
   0x0000000000000770 <+12>:    e0 0f 00 f9 str x0, [sp, #24]
   0x0000000000000774 <+16>:    40 00 80 d2 mov x0, #0x2                    // #2
   0x0000000000000778 <+20>:    e0 13 00 f9 str x0, [sp, #32]

8       __asm__ (
   0x000000000000077c <+24>:    e0 0f 40 f9 ldr x0, [sp, #24]
   0x0000000000000780 <+28>:    e1 13 40 f9 ldr x1, [sp, #32]
   0x0000000000000784 <+32>:    00 00 01 8b add x0, x0, x1
   0x0000000000000788 <+36>:    e0 17 00 f9 str x0, [sp, #40]

9           "add %[out], %[in0], %[in1];"
10          : [out] "=r" (out)
11          : [in0] "r"  (in0),
12            [in1] "r"  (in1)
13      );

So we see that r translated into stack sp relative str loads, which is where local variables live.

Tested in Ubuntu 18.10, GCC 8.2.0, QEMU 2.12.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Alexis Wilke
Solution 2 Ciro Santilli