是否允许写入链接器文件中未定义的部分?

Ese*_*ose 1 c embedded

在我的链接器文件中,我有以下内存部分。

MEMORY
{
    ram (rwx) : ORIGIN = 0x20000000, LENGTH = 64k

    mram (rwx) : ORIGIN = 0xA0000010, LENGTH = 1M
}
Run Code Online (Sandbox Code Playgroud)

mram 外设的实际地址从 0xA0000000 开始。在 C 文件中,我可以写入特定的内存地址,如下所示

 (*(uint32_t *)(void *)0xA0000000) = 0xaabbccdd;
Run Code Online (Sandbox Code Playgroud)

这会导致任何问题吗?

Cli*_*ord 5

链接器脚本中指定的空间定义了链接器可以在定义的内存区域中定位的位置和内容。它不会在您留下的“洞”中定位任何东西,0xA0000000 to 0xA000000F因为它不知道这一点。

从这个意义上说,它是“安全的”,因为链接器不会尝试使用该空间。它完全由您的代码控制 - 您已通过不将该区域交给链接器来对该区域负责。确实是这样的声明:

(*(uint32_t *)(void *)0xA0000000) = 0xaabbccdd;
Run Code Online (Sandbox Code Playgroud)

将向该位置写入一个 32 位值。关键是编译器和链接器都不会阻止您在该区域执行您将要执行的操作。

不太可信的是LENGTH = 1M。这会让你的mram 0x100010字节变长(即1M + 0x10)。0x100000这是一个问题,因为链接器可以自由地将对象定位到 的区域中0x10000F。其后果取决于您的硬件,但很可能它会包裹到0x100000 to 0x10000F您试图对链接器隐藏的区域。我想你需要 LENGTH = 1M - 0x10LENGTH = 0x0FFFF0

现在,虽然您可以免除链接器管理该区域的责任,以便在代码中管理它,但这可能不是最好的方法。最好在所需的绝对地址处创建链接器符号。

所以给出:

MEMORY
{
    ram (rwx) : ORIGIN = 0x20000000, LENGTH = 64k

    mram (rwx) : ORIGIN = 0xA0000000, LENGTH = 1M
} 

Run Code Online (Sandbox Code Playgroud)

您将在 mram 中创建一个链接器符号:

SECTIONS
{
    ...

    .mram :
    {
        reserved_mram = 0 ; /* address is zero offset from .mram1 */
        . += 0x10 ;         /* create 16 byte "hole" at address reserved_mram */

        ...  /* other mram linker assignment follows */

    } < mram

    ...
}

Run Code Online (Sandbox Code Playgroud)

然后在您的代码中您应该能够声明:

extern uint32_t reserved_mram[] ;  // 4 x 32-bit word array.
Run Code Online (Sandbox Code Playgroud)

通过reserved_mram您可以象征性地访问 0xA00000000 处的内存,并且代码始终与链接器脚本同步,因此您可以轻松地重新定位空间而不会引入冲突。

当然,没有边界检查,也没有大小信息 - 您仍然需要限制reserved_mram[0]reserved_mram[3].

您也可以为每个位置创建一个单独的符号(使用特定于您的应用程序的有意义的名称):

    .mram :
    {
        reserved_mram1 = 0 ;
        . += 4 ;
        reserved_mram2 = . ;
        . += 4 ;
        reserved_mram3 = . ;
        . += 4 ;
        reserved_mram4 = . ;
        . += 4 ;

        ...  /* other mram linker usage follows */

    } < mram
Run Code Online (Sandbox Code Playgroud)

然后在你的代码中:

extern uint32_t reserved_mram1 ;
extern uint32_t reserved_mram2 ;
extern uint32_t reserved_mram3 ;
extern uint32_t reserved_mram4 ;
Run Code Online (Sandbox Code Playgroud)

另一种选择;您可以为该区域创建一个独立的部分,然后使用__attribute__((section(.xxx)))代码中的指令在其中创建变量。例如:

MEMORY
{
    ram (rwx) : ORIGIN = 0x20000000, LENGTH = 64k

    mram1 (rwx) : ORIGIN = 0xA0000000, LENGTH = 0x10
    mram2 (rwx) : ORIGIN = 0xA0000010, LENGTH = 0xFFFF0
} 

SECTIONS
{
    ...

    .mram1 :
    {
        *(.mram1) 
    } < mram1

    ...
}
Run Code Online (Sandbox Code Playgroud)

然后在你的代码中:

uint32_t my_mram_data __attribute__ ((section (".mram1"))) ;
Run Code Online (Sandbox Code Playgroud)

该变量my_mram_data将在 中的某个位置.mram1创建,但由链接器决定在何处创建。这样做的优点是,您可以在代码中创建任意变量,而无需修改链接器脚本,如果您尝试分配比.mram1可用数据更多的数据,您将收到链接器错误。

请注意,链接器脚本语法很神秘,并且在链接器之间有所不同 - 我不认为这与 GNU 链接器有关?但我的链接器 foo 是严格按需的(即我在需要时弄清楚),并且我不声明上述任何内容是完整或正确的,甚至是唯一可能的解决方案 - 将其视为说明性的,并参考链接器文档以获取准确信息。