diff options
author | johannst <johannes.stoelp@gmail.com> | 2021-03-20 03:16:23 +0100 |
---|---|---|
committer | johannst <johannes.stoelp@gmail.com> | 2021-03-20 03:16:23 +0100 |
commit | 4e871c9dd0418c4f6d33c83fd3338ad261f7dd3f (patch) | |
tree | b86c40b4dc1ad05b92eee8ad76615cf572bbf5b0 /03_hello_dynld/README.md | |
parent | ef6a411ce8ff615d65e2be105834c2fdbe557de1 (diff) | |
download | dynld-4e871c9dd0418c4f6d33c83fd3338ad261f7dd3f.tar.gz dynld-4e871c9dd0418c4f6d33c83fd3338ad261f7dd3f.zip |
added chapter 03 hello dynld
Diffstat (limited to '03_hello_dynld/README.md')
-rw-r--r-- | 03_hello_dynld/README.md | 127 |
1 files changed, 127 insertions, 0 deletions
diff --git a/03_hello_dynld/README.md b/03_hello_dynld/README.md new file mode 100644 index 0000000..e79e13a --- /dev/null +++ b/03_hello_dynld/README.md @@ -0,0 +1,127 @@ +# Hello `dynld` + +### Goals +- Build dynamic linker `dynld.so` which retrieves the user program's + entrypoint (`AT_ENTRY`) from the auxiliary vector and transfers + control to it. +- Build `no-std` program with a custom `PT_INTERP` entry pointing to + `dynld.so`. + +--- + +## Crafting the `dynld.so` + +As described in the `goals` above, the idea in this section is to +create a simple dynamic linker which just gets the `entrypoint` of the +user application and then jumps to it. This means the linker does not +support things like: +- Loading of additional dependencies. +- Performing re-locations. + +That said, this dynamic linker will not be particularly useful but +act as a skeleton for the upcoming chapters. + +The `entrypoint` of the user executable started by the dynamic linker +can be found in the `auxiliary vector` setup by the Linux Kernel (see +[02_process_init](../02_process_init/README.md)). +The entry of interest is the `AT_ENTRY`: +```text +AT_ENTRY + The `a_ptr` member of this entry holds the entry point of + the application program to which the interpreter program should + transfer control. +``` + +There are two additional entries that need to be discussed, +`AT_EXECFD` and `AT_PHDR`. The x86_64 SystemV ABI states that the OS +Kernel must provide one or the other in the `auxiliary vector`. +For simplicity the dynamic linker in this section only supports +`AT_PHDR`, which means it requires the OS Kernel to already memory map +the user executable. +```text +AT_EXECFD + At process creation the system may pass control to an + interpreter program. When this happens, the system places + either an entry of type `AT_EXECFD` or one of type `AT_PHDR` + in the auxiliary vector. The entry for type `AT_EXECFD` + contains a file descriptor open to read the application + program’s object file. + +AT_PHDR + The system may create the memory image of the application + program before passing control to the interpreter + program. When this happens the `AT_PHDR` entry tells the + interpreter where to find the program header table in the + memory image. +``` + +Using the [`no-std` program](../02_process_init/entry.c) from chapter +[02_process_init](../02_process_init) as starting point, loading and +jumping to the `entrypoint` of the user program can be done as: +```c +void (*user_entry)() = (void (*)())auxv[AT_ENTRY]; +user_entry(); +``` + +## User program + +The next step is to create the user program that will be loaded by +the dynamic linker created in the previous section. +For now the functionality of the user program is not important, but it +must full-fill the requirements no to depend on any shared libraries or +contain any relocations. +For this purpose the following simple `no-std` program is used: +```c +#include <syscall.h> +#include <io.h> + +#include <asm/unistd.h> + +void _start() { + pfmt("Running %s @ %s\n", __FUNCTION__, __FILE__); + + syscall1(__NR_exit, 0); +} +``` + +The important step, when linking the user program, is to inform the +static linker to add the `dynld.so` created above in the `.interp` +section. This can be done with the following command line switch: +```bash +gcc ... -Wl,--dynamic-linker=<path> ... +``` +> The full compile and link command can be seen in the [Makefile - main](./Makefile). + +The result can be seen in the `.interp` sections referenced by the +`PT_INTERP` segment in the program headers: +```bash +readelf -W --string-dump .interp main + +String dump of section '.interp': + [ 0] /home/johannst/dev/dynld/03_hello_dynld/dynld.so +``` +```bash +readelf -W --program-headers main + +Program Headers: + Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align + PHDR 0x000040 0x0000000000000040 0x0000000000000040 0x0002d8 0x0002d8 R 0x8 + INTERP 0x000318 0x0000000000000318 0x0000000000000318 0x000031 0x000031 R 0x1 + [Requesting program interpreter: /home/johannst/dev/dynld/03_hello_dynld/dynld.so] + ... +``` + +As discussed in [01_dynamic_linking](../01_dynamic_linking/README.md) +the `PT_INTERP` segment tells to Linux Kernel which dynamic linker to +load to handle the startup of the user executable. + +When running the `./main` user program, the `dynld.so` will be loaded +by the Linux Kernel and controlled will be handed over to it. The +`dynld.so` will retrieve the `entrypoint` of the user program and then +jump to it. + +## Things to remember +- As defined by the SystemV ABI the OS Kernel must either provide an + entry for `AT_EXECFD` or `AT_PHDR` in the `auxiliary vector`. +- The `AT_ENTRY` points to the `entrypoint` of the user program. +- When linking with gcc, the dynamic linker can be specified via `-Wl,--dynamic-linker=<path>`. |