I have a linker script that links code for 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 = .);
}
When i built, the binary size is just 6 KB. But i can not add any initialized variable. When i add an initialized variable, the binary size jumps to ~246 MB. Why is that? I tried to link the data segment at location following text section by specifying exact location and also providing >DDR for the data segment. Even though this seem to reduce the binary size back to 6 KB, the binary fails to boot. How can i keep my code in the DDR and the data, bss, stack and heap in the internal ram itself, with light binary size?
I read in another thread that " using MEMORY tag in linker script should solve the problem of memory waste", How can this be done?
linker script wastes my memory
Plese do ask if anything else needed. I don't have any experience with linker script. Please help
The readelf --sections output of the binary with no initialized data given is as follows,
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)
and The readelf --sections output of the binary with initialized data given is ,
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)
Hope this is enough...!!!
Note: I am using arm-none-eabi-gcc for linking.
If you are not experienced with linker scripts then either use one that just works or make or borrow a simpler one. Here is a simple one, and this should demonstrate what is most likely going on.
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
}
First program
.text
.globl _start
_start:
mov r0,r1
mov r1,r2
b .
not meant to be a real program just creating some bytes in a segment is all.
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 bytes in .text which is at address 0x1000 in memory which is exactly what we told it to do.
If I use -objcopy a.elf -O binary a.bin I get a 12-byte file as expected, the "binary" file format is a memory image, starting with the first address that has some content in the address space and ending with the last byte of content in the address space. so instead of 0x1000+12 bytes, the binary is 12 bytes ad the user has to know it needs to be loaded at 0x1000.
So change this up a little:
.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
Now we have 12 bytes at 0x1000 and 4 bytes at 0x2000, so -O binary has to give us one memory image from the first defined byte to the last so that would be 0x1000+4.
Sure enough 4100 bytes that is exactly what it did.
.text
.globl _start
_start:
mov r0,r1
mov r1,r2
b .
.data
some_data: .word 0x12345678
.bss
some_more_data: .word 0
which gives
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
Now I only got a 4100-byte file, and that is actually not surprising, it is assumed that the bootstrap is going to zero .bss So that didn't grow the "binary" file.
There is an intimate relationship. A system-level design. Between the linker script and the bootstrap. For what it appears you are trying to do (just ram no rom) you can probably get away with a much simpler linker script, on par with the one I have but if you care about .bss being zeroed then there are some tricks you can use:
MEMORY
{
ram : ORIGIN = 0x00001000, LENGTH = 0x3000
}
SECTIONS
{
.text : { *(.text*) } > ram
.bss : { *(.text*) } > ram
.data : { *(.text*) } > ram
}
make sure there is at least one .data item and your "binary" will have the complete image with bss already zeroed, the bootstrap simply needs to set the stack pointer(s) and jump to main (if this is for C).
Anyway, hopefully, you can see that the jump from 12 bytes to 4100 bytes was because of the addition of a .data element and the "binary" format having to pad the "binary" file so that the file was a memory image from the lowest address with data to the highest address with data (from 0x1000 to 0x2000+sizeof(.data)-1 in this case). Change the linker script, the 0x1000, and the 0x2000 and this all changes. Swap them put .text at 0x2000 and .data at 0x1000, now the "binary" file has to be 0x2000-0x1000+sizeof(.text) rather than 0x2000-0x1000+sizeof(.data). or 0x100C bytes instead of 0x1004. go back to the first linker script and make .data at 0x20000000 now the "binary" will be 0x20000000-0x1000+sizeof(.data) because that is how much information including padding is required to make a memory image in a single file.
It is most likely that is what is going on. As demonstrated here the file size went from 12 bytes to 4100 by simply adding one word of data.
EDIT.
Well if you noload the data then your initialized variable won't be initialized, it is that simple
unsigned int x = 5;
will not be a 5 if you discard (NOLOAD) .data.
As has been stated and stated again, you can have the data put in the .text sector and then use more linker script foo, to have the bootstrap find that data.
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
}
This creates a 16 byte "binary" file. the 12 bytes of instruction and the 4 bytes of .data. But you don't know where the data is unless you do some hardcoding which is a bad idea. This is where things like bss_start and bss_end are found in your linker script.
something like this
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
which gives us.
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
and the toolchain/linker creates and fills those defined names in the linker script, and then fills them into your code when it resolves those externals. Then your bootstrap needs to use those variables (and more that I didn't include here where to find the .data in the .text you know from the above that there are 4 bytes and they need to land at 0x2000 but where in the 0x1000 .text area are those 4 bytes found? More linker script foo. Also, note the gnu linker scripts are very sensitive as to where you define those variables. before or after the squiggly brackets can have different results.
This is why I mentioned it appeared you were using ram. If this is a rom based target and you want .data and zeroed .bss then you pretty much have to put the .data and the size and location of .bss in the flash/rom area and the bootstrap has to copy and zero. Alternatively, you can choose not to use .data nor .bss
unsigned int x=5;
unsigned int y;
instead
unsigned int x;
unsigned int y;
...
x=5;
y=0;
Yes, it is not as efficient binary size-wise, but linker scripts are very much toolchain dependent, and with gnu for example over time the linker script language/rules change, what worked on a prior major version of gnu ld doesn't necessarily work on the current or next, I have had to re-architect my minimal linker script over the years as a result.
As demonstrated here, you can use your command line tools to experiment with settings and locations and see what the toolchain has produced.
Bottom line it sounds like you added some information in .data, but then state you want to NOLOAD it, basically meaning that .data isnt there/used your variables are not initialized correctly, so why bother changing the code to cause all this to happen only to have it not work anyway? Either has .data and use it right, have the right bootstrap and linker script pair, or if it is ram only just pack it all up into the same ram space, or don't use the "binary" format you are using, use elf or ihex or srec or other.
Another trick depending on your system is to build the binary for ram, all packed up, then have another program that wraps around that binary runs from rom and copies to ram and jumps. Take the 16 byte program above, write another that includes those 16 bytes from that build, and copies them to 0x1000 and then branches to 0x1000. Depending on the system and the flash/rom technology and interface you may wish to do this anyway, the system from my day job uses a spi flash to boot, which are known to have read-disturb problems and are...spi... So the fastest, cleanest, most reliable solution, is to do the copy jump before doing anything else. Making the linker script much easier as a free side effect.