如何将变量放在内存中的给定绝对地址(使用GCC)

时间:2010-11-01 09:38:52

标签: c variables gcc arm keil

RealView ARM C编译器supports使用变量属性at(address)将变量放在给定的内存地址:

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

GCC是否有类似的变量属性?

6 个答案:

答案 0 :(得分:18)

我不知道,但你可以轻松地创建一个这样的解决方法:

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

它不是完全相同的东西,但在大多数情况下是一个完美的替代品。它适用于任何编译器,而不仅仅是GCC。

如果您使用GCC,我假设您也使用GNU ld(当然,这不是确定的)并且ld支持放置变量wherever you want them

我想让链接器完成这项工作非常普遍。

受到@rib的回答的启发,我将补充一点,如果绝对地址是针对某个控制寄存器的,我会将volatile添加到指针定义中。如果它只是RAM,那没关系。

答案 1 :(得分:14)

您可以使用section attributes和ld linker script来定义该部分的所需地址。这可能比你的选择更麻烦,但它是一种选择。

答案 2 :(得分:6)

你回答了你的问题, 在上面的链接中指出:

使用GNU GCC编译器,您可以仅使用指针定义来访问绝对内存位置。例如:

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

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

答案 3 :(得分:3)

    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]);

答案 4 :(得分:3)

最小的可运行链接描述文件示例

https://stackoverflow.com/a/4081574/895245中提到了该技​​术,但现在我将提供一个具体示例。

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

编译并运行:

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

输出:

adr 0x12345678
val 0x9abcdef0
val 0x0

所以我们看到它已经放在期望的地址上了。

我找不到GCC手册中记录的位置,但语法如下:

gcc link.ld main.c

似乎将给定的链接描述文件附加到将要使用的默认描述文件上。

-fno-pie -no-pie是必需的,因为默认情况下,Ubuntu工具链现在已配置为生成PIE可执行文件,这导致Linux内核每次都将可执行文件放在不同的地址上,这与我们的实验相混淆。另请参阅:What is the -fPIE option for position-independent executables in gcc and ld?

TODO:编译产生警告:

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

我做错什么了吗?如何摆脱它?另请参阅:How to remove warning: link.res contains output sections; did you forget -T?

在Ubuntu 18.10,GCC 8.2.0上进行了测试。

答案 5 :(得分:0)

在 GCC 中,您可以将变量放入特定部分:

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

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

然后在 GNU Linker Memory Settings 中指定该部分的地址:

.foo=0x800000