欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 健康 > 养生 > ARM学习(39)ARM-GCC编译出的Bin文件过大解决方案

ARM学习(39)ARM-GCC编译出的Bin文件过大解决方案

2025/5/24 7:49:44 来源:https://blog.csdn.net/qq_34430371/article/details/144752447  浏览:    关键词:ARM学习(39)ARM-GCC编译出的Bin文件过大解决方案

笔者之前一直有遇到ARM-GCC生成的bin文件过大的情况,就是要烧录的bin文件高达上百M,比axf文件还大。

1、问题背景

如下图所示,笔者要烧录的bin文件是gd32vf103.bin,有384MB,elf文件才60KB,明显有问题,无法烧录。
在这里插入图片描述
生成的elf文件地址也没问题,因为加载到Trace32上面,地址也显示正常,是通过对比链接脚本观察得出来的结论。

  • 启动代码地址:0x08000164
  • main函数地址:0x20000010
    Trace32 一般不做分散加载,加载到哪的地址就算哪,分散加载一般是启动代码做的,属于运行态。具体分散加载可以参考这篇文章ARM学习(4) 分散加载启动。
    在这里插入图片描述
    在这里插入图片描述
    链接脚本的代码如下:
OUTPUT_ARCH( "riscv" )ENTRY( _start )MEMORY
{ /* Run in FLASH */ flash (rxai!w) : ORIGIN = 0x08000000, LENGTH = 128kram   (wxa!ri) : ORIGIN = 0x20000000, LENGTH = 32K   
}SECTIONS
{__stack_size = DEFINED(__stack_size) ? __stack_size : 2K;.init           :{KEEP (*(SORT_NONE(.init)))}>flash AT>flash .boot :{KEEP (*(SORT_NONE(.boot)))} >flash AT>flash .entry :{KEEP (*(SORT_NONE(.entry)))FILL(0xAA)} >flash AT>flash /*  .sysinit :{FILL(0xAA)obj/system_gd32vf103.oFILL(0xAA)}>flash AT>flash   */.ilalign         :{FILL(0xAA). = ALIGN(4);PROVIDE( _ilm_lma = . );}>ram AT>ram .ialign         :{FILL(0xAA)PROVIDE( _ilm = . );}>ram AT>ram .text           :{FILL(0xAA)*(.rodata .rodata.*)  *(.text.unlikely .text.unlikely.*)*(.text.startup .text.startup.*)*(.text .text.*)*(.gnu.linkonce.t.*)}>ram AT>ram .fini           :{KEEP (*(SORT_NONE(.fini)))}>ram AT>ram . = ALIGN(4);PROVIDE (__etext = .);PROVIDE (_etext = .);/*0x80022c8*/PROVIDE (etext = .);/*0x80022c8*/PROVIDE( _eilm = . );.preinit_array  :{PROVIDE_HIDDEN (__preinit_array_start = .);KEEP (*(.preinit_array))PROVIDE_HIDDEN (__preinit_array_end = .);}>ram AT>ram .init_array     :{PROVIDE_HIDDEN (__init_array_start = .);KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))PROVIDE_HIDDEN (__init_array_end = .);} >ram AT>ram .fini_array     :{PROVIDE_HIDDEN (__fini_array_start = .);KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))PROVIDE_HIDDEN (__fini_array_end = .);}>ram AT>ram .ctors          :{/* gcc uses crtbegin.o to find the start ofthe constructors, so we make sure it isfirst.  Because this is a wildcard, itdoesn't matter if the user does notactually link against crtbegin.o; thelinker won't look for a file to match awildcard.  The wildcard also means that itdoesn't matter which directory crtbegin.ois in.  */KEEP (*crtbegin.o(.ctors))KEEP (*crtbegin?.o(.ctors))/* We don't want to include the .ctor section fromthe crtend.o file until after the sorted ctors.The .ctor section from the crtend file contains theend of ctors marker and it must be last */KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))KEEP (*(SORT(.ctors.*)))KEEP (*(.ctors))}>ram AT>ram .dtors          :{KEEP (*crtbegin.o(.dtors))KEEP (*crtbegin?.o(.dtors))KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))KEEP (*(SORT(.dtors.*)))KEEP (*(.dtors))}>ram AT>ram . = ALIGN(4);PROVIDE( _eilm = . );.lalign         :{. = ALIGN(4);PROVIDE( _data_lma = . );}>ram AT>ram .dalign         :{. = ALIGN(4);PROVIDE( _data = . );}>ram AT>ram .data          :{*(.rdata) *(.gnu.linkonce.r.*)*(.data .data.*)*(.gnu.linkonce.d.*). = ALIGN(8);PROVIDE( __global_pointer$ = . + 0x800); *(.sdata .sdata.*)*(.gnu.linkonce.s.*). = ALIGN(8);*(.srodata.cst16)*(.srodata.cst8)*(.srodata.cst4)*(.srodata.cst2)*(.srodata .srodata.*)}>ram AT>ram . = ALIGN(4);PROVIDE( _edata = . );PROVIDE( edata = . );PROVIDE( _fbss = . ); /*0X200052A0  0X200002A0*/PROVIDE( __bss_start = . );.bss            :{*(.sbss*)*(.gnu.linkonce.sb.*)*(.bss .bss.*)*(.gnu.linkonce.b.*)*(COMMON). = ALIGN(4);} >ram AT>ram . = ALIGN(8);PROVIDE( _end = . ); /*0X2000,0340*/PROVIDE( end = . );.stack ORIGIN(ram) + LENGTH(ram) - __stack_size :{PROVIDE( _heap_end = . ); . = __stack_size;  PROVIDE( _sp = . ); } >ram AT>ram 
}

flash的起始地址:0x08000000
RAM的起始地址:0x20000000
关键的地方如下面所示,

  • entry在flash位置,而test 代码段放到了ram的位置,且加载地址也是ram地址,
  • 这样一来就导致代码加载的地址也放到ram的位置,和前面启动代码的加载地址有gap,从0x0800000到0x20000000,
  • 前后的代码均需要加载,在生成一个bin的情况,又需要把其加载到争取的偏移位置,看起来就只能通过补数据(填充0x0或者其他数据)到0x20000000,
  • 最后就导致生成bin文件暴大,bin文件的size还取决于gap的大小
  • 0x20000000-0x08000000=0x18000000,大约就是384MB,和笔者碰到一开始的文件大小就匹配上了。
  .entry :{KEEP (*(SORT_NONE(.entry)))FILL(0xAA)} >flash AT>flash .text           :{FILL(0xAA)*(.rodata .rodata.*)  *(.text.unlikely .text.unlikely.*)*(.text.startup .text.startup.*)*(.text .text.*)*(.gnu.linkonce.t.*)}>ram AT>ram 

看起来关键问题是链接脚本的问题,那么如何修改呢?

2、问题解决方案

2.1 链接脚本修改

上面介绍到因为链接脚本中因为加载地址的问题,导致需要连续的bin文件,所以会生成大文件bin,那么我们修改加载地址,也放在flash,可以保证加载地址连续。类似于下面这样。

执行地址还是在ram,然后AT,加载地址在flash。

  .text           :{FILL(0xAA)*(.rodata .rodata.*)  *(.text.unlikely .text.unlikely.*)*(.text.startup .text.startup.*)*(.text .text.*)*(.gnu.linkonce.t.*)}>ram AT>flash .data          :{*(.rdata) *(.gnu.linkonce.r.*)*(.data .data.*)*(.gnu.linkonce.d.*). = ALIGN(8);PROVIDE( __global_pointer$ = . + 0x800); *(.sdata .sdata.*)*(.gnu.linkonce.s.*). = ALIGN(8);*(.srodata.cst16)*(.srodata.cst8)*(.srodata.cst4)*(.srodata.cst2)*(.srodata .srodata.*)}>ram AT>flash 

经过修改之后,编译后的bin大小正常,烧录进行也可以使用,

在这里插入图片描述
在这里插入图片描述
注意的点:要自己写copy函数,因为代码段是放在加载地址的,要在执行地址跑起来,就必须要搬代码到对应地址。

根据代码段的起始位置,可以编写copy函数,copy到对应位置。

__attribute__((section(".boot")))extern void* _ilm_lma; 
extern void* _ilm; 
extern void* _eilm;
void copy(void)
{unsigned char *pa,*pb;unsigned int i=0;pa=(unsigned char *)(&_ilm_lma);pb=(unsigned char *)(&_ilm);int text_size = (int)((char*)&_eilm-(char*)&_ilm);for(i=0;i<(text_size);i++){*pb = *pa;pb++;pa++;}
}

在这里插入图片描述

通过看map文件,可以确认相关地址的正确性
在这里插入图片描述
在这里插入图片描述

至于数据段的搬移,本身启动代码里面就有相关的搬移代码。

	/* Load data section */la a0, _data_lmala a1, _datala a2, _edatabgeu a1, a2, 2f
1:lw t0, (a0)sw t0, (a1)addi a0, a0, 4addi a1, a1, 4bltu a1, a2, 1b
2:/* Clear bss section */la a0, __bss_startla a1, _endbgeu a0, a1, 2f
1:sw zero, (a0)addi a0, a0, 4bltu a0, a1, 1b

由于代码还处于加载地方,所以会看到main函数出都是空的,没有任何指令。
在这里插入图片描述
注意的是如果开的-O2优化,则copy函数里面的逻辑会被优化成memcopy,导致无法完成拷贝,因为memcpy的代码还没有在代码段,所以不能进行优化,当然可以改成-O0,笔者尝试了可以拷贝过去。
在这里插入图片描述
笔者优化了一下copy函数,然后就正常了,将 拷贝的指针定义成volatile,则这里的拷贝编译器就不优化了。

extern void* _ilm_lma; 
extern void* _ilm; 
extern void* _eilm;
void copy(void)
{volatile  char *pa,*pb;unsigned int i=0;pa=(unsigned char *)(&_ilm_lma);pb=(unsigned char *)(&_ilm);int text_size = (int)((char*)&_eilm-(char*)&_ilm);for(i=0;i<(text_size);i++){*pb = *(volatile char*)pa;pb++;pa++;}
}

可以看到下图中,右侧的main函数只有部分指令,当copy完之后,则所有的数据都有了。

在这里插入图片描述
在这里插入图片描述
当然为什么启动代码没有代码段的copy,因为代码本身就可以放到flash中,加载地址就是执行地址,这样就不用拷贝代码段。
在这里插入图片描述
在这里插入图片描述

2.2 不修改链接脚本

如果不修改链接脚本,则需要自己处理暴大的bin文件,将中间的无效数据处理掉。
在这里插入图片描述
通用需要分散加载,把text放到对应的ram地址,但是这次链接脚本里面没有响应的符号,表明加载地址等,需要自己考虑header等格式,然后搬移。
还有一个办法,就是通过0bjdump工具,生成一个个的小section bin文件,然后处理形成一个大bin文件。
在这里插入图片描述

  • -j:表示保留的section名字,其他均去除。
    在这里插入图片描述

3、参考

1、arm-gcc ld链接器中的链接脚本格式编写

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词