diff options
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | 01_hello_dynld/Makefile | 13 | ||||
-rw-r--r-- | 01_hello_dynld/README.md | 98 | ||||
-rw-r--r-- | 01_hello_dynld/libgreet.c | 5 | ||||
-rw-r--r-- | 01_hello_dynld/main.c | 6 |
5 files changed, 125 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a11eb0a --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.so +*.o +main diff --git a/01_hello_dynld/Makefile b/01_hello_dynld/Makefile new file mode 100644 index 0000000..efcc457 --- /dev/null +++ b/01_hello_dynld/Makefile @@ -0,0 +1,13 @@ +inspect: build + readelf -W --sections main + readelf -W --dynamic main + readelf -W --program-headers main + readelf -W --string-dump .interp main + gdb -q --batch -ex 'starti' -ex 'bt' ./main + +build: libgreet.c main.c + gcc -o libgreet.so libgreet.c -shared -fPIC + gcc -o main main.c -lgreet -L. -Wl,--rpath=$(PWD) + +clean: + rm -f libgreet.so main diff --git a/01_hello_dynld/README.md b/01_hello_dynld/README.md new file mode 100644 index 0000000..b7b627b --- /dev/null +++ b/01_hello_dynld/README.md @@ -0,0 +1,98 @@ +# Hello dynamic linking + +In `dynamic linking` a program can use code that is not contained in the +program itself but rather in separate library files, so called shared objects. + +A statically linked program contains all the `code` & `data` that it needs to +run from start until completion. The program will be loaded by the OS from the +disk into the virtual address space and control is handed over to the mapped +program. +```text + @vm + | | + @disk |--------| ++--------+ execve(2) | | <- $rip +| prog A | ------------> | prog A | ++--------+ | | + |--------| + | | +``` + +A dynamically linked program needs to specify a `dynamic linker` which is +basically a runtime interpreter. The OS will additionally load that interpreter +into the virtual address space and give control to the interpreter rather than +the user program. +The interpreter will prepare the execution environment, like loading the +dependencies and so on and once that is done pass control to the user program. +```text + @vm @vm + | | | | + @disk |--------| |----------| ++--------------+ execve(2) | | | | <- $rip +| prog A | ------------> | prog A | | prog A | ++--------------+ | | load deps | | +| interp ldso | |--------| ------------> |----------| +| dep libgreet | | | | | ++--------------+ |--------| |----------| + | ldso | <- $rip | ldso | + |--------| |----------| + | | + |----------| + | libgreet | + |----------| +``` +> NOTE: Technically the OS does not need to load the user program itself in +> case it is dynamically linked, but that detail is not important here. + +In `ELF` files the name of the dynamic linker is specified in the `.interp` section. +```bash +readelf -W --string-dump .interp main + +String dump of section '.interp': + [ 0] /lib64/ld-linux-x86-64.so.2 +``` + +The `.interp` section is referenced by the `PT_INTERP` segment in the program +headers. During `execve(2)` in the [`load_elf_binary`][load_elf_binary] +function (Linux Kernel) this segment is used to check if the program needs a +dynamic linker and to get its name. +```bash +readelf -W --sections --program-headers main + +Section Headers: + [Nr] Name Type Address Off Size ES Flg Lk Inf Al + [ 0] NULL 0000000000000000 000000 000000 00 0 0 0 + [ 1] .interp PROGBITS 00000000000002a8 0002a8 00001c 00 A 0 0 1 + ... + +Program Headers: + Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align + PHDR 0x000040 0x0000000000000040 0x0000000000000040 0x000268 0x000268 R 0x8 + INTERP 0x0002a8 0x00000000000002a8 0x00000000000002a8 0x00001c 0x00001c R 0x1 + [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2] + ... +``` + +Using `gdb` to break on the first instruction (`starti`) and printing the +backtrace (`bt`) it can be seen that the control first is passed to the +dynamic linker `ld-linux-x86.so.2` rather than to the user program. +```bash +gdb -q --batch -ex 'starti' -ex 'bt' ./main + +Program stopped. +0x00007ffff7fd2090 in _start () from /lib64/ld-linux-x86-64.so.2 +#0 0x00007ffff7fd2090 in _start () from /lib64/ld-linux-x86-64.so.2 +#1 0x0000000000000001 in ?? () +#2 0x00007fffffffe43e in ?? () +#3 0x0000000000000000 in ?? () +``` +> NOTE: Frames `#1`, `#2`, `#3` don't actually exist, gdb's unwinder just tried to further unwind the stack. + +## Things to remember +- Dynamically linked programs use code contained in separate library files. +- The `dynamic linker` is an interpreter loaded by the OS and gets control + before the user program. +- A dynamically linked program specifies the dynamic linker needed in the + `.interp` ELF section. + +[load_elf_binary]: https://elixir.bootlin.com/linux/v5.9.8/source/fs/binfmt_elf.c#L850 diff --git a/01_hello_dynld/libgreet.c b/01_hello_dynld/libgreet.c new file mode 100644 index 0000000..2419d92 --- /dev/null +++ b/01_hello_dynld/libgreet.c @@ -0,0 +1,5 @@ +#include <stdio.h> + +void greet() { + puts("Hello from libgreet.so!"); +} diff --git a/01_hello_dynld/main.c b/01_hello_dynld/main.c new file mode 100644 index 0000000..a12a448 --- /dev/null +++ b/01_hello_dynld/main.c @@ -0,0 +1,6 @@ +extern void greet(); + +int main() { + greet(); + return 0; +} |