I'm trying to drop a C program down to a size < 1kb. I'm getting close, but I'm stuck on editing my ELF executable. My program, main.c looks like:
#include<unistd.h>
#include<sys/syscall.h>
void _start() {
const char msg [] = "Hello World!";
syscall(SYS_write, 0, msg, sizeof(msg)-1);
syscall(SYS_exit, 0);
}
I'm compiling it with
gcc -nostdlib -s -O3 -o main main.c /usr/lib/path/to/libc.a
Then I strip it. But If I did an objdump on it before stripping it I see
main: file format elf64-x86-64
SYMBOL TABLE:
0000000000400158 l d .note.gnu.build-id 0000000000000000 .note.gnu.build-id
0000000000400180 l d .text 0000000000000000 .text
0000000000400214 l d .eh_frame_hdr 0000000000000000 .eh_frame_hdr
0000000000400238 l d .eh_frame 0000000000000000 .eh_frame
0000000000601000 l d .tbss 0000000000000000 .tbss
0000000000000000 l d .comment 0000000000000000 .comment
0000000000000000 l df *ABS* 0000000000000000 main.c
0000000000000000 l df *ABS* 0000000000000000
00000000004001d0 g F .text 0000000000000026 syscall
0000000000000000 g .tbss 0000000000000004 errno
0000000000400203 g .text 0000000000000000 __syscall_error_1
0000000000400180 g F .text 0000000000000048 _start
0000000000000000 g .tbss 0000000000000004 __libc_errno
0000000000400200 g F .text 0000000000000013 __syscall_error
0000000000601000 g .eh_frame 0000000000000000 __bss_start
0000000000601000 g .eh_frame 0000000000000000 _edata
0000000000000000 *UND* 0000000000000000 _GLOBAL_OFFSET_TABLE_
0000000000601000 g .eh_frame 0000000000000000 _end
It seems like there's some stuff I can remove to manually reduce the size of the executable? note: I know this is not something I'd actually do, but I'm simply trying to remove any boilerplate existing.
What would I remove from the executable, main, to reduce it's size? And how could I do that?
side note: I've already read this and this article. No need to link them. I am purposely choosing to stay with C
Simple stuff
You can remove quite a few useless bits with:
-fno-asynchronous-unwind-tables -Qn;
using a custom linker script -rlinker_script.
I get a working binary of 992 bytes with this (after strip).
Linker script
Let's look at the sections (before stripping):
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .note.gnu.build-i NOTE 0000000000400120 00000120
0000000000000024 0000000000000000 A 0 0 4
[ 2] .text PROGBITS 0000000000400150 00000150
0000000000000090 0000000000000000 AX 0 0 16
[ 3] .eh_frame PROGBITS 00000000004001e0 000001e0
0000000000000048 0000000000000000 A 0 0 8
[ 4] .tbss NOBITS 0000000000601000 00000228
0000000000000004 0000000000000000 WAT 0 0 4
[ 5] .shstrtab STRTAB 0000000000000000 000003e7
0000000000000044 0000000000000000 0 0 1
[ 6] .symtab SYMTAB 0000000000000000 00000228
0000000000000168 0000000000000018 7 6 8
[ 7] .strtab STRTAB 0000000000000000 00000390
0000000000000057 0000000000000000
From program header 5, everything is stripped but we gave two comparatively useless sections which are not stripped: .note.gnu.build-id and .eh_frame. The .eh_frame was disabled in the compiler but some .eh_frame is contributed from the static libc.
We can completely get rid of the .eh_frame and .note.gnu.build-id sections with a custom linker script (gcc -T linker_script).
First, we get the default linker script:
gcc test.c -Wl,--verbose
We remove those lines:
.eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) }
.eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) }
.note.gnu.build-id : { *(.note.gnu.build-id) }
and modify this line:
/DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) *(.note.gnu.build-id) *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) *(.eh_frame) *(.eh_frame.*) }
I get 664 bytes using this.
Additional options
Other solutions for a reduced size:
optimize for size (-Os);
32 bit compilation (-m32).
With all of this, I get a binary with 760 bytes without the custom linker script and 488 bytes with the modified linker script.
Get rid of errno
There are quite few "useless" things remaining (such as errno handling and the TLS) that could be removed.
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 080480a0 0000a0 00008e 00 AX 0 0 16
[ 2] .tbss NOBITS 08049130 000130 000004 00 WAT 0 0 4
[ 3] .shstrtab STRTAB 00000000 000257 000027 00 0 0 1
[ 4] .symtab SYMTAB 00000000 000130 0000d0 10 5 4 4
[ 5] .strtab STRTAB 00000000 000200 000057 00 0 0 1
(Everything beginning with section 3 is stripped.)
By writing our own syscall code we could get rid of errno handling. We would remove:
4 bytes of .symtab;
errno related instructions.
But doing this involves using (inline) assembly.
Related
When trying to run the command,
sudo tc filter add dev veth0 egress bpf direct-action obj a.o sec classifier
I'm getting this error,
libbpf: prog 'pdm_main': bad map relo against '.rodata.str1.1' in section '.rodata.str1.1'
ERROR: opening BPF object file failed
Unable to load program
The sections in my object file,
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00000000 0000000000000000 0000000000000000 00000040 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 classifier 00000638 0000000000000000 0000000000000000 00000040 2**3
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
2 .maps 00000020 0000000000000000 0000000000000000 00000678 2**3
CONTENTS, ALLOC, LOAD, DATA
3 .rodata.str1.1 0000002c 0000000000000000 0000000000000000 00000698 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
4 .rodata.str1.16 00000036 0000000000000000 0000000000000000 000006d0 2**4
CONTENTS, ALLOC, LOAD, READONLY, DATA
5 license 00000004 0000000000000000 0000000000000000 00000706 2**0
CONTENTS, ALLOC, LOAD, DATA
6 .debug_loclists 00000105 0000000000000000 0000000000000000 0000070a 2**0
CONTENTS, READONLY, DEBUGGING, OCTETS
7 .debug_abbrev 000001c9 0000000000000000 0000000000000000 0000080f 2**0
CONTENTS, READONLY, DEBUGGING, OCTETS
8 .debug_info 0000080f 0000000000000000 0000000000000000 000009d8 2**0
CONTENTS, RELOC, READONLY, DEBUGGING, OCTETS
9 .debug_str_offsets 000002a4 0000000000000000 0000000000000000 000011e7 2**0
CONTENTS, RELOC, READONLY, DEBUGGING, OCTETS
10 .debug_str 000006ec 0000000000000000 0000000000000000 0000148b 2**0
CONTENTS, READONLY, DEBUGGING, OCTETS
11 .debug_addr 00000030 0000000000000000 0000000000000000 00001b77 2**0
CONTENTS, RELOC, READONLY, DEBUGGING, OCTETS
12 .BTF 00001037 0000000000000000 0000000000000000 00001ba8 2**2
CONTENTS, RELOC, READONLY
13 .BTF.ext 00000470 0000000000000000 0000000000000000 00002be0 2**2
CONTENTS, RELOC, READONLY
14 .eh_frame 00000030 0000000000000000 0000000000000000 00003050 2**3
CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
15 .debug_line 00000220 0000000000000000 0000000000000000 00003080 2**0
CONTENTS, RELOC, READONLY, DEBUGGING, OCTETS
16 .debug_line_str 000000f8 0000000000000000 0000000000000000 000032a0 2**0
CONTENTS, READONLY, DEBUGGING, OCTETS
How to fix this? When i don't use eBPF map in my code it doesn't give the same error.
I am trying to compile and run the sockex3 example (sockex3_user.c and sockex3_kern.c from v5.10) in a new project (out of the kernel tree) with libbpf v0.3 as submodule of my repository. I have compiled the code with the following flags:
kernel (ebpf program)
set(CMAKE_C_COMPILER clang)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -g -target bpf")
user
set(CMAKE_C_COMPILER clang)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -g")
clang
$ clang -v
clang version 11.0.0
So, when I run the binary, I have faced the following error:
$ sudo ./build/samples/sockex3
[sudo] password for navarro:
libbpf: loading ./build/samples/bpf/CMakeFiles/sockex3_kern.dir/sockex3_kern.c.o
libbpf: elf: section(3) socket/3, size 1264, link 0, flags 6, type=1
libbpf: sec 'socket/3': found program 'bpf_func_PARSE_IP' at insn offset 0 (0 bytes), code size 158 insns (1264 bytes)
libbpf: elf: section(4) .relsocket/3, size 128, link 31, flags 0, type=9
libbpf: elf: section(5) socket/4, size 1360, link 0, flags 6, type=1
libbpf: sec 'socket/4': found program 'bpf_func_PARSE_IPV6' at insn offset 0 (0 bytes), code size 170 insns (1360 bytes)
libbpf: elf: section(6) .relsocket/4, size 112, link 31, flags 0, type=9
libbpf: elf: section(7) socket/1, size 256, link 0, flags 6, type=1
libbpf: sec 'socket/1': found program 'bpf_func_PARSE_VLAN' at insn offset 0 (0 bytes), code size 32 insns (256 bytes)
libbpf: elf: section(8) .relsocket/1, size 16, link 31, flags 0, type=9
libbpf: elf: section(9) socket/2, size 120, link 0, flags 6, type=1
libbpf: sec 'socket/2': found program 'bpf_func_PARSE_MPLS' at insn offset 0 (0 bytes), code size 15 insns (120 bytes)
libbpf: elf: section(10) .relsocket/2, size 16, link 31, flags 0, type=9
libbpf: elf: section(11) socket/0, size 32, link 0, flags 6, type=1
libbpf: sec 'socket/0': found program 'main_prog' at insn offset 0 (0 bytes), code size 4 insns (32 bytes)
libbpf: elf: section(12) license, size 4, link 0, flags 3, type=1
libbpf: license of ./build/samples/bpf/CMakeFiles/sockex3_kern.dir/sockex3_kern.c.o is GPL
libbpf: elf: section(13) .maps, size 96, link 0, flags 3, type=1
libbpf: elf: section(22) .BTF, size 4807, link 0, flags 0, type=1
libbpf: elf: section(24) .BTF.ext, size 2704, link 0, flags 0, type=1
libbpf: elf: section(31) .symtab, size 5904, link 1, flags 0, type=2
libbpf: looking for externs among 246 symbols...
libbpf: failed to find BTF for extern 'unlikely': -2
ERROR: opening BPF object file failed
I am trying to understand how/when this symbol is define. I found the symbol here.
Why libbpf is not finding it? Am I missing anything? FYI, I removed the uapi from the kernel file (sockex3_kern.c) and copied the dependencies (i.e. bpf_legacy.h) to my repository.
Another thing that I cant understand is why the sections (below) of the object file (out of the kernel tree) are different
31 .llvm_addrsig 00000012 0000000000000000 <--- Available in the object out of the tree
when comparing to the object file generated in the kernel tree.
Out-of-tree
$ llvm-objdump -h build/samples/bpf/CMakeFiles/sockex3_kern.dir/sockex3_kern.c.o
build/samples/bpf/CMakeFiles/sockex3_kern.dir/sockex3_kern.c.o: file format elf64-bpf
Sections:
Idx Name Size VMA Type
0 00000000 0000000000000000
1 .strtab 000002c2 0000000000000000
2 .text 00000000 0000000000000000 TEXT
3 socket/3 000004d0 0000000000000000 TEXT
4 .relsocket/3 00000070 0000000000000000
5 socket/4 00000550 0000000000000000 TEXT
6 .relsocket/4 00000070 0000000000000000
7 socket/1 00000100 0000000000000000 TEXT
8 .relsocket/1 00000010 0000000000000000
9 socket/2 00000078 0000000000000000 TEXT
10 .relsocket/2 00000010 0000000000000000
11 socket/0 000000e8 0000000000000000 TEXT
12 .relsocket/0 00000010 0000000000000000
13 license 00000004 0000000000000000 DATA
14 .maps 00000060 0000000000000000 DATA
15 .debug_loc 00000ef1 0000000000000000
16 .rel.debug_loc 00000370 0000000000000000
17 .debug_abbrev 000002b9 0000000000000000
18 .debug_info 00000f1b 0000000000000000
19 .rel.debug_info 000013c0 0000000000000000
20 .debug_ranges 000002e0 0000000000000000
21 .rel.debug_ranges 00000420 0000000000000000
22 .debug_str 000007c2 0000000000000000
23 .BTF 000012e1 0000000000000000
24 .rel.BTF 00000040 0000000000000000
25 .BTF.ext 00000ac0 0000000000000000
26 .rel.BTF.ext 00000a70 0000000000000000
27 .debug_frame 00000088 0000000000000000
28 .rel.debug_frame 000000a0 0000000000000000
29 .debug_line 0000049e 0000000000000000
30 .rel.debug_line 00000050 0000000000000000
31 .llvm_addrsig 00000012 0000000000000000
32 .symtab 00001788 0000000000000000
Kernel tree
$ llvm-objdump -h sockex3_kern.o
sockex3_kern.o: file format elf64-bpf
Sections:
Idx Name Size VMA Type
0 00000000 0000000000000000
1 .strtab 000002b1 0000000000000000
2 .text 00000000 0000000000000000 TEXT
3 socket/3 000004d8 0000000000000000 TEXT
4 .relsocket/3 00000070 0000000000000000
5 socket/4 00000558 0000000000000000 TEXT
6 .relsocket/4 00000070 0000000000000000
7 socket/1 00000100 0000000000000000 TEXT
8 .relsocket/1 00000010 0000000000000000
9 socket/2 00000078 0000000000000000 TEXT
10 .relsocket/2 00000010 0000000000000000
11 socket/0 000000e8 0000000000000000 TEXT
12 .relsocket/0 00000010 0000000000000000
13 license 00000004 0000000000000000 DATA
14 .maps 00000060 0000000000000000 DATA
15 .debug_loc 00000ef1 0000000000000000
16 .rel.debug_loc 00000370 0000000000000000
17 .debug_abbrev 0000029f 0000000000000000
18 .debug_info 00000d4b 0000000000000000
19 .rel.debug_info 000011b0 0000000000000000
20 .debug_ranges 000002e0 0000000000000000
21 .rel.debug_ranges 00000420 0000000000000000
22 .debug_str 0000067d 0000000000000000
23 .BTF 000012c2 0000000000000000
24 .rel.BTF 00000040 0000000000000000
25 .BTF.ext 00000ac0 0000000000000000
26 .rel.BTF.ext 00000a70 0000000000000000
27 .eh_frame 000000b0 0000000000000000 DATA
28 .rel.eh_frame 00000050 0000000000000000
29 .debug_line 0000046b 0000000000000000
30 .rel.debug_line 00000050 0000000000000000
31 .symtab 00001500 0000000000000000
Thank you in advance!
I am running Ubuntu 18.04.2 LTS and trying to build some code using gcc and getting the following error:
/usr/lib/gcc/x86_64-linux-gnu/7/../../../../x86_64-linux-gnu/lib/../lib/crt1.o: In function `_start':
(.text+0x20): undefined reference to `main'
/lib64/libgcc_s.so.1: undefined reference to `memcpy#GLIBC_2.14'
collect2: error: ld returned 1 exit status
I believe that the problem does not lie in the project but the object file that is trying to use a GLIBC version unavailable on my system but is part of my system.
The object file is /usr/x86_64-linux-gnu/lib/crt1.o part of libc6-dev-amd64-cross version 2.27-3ubuntu1cross1.1.
I am not too familiar with gcc and it seems that I cannot just pull a libc 2.14 and get it to build.
I am stuck.
What should I check? Do I need to use an option for gcc? Or perhaps is this a problem in my distribution?
More info:
# readlink -f /usr/lib/gcc/x86_64-linux-gnu/7/../../../../x86_64-linux-gnu/lib/../lib/crt1.o
/usr/x86_64-linux-gnu/lib/crt1.o
# file /usr/x86_64-linux-gnu/lib/crt1.o
/usr/x86_64-linux-gnu/lib/crt1.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), for GNU/Linux 3.2.0, not stripped
# apt-file search /usr/x86_64-linux-gnu/lib/crt1.o
libc6-dev-amd64-cross: /usr/x86_64-linux-gnu/lib/crt1.o
# dpkg -l | grep libc6-dev-amd64-cross
ii libc6-dev-amd64-cross 2.27-3ubuntu1cross1.1 [...]
# objdump -t /usr/x86_64-linux-gnu/lib/crt1.o
/usr/x86_64-linux-gnu/lib/crt1.o: file format elf64-x86-64
SYMBOL TABLE:
0000000000000000 l d .text 0000000000000000 .text
0000000000000000 l d .note.ABI-tag 0000000000000000 .note.ABI-tag
0000000000000000 l d .rodata.cst4 0000000000000000 .rodata.cst4
0000000000000000 l d .eh_frame 0000000000000000 .eh_frame
0000000000000000 l d .data 0000000000000000 .data
0000000000000000 l d .bss 0000000000000000 .bss
0000000000000000 l d .note.GNU-stack 0000000000000000 .note.GNU-stack
0000000000000000 *UND* 0000000000000000 __libc_csu_fini
0000000000000030 g F .text 0000000000000002 .hidden _dl_relocate_static_pie
0000000000000000 g F .text 000000000000002b _start
0000000000000000 *UND* 0000000000000000 __libc_csu_init
0000000000000000 *UND* 0000000000000000 main
0000000000000000 w .data 0000000000000000 data_start
0000000000000000 *UND* 0000000000000000 _GLOBAL_OFFSET_TABLE_
0000000000000000 g O .rodata.cst4 0000000000000004 _IO_stdin_used
0000000000000000 *UND* 0000000000000000 __libc_start_main
0000000000000000 g .data 0000000000000000 __data_start
# gcc --version
gcc (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0
[...]
# ldd --version
ldd (Ubuntu GLIBC 2.27-3ubuntu1) 2.27
[...]
Edit 1: I have checked out this question but as crt1.o is not an object I compiled but part of a system package installed by Ubuntu, I think I am unable to re-compile it. But I might be wrong, I am not really familiar with gcc.
I am currently writing a compiler (http://curly-lang.org if you're curious), and have been encountering a strange bug when trying to run the generated ELF binaries on the latest Linux kernel. The same binaries run fine on older kernels (I've tried on several Ubuntu boxes, uname 4.4.0-1049-aws), but on my updated Arch box (uname 4.17.11-arch1), I can't even open them under GDB.
The error message given by GDB is During startup program terminated with signal SIGSEGV, Segmentation fault, which as I understand is indicative of a failure to load the program segments before the first instruction is ever run.
I compiled a minimal ELF executable with GCC/NASM to try and reproduce the problem, but the GCC-produced executable loads without a hitch, whereas my programs definitely do not.
Here are the printouts of readelf -a for both executables, for reference. The first is the program generated by my compiler :
$ readelf -a my-program
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x400040
Start of program headers: 2400 (bytes into file)
Start of section headers: 2568 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 3
Size of section headers: 64 (bytes)
Number of section headers: 6
Section header string table index: 1
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] STRTAB 0000000000000000 00000b88
000000000000009e 0000000000000000 0 0 0
[ 2] .init PROGBITS 0000000000400040 00000040
000000000000006b 0000000000000000 AX 0 0 0
[ 3] .text PROGBITS 00000000004000ab 000000ab
0000000000000824 0000000000000000 AX 0 0 0
[ 4] .data PROGBITS 00000000008008cf 000008cf
0000000000000091 0000000000000000 WA 0 0 0
[ 5] .symtab SYMTAB 0000000000000000 00000c26
00000000000000c0 0000000000000018 1 8 0
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)
There are no section groups in this file.
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x0000000000000040 0x0000000000400040 0x0000000000000000
0x000000000000006b 0x000000000000006b R E 0x1000
LOAD 0x00000000000000ab 0x00000000004000ab 0x0000000000000000
0x0000000000000824 0x0000000000000824 R E 0x1000
LOAD 0x00000000000008cf 0x00000000008008cf 0x0000000000000000
0x0000000000000091 0x0000000000000091 RW 0x1000
Section to Segment mapping:
Segment Sections...
00 .init
01 .text
02 .data
There is no dynamic section in this file.
There are no relocations in this file.
The decoding of unwind sections for machine type Advanced Micro Devices X86-64 is not currently supported.
Symbol table '.symtab' contains 8 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000004004e0 0 NOTYPE LOCAL HIDDEN 3 .text.argument
1: 00000000004005a0 0 NOTYPE LOCAL HIDDEN 3 .text.constant
2: 0000000000400270 0 NOTYPE LOCAL HIDDEN 3 .text.memextend-page
3: 0000000000400210 0 NOTYPE LOCAL HIDDEN 3 .text.memextend-pool-32
4: 00000000004002b0 0 NOTYPE LOCAL HIDDEN 3 .text.unit
5: 00000000004005f0 0 NOTYPE LOCAL HIDDEN 3 .text.write
6: 00000000008008d0 0 NOTYPE LOCAL HIDDEN 4 .data.brkaddr
7: 0000000000400040 0 NOTYPE LOCAL HIDDEN 2 .init.brkaddr-init
No version information found in this file.
And for the GCC-generated program :
$ readelf -a gcc-program
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x400110
Start of program headers: 64 (bytes into file)
Start of section headers: 336 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 3
Size of section headers: 64 (bytes)
Number of section headers: 5
Section header string table index: 4
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .note.gnu.build-i NOTE 00000000004000e8 000000e8
0000000000000024 0000000000000000 A 0 0 4
[ 2] .text PROGBITS 0000000000400110 00000110
0000000000000010 0000000000000000 AX 0 0 16
[ 3] .data PROGBITS 0000000000600120 00000120
0000000000000001 0000000000000000 WA 0 0 4
[ 4] .shstrtab STRTAB 0000000000000000 00000121
000000000000002a 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),
l (large), p (processor specific)
There are no section groups in this file.
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x0000000000000120 0x0000000000000120 R E 0x200000
LOAD 0x0000000000000120 0x0000000000600120 0x0000000000600120
0x0000000000000001 0x0000000000000001 RW 0x200000
NOTE 0x00000000000000e8 0x00000000004000e8 0x00000000004000e8
0x0000000000000024 0x0000000000000024 R 0x4
Section to Segment mapping:
Segment Sections...
00 .note.gnu.build-id .text
01 .data
02 .note.gnu.build-id
There is no dynamic section in this file.
There are no relocations in this file.
The decoding of unwind sections for machine type Advanced Micro Devices X86-64 is not currently supported.
No version information found in this file.
Displaying notes found in: .note.gnu.build-id
Owner Data size Description
GNU 0x00000014 NT_GNU_BUILD_ID (unique build ID bitstring)
Build ID: 1a3e678b08996ee6a9d289c3f76c7c52cd4a30aa
As you can see, I've tried to mirror GCC's segment placement (~0x400000 for the code, and ~0x800000 for the data), and the two ELF headers are strictly identical. The only meaningful difference I can think of is that my custom binaries have two LOAD segments (one for the initialization code, one for the rest) that share the same page, whereas GCC only produces a single code LOAD segment. That shouldn't pose a problem, though, since they both share the same permissions and don't overlap.
Other than that, I do not see what could possibly keep the first program from loading correctly. If anyone well-versed in the arcanes of the Linux ELF loader could enlighten me, that would be greatly appreciated.
Thank you for your attention,
Nevermind everyone, it was the page-sharing segments that were causing the problem all along.
Thinking the problem was probably in the kernel loader, I should have thought about running dmesg much earlier, where I would have noticed the following message, clear as day :
[54178.211348] 12766 (my-program): Uhuuh, elf segment at 0000000000400000 requested but the memory is mapped already
Apparently, some benevolent mastermind decided 3 months ago that it would be good to actually catch double-mapping errors instead of just letting them go silently as we always did in the ELF loader.
It's not that my binaries used to be correct, it's that the error they were causing wasn't caught before. I don't know if I should be proud or ashamed for my bugs to have eluded detection all this time.
Anyway, I leave this answer to warn anyone foolish enough to map multiple segments on a single page in an ELF binary : do not. There is no try.
PS: #rodrigo: Thanks for your answer, I didn't even notice the PhysAddr before you pointed them out. The manual says that they're used "on systems where physical addressing is relevant", which doesn't seem to be the case here, but I'll remember to keep a lookout for them next time.
I went through this post Hiding symbol names in library but still could not get proper conclusion
1) Filename : lib_mylib.h
int fun();
int fun1(void);
int fun2(void);
2) Filename: lib_mylib.c */
#include <stdio.h>
int fun() {
return 2;
}
int fun1(void) {
return 2;
}
int fun2(void) {
return 2;
}
3) File :my_lib_shared.c
#include "my_lib.h"
int foo(void) {
return fun() + 4;
}
4) File: my_lib_shared.h
int foo();
5) File driver.c
/* filename: driver.c */
#include "my_lib.h"
#include "my_lib_shared.h"
#include <stdio.h>
int main() {
int x = foo() ;
return 0;
}
If I execute following steps
1)gcc -fPIC -fvisibility=hidden -c my_lib.c -o lib_mylib.o
2)gcc -fPIC -fvisibility=hidden -shared -o libMyLibHidden.so lib_mylib.o
3)gcc -fPIC -c my_lib_shared.c -o my_lib_shared.o
4)gcc -shared -o libMyShared2.so my_lib_shared.o
5)gcc -L. -Wall -o test_hidden driver.c -lMyLibHidden -lMyShared2
The output of objdump is as
objdump -T libMyLibHidden.so
libMyLibHidden.so: file format elf64-x86-64
DYNAMIC SYMBOL TABLE:
0000000000000448 l d .init 0000000000000000 .init
0000000000000000 w D *UND* 0000000000000000 __gmon_start__
0000000000000000 w D *UND* 0000000000000000 _Jv_RegisterClasses
0000000000000658 g DF .fini 0000000000000000 Base _fini
0000000000000000 w D *UND* 0000000000000000 _deregisterTMCloneTable
0000000000000000 w D *UND* 0000000000000000 _ITM_registerTMCloneTable
0000000000000000 w DF *UND* 0000000000000126 GLIBC_2.2.5 _cxa_finalize
0000000000200938 g D *ABS* 0000000000000000 Base __bss_start
0000000000200948 g D *ABS* 0000000000000000 Base _end
0000000000200938 g D *ABS* 0000000000000000 Base _edata
0000000000000448 g DF .init 0000000000000000 Base _init
I did not any reference of functions of fun, fun1 and fun2 in the above objdump
and I get following error at final linking step
/tmp/ccMsEpFq.o: In function main':
driver.c:(.text+0xe): undefined reference tofun'
collect2: error: ld returned 1 exit status
When I make static library from lib_mylib.c
1)gcc -g -fvisibility=hidden -c my_lib.c -o lib_mylib.o
2)ar rcs libMylib_static_hidden.a lib_mylib.o
3)gcc -g -fvisibility=hidden -c -fPIC my_lib_shared.c -o my_lib_shared.o
4)gcc -g -shared -o libMyShare.so my_lib_shared.o
5)gcc -g driver.c -L . -Wall -o test_static_hidden -lMyShare -lMylib_static_hidden
and I get following objdump
objdump -t libMylib_static_hidden.a
In archive libMylib_static_hidden.a:
lib_mylib.o: file format elf64-x86-64
SYMBOL TABLE:
0000000000000000 l df *ABS* 0000000000000000 my_lib.c
0000000000000000 l d .text 0000000000000000 .text
0000000000000000 l d .data 0000000000000000 .data
0000000000000000 l d .bss 0000000000000000 .bss
0000000000000000 l d .debug_abbrev 0000000000000000 .debug_abbrev
0000000000000000 l d .debug_info 0000000000000000 .debug_info
0000000000000000 l d .debug_line 0000000000000000 .debug_line
0000000000000000 l d .debug_frame 0000000000000000 .debug_frame
0000000000000000 l d .eh_frame 0000000000000000 .eh_frame
0000000000000000 l d .debug_pubnames 0000000000000000 .debug_pubnames
0000000000000000 l d .debug_aranges 0000000000000000 .debug_aranges
0000000000000000 l d .debug_str 0000000000000000 .debug_str
0000000000000000 l d .note.GNU-stack 0000000000000000 .note.GNU-stack
0000000000000000 l d .comment 0000000000000000 .comment
0000000000000000 g F .text 000000000000000b .hidden fun
000000000000000b g F .text 000000000000000b .hidden fun1
0000000000000016 g F .text 000000000000000b .hidden fun2
When I final link the library
5)gcc -g driver.c -L . -Wall -o test_static_hidden -lMylib_static_hidden -lMyShare
I get following linker error
test_static_hidden:
hidden symbol `fun' in
libMylib_static_hidden.a(lib_mylib.o) is referenced by DSO
/tools/linux64/gcc-4.0.2/bin/ld: final link failed: Nonrepresentable section on output
I need guidance on why hidden visibility does not get applied when I make shared libraries and why I get undefined reference in case of linking shared library libMyLibHidden.so
I