You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用-Os编译时无法写入LD链接脚本定义的变量问题排查

裸机程序LD链接脚本变量写入的优化问题

编写裸机程序时,尝试向自定义LD链接脚本中定义的符号地址写入数据,使用-O0编译时运行正常,但-Os编译下写入语句被优化,结果不符合预期。


问题代码与配置

main.c(原写法)

#define TTB_BASE (&Image$$TTB)
extern unsigned int Image$$TTB;

int main ()
{
    *TTB_BASE = 56326;

    unsigned int *ttb=TTB_BASE+16;

    for (int i = 0; i < 8; i++ )
    {
        *ttb++ = 1;
    }
    *(volatile unsigned int*)(0x00100000) = *TTB_BASE;
}

链接脚本ARMCA7.ld

ENTRY(main)
SECTIONS
{
    . = 0x00160000;
    .text : { *(.text*) }
    .data : { *(.data .data.* .gnu.linkonce.d*) }
    .bss (NOLOAD) : {
        . = ALIGN(16);
        *(.bss .bss.*)
        *(COMMON)
    }
    . = 0x00170000;
    .ttb :
    {
        Image$$TTB = .;
    }
}

编译命令

arm-none-eabi-gcc -mcpu=cortex-a7 -Os -std=gnu99 -c -o main.o main.c
arm-none-eabi-gcc -mcpu=cortex-a7 -Os -T ARMCA7.ld -nostartfiles -Xlinker --gc-sections -Wl,-Map,"main.map" -o main.elf main.o

反汇编结果

Address : Opcode         Statement
-------   ------         ---------
 8                        unsigned int *ttb=TTB_BASE+16;
                      main:
00160000: 24 30 9f e5   ldr     r3, [pc, #36]   ; 0x16002c <main+44>
12                            *ttb++ = 1;
00160004: 01 10 a0 e3   mov     r1, #1
00160008: 20 20 83 e2   add     r2, r3, #32
0016000c: 04 10 83 e4   str     r1, [r3], #4
10                        for (int i = 0; i < 8; i++ )
00160010: 02 00 53 e1   cmp     r3, r2
00160014: fc ff ff 1a   bne     0x16000c <main+12>
14                        *(volatile unsigned int*)(0x00100000) = *TTB_BASE;
00160018: 60 20 13 e5   ldr     r2, [r3, #-96]  ; 0xffffffa0
0016001c: 01 36 a0 e3   mov     r3, #1048576    ; 0x100000
15                    }
00160020: 00 00 a0 e3   mov     r0, #0
14                        *(volatile unsigned int*)(0x00100000) = *TTB_BASE;
00160024: 00 20 83 e5   str     r2, [r3]
15                    }
00160028: 1e ff 2f e1   bx      lr
0016002c: 40 00 17 00   andseq  r0, r7, r0, asr #32

从反汇编可见,没有任何操作写入链接脚本定义的0x00170000地址,最终写入0x00100000的值也不正确。


问题原因分析

链接脚本中Image$$TTB = .;定义的是一个符号地址标记,而非实际的变量存储空间。原代码中extern unsigned int Image$$TTB;告诉编译器这是一个unsigned int类型的变量,但实际上该符号只是一个地址值,没有对应的内存区域。

  • -O0编译时,编译器不做优化,会严格执行所有内存读写操作;
  • -Os(空间优化)时,编译器会分析代码逻辑:认为对*TTB_BASE的写入是无意义操作(因为没有合法的后续读取依赖),直接优化掉写入指令,导致目标地址未被赋值。

而修改后的写法:

#define TTB_BASE (Image$$TTB)
extern unsigned int Image$$TTB[];

将Image$$TTB声明为数组,编译器会将其视为指向内存区域的指针符号,而非具体变量值,因此不会优化掉对该地址的读写操作,能正确访问目标地址。


解决方案

以下三种方式均可解决优化问题:

1. 数组声明符号

extern unsigned int Image$$TTB[];
#define TTB_BASE (Image$$TTB)

明确告知编译器Image$$TTB是内存区域的起始地址,编译器会正确生成读写指令。

2. 用volatile强制保留操作

extern volatile unsigned int Image$$TTB;
#define TTB_BASE (&Image$$TTB)

volatile关键字会强制编译器保留对该地址的读写操作,避免被优化。

3. 链接脚本分配实际存储空间

在.ttb段中分配真实内存,让符号指向有效区域:

.ttb :
{
    Image$$TTB = .;
    . += 0x400; /* 按需分配空间,示例为1KB */
}

此时编译器会认为该地址对应有效内存,不会优化读写操作。


测试环境

使用的GCC版本:

arm-none-eabi-gcc --version
arm-none-eabi-gcc.exe (Arm GNU Toolchain 11.3.Rel1) 11.3.1 20220712
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

同时在10.3.1版本中也复现了相同问题。

内容的提问来源于stack exchange,提问作者Feihu Liu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 22:15:35