Domanda

I'm am compiling a code to ARM, and the assembly generated is not what I expected.

The following code:

#include <stdint.h>

extern uint8_t* a;
extern uint8_t b[];

void teste(void)
{
    *a = b[1];
    b[2] = *a;
}

when compiling on ARM GCC 4.7.3 and ARM GCC 4.8.3 generate the following asm:

00000000 <teste>:
   0:   4a04            ldr     r2, [pc, #16]   ; (14 <teste+0x14>)
   2:   4b05            ldr     r3, [pc, #20]   ; (18 <teste+0x18>)
   4:   6811            ldr     r1, [r2, #0]
   6:   7858            ldrb    r0, [r3, #1]
   8:   7008            strb    r0, [r1, #0]
   a:   6812            ldr     r2, [r2, #0]
   c:   7812            ldrb    r2, [r2, #0]
   e:   709a            strb    r2, [r3, #2]
  10:   4770            bx      lr
  12:   bf00            nop
         ...

Obs: the r2 get the address of "a", and r3 address of "b".

This asm is not what I want. To get the asm that works properly i have to do

extern uint8_t a[];

and generate the following asm:

 00000000 <teste>:
    0:   4a02            ldr     r2, [pc, #8]    ; (c <teste+0xc>)
    2:   4903            ldr     r1, [pc, #12]   ; (10 <teste+0x10>)
    4:   7853            ldrb    r3, [r2, #1]
    6:   7093            strb    r3, [r2, #2]
    8:   700b            strb    r3, [r1, #0]
    a:   4770            bx      lr
         ...

Obs: the r2 get the address of "b", and r1 address of "a".

NOTE: I made a dynamic Linking to input the correct values of "a" and "b". so on the begining of the code r2 and r3 (on the first code) or r1 and r2 (on the second code) gets the right values.

To compiling I am using the following:

arm-none-eabi-gcc.exe -c code.c -o code.o -mthumb -mcpu=cortex-m4 -O2 -mlong-calls -mword-relocations -mabi=atpcs -mfloat-abi=soft -mcaller-super-interworking 
arm-none-eabi-ld.exe -o code.elf code.o --relocatable --strip-all --discard-all --embedded-relocs 

Does anyone know why the first method does not work properly?

The "a" is a address of a byte variable allocated in memory and therefore makes no sense to declare it as a vector.

Thanks for any help.

È stato utile?

Soluzione 2

Here is what a and b look like in memory:

     +------------------------------------------+
   a | uint8_t*             >----------------------+
     +------------------------------------------+  |
                                                   |  +---------+
     +---------+                                   +->| uint8_t | a[0] (or *a)
b[0] | uint8_t |                                      +---------+
     +---------+                                      | uint8_t | a[1] (or *(a+1))
b[1] | uint8_t |                                      +---------+
     +---------+                                      |  ...    |
b[2] | uint8_t |
     +---------+
     |  ...    |

Note that b[1], and b[2] (and possibly b[0]) are shown as where they would logically reside, even though no actual storage will have been allocated. Also, a was not initialized, so it may not be pointing at a valid memory location.

Once linked/loaded, the address of a and b will be known. These addresses must be loaded into registers in order to access the memory where the variables reside. In ARM, the addresses are stashed as data values, and accessed with pc-relative addressing:

         +------------------------------------------+
teste+0  |                                          |
         | I n s t r u c t i o n s                  |
         |                                          |
         +------------------------------------------+          +------------
teste+14 | uint8_t**         >-------------------------->    a | uint8_t * ...
         +------------------------------------------+          +------------
teste+18 | uint8_t*          >------------------------+
         +------------------------------------------+ |        +---------+
                                                      +-> b[0] | uint8_t |
                                                               +---------+
                                                          b[1] | uint8_t |
                                                               +---------+
                                                          b[2] | uint8_t |
                                                               +---------+
                                                               |  ...    |

By storing these constant addresses at fixed (small) offsets from the instructions, the code can use 16-bit THUMB opcodes to load them into registers. In contrast, MIPS code will typically use a 2 32-bit instruction sequence to accomplish the same thing by loading 16-bit immediates embedded in the instructions into the upper and lower halves of the target register.

Now let's step through the instructions.

    0:   4a04            ldr     r2, [pc, #16]   ; (14 <teste+0x14>)

This line is loading the address of a (which was stashed in the code after the subroutine) into r2.

    2:   4b05            ldr     r3, [pc, #20]   ; (18 <teste+0x18>)

This line is loading the address of b[0] (which was stashed in the code after the subroutine) into r3.

    4:   6811            ldr     r1, [r2, #0]

This line is loading the pointer stored in a into r1. So, r1 is now pointing at some uint8_t (the one floating to the right in the diagram).

    6:   7858            ldrb    r0, [r3, #1]

This line loads a byte from address r3+1 (aka b[1]) into r0.

    8:   7008            strb    r0, [r1, #0]

This stores the byte we just loaded into address r1+0 (aka *a).

    a:   6812            ldr     r2, [r2, #0]

This line reloads a into r2. This is unnecessary, since we already have this value in r1; however, I'm guessing you have optimization disabled.

    c:   7812            ldrb    r2, [r2, #0]

This line loads a byte from address r2+0 (aka *a) into r2.

    e:   709a            strb    r2, [r3, #2]

This line stores the byte we just loaded into address r3+2 (aka b[2]).

   10:   4770            bx      lr

And finally, we return from the subroutine.

Altri suggerimenti

Just think about the difference between an array and a pointer:

  • An array is a symbol that does not have an l-value. As such, it cannot change during runtime.
  • A pointer is a symbol that does have an l-value. As such, it can change during runtime.

If the compiler "thinks" that variable a may change during runtime, then it has to add a code for loading its value from memory before attempting to load the value from the memory address pointed by it.

If the compiler "knows" that variable a never changes during runtime, then it can add a code for loading the value directly from the (constant) memory address pointed by it.

BTW, although your code may compile and link without an error, I'm not sure it won't crash during runtime due to the ambiguous declaration of variable a, so I suggest that you simply declare uint8_t a[1].

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top