# Executables and Loading Today we are going to have a look at how executables are loaded and run on Linux. I am running an Arch setup with the current `gcc --version` = gcc (GCC) 12.2.0 To start off with, let's just build a straight forward `C` program. No dependencies yet. ```C # include int main(void) { printf("Foobar\n"); } ``` Compiling it ```console $ gcc executable.c Let us first inspect the resulting binary executable, using the standard tools: ```console $ ls -lah ./a.out -rwxr-xr-x 1 olepor users 21K Dec 12 19:22 ./a.out ``` So we can see that our gcc compiler driver has created an executable file called `a.out`, which is owned by me (olepor), and is executable by everyone (lucky world!). The file is `32K` = `32` Kilo bytes, or `0x32000000` (TODO - double check this) ```console $ file ./a.out ./a.out: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=4fc1e1dd4ebdd314cfbcfc9c1463910c803f3065, for GNU/Linux 4.4.0, with debug_info, not stripped ``` Let's break this down into pieces: First: `ELF` This is the executable format. I.e., the structure given to the binary blob, or rather the wrapper around what is the binary blob that is the program. The ELF format is the container, and contains a lot more meta-data than just the executable code in and of itself. TODO - More on this later (separate blog-post). Then: `64-bit` This is the architecture memory size. Or rather the adressable memory of the CPU. (what does Wikipedia say?) Then: `LSB` This means that the file is structured in the _least significant bit_ first bit order. Which means that every byte in the file has the lowest order 2^n first, as opposed to last, as we are used to as humans. Why this is I am not completely sure, maybe we should dig into the history a little bit here. Then: `pie` This means that the executable is is `position independent`. This means that the code is able to run, no matter where in memory it is loaded (TODO - Is this really true? - and if so - how?) `x86-64` is the architecture. `version 1 (SYSV)` is the application binary interface (TODO check this). `Dynamically linked` just means that the binary is relying on external libraries (shared libraries) - we will get to this later. For now, it suffices to say that all `C` programs when run through `GCC` is linked to the `libc` standard library, and is using the system loader (TODO - this we will also look more closely into later). `interpreter /lib64/ld-linux-x86-64.so.2` This is the `dynamic linker`, `system loader` or whatever you like to call it. This is the path that the system uses to run the ELF binary on your system (TODO - Look closer into loading later). In other words, in order to run your program, your OS (computer) basically calls: ```console $ /lib64/ld-linux-x86-64.so.2 ./a.out Foobar ``` Which prints out `Foobar` like expected (Hey, we're an OS now). `BuildID[sha1]=4fc1e1dd4ebdd314cfbcfc9c1463910c803f3065` The `BuildID` I'm not familiar with. TODO - figure this out `for GNU/Linux 4.4.0` - This I'm also not familiar with `with debug_info, not stripped` - Meaning what it says In more detail: +---------------------+ | | | .......... | | | | | +---------------------+ | | | .symtab | <- Not stripped = symbol table is still present |---------------------+ | | +---------------------+ | .debug | <- Debug info is present here (Also symbols - and then som) |---------------------+ | | +---------------------+ In fact, all of this information, we can gleam from the ELF file ourself, using the `readelf` program. (See ELF blog-post) Whew - that was quite the mouthful, even though we did not go into depth about any of the constituents. Moving on. As we saw above, the file is dynamically linked, and we saw which dynamic linker was expected (going to be used), and we also mentioned that it linked to `libc`. So let us see what we can dig out of information here: The tool of choice is `ldd`: ```console ldd prints the shared objects (shared libraries) required by each program or shared object specified on the command line. ``` ```console $ ldd ./a.out linux-vdso.so.1 (0x00007fff07d00000) libc.so.6 => /usr/lib/libc.so.6 (0x00007f92e868f000) /lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007f92e88ba000) ``` In which, as expected, we can see that it does indeed link with the `libc` library, and the `linux linker`. The `linux-vdso.so.1` is a special library that the kernel links into every running user binary, to help optimize system calls - but we will not concern ourselves with this for now. This data actually comes from a section we have not yet mentioned... The `.rel.dyn` section. (Or is it the Global offset table, or the PLT (procedure linkage table). The GOT (Global Offset Table), is a relocation table present in the binary, in the case the code links to a shared library. Then this library holds the address of the function in the shared library, and where it is located in memory. In order to find this function, another table is needed (the PLT for Procedure Linkage Table), which holds the information needed in order to do lazy loading (we don't want to load all of libc since we are only calling `printf`. In essence the GOT contains 8 byte entries for each function linked to, and each function linked to has each its own PLT entry. ### Executable object file structure #### ELF Header The first section of any ELF executable is the _ELF Header_. ```console $ readelf --file-header ./a.out ``` 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: DYN (Position-Independent Executable file) Machine: Advanced Micro Devices X86-64 Version: 0x1 Entry point address: 0x1040 Start of program headers: 64 (bytes into file) Start of section headers: 18184 (bytes into file) Flags: 0x0 Size of this header: 64 (bytes) Size of program headers: 56 (bytes) Number of program headers: 13 Size of section headers: 64 (bytes) Number of section headers: 37 Section header string table index: 36 Which we can see holds all the meta information about how to load this file into memory. The Magic bytes, are simply the first _16_ bytes of the file, and is used for specifying which file type this is, as opposed to windows, the file endings don't describe what kind of file this is. More information about this topic can be found through: ```console $ man magic ``` In addition to what we have seen from the `file` command, we can also see that it specifies the _ABI_, which is the _application binary interface_. Or rather, how the program passes variables to and from functions (calling convention). See [here](https://wiki.osdev.org/System_V_ABI) for more information. TODO - Blog post about the System-V ABI, and calling conventions on x86 (32 & 64) This is specified as the UNIX - System V interface. Another interesting tidbit is the _program entrypoint_, which is specified as `0x1040`. This actually surprised me a little bit, as I thought all programs were loaded at 4MiB's. #### Segment Header Moving on to the segment header, we see that ```console $ readelf --program-headers ./a.out ``` ```console Elf file type is DYN (Position-Independent Executable file) Entry point 0x1040 There are 13 program headers, starting at offset 64 Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flags Align PHDR 0x0000000000000040 0x0000000000000040 0x0000000000000040 0x00000000000002d8 0x00000000000002d8 R 0x8 INTERP 0x0000000000000318 0x0000000000000318 0x0000000000000318 0x000000000000001c 0x000000000000001c R 0x1 [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2] LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000630 0x0000000000000630 R 0x1000 LOAD 0x0000000000001000 0x0000000000001000 0x0000000000001000 0x0000000000000161 0x0000000000000161 R E 0x1000 LOAD 0x0000000000002000 0x0000000000002000 0x0000000000002000 0x00000000000000ac 0x00000000000000ac R 0x1000 LOAD 0x0000000000002dd0 0x0000000000003dd0 0x0000000000003dd0 0x0000000000000248 0x0000000000000250 RW 0x1000 DYNAMIC 0x0000000000002de0 0x0000000000003de0 0x0000000000003de0 0x00000000000001e0 0x00000000000001e0 RW 0x8 NOTE 0x0000000000000338 0x0000000000000338 0x0000000000000338 0x0000000000000040 0x0000000000000040 R 0x8 NOTE 0x0000000000000378 0x0000000000000378 0x0000000000000378 0x0000000000000044 0x0000000000000044 R 0x4 GNU_PROPERTY 0x0000000000000338 0x0000000000000338 0x0000000000000338 0x0000000000000040 0x0000000000000040 R 0x8 GNU_EH_FRAME 0x000000000000200c 0x000000000000200c 0x000000000000200c 0x0000000000000024 0x0000000000000024 R 0x4 GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 RW 0x10 GNU_RELRO 0x0000000000002dd0 0x0000000000003dd0 0x0000000000003dd0 0x0000000000000230 0x0000000000000230 R 0x1 ``` In which the first interesting sections are: ```console Type Offset VirtAddr PhysAddr FileSize MemSiz Flags Align LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000630 0x0000000000000630 R 0x1000 LOAD 0x0000000000001000 0x0000000000001000 0x0000000000001000 0x0000000000000161 0x0000000000000161 R E 0x1000 LOAD 0x0000000000002000 0x0000000000002000 0x0000000000002000 0x00000000000000ac 0x00000000000000ac R 0x1000 LOAD 0x0000000000002dd0 0x0000000000003dd0 0x0000000000003dd0 0x0000000000000248 0x0000000000000250 RW 0x1000 ``` Where we see the loading information for the file. The first entry ```console Type Offset VirtAddr PhysAddr FileSize MemSiz Flags Align LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000630 0x0000000000000630 R 0x1000 ``` Is a read-only segment, starting at offset _0x0_ in the executable, loads to virtual address _0x0_, and has a _0x0_ physical address... What does this mean, since the filesize is 0x630? ```console Type Offset VirtAddr PhysAddr FileSize MemSiz Flags Align LOAD 0x0000000000001000 0x0000000000001000 0x0000000000001000 0x0000000000000161 0x0000000000000161 R E 0x1000 ``` The second entry we can vager a guess at what is. It is a readable and executable segment, loaded from offset `0x1000`, which is `4096 bytes`, or `4 Kilo bytes` and loaded to virtual address `0x1000` in memory. Assuming that this is the `.init`, `.text` and `.rodata` segments of our ELF file, this lines up well with the program entry point, which is `0x1040`. The filesize is 0x161 bytes in memory, and it is 0x1000` aligned. To examine this further, we can have a look at the `0x161` bytes located from offset `0x1000` in the executable by running: ```console hexdump --skip 0x1000 --length 0x161 ./a.out 0001000 0ff3 fa1e 8348 08ec 8b48 c105 002f 4800 0001010 c085 0274 d0ff 8348 08c4 00c3 0000 0000 0001020 35ff 2fca 0000 25ff 2fcc 0000 1f0f 0040 0001030 25ff 2fca 0000 0068 0000 e900 ffe0 ffff 0001040 0ff3 fa1e ed31 8949 5ed1 8948 48e2 e483 0001050 50f0 4554 c031 c931 8d48 da3d 0000 ff00 0001060 5b15 002f f400 2e66 1f0f 0084 0000 0000 0001070 8d48 a13d 002f 4800 058d 2f9a 0000 3948 0001080 74f8 4815 058b 2f3e 0000 8548 74c0 ff09 0001090 0fe0 801f 0000 0000 0fc3 801f 0000 0000 00010a0 8d48 713d 002f 4800 358d 2f6a 0000 2948 00010b0 48fe f089 c148 3fee c148 03f8 0148 48c6 00010c0 fed1 1474 8b48 0d05 002f 4800 c085 0874 00010d0 e0ff 0f66 441f 0000 0fc3 801f 0000 0000 00010e0 0ff3 fa1e 3d80 2f2d 0000 7500 5533 8348 00010f0 ea3d 002e 0000 8948 74e5 480d 3d8b 2f0e 0001100 0000 15ff 2ed8 0000 63e8 ffff c6ff 0405 0001110 002f 0100 c35d 2e66 1f0f 0084 0000 0000 0001120 66c3 2e66 1f0f 0084 0000 0000 1f0f 0040 0001130 0ff3 fa1e 67e9 ffff 55ff 8948 48e5 058d 0001140 0ec0 0000 8948 e8c7 fee4 ffff 00b8 0000 0001150 5d00 00c3 0ff3 fa1e 8348 08ec 8348 08c4 0001160 00c3 0001161 ``` Surely enough, since our assembly is a little rusty, we run the executable through the de-assembler, and compare: ```console $ objdump --disassemble ./a.out ``` ```console $ objdump -d ./a.out ./a.out: file format elf64-x86-64 Disassembly of section .init: 0000000000001000 <_init>: 1000: f3 0f 1e fa endbr64 1004: 48 83 ec 08 sub $0x8,%rsp 1008: 48 8b 05 c1 2f 00 00 mov 0x2fc1(%rip),%rax # 3fd0 <__gmon_start__@Base> 100f: 48 85 c0 test %rax,%rax 1012: 74 02 je 1016 <_init+0x16> 1014: ff d0 call *%rax 1016: 48 83 c4 08 add $0x8,%rsp 101a: c3 ret Disassembly of section .plt: 0000000000001020 : 1020: ff 35 ca 2f 00 00 push 0x2fca(%rip) # 3ff0 <_GLOBAL_OFFSET_TABLE_+0x8> 1026: ff 25 cc 2f 00 00 jmp *0x2fcc(%rip) # 3ff8 <_GLOBAL_OFFSET_TABLE_+0x10> 102c: 0f 1f 40 00 nopl 0x0(%rax) 0000000000001030 : 1030: ff 25 ca 2f 00 00 jmp *0x2fca(%rip) # 4000 1036: 68 00 00 00 00 push $0x0 103b: e9 e0 ff ff ff jmp 1020 <_init+0x20> Disassembly of section .text: 0000000000001040 <_start>: 1040: f3 0f 1e fa endbr64 1044: 31 ed xor %ebp,%ebp 1046: 49 89 d1 mov %rdx,%r9 1049: 5e pop %rsi 104a: 48 89 e2 mov %rsp,%rdx 104d: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp 1051: 50 push %rax 1052: 54 push %rsp 1053: 45 31 c0 xor %r8d,%r8d 1056: 31 c9 xor %ecx,%ecx 1058: 48 8d 3d da 00 00 00 lea 0xda(%rip),%rdi # 1139
105f: ff 15 5b 2f 00 00 call *0x2f5b(%rip) # 3fc0 <__libc_start_main@GLIBC_2.34> 1065: f4 hlt 1066: 66 2e 0f 1f 84 00 00 cs nopw 0x0(%rax,%rax,1) 106d: 00 00 00 0000000000001070 : 1070: 48 8d 3d a1 2f 00 00 lea 0x2fa1(%rip),%rdi # 4018 <__TMC_END__> 1077: 48 8d 05 9a 2f 00 00 lea 0x2f9a(%rip),%rax # 4018 <__TMC_END__> 107e: 48 39 f8 cmp %rdi,%rax 1081: 74 15 je 1098 1083: 48 8b 05 3e 2f 00 00 mov 0x2f3e(%rip),%rax # 3fc8 <_ITM_deregisterTMCloneTable@Base> 108a: 48 85 c0 test %rax,%rax 108d: 74 09 je 1098 108f: ff e0 jmp *%rax 1091: 0f 1f 80 00 00 00 00 nopl 0x0(%rax) 1098: c3 ret 1099: 0f 1f 80 00 00 00 00 nopl 0x0(%rax) 00000000000010a0 : 10a0: 48 8d 3d 71 2f 00 00 lea 0x2f71(%rip),%rdi # 4018 <__TMC_END__> 10a7: 48 8d 35 6a 2f 00 00 lea 0x2f6a(%rip),%rsi # 4018 <__TMC_END__> 10ae: 48 29 fe sub %rdi,%rsi 10b1: 48 89 f0 mov %rsi,%rax 10b4: 48 c1 ee 3f shr $0x3f,%rsi 10b8: 48 c1 f8 03 sar $0x3,%rax 10bc: 48 01 c6 add %rax,%rsi 10bf: 48 d1 fe sar %rsi 10c2: 74 14 je 10d8 10c4: 48 8b 05 0d 2f 00 00 mov 0x2f0d(%rip),%rax # 3fd8 <_ITM_registerTMCloneTable@Base> 10cb: 48 85 c0 test %rax,%rax 10ce: 74 08 je 10d8 10d0: ff e0 jmp *%rax 10d2: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1) 10d8: c3 ret 10d9: 0f 1f 80 00 00 00 00 nopl 0x0(%rax) 00000000000010e0 <__do_global_dtors_aux>: 10e0: f3 0f 1e fa endbr64 10e4: 80 3d 2d 2f 00 00 00 cmpb $0x0,0x2f2d(%rip) # 4018 <__TMC_END__> 10eb: 75 33 jne 1120 <__do_global_dtors_aux+0x40> 10ed: 55 push %rbp 10ee: 48 83 3d ea 2e 00 00 cmpq $0x0,0x2eea(%rip) # 3fe0 <__cxa_finalize@GLIBC_2.2.5> 10f5: 00 10f6: 48 89 e5 mov %rsp,%rbp 10f9: 74 0d je 1108 <__do_global_dtors_aux+0x28> 10fb: 48 8b 3d 0e 2f 00 00 mov 0x2f0e(%rip),%rdi # 4010 <__dso_handle> 1102: ff 15 d8 2e 00 00 call *0x2ed8(%rip) # 3fe0 <__cxa_finalize@GLIBC_2.2.5> 1108: e8 63 ff ff ff call 1070 110d: c6 05 04 2f 00 00 01 movb $0x1,0x2f04(%rip) # 4018 <__TMC_END__> 1114: 5d pop %rbp 1115: c3 ret 1116: 66 2e 0f 1f 84 00 00 cs nopw 0x0(%rax,%rax,1) 111d: 00 00 00 1120: c3 ret 1121: 66 66 2e 0f 1f 84 00 data16 cs nopw 0x0(%rax,%rax,1) 1128: 00 00 00 00 112c: 0f 1f 40 00 nopl 0x0(%rax) 0000000000001130 : 1130: f3 0f 1e fa endbr64 1134: e9 67 ff ff ff jmp 10a0 0000000000001139
: 1139: 55 push %rbp 113a: 48 89 e5 mov %rsp,%rbp 113d: 48 8d 05 c0 0e 00 00 lea 0xec0(%rip),%rax # 2004 <_IO_stdin_used+0x4> 1144: 48 89 c7 mov %rax,%rdi 1147: e8 e4 fe ff ff call 1030 114c: b8 00 00 00 00 mov $0x0,%eax 1151: 5d pop %rbp 1152: c3 ret Disassembly of section .fini: 0000000000001154 <_fini>: 1154: f3 0f 1e fa endbr64 1158: 48 83 ec 08 sub $0x8,%rsp 115c: 48 83 c4 08 add $0x8,%rsp 1160: c3 ret ➜ ``` Which matches well with our expectation that the sections should be `.init`, `.text` and `.rodata`. But where is the `.rodata`? ```console Section Headers: [Nr] Name Type Address Offset Size EntSize Flags Link Info Align [ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0 [16] .rodata PROGBITS 0000000000002000 00002000 000000000000000b 0000000000000000 A 0 0 4 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), l (large), p (processor specific) ``` Which means we can find the `Foobar` string at: ```console $ hexdump --skip 0x2000 --length 0xb ./a.out o F b o r a 0002000 0001 0002 6f46 626f 7261 0000 000200b ``` as expected! But we are getting ahead of ourselves here We are now to inspect the missing data and bss load section ```console Type Offset VirtAddr PhysAddr FileSize MemSiz Flags Align LOAD 0x0000000000002dd0 0x0000000000003dd0 0x0000000000003dd0 0x0000000000000248 0x0000000000000250 RW 0x1000 ``` Which we see is a section from offset `0x2dd0` in the executable file, which is to be loaded to virtual address `0x3dd0`, and is `0x248` bytes in the executable file, and `0x250` in memory... This is due to `zero bytes` allocated to the `.bss` section, for uninitialized variables. (but which in this program?) Why is this you might be wondering? Let us first have a look at the contents of the file itself. ```console $ hexdump --skip 0x2dd0 --length 0x248 ./a.out 0002dd0 1130 0000 0000 0000 10e0 0000 0000 0000 0002de0 0001 0000 0000 0000 0027 0000 0000 0000 0002df0 000c 0000 0000 0000 1000 0000 0000 0000 0002e00 000d 0000 0000 0000 1154 0000 0000 0000 0002e10 0019 0000 0000 0000 3dd0 0000 0000 0000 0002e20 001b 0000 0000 0000 0008 0000 0000 0000 0002e30 001a 0000 0000 0000 3dd8 0000 0000 0000 0002e40 001c 0000 0000 0000 0008 0000 0000 0000 0002e50 fef5 6fff 0000 0000 03c0 0000 0000 0000 0002e60 0005 0000 0000 0000 0488 0000 0000 0000 0002e70 0006 0000 0000 0000 03e0 0000 0000 0000 0002e80 000a 0000 0000 0000 008d 0000 0000 0000 0002e90 000b 0000 0000 0000 0018 0000 0000 0000 0002ea0 0015 0000 0000 0000 0000 0000 0000 0000 0002eb0 0003 0000 0000 0000 3fe8 0000 0000 0000 0002ec0 0002 0000 0000 0000 0018 0000 0000 0000 0002ed0 0014 0000 0000 0000 0007 0000 0000 0000 0002ee0 0017 0000 0000 0000 0618 0000 0000 0000 0002ef0 0007 0000 0000 0000 0558 0000 0000 0000 0002f00 0008 0000 0000 0000 00c0 0000 0000 0000 0002f10 0009 0000 0000 0000 0018 0000 0000 0000 0002f20 fffb 6fff 0000 0000 0000 0800 0000 0000 0002f30 fffe 6fff 0000 0000 0528 0000 0000 0000 0002f40 ffff 6fff 0000 0000 0001 0000 0000 0000 0002f50 fff0 6fff 0000 0000 0516 0000 0000 0000 0002f60 fff9 6fff 0000 0000 0003 0000 0000 0000 0002f70 0000 0000 0000 0000 0000 0000 0000 0000 * 0002fe0 0000 0000 0000 0000 3de0 0000 0000 0000 0002ff0 0000 0000 0000 0000 0000 0000 0000 0000 0003000 1036 0000 0000 0000 0000 0000 0000 0000 0003010 4010 0000 0000 0000 0003018 ``` Which is a larger data section than I was expecting... Why? #### .init TODO #### .text #### .rodata #### .data #### .bss #### .symtab #### .debug #### .line #### .strtab ### Aside a.out The executable code format on (old) Unix. TODO ## Loading Loading essentially consists of two parts: 1. Using the program header, that we have just inspected to load the code and data sections into memory. 2. Jump to the address of the `_start` function ### Inspection If we run the process, and inspect the process with: ```console $ pmap ``` we get ```console $ pmap -x 863151 863151: ./a.out Address Kbytes RSS Dirty Mode Mapping 0000557b6f1cc000 4 4 0 r---- a.out 0000557b6f1cd000 4 4 0 r-x-- a.out 0000557b6f1ce000 4 4 0 r---- a.out 0000557b6f1cf000 4 4 4 r---- a.out 0000557b6f1d0000 4 4 4 rw--- a.out 0000557b6f32c000 132 4 4 rw--- [ anon ] 00007f43e62c2000 8 4 4 rw--- [ anon ] 00007f43e62c4000 136 136 0 r---- libc.so.6 00007f43e62e6000 1388 608 0 r-x-- libc.so.6 00007f43e6441000 348 56 0 r---- libc.so.6 00007f43e6498000 16 16 16 r---- libc.so.6 00007f43e649c000 8 8 8 rw--- libc.so.6 00007f43e649e000 60 28 28 rw--- [ anon ] 00007f43e64ea000 4 4 0 r---- ld-linux-x86-64.so.2 00007f43e64eb000 152 152 0 r-x-- ld-linux-x86-64.so.2 00007f43e6511000 40 36 0 r---- ld-linux-x86-64.so.2 00007f43e651b000 8 8 8 r---- ld-linux-x86-64.so.2 00007f43e651d000 8 8 8 rw--- ld-linux-x86-64.so.2 00007ffde18a6000 136 16 16 rw--- [ stack ] 00007ffde1903000 16 0 0 r---- [ anon ] 00007ffde1907000 8 4 0 r-x-- [ anon ] ffffffffff600000 4 0 0 --x-- [ anon ] ---------------- ------- ------- ------- total kB 2492 1108 100 ``` And ```console $ pmap -X 863151 863151: ./a.out Address Perm Offset Device Inode Size Rss Pss Referenced Anonymous LazyFree ShmemPmdMapped FilePmdMapped Shared_Hugetlb Private_Hugetlb Swap SwapPss Locked THPeligible Mapping 557b6f1cc000 r--p 00000000 fe:03 2099417 4 4 4 4 0 0 0 0 0 0 0 0 0 0 a.out 557b6f1cd000 r-xp 00001000 fe:03 2099417 4 4 4 4 0 0 0 0 0 0 0 0 0 0 a.out 557b6f1ce000 r--p 00002000 fe:03 2099417 4 4 4 4 0 0 0 0 0 0 0 0 0 0 a.out 557b6f1cf000 r--p 00002000 fe:03 2099417 4 4 4 4 4 0 0 0 0 0 0 0 0 0 a.out 557b6f1d0000 rw-p 00003000 fe:03 2099417 4 4 4 0 4 0 0 0 0 0 0 0 0 0 a.out 557b6f32c000 rw-p 00000000 00:00 0 132 4 4 4 4 0 0 0 0 0 0 0 0 0 [heap] 7f43e62c2000 rw-p 00000000 00:00 0 8 4 4 4 4 0 0 0 0 0 0 0 0 0 7f43e62c4000 r--p 00000000 fe:02 2624788 136 136 5 136 0 0 0 0 0 0 0 0 0 0 libc.so.6 7f43e62e6000 r-xp 00022000 fe:02 2624788 1388 608 17 608 0 0 0 0 0 0 0 0 0 0 libc.so.6 7f43e6441000 r--p 0017d000 fe:02 2624788 348 56 0 56 0 0 0 0 0 0 0 0 0 0 libc.so.6 7f43e6498000 r--p 001d4000 fe:02 2624788 16 16 16 8 16 0 0 0 0 0 0 0 0 0 libc.so.6 7f43e649c000 rw-p 001d8000 fe:02 2624788 8 8 8 0 8 0 0 0 0 0 0 0 0 0 libc.so.6 7f43e649e000 rw-p 00000000 00:00 0 60 28 28 20 28 0 0 0 0 0 0 0 0 0 7f43e64ea000 r--p 00000000 fe:02 2624779 4 4 0 4 0 0 0 0 0 0 0 0 0 0 ld-linux-x86-64.so.2 7f43e64eb000 r-xp 00001000 fe:02 2624779 152 152 4 152 0 0 0 0 0 0 0 0 0 0 ld-linux-x86-64.so.2 7f43e6511000 r--p 00027000 fe:02 2624779 40 36 1 36 0 0 0 0 0 0 0 0 0 0 ld-linux-x86-64.so.2 7f43e651b000 r--p 00031000 fe:02 2624779 8 8 8 4 8 0 0 0 0 0 0 0 0 0 ld-linux-x86-64.so.2 7f43e651d000 rw-p 00033000 fe:02 2624779 8 8 8 4 8 0 0 0 0 0 0 0 0 0 ld-linux-x86-64.so.2 7ffde18a6000 rw-p 00000000 00:00 0 136 16 16 16 16 0 0 0 0 0 0 0 0 0 [stack] 7ffde1903000 r--p 00000000 00:00 0 16 0 0 0 0 0 0 0 0 0 0 0 0 0 [vvar] 7ffde1907000 r-xp 00000000 00:00 0 8 4 0 4 0 0 0 0 0 0 0 0 0 0 [vdso] ffffffffff600000 --xp 00000000 00:00 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0 [vsyscall] ==== ==== === ========== ========= ======== ============== ============= ============== =============== ==== ======= ====== =========== 2492 1108 139 1072 100 0 0 0 0 0 0 0 0 0 KB ``` in which we can see how the address space is laid out. With code and data mapped in to the lower addresses, and the heap following it. Then the mapped shared libraries as present, and finally, at the top of our adressable address space is the stack (with the unadressable kernel address space on top). ## Linking