31
votes

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?

6
I wrote an article, where I enumerate the means to do so. Might be useful for some.a3f

6 Answers

25
votes

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.

18
votes

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.

11
votes

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

10
votes

Minimal runnable linker script example

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

main.c

#include <stdio.h>

int myvar __attribute__((section(".mySection"))) = 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 -fno-pie -no-pie -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.

-fno-pie -no-pie is required, because the Ubuntu toolchain is now configured to generate PIE executables by default, which leads the Linux kernel to place the executable on a different address every time, which messes with our experiment. See also: What is the -fPIE option for position-independent executables in gcc and ld?

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 18.10, GCC 8.2.0.

4
votes
    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]);
0
votes

In GCC you can place variable into specific section:

__attribute__((section (".foo"))) static uint8_t * _rxBuffer;

or

static uint8_t * _rxBuffer __attribute__((section (".foo")));

and then specify address of the section in GNU Linker Memory Settings:

.foo=0x800000