Because of vastly better C++ compile times I've recently added the option to compile a project for an ARM Cortex-M4 microcontroller with Clang instead of the arm-none-eabi-gcc toolchain. The whole process ran quite smoothly and I quickly had working ELF and HEX files. It wasn't until yesterday evening that I noticed that the ELF files actually differ quite a lot...
Before I continue let's inspect the ELF produced by GCC to get some kind of baseline.
GCC's ELF contains the following sections (apart from debug stuff)
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .vector_table PROGBITS 08000000 010000 000010 00 A 0 0 4
[ 2] .version NOBITS 08000010 010010 000010 00 WA 0 0 1
[ 3] .text PROGBITS 08000020 010020 000138 00 AX 0 0 4
[ 4] .rodata PROGBITS 08000158 010158 000000 00 WA 0 0 1
[ 5] .data PROGBITS 20000000 010158 000000 00 WA 0 0 1
[ 6] .data2 PROGBITS 10000000 010158 000000 00 W 0 0 1
[ 7] .bss NOBITS 20000000 020000 00038c 00 WA 0 0 512
[ 8] .bss2 PROGBITS 2000038c 010158 000000 00 W 0 0 1
[ 9] ._user_heap_stack NOBITS 2000038c 020000 000e04 00 WA 0 0 1
But despite .data and .bss being marked with an "A" (alloc) flag only loads the following
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x010000 0x08000000 0x08000000 0x00158 0x00158 RWE 0x10000
LOAD 0x000000 0x20000000 0x20000000 0x00000 0x01190 RW 0x10000
Section to Segment mapping:
Segment Sections...
00 .vector_table .version .text
01 .bss ._user_heap_stack
So far so good.
The problem emerged when I tried to create binaries from an ELF produced by Clang. Those files where huge in size (256MB) which is nowhere near what I had expected. Now, if you're not familiar with ARM microcontrollers, they usually contain FLASH and RAM memory at very different address locations (e.g. 0x0800'0000 for FLASH and 0x2000'0000 for RAM as seen above). So I already had some suspicion on what's going on... I checked my linker script and put a NOLOAD directive on every section which goes solely to RAM. Problem solved?
Well... not really. In fact my binaries grew even bigger.
Let's take a look at Clang's ELF. It's bugging me a little that Clang doesn't seem to remove the section for unwinding (ARM.exidx) although I compile with -fno-unwind-tables and -gc-sections but ok, I can live with those 16B.
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .vector_table PROGBITS 08000000 001000 000010 00 A 0 0 4
[ 2] .version PROGBITS 08000010 001010 000010 00 A 0 0 1
[ 3] .text PROGBITS 08000020 001020 000334 00 AX 0 0 4
[ 4] .rodata PROGBITS 08000354 001354 000000 00 AX 0 0 1
[ 5] .ARM.exidx ARM_EXIDX 08000354 001354 000010 00 AL 3 0 4
[ 6] .preinit_array PROGBITS 08000364 001364 000000 00 A 0 0 1
[ 7] .init_array INIT_ARRAY 08000364 001364 000004 04 WA 0 0 4
[ 8] .fini_array FINI_ARRAY 08000368 001368 000004 04 WA 0 0 4
[ 9] .data PROGBITS 20000000 002000 000000 00 WA 0 0 1
[10] .data2 PROGBITS 10000000 002000 000000 00 WA 0 0 1
[11] .bss NOBITS 20000000 002000 0001ac 00 WA 0 0 512
[12] .bss2 PROGBITS 200001ac 002000 000000 00 WA 0 0 1
[13] ._user_heap_stack PROGBITS 200001ac 002000 000e04 00 WA 0 0 1
Now this is where it gets interesting and where I have no clue whats happening. What is GNU_RELRO and GNU_STACK and how does it end up there? Why is GNU_STACK at address 0. Any chance this entry is bloating my binaries?
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x001000 0x08000000 0x08000000 0x00364 0x00364 R E 0x1000
LOAD 0x001364 0x08000364 0x08000364 0x00008 0x00008 RW 0x1000
GNU_RELRO 0x001364 0x08000364 0x08000364 0x00008 0x00c9c R 0x1
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0
EXIDX 0x001354 0x08000354 0x08000354 0x00010 0x00010 R 0x4
Section to Segment mapping:
Segment Sections...
00 .vector_table .version .text .rodata .ARM.exidx
01 .preinit_array .init_array .fini_array
02 .preinit_array .init_array .fini_array
03
04 .rodata .ARM.exidx
Further questions:
How is GCC able to remove all the RAM sections despite them
previously not having a NOLOAD directive in the linker script?
Running size on Clang's ELF counts the minimum stack size I define in the linker script to the .data section whereas on GCC's ELF it doesn't. How is that? My linker script contains a section which looks like this
._user_heap_stack (NOLOAD) :
{
. = ALIGN(8);
PROVIDE ( end = . );
PROVIDE ( _end = . );
. = . + _Min_Heap_Size;
. = . + _Min_Stack_Size;
. = ALIGN(8);
} >RAM
but to my knowledge this should only "check" that there is at least
enough RAM left to cover my defined minimum heap and stack size. This
section doesn't actually contain anything so how can it be counted to
.data?
I know that I could remove unwanted sections with objcopy when actually creating the binaries, but I'd really like to understand those subtle differences between GCC and Clang.
/edit
I just noticed that my ._user_heap_stack section has different types depending on the compiler (NOBITS vs PROGBITS). Guess that explains why it's counted to .data...
/edit
Now a (potential) bug over # LLVM
https://bugs.llvm.org/show_bug.cgi?id=46299
/edit
And closed as of lld 10.0.1
https://bugs.llvm.org/show_bug.cgi?id=46225
Related
My binutils version:
GNU ld (GNU Binutils) 2.39
and there is a new warn 'LOAD segment with RWX permissions'
here is the linker script
OUTPUT_ARCH( "riscv" )
ENTRY( _start )
MEMORY
{
/* Fake ROM area */
rom (rxa) : ORIGIN = 0x80000000, LENGTH = 512K
ram (wxa) : ORIGIN = 0x80080000, LENGTH = 512K
}
SECTIONS
{
.init :
{
_text = .;
KEEP (*(SORT_NONE(.init)))
} >rom AT>rom
.text :
{
*(.text.unlikely .text.unlikely.*)
*(.text.startup .text.startup.*)
*(.text .text.*)
*(.gnu.linkonce.t.*)
} >rom AT>rom
.fini :
{
KEEP (*(SORT_NONE(.fini)))
_etext = .;
} >rom AT>rom
.rodata.align :
{
. = ALIGN(4);
_rodata = .;
} >rom AT>rom
.rodata.start :
{
_rodata_lma = LOADADDR(.rodata.start);
} >rom AT>rom
.rodata :
{
*(.rdata)
*(.rodata .rodata.*)
*(.gnu.linkonce.r.*)
. = ALIGN(4);
_erodata = .;
} >rom AT>rom
.data.align :
{
. = ALIGN(4);
_data = .;
} >ram AT>rom
.data.start :
{
_data_lma = LOADADDR(.data.start);
} >ram AT>rom
.data :
{
*(.data .data.*)
*(.gnu.linkonce.d.*)
. = ALIGN(8);
PROVIDE( __global_pointer$ = . + 0x800 );
*(.sdata .sdata.*)
*(.sdata2 .sdata2.*)
*(.gnu.linkonce.s.*)
. = ALIGN(8);
*(.srodata.cst16)
*(.srodata.cst8)
*(.srodata.cst4)
*(.srodata.cst2)
*(.srodata .srodata.*)
. = ALIGN(4);
_edata = .;
} >ram AT>rom
.bss.align :
{
. = ALIGN(4);
_bss = .;
} >ram AT>rom
.bss.start :
{
_bss_lma = LOADADDR(.bss.start);
} >ram AT>rom
.bss :
{
*(.sbss*)
*(.gnu.linkonce.sb.*)
*(.bss .bss.*)
*(.gnu.linkonce.b.*)
*(COMMON)
. = ALIGN(4);
_ebss = .;
} >ram AT>rom
. = ALIGN(8);
_end = .;
.stack :
{
. = ALIGN(16);
. += __stack_size;
_stack_top = .;
} >ram AT>ram
}
I found that if I change the rodata.align section as below, the problem can fix.
.rodata.align ALIGN(4):
{
_rodata = .;
} >rom AT>rom
readelf -Sl original.elf
There are 24 section headers, starting at offset 0x29a08:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .init PROGBITS 80000000 001000 000076 00 AX 0 0 1
[ 2] .text PROGBITS 80000100 001100 00b6f2 00 AX 0 0 256
[ 3] .rodata.align NOBITS 8000b7f2 00c7f2 000002 00 WA 0 0 1
[ 4] .rodata PROGBITS 8000b7f4 00c7f4 0009c4 00 A 0 0 4
[ 5] .data.align PROGBITS 80080000 00e9d0 000000 00 WA 0 0 1
[ 6] .data PROGBITS 80080000 00e000 0009d0 00 WA 0 0 8
[ 7] .bss.align NOBITS 800809d0 000000 000000 00 WA 0 0 1
[ 8] .bss NOBITS 800809d0 00e9d0 011e08 00 WA 0 0 16
[ 9] .stack NOBITS 800927d8 00f7d8 000134 00 WA 0 0 1
[10] .comment PROGBITS 00000000 00e9d0 00002d 01 MS 0 0 1
[11] .riscv.attributes RISCV_ATTRIBUTE 00000000 00e9fd 00002f 00 0 0 1
[12] .debug_info PROGBITS 00000000 00ea2c 003a54 00 0 0 1
[13] .debug_abbrev PROGBITS 00000000 012480 0010bc 00 0 0 1
[14] .debug_loclists PROGBITS 00000000 01353c 0097da 00 0 0 1
[15] .debug_aranges PROGBITS 00000000 01cd16 000178 00 0 0 1
[16] .debug_rnglists PROGBITS 00000000 01ce8e 001154 00 0 0 1
[17] .debug_line PROGBITS 00000000 01dfe2 007949 00 0 0 1
[18] .debug_str PROGBITS 00000000 02592b 0008ed 01 MS 0 0 1
[19] .debug_line_str PROGBITS 00000000 026218 000388 01 MS 0 0 1
[20] .debug_frame PROGBITS 00000000 0265a0 000288 00 0 0 4
[21] .symtab SYMTAB 00000000 026828 002020 10 22 343 4
[22] .strtab STRTAB 00000000 028848 0010bf 00 0 0 1
[23] .shstrtab STRTAB 00000000 029907 0000fe 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
D (mbind), p (processor specific)
Elf file type is EXEC (Executable file)
Entry point 0x80000000
There are 4 program headers, starting at offset 52
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
RISCV_ATTRIBUT 0x00e9fd 0x00000000 0x00000000 0x0002f 0x00000 R 0x1
LOAD 0x001000 0x80000000 0x80000000 0x0c1b8 0x0c1b8 RWE 0x1000
LOAD 0x00e000 0x80080000 0x8000c1b8 0x009d0 0x127d8 RW 0x1000
LOAD 0x0007d8 0x800927d8 0x800927d8 0x00000 0x00134 RW 0x1000
Section to Segment mapping:
Segment Sections...
00 .riscv.attributes
01 .init .text .rodata.align .rodata
02 .data .bss.align .bss
03 .stack
readelf -Sl modify.elf
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .init PROGBITS 80000000 001000 000076 00 AX 0 0 1
[ 2] .text PROGBITS 80000100 001100 00b6f2 00 AX 0 0 256
[ 3] .rodata PROGBITS 8000b7f4 00c7f4 0009c4 00 A 0 0 4
[ 4] .data.align PROGBITS 80080000 00e9d0 000000 00 WA 0 0 1
[ 5] .data PROGBITS 80080000 00e000 0009d0 00 WA 0 0 8
[ 6] .bss.align NOBITS 800809d0 000000 000000 00 WA 0 0 1
[ 7] .bss NOBITS 800809d0 00e9d0 011e08 00 WA 0 0 16
[ 8] .stack NOBITS 800927d8 00f7d8 000134 00 WA 0 0 1
[ 9] .comment PROGBITS 00000000 00e9d0 00002d 01 MS 0 0 1
[10] .riscv.attributes RISCV_ATTRIBUTE 00000000 00e9fd 00002f 00 0 0 1
[11] .debug_info PROGBITS 00000000 00ea2c 003a54 00 0 0 1
[12] .debug_abbrev PROGBITS 00000000 012480 0010bc 00 0 0 1
[13] .debug_loclists PROGBITS 00000000 01353c 0097da 00 0 0 1
[14] .debug_aranges PROGBITS 00000000 01cd16 000178 00 0 0 1
[15] .debug_rnglists PROGBITS 00000000 01ce8e 001154 00 0 0 1
[16] .debug_line PROGBITS 00000000 01dfe2 007949 00 0 0 1
[17] .debug_str PROGBITS 00000000 02592b 0008ed 01 MS 0 0 1
[18] .debug_line_str PROGBITS 00000000 026218 000388 01 MS 0 0 1
[19] .debug_frame PROGBITS 00000000 0265a0 000288 00 0 0 4
[20] .symtab SYMTAB 00000000 026828 002010 10 21 342 4
[21] .strtab STRTAB 00000000 028838 0010bf 00 0 0 1
[22] .shstrtab STRTAB 00000000 0298f7 0000f0 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
D (mbind), p (processor specific)
Elf file type is EXEC (Executable file)
Entry point 0x80000000
There are 4 program headers, starting at offset 52
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
RISCV_ATTRIBUT 0x00e9fd 0x00000000 0x00000000 0x0002f 0x00000 R 0x1
LOAD 0x001000 0x80000000 0x80000000 0x0c1b8 0x0c1b8 R E 0x1000
LOAD 0x00e000 0x80080000 0x8000c1b8 0x009d0 0x127d8 RW 0x1000
LOAD 0x0007d8 0x800927d8 0x800927d8 0x00000 0x00134 RW 0x1000
Why the align command cause the LOAD section has a writable attribute?
I found that if I change the rodata.align section as below, the problem can fix.
.rodata.align ALIGN(4):
{
_rodata = .;
} >rom AT>rom
I'm on linux right now. I'm compiling a super simple C program:
#include <stdio.h>
int main()
{
printf("Hello, world!\n");
return 0;
}
and compiling with
gcc main.c -o main
After running ll to get file sizes, this is what it returns:
-rwxr-xr-x 1 xylight xylight 16K Nov 5 11:30 main
-rw-r--r-- 1 xylight xylight 68 Nov 5 11:23 main.c
-rw-r--r-- 1 xylight xylight 1.5K Nov 5 11:30 main.o
After linking main.o, the file size becomes 16KB! How can I make this smaller? Any linker options?
I'm not sure if this is a duplicate, I couldn't find anything on here. Let me know if it's a duplicate.
After running readelf -h main it says that the ELF type is this: DYN (Shared object file)
Anyone know what I could do to make this smaller?
I wasn't able to find a good explanation of this on SO so let me post one here.
First of all, by default executable includes static symbol table which is used for debugging and is not loaded at runtime. We can get rid of it with strip main which will save us about 2K (down to 15K on my Ubuntu 20).
Now, we can take a closer look at the overheads by running readelf -SW main:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 0] NULL 0000000000000000 000000 000000 00 0 0 0
[ 1] .interp PROGBITS 0000000000000318 000318 00001c 00 A 0 0 1
[ 2] .note.gnu.property NOTE 0000000000000338 000338 000020 00 A 0 0 8
[ 3] .note.gnu.build-id NOTE 0000000000000358 000358 000024 00 A 0 0 4
[ 4] .note.ABI-tag NOTE 000000000000037c 00037c 000020 00 A 0 0 4
[ 5] .gnu.hash GNU_HASH 00000000000003a0 0003a0 000024 00 A 6 0 8
[ 6] .dynsym DYNSYM 00000000000003c8 0003c8 0000a8 18 A 7 1 8
[ 7] .dynstr STRTAB 0000000000000470 000470 000082 00 A 0 0 1
[ 8] .gnu.version VERSYM 00000000000004f2 0004f2 00000e 02 A 6 0 2
[ 9] .gnu.version_r VERNEED 0000000000000500 000500 000020 00 A 7 1 8
[10] .rela.dyn RELA 0000000000000520 000520 0000c0 18 A 6 0 8
[11] .rela.plt RELA 00000000000005e0 0005e0 000018 18 AI 6 24 8
[12] .init PROGBITS 0000000000001000 001000 00001b 00 AX 0 0 4
[13] .plt PROGBITS 0000000000001020 001020 000020 10 AX 0 0 16
[14] .plt.got PROGBITS 0000000000001040 001040 000010 10 AX 0 0 16
[15] .plt.sec PROGBITS 0000000000001050 001050 000010 10 AX 0 0 16
[16] .text PROGBITS 0000000000001060 001060 000185 00 AX 0 0 16
[17] .fini PROGBITS 00000000000011e8 0011e8 00000d 00 AX 0 0 4
[18] .rodata PROGBITS 0000000000002000 002000 000012 00 A 0 0 4
[19] .eh_frame_hdr PROGBITS 0000000000002014 002014 000044 00 A 0 0 4
[20] .eh_frame PROGBITS 0000000000002058 002058 000108 00 A 0 0 8
[21] .init_array INIT_ARRAY 0000000000003db8 002db8 000008 08 WA 0 0 8
[22] .fini_array FINI_ARRAY 0000000000003dc0 002dc0 000008 08 WA 0 0 8
[23] .dynamic DYNAMIC 0000000000003dc8 002dc8 0001f0 10 WA 7 0 8
[24] .got PROGBITS 0000000000003fb8 002fb8 000048 08 WA 0 0 8
[25] .data PROGBITS 0000000000004000 003000 000010 00 WA 0 0 8
[26] .bss NOBITS 0000000000004010 003010 000008 00 WA 0 0 1
[27] .comment PROGBITS 0000000000000000 003010 00002a 01 MS 0 0 1
[28] .shstrtab STRTAB 0000000000000000 00303a 00010a 00 0 0 1
As you can see the first 1.5K (up to, but not including, the .init) hold ELF header and bookkeeping data for loading shared libraries (all those .gnu.hash, .dynsym, etc.).
This is followed by 500 bytes of code (.init, .plt, etc. up to, but not including, the .rodata). Note that code allocation starts at 4K page boundary so we waste 2.5K for padding.
Then 3.5K is wasted to realign code at 4K page boundary before ~1K of data sections (.rodata, unwinding tables, etc.). There is an interesting waste of 3K between .eh_frame and .init_array which happens due to some weird alignment between readonly and normal data (see this question for more details).
So to summarize, only a small fraction of ELF size (1.5+0.5+1=3K i.e. 20%) is really used and the rest is wasted to properly align addresses when ELF is mmaped to memory. Address alignment is needed so that dynamic loader could assign different permissions for memory pages (e.g. code pages can not be written but can be executed and for data pages permissions are reversed).
The multiboot2 header can't be found in my final ELF, because inside the binary file it stands at offset 0x334e0, but the multiboot2 spec tells only the first 32KiB, i.e. 0x8000 bytes, are checked. Therefore it comes "too late".
I don't know how I can solve this. Is the ELF header too bloated? The multiboot2 header itself is correct, i.e. several tools to check multiboot2 headers tell it is correct. It is only not working, when I link the header together with the other code. If I adjust tools to verify multiboot2 headers, i.e. bootimage[1] to look at more than the first 32KiB, it works too.
My linker script (for GNU ld):
/** The "start"-symbol from start.asm. */
ENTRY(start)
SECTIONS {
/* Multiboot2-Header must be 64-bit (8 byte) aligned according to spec. */
. = ALIGN(8);
.multiboot2_header :
{
/* ensure that the multiboot header is at the beginning */
*(.multiboot2_header)
}
.text :
{
*(.text)
}
}
readelf -WSl <my-elf> tells, that the .multiboot2_header section is indeed the first one:
There are 1526 section headers, starting at offset 0x3ca8d8:
Section Headers:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 0] NULL 0000000000000000 000000 000000 00 0 0 0
[ 1] .multiboot2_header PROGBITS 0000000000000000 0334e8 000048 00 0 0 8
[ 2] .debug_abbrev PROGBITS 0000000000000000 033530 00ecea 00 0 0 1
[ 3] .debug_info PROGBITS 0000000000000000 04221a 0f0391 00 0 0 1
[ 4] .debug_aranges PROGBITS 0000000000000000 1325ab 019f40 00 0 0 1
[ 5] .debug_ranges PROGBITS 0000000000000000 14c4eb 0301d0 00 0 0 1
[ 6] .debug_str PROGBITS 0000000000000000 17c6bb 0c5de1 01 MS 0 0 1
[ 7] .debug_pubnames PROGBITS 0000000000000000 24249c 035215 00 0 0 1
[ 8] .debug_pubtypes PROGBITS 0000000000000000 2776b1 058c44 00 0 0 1
[ 9] .debug_frame PROGBITS 0000000000000000 2d02f8 033b60 00 0 0 8
[10] .debug_line PROGBITS 0000000000000000 303e58 08390a 00 0 0 1
[11] .debug_loc PROGBITS 0000000000000000 387762 0038d8 00 0 0 1
[12] .comment PROGBITS 0000000000000000 38b03a 000013 01 MS 0 0 1
[13] .symtab SYMTAB 0000000000000000 38b050 0089d0 18 15 803 8
[14] .shstrtab STRTAB 0000000000000000 393a20 01cec9 00 0 0 1
[15] .strtab STRTAB 0000000000000000 3b08e9 019fee 00 0 0 1
[16] .rodata._ZN137_$LT$rust_multiboot2_64_bit_kernel..logger..BootStageAwareLogger$u20$as$u20$rust_multiboot2_64_bit_kernel..boot_stage..BootStageAware$GT$15next_boot_stage17h5918ecf04a4f1232E PROGBITS 0000000000000000 000160 000010 00 A 0 0 4
...
[179] .rodata..L__unnamed_94 PROGBITS 00000000000045a8 004708 00007f 00 A 0 0 1
[180] .eh_frame_hdr PROGBITS 0000000000004628 004788 00000c 00 A 0 0 4
[181] .eh_frame PROGBITS 0000000000004638 004798 00001c 00 A 0 0 8
[182] .text PROGBITS 0000000000004654 0047b4 00001b 00 AX 0 0 4
[183] .text._ZN29rust_multiboot2_64_bit_kernel6logger20BootStageAwareLogger13apply_to_each17h77f9f12abd1f054eE PROGBITS 0000000000004670 0047d0 000130 00 AX 0 0 16
...
[1388] .text._ZN54_$LT$u32$u20$as$u20$core..ops..bit..Shl$LT$i32$GT$$GT$3shl17h0ba8101e5b58ae12E PROGBITS 0000000000030390 0304f0 000049 00 AX 0 0 16
[1389] .text._ZN58_$LT$$RF$u32$u20$as$u20$core..ops..bit..Shl$LT$i32$GT$$GT$3shl17h518033907a793365E PROGBITS 00000000000303e0 030540 000021 00 AX 0 0 16
[1390] .text.memcpy PROGBITS 0000000000030410 030570 00004e 00 AX 0 0 16
[1391] .text.memset PROGBITS 0000000000030460 0305c0 0000a4 00 AX 0 0 16
[1392] .text.memcmp PROGBITS 0000000000030510 030670 000179 00 AX 0 0 16
[1393] .data.rel.ro..L__unnamed_1 PROGBITS 0000000000030690 0307f0 0002c0 00 WA 0 0 8
[1394] .data.rel.ro..L__unnamed_2 PROGBITS 0000000000030950 030ab0 000300 00 WA 0 0 8
...
[1515] .data.rel.ro..L__unnamed_169 PROGBITS 00000000000332e0 033440 000018 00 WA 0 0 8
[1516] .got PROGBITS 00000000000332f8 033458 000090 00 WA 0 0 8
[1517] .bss._ZN29rust_multiboot2_64_bit_kernel6logger6LOGGER17h0a7e2a9a53f2b5ddE NOBITS 0000000000033388 0334e8 000018 00 WA 0 0 8
[1518] .bss NOBITS 00000000000333a0 0334e8 020000 00 WA 0 0 8
[1519] .bss._ZN29rust_multiboot2_64_bit_kernel3mb225MULTIBOOT2_INFO_STRUCTURE17h67db2667e3bd19ceE NOBITS 00000000000533a0 0334e8 000018 00 WA 0 0 8
[1520] .bss._ZN29rust_multiboot2_64_bit_kernel5panic13PANIC_HANDLER17h02bbbbc17c579b55E NOBITS 00000000000533b8 0334e8 00000c 00 WA 0 0 4
[1521] .bss._ZN29rust_multiboot2_64_bit_kernel5xuefi10UEFI_ST_BS17ha7c24b049d2b76abE NOBITS 00000000000533c8 0334e8 000008 00 WA 0 0 8
[1522] .bss._ZN29rust_multiboot2_64_bit_kernel10boot_stage10BOOT_STAGE17he05c77811885a1c6E NOBITS 00000000000533d0 0334e8 000001 00 WA 0 0 1
[1523] .bss._ZN29rust_multiboot2_64_bit_kernel11kernelalloc9ALLOCATOR17h93bce260f0439187E NOBITS 00000000000533d1 0334e8 000001 00 WA 0 0 1
[1524] .bss._ZN3log5STATE17hdfa5c64bc29aed3eE NOBITS 00000000000533d8 0334e8 000008 00 WA 0 0 8
[1525] .bss._ZN3log20MAX_LOG_LEVEL_FILTER17h84bf10c3ec44ab54E NOBITS 00000000000533e0 0334e8 000008 00 WA 0 0 8
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
l (large), p (processor specific)
Elf file type is EXEC (Executable file)
Entry point 0x4654
There are 5 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000160 0x0000000000000000 0x0000000000000000 0x004654 0x004654 R 0x10
LOAD 0x0047b4 0x0000000000004654 0x0000000000004654 0x02c035 0x02c035 R E 0x10
LOAD 0x0307f0 0x0000000000030690 0x0000000000030690 0x002cf8 0x022d58 RW 0x8
GNU_EH_FRAME 0x004788 0x0000000000004628 0x0000000000004628 0x00000c 0x00000c R 0x4
GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0
Section to Segment mapping:
Segment Sections...
00 .rodata._ZN137_$LT$rust_multib...
01 .text .text._ZN29rust_multiboot2_64_bit_kernel6logger2...
02 .data.rel.ro..L__unnamed_1 .data.rel.ro..L__unnamed_2 ...
03 .eh_frame_hdr
04
There you can see the header comes "too late" in the file. How can I solve this? How can I move it to a smaller offset in the ELF file, i.e. closer to the file begin?
PS: The ELF get's assembled by cargo/rustc/llvm, which uses GNU ld with a custom link script in my case.
Section to Segment mapping:
Segment Sections...
00 .rodata._ZN137_$LT$rust_multib...
01 .text .text._ZN29rust_multiboot2_64_bit_kernel6logger2...
...
It's clear that the .rodata._ZN.... sections are at lower offsets in the file, and the .text is not in fact the very first.
You need to move .rodata... into a separate segment (by adjusting the linker script), or you need to disable separate RO segment for the ELF header (which would allow .text to be in the first segment). See this answer.
I have a STM32 project which involves a bootloader. The bootloader CRCs the entire application region of flash and then compares this value against a firmware header stored just after the application image region in flash.
I wrote a python script which runs after the binary is built. The script takes the elf file resulting from the build, and loads each section into a "virtual flash" image, which represents what should be exactly what is present on the mcu after the elf would be normally loaded. The array starts out being the size of the application region, with initial values of 0xff for every byte, just as flash would be after a full erase. Then, the script takes each section from the elf and overwrites the section of the virtual flash images where that section should reside.
Finally, the script CRCs the application region and injects the resulting value into the original elf.
This all works fine, but I am seeing additional data in the actual flash contents of the mcu that I cannot determine the origin of. The flash is erased fully before programming the elf so this data is coming from the elf loaded onto the device.
I'm guessing that what is going on here is that there are sections in the elf that my script is ignoring but that are still being written to flash when flashed using conventional means.
The following is the result of a readelf on my application image:
Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .isr_vector PROGBITS 08020000 010000 0001f8 00 WA 0 0 4
[ 2] .firmware_header_ PROGBITS 080201f8 0101f8 000004 00 WA 0 0 4
[ 3] .text PROGBITS 08020200 010200 01e11c 00 AX 0 0 64
[ 4] .ARM.extab PROGBITS 0803e31c 033a68 000000 00 W 0 0 1
[ 5] .exidx ARM_EXIDX 0803e31c 02e31c 000008 00 AL 3 0 4
[ 6] .ARM.attributes ARM_ATTRIBUTES 0803e324 033a68 000030 00 0 0 1
[ 7] .init_array INIT_ARRAY 0803e324 02e324 000008 04 WA 0 0 4
[ 8] .fini_array FINI_ARRAY 0803e32c 02e32c 000004 04 WA 0 0 4
[ 9] .firmware_header PROGBITS 0803e330 02e330 000008 00 WA 0 0 4
[10] .data PROGBITS 20000000 030000 0009c8 00 WA 0 0 8
[11] .RxDecripSection PROGBITS 200009c8 0309c8 000080 00 WA 0 0 4
[12] .RxarraySection PROGBITS 20000a48 030a48 0017d0 00 WA 0 0 4
[13] .TxDescripSection PROGBITS 20002218 032218 000080 00 WA 0 0 4
[14] .TxarraySection PROGBITS 20002298 032298 0017d0 00 WA 0 0 4
[15] .bss NOBITS 20003a68 033a68 045bc0 00 WA 0 0 8
[16] .heap PROGBITS 20049628 033a98 000000 00 W 0 0 1
[17] .reserved_for_sta PROGBITS 20049628 033a98 000000 00 W 0 0 1
[18] .battery_backed_s NOBITS 40024000 034000 00000c 00 WA 0 0 4
[19] .comment PROGBITS 00000000 033a98 000075 01 MS 0 0 1
[20] .debug_frame PROGBITS 00000000 033b10 001404 00 0 0 4
[21] .stab PROGBITS 00000000 034f14 000084 0c 22 0 4
[22] .stabstr STRTAB 00000000 034f98 000117 00 0 0 1
[23] .symtab SYMTAB 00000000 0350b0 009010 10 24 1646 4
[24] .strtab STRTAB 00000000 03e0c0 003dc8 00 0 0 1
[25] .shstrtab STRTAB 00000000 041e88 000132 00 0 0 1
I am loading the following sections into my virtual flash image: .isr_vector, .firmware_header_vector, .text, .exidx, .ARM.attributes, .init_array, .fini_array
I do notice that some sections do have addresses of 0. Are some of these perhaps simply appended to flash?
The additional sections are most probably initial data for DATA segments. The startup code of most systems copies the contents into the RAM space allocated for those segments. This way static variables initialized with non-zero values are set up.
For example static int x = 23; should give you a segment with "23" in it. The address of this "23" in flash is not the address of x in RAM.
Test is on 32 bit x86 Linux with gcc 4.6.3
When using gcc to compile a C program and using readelf to check the section info,
I can see the .eh_frame section and .eh_frame_hdr sections inside.
For example, here is the section info of binary program Perlbench.
readelf -S perlbench
There are 28 section headers, starting at offset 0x102e48:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .interp PROGBITS 08048154 000154 000013 00 A 0 0 1
[ 2] .note.ABI-tag NOTE 08048168 000168 000020 00 A 0 0 4
[ 3] .note.gnu.build-i NOTE 08048188 000188 000024 00 A 0 0 4
[ 4] .gnu.hash GNU_HASH 080481ac 0001ac 000044 04 A 5 0 4
[ 5] .dynsym DYNSYM 080481f0 0001f0 0007b0 10 A 6 1 4
[ 6] .dynstr STRTAB 080489a0 0009a0 0003d6 00 A 0 0 1
[ 7] .gnu.version VERSYM 08048d76 000d76 0000f6 02 A 5 0 2
[ 8] .gnu.version_r VERNEED 08048e6c 000e6c 0000a0 00 A 6 2 4
[ 9] .rel.dyn REL 08048f0c 000f0c 000028 08 A 5 0 4
[10] .rel.plt REL 08048f34 000f34 000388 08 A 5 12 4
[11] .init PROGBITS 080492bc 0012bc 00002e 00 AX 0 0 4
[12] .plt PROGBITS 080492f0 0012f0 000720 04 AX 0 0 16
[13] .text PROGBITS 08049a10 001a10 0cf86c 00 AX 0 0 16
[14] .fini PROGBITS 0811927c 0d127c 00001a 00 AX 0 0 4
[15] .rodata PROGBITS 081192a0 0d12a0 017960 00 A 0 0 32
[16] .eh_frame_hdr PROGBITS 08130c00 0e8c00 003604 00 A 0 0 4
[17] .eh_frame PROGBITS 08134204 0ec204 01377c 00 A 0 0 4
[18] .ctors PROGBITS 08148f0c 0fff0c 000008 00 WA 0 0 4
[19] .dtors PROGBITS 08148f14 0fff14 000008 00 WA 0 0 4
[20] .jcr PROGBITS 08148f1c 0fff1c 000004 00 WA 0 0 4
[21] .dynamic DYNAMIC 08148f20 0fff20 0000d0 08 WA 6 0 4
[22] .got PROGBITS 08148ff0 0ffff0 000004 04 WA 0 0 4
[23] .got.plt PROGBITS 08148ff4 0ffff4 0001d0 04 WA 0 0 4
[24] .data PROGBITS 081491e0 1001e0 002b50 00 WA 0 0 32
[25] .bss NOBITS 0814bd40 102d30 002b60 00 WA 0 0 32
[26] .comment PROGBITS 00000000 102d30 00002a 01 MS 0 0 1
[27] .shstrtab STRTAB 00000000 102d5a 0000ec 00 0 0 1
In my understanding, these two sections are used for handling exceptions, it produce tables that describe how to unwind the stack.
But it is for C++ program, they use eh_frame and gcc_exception_table sections to manage exceptions, then why does compiler put the eh_frame and eh_frame_hdr sections inside ELF compiled from C program?
First of all, the original reason for this was largely political - the people who added DWARF-based unwinding (.eh_frame) wanted it to be a feature that's always there so it could be used for implementing all kinds of stuff other than just C++ exceptions, including:
backtrace()
__attribute__((__cleanup__(f)))
__builtin_return_address(n), for n>0
pthread_cleanup_push, implemented in terms of __attribute__((__cleanup__(f)))
...
However if you don't need any of these things, .eh_frame is something like a 15-30% increase to .text size with no benefit. You can disable generation of .eh_frame with -fno-asynchronous-unwind-tables for individual translation units, and this mostly eliminates the size cost, although you still have a few left over coming from crtbegin.o, etc. You cannot strip them with the strip command later; since .eh_frame is a section that lives in the loaded part of the program (this is the whole point), stripping it modifies the binary in ways that break it at runtime. See https://sourceware.org/bugzilla/show_bug.cgi?id=14037 for an example of how things can break.
Note that DWARF tables are also used for debugging, but for this purpose they do not need to be in the loadable part of the program. Using -fno-asynchronous-unwind-tables will not break debugging, because as long as -g is also passed to the compiler, the tables still get generated; they just get stored in a separate, non-loadable, strippable section of the binary, .debug_frame.