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

时间:2016-11-09 07:46:04

标签: linker arm ld linker-scripts cortex-a

我有一个链接脚本链接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来链接文本部分后面的数据段。尽管这似乎将二进制大小减少到6 KB,但二进制文件无法启动。我如何将代码保存在DDR和内部ram本身的数据,bss,堆栈和堆中,具有较小的二进制大小?

我在另一个帖子中读到"在链接描述文件中使用MEMORY标记可以解决内存浪费问题",如何做到这一点?

linker script wastes my memory

请问是否需要其他任何东西。我对链接器脚本没有任何经验。请帮忙

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

1 个答案:

答案 0 :(得分:1)

如果您对链接器脚本没有经验,那么要么使用一个正常工作的脚本,要么使用更简单的脚本。这是一个简单的,这应该证明最有可能发生的事情。

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二进制文件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字节的文件,这实际上并不奇怪,假设引导程序将为零.bss因此没有增加"二进制文件"文件。

有一种亲密的关系。系统级设计。在链接器脚本和引导程序之间。对于你想要做的事情(只是ram no 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元素和"二进制"格式必须填充"二进制"文件,以便该文件是从最低地址的内存映像,数据到最高地址的数据(在这种情况下从0x1000到0x2000 + sizeof(.data)-1)。更改链接器脚本,0x1000和0x2000,这一切都发生了变化。交换它们。文本在0x2000和.data在0x1000,现在是"二进制"文件必须是0x2000-0x1000 + sizeof(.text)而不是0x2000-0x1000 + sizeof(.data)。或0x100C字节而不是0x1004。回到第一个链接器脚本并在0x20000000处创建.data,现在是#34;二进制文件"将是0x20000000-0x1000 + sizeof(.data),因为这是在一个文件中制作内存映像所需的填充信息。

最有可能的是这是怎么回事。如此处所示,通过简单地添加一个数据字,文件大小从12个字节变为4100。

EDIT。

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

unsigned int x = 5;

如果你丢弃(NOLOAD).data,

将不会是5。

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

MEMORY {     bob:ORIGIN = 0x00001000,LENGTH = 0x100     ted:ORIGIN = 0x00002000,LENGTH = 0x100     alice:ORIGIN = 0x00003000,LENGTH = 0x100 } SECTIONS {     .text:{(。text )}>短发     .data:{(。text )}>特德AT>短发     .bss:{(。text )}> alice AT>短发 }

这会创建一个16字节"二进制"文件。 12个字节的指令和4个字节的.data。但你不知道数据在哪里,除非你做一些硬编码,这是一个坏主意。这是在链接描述文件中找到 bss_start bss_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

并且工具链/链接器在链接描述文件中创建并填充这些已定义的名称,然后在解析这些外部时将它们填充到代码中。然后你的引导程序需要使用那些变量(还有更多我没有在这里包含在哪里找到.text中的.data你从上面知道有4个字节,它们需要降落在0x2000但是在0x1000 .text中area是找到的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链接器脚本langauge /规则会发生变化,对先前主要版本的gnu ld起作用的东西不一定起作用当前或下一步,我不得不多年来重新设计我的最小链接器脚本。

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

底线听起来你在.data中添加了一些信息,但是然后声明你要NOLOAD它,基本上意味着.data不存在/使用你的变量没有正确初始化,所以为什么要费心改变代码导致所有这种情况只会让它无论如何都不起作用?要么有.data并使用它,有正确的bootstrap和链接器脚本对,或者如果它只是将它全部打包到同一个ram空间,或者不使用&#34;二进制&#34;您正在使用的格式,使用elf或ihex或srec或其他。

取决于你的系统的另一个技巧是为ram构建二进制文件,所有打包,然后有另一个包含该二进制文件的程序从rom运行并复制到ram和jumps。取上面的16字节程序,写另一个包含该构建中的16个字节,并将它们复制到0x1000然后分支到0x1000。根据系统和flash / rom技术和接口你可能希望这样做,我日常工作的系统使用spi flash来启动,这已知有读取干扰问题并且是...... spi ..因此,最快,最干净,最可靠的解决方案是在执行任何其他操作之前执行复制跳转。使链接器脚本更容易作为自由副作用。