I'm learning about elf binaries. I want to manually verify that the code written in the program is in an executable region of memory (same for code for shared library that will be linked).
I have a simple program:
int main() { return 0; }
When I do:
readelf -a myprog
I get the following:
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 000020 04 A 5 0 4
[ 5] .dynsym DYNSYM 080481cc 0001cc 000040 10 A 6 1 4
[ 6] .dynstr STRTAB 0804820c 00020c 000045 00 A 0 0 1
[ 7] .gnu.version VERSYM 08048252 000252 000008 02 A 5 0 2
[ 8] .gnu.version_r VERNEED 0804825c 00025c 000020 00 A 6 1 4
[ 9] .rel.dyn REL 0804827c 00027c 000008 08 A 5 0 4
[10] .rel.plt REL 08048284 000284 000010 08 A 5 12 4
[11] .init PROGBITS 08048294 000294 00002e 00 AX 0 0 4
[12] .plt PROGBITS 080482d0 0002d0 000030 04 AX 0 0 16
[13] .text PROGBITS 08048300 000300 00016c 00 AX 0 0 16
[14] .fini PROGBITS 0804846c 00046c 00001a 00 AX 0 0 4
[15] .rodata PROGBITS 08048488 000488 000008 00 A 0 0 4
[16] .eh_frame_hdr PROGBITS 08048490 000490 000034 00 A 0 0 4
[17] .eh_frame PROGBITS 080484c4 0004c4 0000c4 00 A 0 0 4
[18] .ctors PROGBITS 08049f14 000f14 000008 00 WA 0 0 4
[19] .dtors PROGBITS 08049f1c 000f1c 000008 00 WA 0 0 4
[20] .jcr PROGBITS 08049f24 000f24 000004 00 WA 0 0 4
[21] .dynamic DYNAMIC 08049f28 000f28 0000c8 08 WA 6 0 4
[22] .got PROGBITS 08049ff0 000ff0 000004 04 WA 0 0 4
[23] .got.plt PROGBITS 08049ff4 000ff4 000014 04 WA 0 0 4
[24] .data PROGBITS 0804a008 001008 000008 00 WA 0 0 4
[25] .bss NOBITS 0804a010 001010 000008 00 WA 0 0 4
[26] .comment PROGBITS 00000000 001010 00002a 01 MS 0 0 1
[27] .shstrtab STRTAB 00000000 00103a 0000fc 00 0 0 1
[28] .symtab SYMTAB 00000000 0015e8 000400 10 29 45 4
[29] .strtab STRTAB 00000000 0019e8 0001ea 00 0 0 1
To check whether the code of the program is executable, I can see the .text section with the flags AX. Is the X here for making the code inside my main() function is executable ?
Which section will the shared library (e.g. glibc) be loaded into when the program is dynamically linked at run time ? I found explanation online which discussed use of GOT,PLT in context of dynamic linking. The only sections with the mark X are init, plt and fini (in addition to text). Are shared libraries linked into one of these sections to make sure their code is executable when program starts execution ?
(It would be great if some reference(s) could be pointed at when answering the above)
Is the X here for making the code inside my main() function is executable ?
Correct. Although more than just your main() function is involved; there is some other code which is linked into your executable that is also contained in this section.
Which section will the shared library (e.g. glibc) be loaded into when the program is dynamically linked at run time ?
None of them. The shared library is a separate ELF object, and has its own sections, some of which are executable; it is essentially loaded alongside your executable, not into it. That is, the resulting image in memory will contain both your executable's .text section and a number of other .text (and other types of) sections from all the shared libraries it has loaded.
For example, here is the content of /proc/self/maps from a running /bin/cat process on my Linux system. The format of this output is different from what readelf is showing you, but some similarities should become apparent:
00400000-0040b000 r-xp 00000000 08:00 32968 /bin/cat
0060a000-0060b000 r--p 0000a000 08:00 32968 /bin/cat
0060b000-0060c000 rw-p 0000b000 08:00 32968 /bin/cat
0242a000-0244b000 rw-p 00000000 00:00 0 [heap]
7fdf299a2000-7fdf29c6b000 r--p 00000000 08:00 949 /usr/lib/locale/locale-archive
7fdf29c6b000-7fdf29e26000 r-xp 00000000 08:00 18508 /lib/x86_64-linux-gnu/libc-2.19.so
7fdf29e26000-7fdf2a025000 ---p 001bb000 08:00 18508 /lib/x86_64-linux-gnu/libc-2.19.so
7fdf2a025000-7fdf2a029000 r--p 001ba000 08:00 18508 /lib/x86_64-linux-gnu/libc-2.19.so
7fdf2a029000-7fdf2a02b000 rw-p 001be000 08:00 18508 /lib/x86_64-linux-gnu/libc-2.19.so
7fdf2a02b000-7fdf2a030000 rw-p 00000000 00:00 0
7fdf2a030000-7fdf2a053000 r-xp 00000000 08:00 18255 /lib/x86_64-linux-gnu/ld-2.19.so
7fdf2a246000-7fdf2a249000 rw-p 00000000 00:00 0
7fdf2a250000-7fdf2a252000 rw-p 00000000 00:00 0
7fdf2a252000-7fdf2a253000 r--p 00022000 08:00 18255 /lib/x86_64-linux-gnu/ld-2.19.so
7fdf2a253000-7fdf2a254000 rw-p 00023000 08:00 18255 /lib/x86_64-linux-gnu/ld-2.19.so
7fdf2a254000-7fdf2a255000 rw-p 00000000 00:00 0
7ffd516aa000-7ffd516cb000 rw-p 00000000 00:00 0 [stack]
7ffd517f9000-7ffd517fb000 r--p 00000000 00:00 0 [vvar]
7ffd517fb000-7ffd517fd000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
In particular, you can see three sections were loaded from /bin/cat at the top: the first one is executable (r-xp), the second one is read-only (r--p), and the third is read-write (rw-p). Additionally, there are a number of executable and non-executable segments mapped from libc-2.19.so below that, as well as from ld-2.19.so (the dynamic linker).
(There are also some somewhat mysterious segments appearing in this dump, including [vdso] and [vsyscall]. These are mapped into the process by the kernel, and are difficult to explain; I won't get into them here.)
Related
I have memory leak somewhere and unfortunately the repeated topic which appears on stack overflow didn't help; I don't really understand how this happens since I identified the dubious line from gdb run,
void read_data(std::string filename, number_type & parameter, number_type & n_part, number_type & mult){
std::ifstream infile(filename);
std::string line; // ERROR LINE
size_type counter_numbers = 0;
size_type counter_lines = 0;
while (infile)
{
std::getline(infile, line); // Read in current line
.
.
this is where it shows in gdb: free(): invalid next size (normal);
at string definition. Why would this happen; Any ideas?
thanks, Damir
Running as
g++ -std=c++11 -I/users/damir/gsl/include/ -L/users/damir/gsl/lib/ -lgsl -lgslcblas -g -o MultB analysis_mult_b.cpp
PS (valgrind output)
valgrind ./MultB arg1 arg2 arg3 arg4
==39918== Command: ./MultB arg1 arg2 arg3 arg4
==39918==
==39918== Invalid write of size 8
==39918== at 0x405B55: ??? (in /users/damir/Analysis/MultB)
==39918== by 0x404531: ??? (in /users/damir/Analysis/MultB)
==39918== by 0x5582B44: (below main) (libc-start.c:287)
==39918== Address 0x591e530 is 0 bytes after a block of size 400 alloc'd
==39918== at 0x4C28C20: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==39918== by 0x512B654: ??? (in /cvmfs/it/compiler/gcc/9.1.0/lib64/libstdc++.so.6.0.26)
==39918== by 0x404531: ??? (in /users/damir/Analysis/MultB)
==39918== by 0x5582B44: (below main) (libc-start.c:287)
==39918==
--39918-- VALGRIND INTERNAL ERROR: Valgrind received a signal 11 (SIGSEGV) - exiting
--39918-- si_code=80; Faulting address: 0x0; sp: 0x802b99de0
valgrind: the 'impossible' happened:
Killed by fatal signal
host stacktrace:
==39918== at 0x380B1870: ??? (in /usr/lib/valgrind/memcheck-amd64-linux)
==39918== by 0x38072784: ??? (in /usr/lib/valgrind/memcheck-amd64-linux)
==39918== by 0x38072956: ??? (in /usr/lib/valgrind/memcheck-amd64-linux)
==39918== by 0x380F6D27: ??? (in /usr/lib/valgrind/memcheck-amd64-linux)
==39918== by 0x38105B60: ??? (in /usr/lib/valgrind/memcheck-amd64-linux)
sched status:
running_tid=1
Thread 1: status = VgTs_Runnable
==39918== at 0x4C28C20: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==39918== by 0x512B654: ??? (in /cvmfs/it/compiler/gcc/9.1.0/lib64/libstdc++.so.6.0.26)
==39918== by 0x40455D: ??? (in /users/damir/Analysis/MultB)
==39918== by 0x5582B44: (below main) (libc-start.c:287)
Output from readlef -WS ./MultB:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 0] NULL 0000000000000000 000000 000000 00 0 0 0
[ 1] .interp PROGBITS 00000000004002a8 0002a8 00001c 00 A 0 0 1
[ 2] .note.ABI-tag NOTE 00000000004002c4 0002c4 000020 00 A 0 0 4
[ 3] .hash HASH 00000000004002e8 0002e8 000334 04 A 5 0 8
[ 4] .gnu.hash GNU_HASH 0000000000400620 000620 00005c 00 A 5 0 8
[ 5] .dynsym DYNSYM 0000000000400680 000680 0009f0 18 A 6 1 8
[ 6] .dynstr STRTAB 0000000000401070 001070 000fe1 00 A 0 0 1
[ 7] .gnu.version VERSYM 0000000000402052 002052 0000d4 02 A 5 0 2
[ 8] .gnu.version_r VERNEED 0000000000402128 002128 0000c0 00 A 6 4 8
[ 9] .rela.dyn RELA 00000000004021e8 0021e8 000048 18 A 5 0 8
[10] .rela.plt RELA 0000000000402230 002230 000918 18 AI 5 24 8
[11] .init PROGBITS 0000000000403000 003000 00001a 00 AX 0 0 4
[12] .plt PROGBITS 0000000000403020 003020 000620 10 AX 0 0 16
[13] .plt.got PROGBITS 0000000000403640 003640 000008 08 AX 0 0 8
[14] .text PROGBITS 0000000000403650 003650 00a342 00 AX 0 0 16
[15] .fini PROGBITS 000000000040d994 00d994 000009 00 AX 0 0 4
[16] .rodata PROGBITS 000000000040e000 00e000 000208 00 A 0 0 16
[17] .eh_frame_hdr PROGBITS 000000000040e208 00e208 0010cc 00 A 0 0 4
[18] .eh_frame PROGBITS 000000000040f2d8 00f2d8 004568 00 A 0 0 8
[19] .gcc_except_table PROGBITS 0000000000413840 013840 00049c 00 A 0 0 4
[20] .init_array INIT_ARRAY 0000000000414db0 013db0 000010 08 WA 0 0 8
[21] .fini_array FINI_ARRAY 0000000000414dc0 013dc0 000008 08 WA 0 0 8
[22] .dynamic DYNAMIC 0000000000414dc8 013dc8 000230 10 WA 6 0 8
[23] .got PROGBITS 0000000000414ff8 013ff8 000008 08 WA 0 0 8
[24] .got.plt PROGBITS 0000000000415000 014000 000320 08 WA 0 0 8
[25] .data PROGBITS 0000000000415320 014320 000010 00 WA 0 0 8
[26] .bss NOBITS 0000000000415340 014330 000138 00 WA 0 0 32
[27] .comment PROGBITS 0000000000000000 014330 00002d 01 MS 0 0 1
[28] .debug_aranges PROGBITS 0000000000000000 01435d 002060 00 0 0 1
[29] .debug_info PROGBITS 0000000000000000 0163bd 024611 00 0 0 1
[30] .debug_abbrev PROGBITS 0000000000000000 03a9ce 000e97 00 0 0 1
[31] .debug_line PROGBITS 0000000000000000 03b865 005f1d 00 0 0 1
[32] .debug_str PROGBITS 0000000000000000 041782 025677 01 MS 0 0 1
[33] .debug_ranges PROGBITS 0000000000000000 066df9 002260 00 0 0 1
[34] .symtab SYMTAB 0000000000000000 069060 004e60 18 35 57 8
[35] .strtab STRTAB 0000000000000000 06dec0 00a877 00 0 0 1
[36] .shstrtab STRTAB 0000000000000000 078737 000154 00 0 0 1
I have memory leak somewhere
You may have, but you have provided no evidence of that.
The problem is subject line: free(): invalid next size (normal) is not about a memory leak, but rather about heap corruption.
I don't really understand how this happens
Heap corruption bugs often show up as a crash quite far from where they actually happen. This makes finding them without specialized tools hard.
Fortunately, there are specialized tools. Run your program under Valgrind, or with Address Sanitizer.
Update:
Invalid write of size 8
==39918== at 0x405B55: ??? (in /users/damir/Analysis/MultB)
==39918== by 0x404531: ??? (in /users/damir/Analysis/MultB)
==39918== by 0x5582B44: (below main) (libc-start.c:287)
==39918== Address 0x591e530 is 0 bytes after a block of size 400 alloc'd
That's the heap corruption right there: you are writing 8 bytes past the end of a heap block.
Unfortunately you didn't build MultB with debug info, so Valgrind can't tell you where in the source this is happening.
Rebuild your application with -g flag, run it under Valgrind again, and fix the bug Valgrind told you about.
In our system, oom-killer is invoked when we have large throughput, we believe that most of the memory should be consumed by kernel driver, but we can't find the dedicated consumer, really appreciate anyone can give some suggestion for it.
followed is the detailed log of dmesg
[14839.077171] passkey-agent invoked oom-killer: gfp_mask=0x201da, order=0, oom_score_adj=0
[14839.077187] CPU: 0 PID: 3443 Comm: passkey-agent Tainted: G O 4.1.35-rt41 #1
[14839.077190] Hardware name: LS1043A RDB Board (DT)
[14839.077193] Call trace:
[14839.079644] [<ffff8000000898f4>] dump_backtrace+0x0/0x154
[14839.079650] [<ffff800000089a5c>] show_stack+0x14/0x1c
[14839.079656] [<ffff8000008f3174>] dump_stack+0x90/0xb0
[14839.079663] [<ffff80000013eea4>] dump_header.isra.10+0x88/0x1b8
[14839.079668] [<ffff80000013f5f8>] oom_kill_process+0x210/0x3d4
[14839.079672] [<ffff80000013faec>] __out_of_memory.isra.15+0x330/0x374
[14839.079676] [<ffff80000013fd5c>] out_of_memory+0x5c/0x80
[14839.079682] [<ffff8000001442d8>] __alloc_pages_nodemask+0x55c/0x7c4
[14839.079687] [<ffff80000013e0cc>] filemap_fault+0x188/0x400
[14839.079693] [<ffff80000015f424>] __do_fault+0x3c/0x98
[14839.079698] [<ffff8000001641c8>] handle_mm_fault+0xc28/0x14f8
[14839.079704] [<ffff800000094c04>] do_page_fault+0x224/0x2b4
[14839.079709] [<ffff8000000822a0>] do_mem_abort+0x40/0xa0
[14839.079713] Exception stack(0xffff80001e47be20 to 0xffff80001e47bf50)
[14839.079719] be20: 00000000 00000000 000001f4 00000000 ffffffff ffffffff a6f90990 0000ffff
[14839.079725] be40: ffffffff ffffffff 3b969772 00000000 dbbcc280 0000ffff 00085db0 ffff8000
[14839.079730] be60: 00000000 00000000 000001f4 00000000 ffffffff ffffffff a6f90990 0000ffff
[14839.079736] be80: 1e47bea0 ffff8000 000895f8 ffff8000 00000008 00000000 00085b90 ffff8000
[14839.079742] bea0: dbbcc280 0000ffff 00085c9c ffff8000 00000000 00000000 0ee34088 00000000
[14839.079747] bec0: 00000000 00000000 00000001 00000000 dbbcc2b0 0000ffff 00000000 00000000
[14839.079752] bee0: 00000000 00000000 00000000 00000000 000f4240 00000000 00000000 00000000
[14839.079758] bf00: 00000049 00000000 0000001c 00000000 0000011b 00000000 00000013 00000000
[14839.079763] bf20: 00000028 00000000 00000000 00000000 a71c1c20 0000ffff 00000000 003b9aca
[14839.079767] bf40: a720a990 0000ffff a6f90918 0000ffff
[14839.082683] Mem-Info:
[14839.082700] active_anon:16910 inactive_anon:6202 isolated_anon:0
active_file:15 inactive_file:0 isolated_file:26
unevictable:62887 dirty:0 writeback:0 unstable:0
slab_reclaimable:944 slab_unreclaimable:8027
mapped:5421 shmem:2349 pagetables:527 bounce:0
free:5120 free_pcp:627 free_cma:0
[14839.082719] DMA free:20480kB min:22528kB low:28160kB high:33792kB active_anon:67640kB inactive_anon:24808kB active_file:60kB inactive_file:0kB unevictable:251548kB isolated(anon):0kB isolated(file):104kB present:1046528kB managed:890652kB mlocked:251548kB dirty:0kB writeback:0kB mapped:21684kB shmem:9396kB slab_reclaimable:3776kB slab_unreclaimable:32108kB kernel_stack:6064kB pagetables:2108kB unstable:0kB bounce:0kB free_pcp:2508kB local_pcp:424kB free_cma:0kB writeback_tmp:0kB pages_scanned:208 all_unreclaimable? no
[14839.082723] lowmem_reserve[]: 0 0 0
[14839.082729] DMA: 755*4kB (EM) 486*8kB (UEM) 617*16kB (UEM) 2*32kB (M) 1*64kB (R) 2*128kB (R) 1*256kB (R) 0*512kB 1*1024kB (R) 1*2048kB (R) 0*4096kB = 20492kB
[14839.082752] Node 0 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=2048kB
[14839.082755] 7756 total pagecache pages
[14839.082760] 0 pages in swap cache
[14839.082763] Swap cache stats: add 0, delete 0, find 0/0
[14839.082765] Free swap = 0kB
[14839.082768] Total swap = 0kB
[14839.082856] 261632 pages RAM
[14839.082858] 0 pages HighMem/MovableOnly
[14839.082861] 34873 pages reserved
[14839.082863] 4096 pages cma reserved
[14839.082867] [ pid ] uid tgid total_vm rss nr_ptes nr_pmds swapents oom_score_adj name
[14839.082890] [ 1353] 0 1353 876 161 7 3 0 -1000 udevd
[14839.082899] [ 1863] 999 1863 695 48 5 3 0 0 dbus-daemon
[14839.082906] [ 1944] 0 1944 833 23 5 3 0 0 syslogd
[14839.082913] [ 1947] 0 1947 833 18 5 3 0 0 klogd
[14839.082919] [ 1990] 0 1990 2307 686 8 2 0 0 php-fpm
[14839.082925] [ 1991] 65534 1991 2307 857 8 2 0 0 php-fpm
[14839.082932] [ 1992] 65534 1992 2307 857 8 2 0 0 php-fpm
[14839.082938] [ 1999] 0 1999 720 31 5 3 0 0 bash
[14839.083042] [ 2001] 0 2001 1083 393 6 3 0 0 start_appli
[14839.083049] [ 2010] 0 2010 849 26 5 3 0 0 getty
[14839.083055] [ 2115] 0 2115 1262 96 6 4 0 -1000 sshd
[14839.083062] [ 3051] 0 3051 2709 210 6 2 0 0 optf_write
[14839.083068] [ 3052] 0 3052 1719 686 7 2 0 0 launcher
[14839.083074] [ 3055] 0 3055 5056 4196 13 2 0 0 globMW0
[14839.083081] [ 3066] 0 3066 10430 6805 27 2 0 0 confd
[14839.083088] [ 3085] 0 3085 9735 7449 23 2 0 0 hal0
[14839.083095] [ 3086] 0 3086 7781 6642 19 2 0 0 SystemMgr
[14839.083102] [ 3087] 0 3087 7455 6372 20 2 0 0 HWMgr
[14839.083108] [ 3088] 0 3088 8319 7118 20 2 0 0 SWMgr
[14839.083115] [ 3089] 0 3089 7824 6696 19 2 0 0 FaultMgr
[14839.083121] [ 3090] 0 3090 7488 6359 20 2 0 0 TSMgr
[14839.083127] [ 3091] 0 3091 7009 6144 20 2 0 0 SecurityMgr
[14839.083133] [ 3092] 0 3092 7736 6337 20 2 0 0 DHCPRelayMgr
[14839.083225] [ 3093] 0 3093 8747 6555 21 2 0 0 ItfMgr
[14839.083232] [ 3094] 0 3094 8192 6686 21 2 0 0 WlanItfMgr
[14839.083239] [ 3095] 0 3095 7602 6518 20 2 0 0 L2Mgr
[14839.083246] [ 3096] 0 3096 7399 6017 20 2 0 0 QoSMgr
[14839.083252] [ 3097] 0 3097 8647 6486 21 2 0 0 L3Mgr
[14839.083258] [ 3098] 0 3098 7482 6356 17 2 0 0 MulticastMgr
[14839.083264] [ 3099] 0 3099 7783 6609 21 2 0 0 DHCPMgr
[14839.083271] [ 3100] 0 3100 6864 6409 16 2 0 0 CallHomeMgr
[14839.083279] [ 3422] 0 3422 472 23 4 3 0 0 hciattach
[14839.083286] [ 3426] 0 3426 1035 50 6 3 0 0 bluetoothd
[14839.083292] [ 3443] 0 3443 2039 112 8 3 0 0 passkey-agent
[14839.083298] [ 3462] 0 3462 3852 2368 11 3 0 0 dhcpd
[14839.083304] [ 3517] 0 3517 860 161 7 3 0 -1000 udevd
[14839.083393] [ 3518] 0 3518 860 161 7 3 0 -1000 udevd
[14839.083400] [ 3650] 0 3650 1629 132 6 3 0 0 wpa_supplicant
[14839.083406] [ 3720] 0 3720 3134 1711 10 3 0 0 dhclient
[14839.083412] [ 3747] 0 3747 891 149 6 3 0 0 zebra
[14839.083419] [ 3751] 0 3751 834 132 7 3 0 0 ripd
[14839.083425] [ 3949] 0 3949 1037 67 6 4 0 0 ntpd
[14839.083431] [ 8000] 0 8000 721 33 5 3 0 0 sh
[14839.083436] Out of memory: Kill process 3085 (hal0) score 32 or sacrifice child
[14839.083447] Killed process 3085 (hal0) total-vm:38940kB, anon-rss:15236kB, file-rss:14560kB
We have 1G memory in total, and I can found that slab occupied about 35M (3776kB + 32108kB), the kernel stuck is 6064kB, and active_anon + inactive_anon is about 92M (67640kB + 24808kB), and the user space memory consumption is normal as usual.
So where did the memory left goes ? How can I check with it ?
For example, how can I check how much memory it consumed by a dedicated driver of pcie network card?
So out of curiosity, I tried to run this code today (compiled with gcc -m32 1.c):
int main(void)
{
// EB is the opcode for jmp rel/8
// FE is hex for -2
// So this is essentially an infinite loop
((void(*)(void))"\xEB\xFE")();
}
... and it worked! No segfaults, the program (correctly?) goes into an infinite loop. Looking at the disassembly (objdump -d a.out), you can see the call to... whatever is at address 0x8048480:
080483d6 <main>:
....
80483e7: b8 80 84 04 08 mov $0x8048480,%eax
80483ec: ff d0 call *%eax
....
objdump -s -j .rodata a.out gives:
Contents of section .rodata:
8048478 03000000 01000200 ebfe00 ...........
~~~~
So it is indeed executing the string, which is stored in the .rodata section. So I ran readelf --sections a.out and got:
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 000020 04 A 5 0 4
[ 5] .dynsym DYNSYM 080481cc 0001cc 000040 10 A 6 1 4
[ 6] .dynstr STRTAB 0804820c 00020c 000045 00 A 0 0 1
[ 7] .gnu.version VERSYM 08048252 000252 000008 02 A 5 0 2
[ 8] .gnu.version_r VERNEED 0804825c 00025c 000020 00 A 6 1 4
[ 9] .rel.dyn REL 0804827c 00027c 000008 08 A 5 0 4
[10] .rel.plt REL 08048284 000284 000008 08 AI 5 23 4
[11] .init PROGBITS 0804828c 00028c 000023 00 AX 0 0 4
[12] .plt PROGBITS 080482b0 0002b0 000020 04 AX 0 0 16
[13] .plt.got PROGBITS 080482d0 0002d0 000008 00 AX 0 0 8
[14] .text PROGBITS 080482e0 0002e0 000182 00 AX 0 0 16
[15] .fini PROGBITS 08048464 000464 000014 00 AX 0 0 4
[16] .rodata PROGBITS 08048478 000478 00000b 00 A 0 0 4
[17] .eh_frame_hdr PROGBITS 08048484 000484 000034 00 A 0 0 4
[18] .eh_frame PROGBITS 080484b8 0004b8 0000e0 00 A 0 0 4
[19] .init_array INIT_ARRAY 08049f0c 000f0c 000004 04 WA 0 0 4
[20] .fini_array FINI_ARRAY 08049f10 000f10 000004 04 WA 0 0 4
[21] .dynamic DYNAMIC 08049f14 000f14 0000e8 08 WA 6 0 4
[22] .got PROGBITS 08049ffc 000ffc 000004 04 WA 0 0 4
[23] .got.plt PROGBITS 0804a000 001000 000010 04 WA 0 0 4
[24] .data PROGBITS 0804a010 001010 000008 00 WA 0 0 4
[25] .bss NOBITS 0804a018 001018 000004 00 WA 0 0 1
[26] .comment PROGBITS 00000000 001018 00001a 01 MS 0 0 1
[27] .symtab SYMTAB 00000000 001034 0003f0 10 28 45 4
[28] .strtab STRTAB 00000000 001424 0001bd 00 0 0 1
[29] .shstrtab STRTAB 00000000 0015e1 000105 00 0 0 1
So in the ELF binary, the section is marked non-executable. But in memory, the page is executable (cat /proc/xxx/maps):
08048000-08049000 r-xp 00000000 08:01 663551 /home/andrew/Desktop/a.out
08049000-0804a000 r--p 00000000 08:01 663551 /home/andrew/Desktop/a.out
0804a000-0804b000 rw-p 00001000 08:01 663551 /home/andrew/Desktop/a.out
My original guess was that the sections too closely-spaced (there are both AX and A sections in the 08048000-08049000 range), so Linux is forced to give the page the union of the ELF permission bits (AX | A == AX). However, even after increasing the size of the .rodata section (by adding many long strings), all of the pages containing the .rodata section are still executable. Why is this?
(For the record, I'm running on Linux kernel 4.11.7, GCC 7.1.1, and compiling as 64-bit still exhibits this behavior)
My original guess was that the segments too closely-spaced
You should not call sections segments (ELF has both, and they mean different things).
Sections only matter at static link time, and can be completely removed (are not needed at runtime). Only segments matter at runtime, and a typical ELF binary will have two segments with R-X and RW- permissions.
The .rodata section is usually merged with .text section and put into the executable segment. You can change that with the --rosegment flag if you use gold linker (patch which introduced this).
You can see section to segment mapping in the readelf -Wl a.out output.
Update:
Can there ever be a situation where .rodata needs to be executable, or is it for optimization, or something else?
There are no portable situations where .rodata needs to be executable. It is possible to construct a non-portable program that requires it, as you've done in your question.
Merging of .rodata and .text is an optimization: it requires two mmap calls instead of three (a program linked with --rosegment will have three separate PT_LOAD segments with R-X, R-- and R-W protections) and also fragments the virtual space less. In addition, on Linux there is a system-wide limit on total mappings, so you'll reduce the total number of programs you can run at once by 50% if you link everything with --rosegment.
Update 2:
Recent Linux distributions stopped merging .text and .rodata, and now have three or four separate LOAD segments. See this answer.
I wrote a "hello world" program with PIE/PIC enabled. I observed the program headers have 2 LOAD entries:
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
... ...
LOAD 0x000000 0x00000000 0x00000000 0x00870 0x00870 R E 0x1000
LOAD 0x000eb0 0x00001eb0 0x00001eb0 0x0015c 0x00164 RW 0x1000
So in my understanding, the ELF binary will be loaded into 2 pages. The 1st page contains the binary from offset 0 to file size 0x870, and it is Read & Execute. Since they are 0x1000 aligned, the 2nd entry will be loaded in the 2nd page, which contains the binary(from offset 0xeb0 to 0xeb0+0x15c). This page has Read & Write privilege.
While I "pmap" the running process (or cat /proc/pid/maps), it show there are 3 pages for the running program:
b7554000 4K rw--- [ anon ]
b7555000 1700K r-x-- libc-2.19.so
b76fe000 4K ----- libc-2.19.so
b76ff000 8K r---- libc-2.19.so
b7701000 4K rw--- libc-2.19.so
b7702000 12K rw--- [ anon ]
b771b000 16K rw--- [ anon ]
b771f000 4K r-x-- [ anon ]
b7720000 128K r-x-- ld-2.19.so
b7740000 4K r---- ld-2.19.so
b7741000 4K rw--- ld-2.19.so
b7742000 4K r-x-- main
b7743000 4K r---- main
b7744000 4K rw--- main
bfe68000 132K rw--- [ stack ]
total 2032K
So how the ELF binary is loaded, and how the program header indicates a LOAD directive?
Part of the sections headers like:
[13] .text PROGBITS 00000480 000480 0002c2 00 AX 0 0 16
[14] .fini PROGBITS 00000744 000744 000014 00 AX 0 0 4
[15] .rodata PROGBITS 00000758 000758 000060 00 A 0 0 4
[16] .eh_frame PROGBITS 000007b8 0007b8 000094 00 A 0 0 4
[17] .eh_frame_hdr PROGBITS 0000084c 00084c 000024 00 A 0 0 4
[18] .jcr PROGBITS 00001eb0 000eb0 000004 00 WA 0 0 4
[19] .fini_array FINI_ARRAY 00001eb4 000eb4 000004 00 WA 0 0 4
[20] .init_array INIT_ARRAY 00001eb8 000eb8 000004 00 WA 0 0 4
[21] .dynamic DYNAMIC 00001ebc 000ebc 000108 08 WA 5 0 4
[22] .got PROGBITS 00001fc4 000fc4 00001c 00 WA 0 0 4
[23] .got.plt PROGBITS 00001fe0 000fe0 000020 00 WA 0 0 4
[24] .data PROGBITS 00002000 001000 00000c 00 WA 0 0 4
[25] .tm_clone_table PROGBITS 0000200c 00100c 000000 00 WA 0 0 4
[26] .bss NOBITS 0000200c 00100c 000008 00 WA 0 0 4
I observed the program headers have 2 LOAD entries:
You've omitted an important program header: GNU_RELRO, which tells the loader that after mapping the LOAD segments, it should mprotect part of them as read-only.
When mprotect with different permissions is called on existing mapping, the kernel has to split that mapping into multiple mappings, which is where the extra entry is coming from.
In my test binary:
LOAD 0x000000 0x00000000 0x00000000 0x0075c 0x0075c R E 0x1000
LOAD 0x000ef4 0x00001ef4 0x00001ef4 0x0012c 0x00130 RW 0x1000
...
GNU_RELRO 0x000ef4 0x00001ef4 0x00001ef4 0x0010c 0x0010c R 0x1
The kernel actually maps the LOAD segments before the loader even starts, and the map at that point looks like this:
56555000-56556000 r-xp 00000000 fc:02 801500 /tmp/a.out
56556000-56558000 rw-p 00000000 fc:02 801500 /tmp/a.out
f7fdb000-f7fdc000 r-xp 00000000 00:00 0 [vdso]
...
After the mprotect, the map looks like this:
56555000-56556000 r-xp 00000000 fc:02 801500 /tmp/a.out
56556000-56557000 r--p 00000000 fc:02 801500 /tmp/a.out
56557000-56558000 rw-p 00001000 fc:02 801500 /tmp/a.out
...
I have a library that I got from a proprietary source and I'm trying to link against it but I'm getting the following error
libxxx.so: file not recognized: File format not recognized
collect2: ld returned 1 exit status
and indeed
$ ldd ./libxxx.so
statically linked
what does that exactly mean? Never saw a statically linked .so in my life. It might be worth noting that the last version of the same software included the same .so for which ldd shows the "normal" output and that works fine.
$ file ./libxxx.so
./libxxx.so: ELF 32-bit LSB shared object, Intel 80386, version 1
(SYSV), stripped
but nm, objdump also can't see anything and readelf returns errors reading string table. Am I safe to assume this .so is botched or there is something else I can try before going to "enterprise support" and waiting for two months for them to acknowledge my existence.
$ objdump -a libxxx.so
objdump: libxxx.so: File format not recognized
$ readelf -a libxxx.so
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: DYN (Shared object file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x4f00
Start of program headers: 52 (bytes into file)
Start of section headers: 147936 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 4
Size of section headers: 40 (bytes)
Number of section headers: 29
Section header string table index: 26
readelf: Error: Unable to read in 0xb70000 bytes of string table
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] <no-name> <unknown>: b400 08e40000 020000 000000 b0000 xo 262144 262144 2162688
[ 1] <no-name> <unknown>: 9980 13000000 030000 1a0000 30000 xo 262144 1048576 2686976
[ 2] <no-name> <unknown>: 1c98 13ad0000 000000 000000 ffff0000 xop 65536 0 3211264
[ 3] <no-name> <unknown>: 3046 02600000 020000 000000 fffe0000 xxop 131072 131072 4063232
[ 4] <no-name> <unknown>: 32a8 00400000 030000 010000 90000 xop 262144 0 5046272
[ 5] <no-name> <unknown>: 32e8 12c00000 020000 000000 90000 xop 262144 524288 5636096
[ 6] <no-name> <unknown>: 45a8 03100000 020000 090000 10000 xop 262144 524288 6225920
[ 7] <no-name> <unknown>: 48b8 00170000 000000 000000 10000 xop 262144 0 5898240
[ 8] <no-name> <unknown>: 48d0 06300000 000000 000000 10000 op 262144 262144 6619136
[ 9] <no-name> <unknown>: 4f00 59740000 000001 000000 10000 op 1048576 0 7012352
[10] <no-name> LOUSER+28740000 001a0001 000000 000000 10000 Wxop 262144 0 7405568
[11] <no-name> LOUSER+28a00000 7bb60001 000000 000000 10000 Wop 2097152 0 7929856
[12] <no-name> <unknown>: 3000 09900002 000000 000000 10000 Ap 2097152 0 8323072
[13] <no-name> <unknown>: 3990 00040002 000000 000000 60000 Aop 262144 0 8978432
[14] <no-name> <unknown>: 3994 00d80002 030000 000000 10000 Axop 262144 524288 9568256
[15] <no-name> <unknown>: 3a6c 00080002 000000 000000 10000 Axxop 262144 0 10027008
[16] <no-name> <unknown>: 3a74 00080002 000000 000000 10000 Axop 262144 0 10485760
[17] <no-name> <unknown>: 3a7c 00040002 000000 000000 10000 Axxop 262144 0 10813440
[18] <no-name> <unknown>: 3a80 020c0002 000000 000000 80000 Aop 262144 262144 11141120
[19] <no-name> <unknown>: 3c8c 001c0002 000000 000000 10000 Axxop 262144 0 11468800
[20] <no-name> NULL 01ee0002 000000 000000 10000 xxop 65536 0 12058624
[21] <no-name> NULL 00580002 000000 000000 10000 op 524288 0 13041664
[22] <no-name> NULL 00fa0002 000000 000000 10000 xop 65536 0 13828096
[23] <no-name> NULL 00200002 000000 000000 10000 xop 65536 0 14745600
[24] <no-name> NULL 01000002 000000 000000 30000 xop 65536 0 1114112
[25] <no-name> NULL 00ed0002 000000 000000 20000 xop 65536 0 65536
[26] <no-name> NULL 1cd00002 1c0000 b70000 30000 xop 262144 1048576 589824
[27] <no-name> NULL 194d0002 000000 000000 00 xop 65536 0 0
[28] <no-name> NULL 00b40000 000000 030000 30000 1 160956416 0
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
There are no section groups in this file.
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000000 0x00000000 0x00000000 0x22456 0x22456 R E 0x1000
LOAD 0x023000 0x00023000 0x00023000 0x00c8c 0x00ca8 RW 0x1000
readelf: Error: no .dynamic section in the dynamic segment
DYNAMIC 0x023994 0x00023994 0x00023994 0x000d8 0x000d8 RW 0x4
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x4
There is no dynamic section in this file.
There are no relocations in this file.
There are no unwind sections in this file.
No version information found in this file.
Am I safe to assume this .so is botched
Yes, a .so file without a .dynamic section is almost certainly botched.
Did someone do an ASCII ftp transfer on it?