How to place a variable at a given absolute addres

2019-01-11 17:43发布

问题:

The RealView ARM C Compiler supports placing a variable at a given memory address using the variable attribute at(address):

int var __attribute__((at(0x40001000)));
var = 4;   // changes the memory located at 0x40001000

Does GCC have a similar variable attribute?

回答1:

I don't know, but you can easily create a workaround like this:

int *var = (int*)0x40001000;
*var = 4;

It's not exactly the same thing, but in most situations a perfect substitute. It will work with any compiler, not just GCC.

If you use GCC, i assume you also use GNU ld (although it is not a certainty, of course) and ld has support for placing variables wherever you want them.

I imagine letting the linker do that job is pretty common.

Inspired by answer by @rib, I'll add that if the absolute address is for some control register, I'd add volatile to the pointer definition. If it is just RAM, it doesn't matter.



回答2:

You could use the section attributes and an ld linker script to define the desired address for that section. This is probably messier than your alternatives, but it is an option.



回答3:

You answered your question, In your link above it states:

With the GNU GCC Compiler you may use only pointer definitions to access absolute memory locations. For example:

#define IOPIN0         (*((volatile unsigned long *) 0xE0028000))
IOPIN0 = 0x4;

Btw http://gcc.gnu.org/onlinedocs/gcc-4.5.0/gcc/Variable-Attributes.html#Variable%20Attributes



回答4:

    extern const uint8_t dev_serial[12];
    asm(".equ dev_serial, 0x1FFFF7E8");
/* or    asm("dev_serial = 0x1FFFF7E8"); */
    ...

    for (i = 0 ; i < sizeof(dev_serial); i++)
        printf((char *)"%02x ", dev_serial[i]);


回答5:

Minimal runnable linker script example

The technique was mentioned at: https://stackoverflow.com/a/4081574/895245 but now I will try to provide a concrete example.

main.c

#include <stdio.h>

int __attribute__((section(".mySection"))) myvar = 0x9ABCDEF0;

int main(void) {
    printf("adr %p\n", (void*)&myvar);
    printf("val 0x%x\n", myvar);
    myvar = 0;
    printf("val 0x%x\n", myvar);
    return 0;
}

link.ld

SECTIONS
{
  .mySegment 0x12345678 : {KEEP(*(.mySection))}
}

GitHub upstream.

Compile and run:

gcc -o main.out -std=c99 -Wall -Wextra -pedantic link.ld main.c
./main.out

Output:

adr 0x12345678
val 0x9abcdef0
val 0x0

So we see that it was put at the desired address.

I cannot find where this is documented in the GCC manual, but the following syntax:

gcc link.ld main.c

seems to append the given linker script to the default one that would be used.

TODO: compilation produces a warning:

/usr/bin/x86_64-linux-gnu-ld: warning: link.ld contains output sections; did you forget -T?

Am I doing something wrong? How to get rid of it? See also: How to remove warning: link.res contains output sections; did you forget -T?

Tested on Ubuntu 16.04, GCC 6.4.0.