How to place a variable at a given absolute address in memory (with GCC)
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?
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.
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.
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
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.