Linking with gcc increases file size to 16 KB - c

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

Related

Why .eh_frame and .eh_frame_hdr does not exist in the clang 32bit so?

I'm trying to use the command readelf -S libtest.so on the 32bit libtest.so which compiled with clang11 --target=arm-linux-androideabi21 -march=armv7-a & cflags -funwind-table -fno-exceptions.
The .eh_frame or .eh_frame_hdr segment can not be found in the output.
However,it is surely exist in the 64-bit .so(compiled with --target=aarch64-linux-android21). Does anyone known the reason?
32bit:
---------------------------------------------------------------------------------
There are 29 section headers, starting at offset 0x2c3c6b8:
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 00000174 000174 000013 00 A 0 0 1
[ 2] .note.android.ide NOTE 00000188 000188 000098 00 A 0 0 4
[ 3] .note.crashpad.in NOTE 00000220 000220 00001c 00 A 0 0 4
[ 4] .note.gnu.build-i NOTE 0000023c 00023c 000024 00 A 0 0 4
[ 5] .dynsym DYNSYM 00000260 000260 005550 10 A 10 1 4
[ 6] .gnu.version VERSYM 000057b0 0057b0 000aaa 02 A 5 0 2
[ 7] .gnu.version_r VERNEED 0000625c 00625c 000060 00 A 10 3 4
[ 8] .gnu.hash GNU_HASH 000062bc 0062bc 001a14 00 A 5 0 4
[ 9] .hash HASH 00007cd0 007cd0 002ab0 04 A 5 0 4
[10] .dynstr STRTAB 0000a780 00a780 016bf6 00 A 0 0 1
[11] .rel.dyn LOOS+0x1 00021378 021378 0297f6 01 A 5 0 4
[12] .ARM.exidx ARM_EXIDX 0004ab70 04ab70 1be028 00 AL 16 0 4
[13] .rel.plt REL 00208b98 208b98 000db0 08 A 5 23 4
[14] .rodata PROGBITS 00209a00 209a00 55b9ac 00 AMS 0 0 256
[15] .ARM.extab PROGBITS 007653ac 7653ac 005d04 00 A 0 0 4
[16] .text PROGBITS 0076b0c0 76b0c0 22e96f4 00 AX 0 0 64
[17] .plt PROGBITS 02a547c0 2a547c0 001b80 00 AX 0 0 16
[18] .fini_array FINI_ARRAY 02a57340 2a56340 000008 00 WA 0 0 4
[19] .data.rel.ro PROGBITS 02a57348 2a56348 1c300c 00 WA 0 0 8
[20] .init_array INIT_ARRAY 02c1a354 2c19354 000018 00 WA 0 0 4
[21] .dynamic DYNAMIC 02c1a36c 2c1936c 000100 08 WA 10 0 4
[22] .got PROGBITS 02c1a46c 2c1946c 000298 00 WA 0 0 4
[23] .got.plt PROGBITS 02c1a704 2c19704 0006e4 00 WA 0 0 4
[24] .bss.rel.ro NOBITS 02c1ade8 2c19de8 000000 00 WA 0 0 1
[25] .data PROGBITS 02c1bde8 2c19de8 022624 00 WA 0 0 8
[26] .bss NOBITS 02c3e410 2c3c40c 439c3c 00 WA 0 0 16
[27] .comment PROGBITS 00000000 2c3c40c 000193 01 MS 0 0 1
[28] .shstrtab STRTAB 00000000 2c3c59f 000116 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),
y (purecode), p (processor specific)
---------------------------------------------------------------------------------
64bit:
---------------------------------------------------------------------------------
There are 28 section headers, starting at offset 0x59b4b30:
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .note.android.ide NOTE 0000000000000238 00000238
0000000000000098 0000000000000000 A 0 0 4
[ 2] .note.crashpad.in NOTE 00000000000002d0 000002d0
0000000000000020 0000000000000000 A 0 0 4
[ 3] .note.gnu.build-i NOTE 00000000000002f0 000002f0
0000000000000024 0000000000000000 A 0 0 4
[ 4] .dynsym DYNSYM 0000000000000318 00000318
0000000000007cb0 0000000000000018 A 9 1 8
[ 5] .gnu.version VERSYM 0000000000007fc8 00007fc8
0000000000000a64 0000000000000002 A 4 0 2
[ 6] .gnu.version_r VERNEED 0000000000008a2c 00008a2c
0000000000000060 0000000000000000 A 9 3 4
[ 7] .gnu.hash GNU_HASH 0000000000008a90 00008a90
00000000000018e0 0000000000000000 A 4 0 8
[ 8] .hash HASH 000000000000a370 0000a370
0000000000002998 0000000000000004 A 4 0 4
[ 9] .dynstr STRTAB 000000000000cd08 0000cd08
000000000001949f 0000000000000000 A 0 0 1
[10] .rela.dyn LOOS+0x2 00000000000261a8 000261a8
0000000000137999 0000000000000001 A 4 0 8
[11] .rela.plt RELA 000000000015db48 0015db48
0000000000002af0 0000000000000018 AI 4 24 8
[12] .rodata PROGBITS 0000000000160700 00160700
00000000006e0d98 0000000000000000 AMS 0 0 256
[13] .gcc_except_table PROGBITS 0000000000841498 00841498
0000000000000f84 0000000000000000 A 0 0 4
[14] .eh_frame_hdr PROGBITS 000000000084241c 0084241c
00000000003539c4 0000000000000000 A 0 0 4
[15] .eh_frame PROGBITS 0000000000b95de0 00b95de0
0000000000d5494c 0000000000000000 A 0 0 8
[16] .text PROGBITS 00000000018ea740 018ea740
0000000003cb336c 0000000000000000 AX 0 0 64
[17] malloc_hook PROGBITS 000000000559daac 0559daac
00000000000000c8 0000000000000000 AX 0 0 2
[18] .plt PROGBITS 000000000559db80 0559db80
0000000000001cc0 0000000000000000 AX 0 0 16
[19] .data.rel.ro PROGBITS 00000000055a0840 0559f840
00000000003a4d48 0000000000000000 WA 0 0 8
[20] .fini_array FINI_ARRAY 0000000005945588 05944588
0000000000000010 0000000000000008 WA 0 0 8
[21] .init_array INIT_ARRAY 0000000005945598 05944598
0000000000000020 0000000000000000 WA 0 0 8
[22] .dynamic DYNAMIC 00000000059455b8 059445b8
0000000000000200 0000000000000010 WA 9 0 8
[23] .got PROGBITS 00000000059457b8 059447b8
000000000001c578 0000000000000000 WA 0 0 8
[24] .got.plt PROGBITS 0000000005961d30 05960d30
0000000000000e68 0000000000000000 WA 0 0 8
[25] .data PROGBITS 0000000005963b98 05961b98
0000000000052e78 0000000000000000 WA 0 0 8
[26] .bss NOBITS 00000000059b6a40 059b4a10
00000000004cbb40 0000000000000000 WA 0 0 64
[27] .shstrtab STRTAB 0000000000000000 059b4a10
000000000000011b 0000000000000000 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),
p (processor specific)
---------------------------------------------------------------------------------
I found the size different between -funwind-table and -fno-unwind-table in arm32's libtest.so:
-funwind-table:
FILE SIZE VM SIZE
-------------- --------------
5.2% 2.77Mi 4.8% 2.77Mi .ARM.exidx
0.0% 25.6Ki 0.0% 25.6Ki .ARM.extab
-fno-unwind-table:
FILE SIZE VM SIZE
-------------- --------------
0.0% 5.36Ki 0.0% 5.36Ki .ARM.exidx
0.0% 5.62Ki 0.0% 5.62Ki .ARM.extab
so, the unwind-table info of arm32 is in .ARM.exidx/.ARM.extab, not the same with .eh_frame/.eh_frame_hdr in arm64.

multiboot2 header comes "too late" in ELF file (to large offset), even if it is the very first section

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.

Additional data present in flash after last loaded section of elf

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.

Why GCC compiled C program needs .eh_frame section?

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.

How to genernate position-dependent code for executable file when linking with shared library in gcc?

I'm learning ELF.I want to find the difference of elf format between position-dependent executable file and position-independent executable file when linking shared library.
But I can't genernate position-dependent code for executable file when linking with shared library.
/*Lib.c*/
static int a;
extern int b;
int c=1;
extern void exit();
void set_value()
{
a=1;
b=1;
c=1;
}
void run()
{
set_value();
exit();
}
Fist,use gcc to genrnate a shared dynamic library:
gcc -m32 -nostdlib -o Lib.so Lib.c
Note that I don't use -fpic to generate position-independent code for Lib.so.
Now,I have another file main.c which need to link with Lib.so:
/*main.c*/
extern void run();
int b=2;
void nomain()
{
run();
}
void exit()
{
asm("int $0x80 \n\t"
::"a"(1),"b"(42));
}
use following command to link main.c with Lib.so:
gcc -m32 -e nomain -nostartfiles -fno-builtin -o a.out main.c ./Lib.so
Howerve,gcc will compile main.c to position-independent code defaultly when link with shared library even this library don't use pic.
I wonder whether gcc have some option to genernate position-dependent for executable file?
I post the section information for Lib.so and a.out.We can see there are '.plt' and '.got.plt' section in a.out which mean a.out use PIC.
/*Section for Lib.so*/
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .gnu.hash GNU_HASH 000000b4 0000b4 00003c 04 A 2 0 4
[ 2] .dynsym DYNSYM 000000f0 0000f0 000090 10 A 3 1 4
[ 3] .dynstr STRTAB 00000180 000180 000030 00 A 0 0 1
[ 4] .rel.dyn REL 000001b0 0001b0 000028 08 A 2 0 4
[ 5] .text PROGBITS 000001d8 0001d8 000033 00 AX 0 0 4
[ 6] .dynamic DYNAMIC 0000120c 00020c 000078 08 WA 3 0 4
[ 7] .got.plt PROGBITS 00001284 000284 00000c 04 WA 0 0 4
[ 8] .data PROGBITS 00001290 000290 000004 00 WA 0 0 4
[ 9] .bss NOBITS 00001294 000294 000004 00 WA 0 0 4
[10] .comment PROGBITS 00000000 000294 00002e 00 0 0 1
[11] .shstrtab STRTAB 00000000 0002c2 00006a 00 0 0 1
[12] .symtab SYMTAB 00000000 00055c 000170 10 13 15 4
[13] .strtab STRTAB 00000000 0006cc 000057 00 0 0 1
/*Section for a.out*/
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 080480f4 0000f4 000013 00 A 0 0 1
[ 2] .gnu.hash GNU_HASH 08048108 000108 000034 04 A 3 0 4
[ 3] .dynsym DYNSYM 0804813c 00013c 000070 10 A 4 1 4
[ 4] .dynstr STRTAB 080481ac 0001ac 000037 00 A 0 0 1
[ 5] .rel.plt REL 080481e4 0001e4 000008 08 A 3 6 4
[ 6] .plt PROGBITS 080481ec 0001ec 000020 04 AX 0 0 4
[ 7] .text PROGBITS 0804820c 00020c 000020 00 AX 0 0 4
[ 8] .dynamic DYNAMIC 0804922c 00022c 000090 08 WA 4 0 4
[ 9] .got.plt PROGBITS 080492bc 0002bc 000010 04 WA 0 0 4
[10] .data PROGBITS 080492cc 0002cc 000004 00 WA 0 0 4
[11] .comment PROGBITS 00000000 0002d0 00002e 00 0 0 1
[12] .shstrtab STRTAB 00000000 0002fe 00006d 00 0 0 1
[13] .symtab SYMTAB 00000000 0005c4 000160 10 14 15 4
[14] .strtab STRTAB 00000000 000724 000051 00 0 0 1
You should compile your Lib.so with gcc -Wall -Wextra -m32 -shared -Wl,-soname,Lib.so -o Lib.so Lib.c.
What you did here is just to generate a normal program without relocation information.
Look at this tutorial to know more about dynamic libraries.
Note: Also, don't forget to set your LD_LIBRARY_PATH environment variable in order to point to Lib.so.

Resources