【问题标题】:gcc changes address of mapped variable during optimizationgcc 在优化期间更改映射变量的地址
【发布时间】:2016-03-06 01:28:35
【问题描述】:

我正在开源环境中使用 arm-none-eabi-gcc v4.9.3 为 ARM stm32 微控制器编译 C 代码。 代码在没有编译器优化的情况下运行良好(gcc -g -O0 ...)。

当我启用最轻微的优化 (gcc -g -O1 ...) 时,链接器定义变量的地址会改变。

文件内存.ld

register_cortexm3_ACTLR      = 0xe000e008;
...

MEMORY
{
                 rom (rx ) : ORIGIN = 0x08000000,  LENGTH = 256K
                 ram (rxw) : ORIGIN = 0x20000000,  LENGTH = 64K
        sram_bitband (rw ) : ORIGIN = 0x22000000,  LENGTH = 32768K
          peripheral (rw ) : ORIGIN = 0x40000000,  LENGTH = 1024K
  peripheral_bitband (rw ) : ORIGIN = 0x42000000,  LENGTH = 32768K
                sram (rwx) : ORIGIN = 0x60000000,  LENGTH = 1048576K
        ram_external (rwx) : ORIGIN = 0x60000000,  LENGTH = 1048576K
                code (rwx) : ORIGIN = 0x60000000,  LENGTH = 1048576K
     device_external (rw ) : ORIGIN = 0xa0000000,  LENGTH = 1048576K
  private_peripheral (rw ) : ORIGIN = 0xe0000000,  LENGTH = 1024K
       vendor_memory (rw ) : ORIGIN = 0xe0100000,  LENGTH = 523264K
}

SECTIONS
{
 /* Section for variables mapped onto registers */
 .peripherals (OVERLAY) :
 {
    . = ALIGN(4);
    *(.peripherals)
 } >peripheral

 .private_peripherals (OVERLAY) :
 {
    . = ALIGN(4);
    *(.private_peripherals)
} >private_peripheral

...

文件 register_cortexm3.h

typedef struct {
    unsigned DISMCYCINT  : 1;
    unsigned DISDEFWBUF  : 1;
    unsigned DISFOLD     : 1;
    unsigned reserved1   : 13;
    unsigned reserved2   : 16;
} __attribute__( ( __packed__ ) ) register_cortexm3_actlr_t;

...

文件 register_cortexm3.c

void check_same( volatile void* Address1, volatile void* Address2 ) {

    if ( Address1 != Address2 )
    { Assert_Halt_EC( ec_InvalidImplementation ); }
}

volatile register_cortexm3_actlr_t register_cortexm3_ACTLR   
__attribute__( ( section( ".private_peripherals" ) ) ); 

void register_cortexm3_prepare() {

    // checking for correct linker script settings
    check_same(&( register_cortexm3_ACTLR ), ( volatile void* ) 0xe000e008);
}

启用优化后,上述比较失败,因为在 check_same() 函数内部,根据 gdb,第一个参数是 0xe0000000(其内存段的起始地址):

Breakpoint 1, Assert_Halt_EC   (ErrorCode=ErrorCode@entry=ec_InvalidImplementation) at ttc-lib/ttc_basic.c:65
65      void Assert_Halt_EC( volatile ErrorCode_e ErrorCode ) { // block endless
(gdb) up
#1  0x08004440 in check_same (Address1=Address1@entry=0xe0000000, Address2=Address2@entry=0xe000e008) at ttc-lib/ttc_basic.c:58
58          { Assert_Halt_EC( ec_InvalidImplementation ); }
(gdb) up
#2  0x080034f6 in register_cortexm3_prepare () at ttc-lib/register/register_cortexm3.c:40
40          Assert_SameAddress( &( register_cortexm3_ACTLR ), ( void* ) 0xe000e008 );
(gdb) x &( register_cortexm3_ACTLR )
0xe000e008:     0x00000000

正如您在最后一个 gdb 输出行中看到的,gdb 知道 register_cortexm3_ACTLR 的正确地址。

这是 gcc 错误还是功能? 如何解决?

【问题讨论】:

  • 在哪里定义register_cortexm3应该在0xe000e008?
  • 在 memory.ld 的第一行,就在 ...

标签: c gcc optimization linker memory-mapping


【解决方案1】:

我不确定为什么优化会对此产生任何影响,但基本问题是您给出了符号 register_cortexm3_ACTLR 的两个不同且相互冲突的定义。一个定义,在你的链接描述文件中,说它在任何部分的地址 0xe000e008,而另一个,在你的 C 代码中,说它在 .private_peripherals 部分的某个偏移处。

您需要选择其中之一。如果您想要链接描述文件中给出的地址处的变量,请使用extern 来引用它。比如:

extern volatile register_cortexm3_actlr_t register_cortexm3_ACTLR;

【讨论】:

  • 这行得通!它现在与 -O1 .. -O9 一起运行! :-) 我一直认为我必须在 RAM 之外声明一个变量,然后通过链接描述文件将其移动到特定位置。
  • @Gregor 您不能使用链接描述文件来移动单个变量,您只能移动部分。
  • 当我不启用编译优化时,我可以! ;-) 事实上,除了 c 文件中的变量声明之外,我在头文件中还有一个 extern 声明。所以问题应该是:为什么它与禁用的优化一起工作?
  • @Gregor 实际上并没有移动变量,它只是选择了要使用的符号的不同定义。您在 C 代码中声明的变量仍在 .private_peripherals 部分中的某个偏移处占用空间。另一种可能性是 C 变量恰好在没有优化的情况下最终到达正确的地址。这将要求您在 .private_peripherals 中定义其他变量,并且优化更改了编译器发出这些变量的顺序。
【解决方案2】:

把它们放在一起。这就是如何将变量映射到固定地址而不是使用指针变量。

映射变量相对于指针的优势: - 不使用 RAM(在 32 位架构中每个指针占用 4 个字节) - 没有指针取消引用寄存器访问(少一个内存访问) - 没有初始化代码(指针必须被初始化)

假设我们想要一个变量 u32_t register_Foo 映射到地址 0x12345678。 在链接描述文件中添加一行(此处为 memory.ld):

内存.ld

register_Foo = 0x12345678;

在你的头文件中添加一个寄存器结构和一个外部声明。

foo.h

typedef struct {
    unsigned DISMCYCINT  : 1;
    unsigned DISDEFWBUF  : 1;
    unsigned DISFOLD     : 1;
    unsigned reserved1   : 13;
    unsigned reserved2   : 16;
} __attribute__( ( __packed__ ) ) register_foo_t;

extern volatile register_foo_t register_Foo;

在您的源代码中访问您的寄存器。

foo.c

#include "foo.h"

void foo() {
    register_Foo.DISFOLD = 1;
    ...
}

感谢您弄清楚这一点。

【讨论】:

  • 启用优化后,编译器将为static register_foo_t volatile * const register_foo_ptr = (register_foo_t *) 0x12345678; ...; register_foo_ptr->DISFOLD = 1 生成与register_foo.DISFOLD = 1 相同的代码。没有额外的 RAM 使用或初始化,因为静态变量定义得到了优化。
  • 那么与静态指针变量相比就没有优势了吗?
  • 您可以键入. 而不是->,并且最好将地址定义在链接描述文件中的一个位置,而不是分散在各种C 头文件中。它也不依赖于优化。
猜你喜欢
  • 2020-06-13
  • 2016-01-29
  • 1970-01-01
  • 2021-11-28
  • 2020-12-06
  • 2021-09-24
  • 1970-01-01
  • 2023-03-03
  • 2018-08-26
相关资源
最近更新 更多