Linker ld链接时的巨大二进制大小

Linker ld链接时的巨大二进制大小,linker,arm,ld,linker-scripts,cortex-a,Linker,Arm,Ld,Linker Scripts,Cortex A,我有一个链接器脚本,用于链接imx6q(cortex-A9)的代码: 当我构建时,二进制大小只有6KB。但是我不能添加任何初始化变量。当我添加一个初始化变量时,二进制大小跳到~246MB。为什么呢?我试图通过指定精确位置并为数据段提供>DDR,将数据段链接到文本部分后面的位置。尽管这似乎将二进制文件的大小减少到6KB,但二进制文件无法启动。如何将代码保存在DDR中,将数据、bss、堆栈和堆保存在内部ram中,并保持较小的二进制大小 我在另一篇文章中读到“在链接器脚本中使用内存标签应该可以解决内存

我有一个链接器脚本,用于链接imx6q(cortex-A9)的代码:

当我构建时,二进制大小只有6KB。但是我不能添加任何初始化变量。当我添加一个初始化变量时,二进制大小跳到~246MB。为什么呢?我试图通过指定精确位置并为数据段提供>DDR,将数据段链接到文本部分后面的位置。尽管这似乎将二进制文件的大小减少到6KB,但二进制文件无法启动。如何将代码保存在DDR中,将数据、bss、堆栈和堆保存在内部ram中,并保持较小的二进制大小

我在另一篇文章中读到“在链接器脚本中使用内存标签应该可以解决内存浪费的问题”,如何做到这一点

如果还需要什么,请尽管问。我没有任何使用链接器脚本的经验。请帮忙

未给出初始化数据的二进制文件的readelf--sections输出如下所示

There are 19 section headers, starting at offset 0xd804:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .vector           NOBITS          0093ff80 007f80 000048 00  WA  0   0 32
  [ 2] .text             PROGBITS        10000000 008000 0016fc 00  AX  0   0  8
  [ 3] .text.vectors     PROGBITS        100016fc 0096fc 000048 00  AX  0   0  4
  [ 4] .text.proc        PROGBITS        10001744 009744 000034 00  AX  0   0  4
  [ 5] .bss              NOBITS          0093ffc8 007fc8 000294 00  WA  0   0  4
  [ 6] .mmu_page_table   NOBITS          00938000 008000 004000 00  WA  0   0  1
  [ 7] .comment          PROGBITS        00000000 009778 00001f 01  MS  0   0  1
  [ 8] .ARM.attributes   ARM_ATTRIBUTES  00000000 009797 00003d 00      0   0  1
  [ 9] .debug_aranges    PROGBITS        00000000 0097d8 000108 00      0   0  8
  [10] .debug_info       PROGBITS        00000000 0098e0 0018a7 00      0   0  1
  [11] .debug_abbrev     PROGBITS        00000000 00b187 00056f 00      0   0  1
  [12] .debug_line       PROGBITS        00000000 00b6f6 00080e 00      0   0  1
  [13] .debug_frame      PROGBITS        00000000 00bf04 000430 00      0   0  4
  [14] .debug_str        PROGBITS        00000000 00c334 0013dd 01  MS  0   0  1
  [15] .debug_ranges     PROGBITS        00000000 00d718 000020 00      0   0  8
  [16] .shstrtab         STRTAB          00000000 00d738 0000cb 00      0   0  1
  [17] .symtab           SYMTAB          00000000 00dafc 000740 10     18  60  4
  [18] .strtab           STRTAB          00000000 00e23c 000511 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)
给定初始化数据的二进制文件的readelf--sections输出是

 There are 20 section headers, starting at offset 0xd82c:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .vector           NOBITS          0093ff80 007f80 000048 00  WA  0   0 32
  [ 2] .text             PROGBITS        10000000 008000 0016fc 00  AX  0   0  8
  [ 3] .text.vectors     PROGBITS        100016fc 0096fc 000048 00  AX  0   0  4
  [ 4] .text.proc        PROGBITS        10001744 009744 000034 00  AX  0   0  4
  [ 5] .data             PROGBITS        0093ffc8 007fc8 000004 00  WA  0   0  8
  [ 6] .bss              NOBITS          0093ffcc 007fcc 000294 00  WA  0   0  4
  [ 7] .mmu_page_table   NOBITS          00938000 008000 004000 00  WA  0   0  1
  [ 8] .comment          PROGBITS        00000000 009778 00001f 01  MS  0   0  1
  [ 9] .ARM.attributes   ARM_ATTRIBUTES  00000000 009797 00003d 00      0   0  1
  [10] .debug_aranges    PROGBITS        00000000 0097d8 000108 00      0   0  8
  [11] .debug_info       PROGBITS        00000000 0098e0 0018b6 00      0   0  1
  [12] .debug_abbrev     PROGBITS        00000000 00b196 000580 00      0   0  1
  [13] .debug_line       PROGBITS        00000000 00b716 00080e 00      0   0  1
  [14] .debug_frame      PROGBITS        00000000 00bf24 000430 00      0   0  4
  [15] .debug_str        PROGBITS        00000000 00c354 0013dd 01  MS  0   0  1
  [16] .debug_ranges     PROGBITS        00000000 00d738 000020 00      0   0  8
  [17] .shstrtab         STRTAB          00000000 00d758 0000d1 00      0   0  1
  [18] .symtab           SYMTAB          00000000 00db4c 000770 10     19  62  4
  [19] .strtab           STRTAB          00000000 00e2bc 000513 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)
希望这足够了


注意:我正在使用arm none eabi gcc进行链接。

如果您没有使用链接器脚本的经验,请使用一个只起作用的脚本,或者制作或借用一个更简单的脚本。这里是一个简单的例子,这应该说明最有可能发生的事情

MEMORY
{
    bob : ORIGIN = 0x00001000, LENGTH = 0x100
    ted : ORIGIN = 0x00002000, LENGTH = 0x100
    alice : ORIGIN = 0x00003000, LENGTH = 0x100
}

SECTIONS
{
    .text : { *(.text*) } > bob
    .data : { *(.text*) } > ted
    .bss : { *(.text*) } > alice
}
第一个节目

.text
.globl _start
_start:
mov r0,r1
mov r1,r2
b .
不意味着要成为一个真正的程序,只是在一个段中创建一些字节而已

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .text             PROGBITS        00001000 001000 00000c 00  AX  0   0  4
12字节。文本位于内存中地址0x1000处,这正是我们告诉它要做的

如果我使用-objcopy a.elf-O binary a.bin,我会像预期的那样得到一个12字节的文件,“二进制”文件格式是一个内存映像,从地址空间中包含某些内容的第一个地址开始,到地址空间中内容的最后一个字节结束。因此,二进制文件不是0x1000+12字节,而是12字节,用户必须知道它需要在0x1000加载

所以把这个稍微改变一下:

.text
.globl _start
_start:
mov r0,r1
mov r1,r2
b .
.data
some_data: .word 0x12345678

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .text             PROGBITS        00001000 001000 00000c 00  AX  0   0  4
  [ 2] .data             PROGBITS        00002000 002000 000004 00  WA  0   0  1
现在我们在0x1000有12个字节,在0x2000有4个字节,所以-O二进制必须给我们一个从第一个定义字节到最后一个定义字节的内存映像,这就是0x1000+4

当然,4100字节正是它所做的

.text
.globl _start
_start:
mov r0,r1
mov r1,r2
b .
.data
some_data: .word 0x12345678
.bss
some_more_data: .word 0

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .text             PROGBITS        00001000 001000 00000c 00  AX  0   0  4
  [ 2] .data             PROGBITS        00002000 002000 000004 00  WA  0   0  1
  [ 3] .bss              NOBITS          00003000 003000 000004 00  WA  0   0  1
现在我只得到了一个4100字节的文件,这其实并不奇怪,它假设引导程序将为0.bss,因此不会增长“二进制”文件

有一种亲密的关系。系统级设计。在链接器脚本和引导程序之间。对于你正在尝试做的事情(只是RAM没有ROM),你可以用一个简单得多的链接器脚本,与我所拥有的一样,但是如果你关心的话,BSS被归零,那么你可以使用一些技巧:

MEMORY
{
    ram : ORIGIN = 0x00001000, LENGTH = 0x3000
}
SECTIONS
{
    .text : { *(.text*) } > ram
    .bss : { *(.text*) } > ram
    .data : { *(.text*) } > ram
}
确保至少有一个.data项,并且您的“二进制”将具有bss已归零的完整映像,引导只需设置堆栈指针并跳转到main(如果这是针对C的)

无论如何,希望您能看到,从12字节跳到4100字节是因为添加了.data元素和“binary”格式,必须填充“binary”文件,以便该文件是一个内存映像,从包含数据的最低地址到包含数据的最高地址(在本例中为0x1000到0x2000+sizeof(.data)-1)。更改链接器脚本、0x1000和0x2000,所有这些都会更改。将它们交换为将.text放在0x2000,将.data放在0x1000,现在“二进制”文件必须是0x2000-0x1000+sizeof(.text),而不是0x2000-0x1000+sizeof(.data)。或0x100C字节,而不是0x1004。返回到第一个链接器脚本并在0x20000000处生成.data现在“二进制”将是0x20000000-0x1000+sizeof(.data),因为在单个文件中生成内存映像需要包含填充的信息量

这很可能就是正在发生的事情。如本文所示,只需添加一个数据字,文件大小就从12字节增加到4100字节

编辑

如果你不加载数据,那么初始化的变量就不会被初始化,就是这么简单

无符号整数x=5;
如果您放弃(空载)。数据,则不会为5

如前所述,您可以将数据放入.text扇区,然后使用更多链接器脚本foo,让引导程序查找该数据

MEMORY
{
    bob : ORIGIN = 0x00001000, LENGTH = 0x100
    ted : ORIGIN = 0x00002000, LENGTH = 0x100
    alice : ORIGIN = 0x00003000, LENGTH = 0x100
}
SECTIONS
{
    .text : { *(.text*) } > bob
    .data : { *(.text*) } > ted AT > bob
    .bss : { *(.text*) } > alice AT > bob
}
这将创建一个16字节的“二进制”文件。12字节的指令和4字节的.data。但是你不知道数据在哪里,除非你做一些硬编码,这是一个坏主意。这就是链接器脚本中的bss\u开始bss\u结束等内容

像这样的

MEMORY
{
    bob : ORIGIN = 0x00001000, LENGTH = 0x100
    ted : ORIGIN = 0x00002000, LENGTH = 0x100
    alice : ORIGIN = 0x00003000, LENGTH = 0x100
}
SECTIONS
{
    .text : { *(.text*) } > bob
    .data : {
        __data_start__ = .;
        *(.data*)
    } > ted AT > bob
    __data_end__ = .;
    __data_size__ = __data_end__ - __data_start__;
    .bss : { *(.text*) } > alice AT > bob
}
这就给了我们

Disassembly of section .text:
    
00001000 <_start>:
    1000:   e1a00001    mov r0, r1
    1004:   e1a01002    mov r1, r2
    1008:   eafffffe    b   1008 <_start+0x8>
    
0000100c <hello>:
    100c:   00002000    andeq   r2, r0, r0
    1010:   00002004    andeq   r2, r0, r4
    1014:   00000004    andeq   r0, r0, r4
    
Disassembly of section .data:
    
00002000 <__data_start__>:
    2000:   12345678    eorsne  r5, r4, #120, 12    ; 0x7800000
反而

无符号整数x;
无符号整数y;
...
x=5;
y=0;
是的,就二进制大小而言,它没有那么有效,但是链接器脚本非常依赖于工具链,并且对于gnu来说,例如,随着时间的推移,链接器脚本语言/规则会发生变化,在gnu ld的先前主要版本上起作用的内容不一定在当前或下一个版本上起作用,多年来,我不得不重新构建我的最小链接器脚本

如本文所示,您可以使用命令行工具试验设置和位置,并查看工具链产生了什么

一句话听起来像是您在.data中添加了一些信息,但随后又声明要卸载它,这基本上意味着.data不存在/未使用您的变量未正确初始化,那么为什么要费心更改代码以导致所有这些发生,但却使其无法正常工作呢?要么拥有.data并正确使用它,要么拥有正确的引导脚本和链接器脚本对,或者如果它只是ram,只需将其打包到相同的ram空间中,或者不使用您正在使用的“二进制”格式,请使用elf或ihex或srec或其他

根据您的系统,另一个技巧是为ram构建二进制文件,全部打包,然后让另一个程序围绕该二进制文件从rom运行,复制到ram并跳转。以16字节的pro为例
.text
.globl _start
_start:
    mov r0,r1
    mov r1,r2
    b .
    
    
hello:
    .word __data_start__
    .word __data_end__
    .word __data_size__
    
    .data
    some_data: .word 0x12345678
Disassembly of section .text:
    
00001000 <_start>:
    1000:   e1a00001    mov r0, r1
    1004:   e1a01002    mov r1, r2
    1008:   eafffffe    b   1008 <_start+0x8>
    
0000100c <hello>:
    100c:   00002000    andeq   r2, r0, r0
    1010:   00002004    andeq   r2, r0, r4
    1014:   00000004    andeq   r0, r0, r4
    
Disassembly of section .data:
    
00002000 <__data_start__>:
    2000:   12345678    eorsne  r5, r4, #120, 12    ; 0x7800000