ld链接时二进制文件很大

Huge Binary size while ld Linking

我有一个 linker 脚本,links 代码用于 imx6q(cortex-A9):

OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(Reset_Handler)
/* SEARCH_DIR(.) */
GROUP(libgcc.a libc.a)  
/*  INPUT (crtbegin.o crti.o crtend.o crtn.o) */

MEMORY {
/*  IROM (rwx) : ORIGIN = 0x00000000, LENGTH = 96K */
    IRAM (rwx) : ORIGIN = 0x00900000, LENGTH = 256K
    IRAM_MMU (rwx): ORIGIN = 0x00938000, LENGTH = 24K
    IRAM_FREE(rwx): ORIGIN = 0x00907000, LENGTH = 196K
    DDR (rwx) : ORIGIN = 0x10000000, LENGTH = 1024M
}

/* PROVIDE(__cs3_heap_start = _end); */

SECTIONS {
    .vector (ORIGIN(IRAM) + LENGTH(IRAM) - 144 ):ALIGN (32) {
    __ram_vectors_start = . ;
    . += 72 ; 
    __ram_vectors_end = . ;
    . = ALIGN (4);  
    } >IRAM

    . = ORIGIN(DDR); 
    .text(.) :ALIGN(8) {
        *(.entry)
        *(.text)
        /* __init_array_start = .; */
        /* __init_array_end = .; */
        . = ALIGN (4);
        __text_end__ = .;
    } >DDR

    .data :ALIGN(8) {
        *(.data .data.*) 
        __data_end__ = .;
    } 

    .bss(__data_end__) : {
        . = ALIGN (4);
        __bss_start__ = .;
        *(.shbss)
        *(.bss .bss.* .gnu.linkonce.b.*)
        *(COMMON)
        __bss_end__ = .;
    }

    /*       . += 10K; */
    /*       . += 5K; */

    top_of_stacks = .;
    . = ALIGN (4);
    . += 8;
    free_memory_start = .;

    .mmu_page_table : {
    __mmu_page_table_base__ = .;
    . = ALIGN (16K);
    . += 16K;
    } >IRAM_MMU 

    _end = .;
    __end = _end;
    PROVIDE(end = .);
}

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

我在另一个线程中读到“在 linker 脚本中使用 MEMORY 标签应该可以解决内存浪费的问题”,如何做到这一点?

linker script wastes my memory

请问是否还有其他需要。我对 linker 脚本没有任何经验。请帮助

没有给出初始化数据的二进制文件的 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 进行 linking.

如果您没有使用链接描述文件的经验,那么要么使用一个可以正常工作的链接描述文件,要么制作或借用一个更简单的链接描述文件。这是一个简单的例子,这应该可以说明最有可能发生的事情。

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

.text 中的 12 个字节,位于内存中的地址 0x1000,这正是我们告诉它要做的。

如果我使用 -objcopy a.elf -O binary a.bin 我按预期得到一个 12 字节的文件,“二进制”文件格式是一个内存映像,从第一个地址开始在地址 space 中有一些内容,并以地址 space 中内容的最后一个字节结束。因此,二进制文件不是 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 binary 必须给我们一个从第一个定义字节到最后一个定义字节的内存映像,这样就是 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 字节的文件,这其实并不奇怪,假设 bootstrap 将归零 .bss 所以没有增长“二进制”文件。

有亲密关系。系统级设计。在链接描述文件和 bootstrap 之间。对于您正在尝试做的事情(只是 ram no rom),您可能可以使用一个更简单的链接器脚本,与我拥有的链接器脚本相当,但如果您关心 .bss 被归零,那么您可以使用一些技巧使用:

MEMORY
{
    ram : ORIGIN = 0x00001000, LENGTH = 0x3000
}
SECTIONS
{
    .text : { *(.text*) } > ram
    .bss : { *(.text*) } > ram
    .data : { *(.text*) } > ram
}

确保至少有一个 .data 项,并且您的“二进制文件”将具有 bss 已经归零的完整图像,bootstrap 只需要设置堆栈指针并跳转到 main (如果这是针对 C 的)。

无论如何,希望您能看到从 12 字节跳到 4100 字节是因为添加了 .data 元素和必须填充“二进制”文件的“二进制”格式,以便文件是从最低地址到最高地址的内存映像(在本例中为 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。

编辑。

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

unsigned int x = 5;

如果您丢弃 (NOLOAD).data,则不会是 5。

如前所述,您可以将数据放入 .text 扇区,然后使用更多链接描述文件 foo,让 bootstrap 找到该数据。

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_startbss_end 之类的地方。

像这样

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
}
.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

和 toolchain/linker 在链接描述文件中创建并填充那些已定义的名称,然后在解析这些外部变量时将它们填充到您的代码中。然后你的 bootstrap 需要使用这些变量(还有更多我没有在这里包含的变量在哪里可以找到 .text 中的 .data 你从上面知道有 4 个字节并且它们需要落在 0x2000 但是在 0x1000 .text 区域的哪里找到了这 4 个字节?更多链接器脚本 foo。另外,请注意 gnu 链接器脚本对于定义这些变量的位置非常敏感。在波浪括号之前或之后可能会有不同的结果。

这就是为什么我提到你似乎在使用 ram。如果这是一个基于 rom 的目标并且你想要 .data 和归零的 .bss 那么你几乎必须将 .data 和 .bss 的大小和位置放在 flash/rom 区域并且 bootstrap 有复制和归零。或者,您可以选择不使用 .data 或 .bss

unsigned int x=5;
unsigned int y;

改为

unsigned int x;
unsigned int y;
...
x=5;
y=0;

是的,它在二进制大小方面效率不高,但是链接器脚本非常依赖于工具链,例如随着时间的推移,对于 gnu,链接器脚本 language/rules 发生了变化,这在以前的主要版本上是有效的gnu ld 不一定适用于当前或下一个,因此多年来我不得不重新构建我的最小链接描述文件。

如此处所示,您可以使用命令行工具试验设置和位置,并查看工具链生成的结果。

最重要的是,听起来您在 .data 中添加了一些信息,但随后声明您想要 NOLOAD 它,基本上意味着 .data 不是 there/used 您的变量未正确初始化,所以为什么要费心更改导致这一切发生的代码只是让它无论如何都不起作用?要么有 .data 并正确使用它,要么有正确的 bootstrap 和链接描述文件对,或者如果它只是 ram,则将其全部打包到同一个 ram space,或者不要使用“ binary" 格式,请使用 elf 或 ihex 或 srec 或其他。

根据您的系统,另一个技巧是为 ram 构建二进制文件,全部打包,然后让另一个程序环绕该二进制文件从 rom 运行并复制到 ram 并跳转。采用上面的 16 字节程序,编写另一个包含该构建中的 16 字节的程序,并将它们复制到 0x1000,然后分支到 0x1000。根据系统和 flash/rom 技术和界面,您可能希望无论如何都这样做,我日常工作中的系统使用 spi 闪存启动,已知存在读​​取干扰问题并且是...spi ... 所以最快、最干净、最可靠的解决方案是在执行任何其他操作之前执行复制跳转。作为免费的副作用,使链接描述文件更容易。