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

57hvy0tb  于 2023-01-12  发布在  其他
关注(0)|答案(7)|浏览(125)

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

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

GCC有类似的变量属性吗?

klh5stk1

klh5stk11#

我不知道,但你可以很容易地创建一个这样的解决方案:

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

这不是完全一样的东西,但在大多数情况下是一个完美的替代品。它可以与任何编译器一起工作,不仅仅是GCC。
如果您使用GCC,我假设您也使用GNU ld(当然,这并不一定),并且ld支持将变量放置在您想要的任何位置。
我想让链接器做这项工作是很常见的。
受@rib回答的启发,我将补充一点,如果绝对地址是某个控制寄存器的,我将在指针定义中添加volatile,如果只是RAM,则无所谓。

1bqhqjot

1bqhqjot2#

您可以使用section attributes和ld链接器脚本来定义该节所需的地址,这可能比您的替代方案要混乱,但这是一个选项。

g6baxovj

g6baxovj3#

    • 最小可运行链接器脚本示例**

该技术在以下网址中提及:https://stackoverflow.com/a/4081574/895245,但现在我将提供一个具体的示例。
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.
编译并运行:

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内核每次都将可执行文件放在不同的地址,这会扰乱我们的实验。gcc和ld中位置无关可执行文件的-fPIE选项是什么?
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上测试。

7ajki6be

7ajki6be4#

你回答了你的问题,在你上面的链接它指出:
使用GNU GCC编译器,你可以只使用指针定义来访问绝对内存位置。

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

顺便说一http://gcc.gnu.org/onlinedocs/gcc-4.5.0/gcc/Variable-Attributes.html#Variable%20Attributes

ajsxfq5m

ajsxfq5m5#

下面是一个解决方案,它实际上在内存中的固定地址保留了空间,而不必编辑链接器文件:

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

lyr7nygr6#

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

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

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

然后在GNU链接器内存设置中指定该部分的地址:

.foo=0x800000
mccptt67

mccptt677#

我也遇到过类似的问题。我想在我定义的节中分配一个特殊偏移量的变量。同时我想代码是可移植的(在我的C代码中没有显式的内存地址)。所以我在链接器脚本中定义了RAM节,并定义了一个与我的节长度相同的数组(.noinit节的长度为0x0F)。
uint8_t no_init_sec[0x0f] __attribute__ ((section (".noinit")));
此数组Map此节的所有位置。此解决方案不适用于节很大的情况,因为已分配数组中未使用的位置将浪费数据内存中的空间。

相关问题