{"id":13836030,"url":"https://github.com/unikraft/app-elfloader","last_synced_at":"2025-04-10T17:36:33.044Z","repository":{"id":40316741,"uuid":"342701964","full_name":"unikraft/app-elfloader","owner":"unikraft","description":"Load and execute Linux ELF binaries","archived":false,"fork":false,"pushed_at":"2024-04-16T16:10:48.000Z","size":714,"stargazers_count":19,"open_issues_count":26,"forks_count":29,"subscribers_count":12,"default_branch":"staging","last_synced_at":"2024-04-17T21:16:37.402Z","etag":null,"topics":["application","binary-compatibility","elf","linux-apps","unikernel","unikraft","unikraft-application"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/unikraft.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":"support/gdb-wrapper.template.sh","governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-02-26T21:09:50.000Z","updated_at":"2024-05-30T00:44:00.161Z","dependencies_parsed_at":"2023-10-20T01:11:15.397Z","dependency_job_id":"b26cfd2b-b718-42a2-adf6-7b519188f3d2","html_url":"https://github.com/unikraft/app-elfloader","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unikraft%2Fapp-elfloader","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unikraft%2Fapp-elfloader/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unikraft%2Fapp-elfloader/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unikraft%2Fapp-elfloader/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/unikraft","download_url":"https://codeload.github.com/unikraft/app-elfloader/tar.gz/refs/heads/staging","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248262036,"owners_count":21074233,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["application","binary-compatibility","elf","linux-apps","unikernel","unikraft","unikraft-application"],"created_at":"2024-08-04T15:00:33.328Z","updated_at":"2025-04-10T17:36:33.022Z","avatar_url":"https://github.com/unikraft.png","language":"C","readme":"# Unikraft ELF Loader\n\nThe ELF Loader is the integral part of the binary compatibility layer, that enables Unikraft to run unmodified Linux applications.\nLinux binaries (ELFS - **Executable and Linking Format**) are loaded by the ELF Loader, and then control is passed to them binary.\n\nFollow the instructions below to set up, configure, build and run ELF Loader.\n\nTo get started immediately, you can use Unikraft's companion command-line companion tool, [`kraft`](https://github.com/unikraft/kraftkit).\nStart by running the interactive installer:\n\n```console\ncurl --proto '=https' --tlsv1.2 -sSf https://get.kraftkit.sh | sudo sh\n```\n\nOnce installed, clone [this repository](https://github.com/unikraft/app-elfloader) and run `kraft build`:\n\n```console\ngit clone https://github.com/unikraft/app-elfloader elfloader\ncd elfloader/\nkraft build\n```\n\nThis will guide you through an interactive build process where you can select one of the available targets (architecture/platform combinations).\nOtherwise, we recommend the `elfloader-qemu-x86_64-initrd-strace` target:\n\n```console\nkraft build --target elfloader-qemu-x86_64-initrd-strace\n```\n\nOnce built, you can instantiate the unikernel to run an existing dynamic executable.\nFor that, clone the catalog of dynamic applications from [the `dynamic-apps` repository](https://github.com/unikraft/dynamic-apps), in the same directory where you cloned `app-elfloader`:\n\n```console\ngit clone https://github.com/unikraft/dynamic-apps\n```\n\nRun a simple `helloworld` binary, by running, while inside the `elfloader` directory:\n\n```console\nkraft run --target elfloader-qemu-x86_64-initrd-strace --plat qemu --initrd ../dynamic-apps/lang/c/helloworld/:/ -- /helloworld\n```\n\nBecause we used an `strace` target, the output of the program also consists of system calls invoked by the Linux binary after loading.\n\n## Quick Setup (aka TLDR)\n\nFor a quick setup, run the commands below.\nNote that you still need to install the [requirements](#requirements).\n\nFor building and running everything, follow the steps below.\n\n```console\ngit clone https://github.com/unikraft/dynamic-apps\ngit clone https://github.com/unikraft/app-elfloader elfloader\ncd elfloader/\n./scripts/setup.sh\nwget https://raw.githubusercontent.com/unikraft/app-testing/staging/scripts/generate.py -O scripts/generate.py\nchmod a+x scripts/generate.py\n./scripts/generate.py\n./scripts/build/make-qemu-x86_64-9pfs.sh\n./scripts/run/qemu-x86_64-9pfs-helloworld-c.sh\n```\n\nThis will configure and build the `app-elfloader`.\nAfter that, it will run the `/helloworld` `ELF` file from the [`dynamic-apps` repository](https://github.com/unikraft/dynamic-apps) on top of the `elfloader` image:\n\n**Note**: Close the linger QEMU VM by using `Ctrl+a x`.\nThat is, press `Ctrl` and `a` at the same time, and then, separately, `x`.\n\n## Requirements\n\nIn order to set up, configure, build and run `app-elfloader` on Unikraft, the following packages are required:\n\n* `build-essential` / `base-devel` / `@development-tools` (the meta-package that includes `make`, `gcc` and other development-related packages)\n* `sudo`\n* `flex`\n* `bison`\n* `git`\n* `wget`\n* `uuid-runtime`\n* `qemu-system-x86`\n* `qemu-kvm`\n* `sgabios`\n\nOn Ubuntu/Debian or other `apt`-based distributions, run the following command to install the requirements:\n\n```console\nsudo apt install -y --no-install-recommends \\\n  build-essential \\\n  sudo \\\n  libncurses-dev \\\n  libyaml-dev \\\n  flex \\\n  bison \\\n  git \\\n  wget \\\n  uuid-runtime \\\n  qemu-kvm \\\n  qemu-system-x86 \\\n  sgabios\n```\n\nRunning the ELF loader app with QEMU might require networking support, depending on the Linux application used (such as Nginx, or Redis).\nFor this to work properly a specific configuration must be enabled for QEMU.\nRun the commands below to enable that configuration (for the network bridge to work):\n\n```console\nsudo mkdir /etc/qemu/\necho \"allow all\" | sudo tee /etc/qemu/bridge.conf\n```\n\n## Set Up\n\nThe following repositories are required for `app-elfloader`:\n\n* The application repository (this repository): [`app-elfloader`](https://github.com/unikraft/app-elfloader)\n* The Unikraft core repository: [`unikraft`](https://github.com/unikraft/unikraft)\n* Library repositories:\n  * The networking stack library: [`lib-lwip`](https://github.com/unikraft/lib-lwip)\n  * The `ELF` Tool Chain library: [`lib-libelf`](https://github.com/unikraft/lib-libelf)\n\nFollow the steps below for the setup:\n\n  1. First clone the [`dynamic-apps` repository](https://github.com/unikraft/dynamic-apps) that contains pre-build ELFs to be used with `elfloader`:\n\n     ```console\n     git clone https://github.com/unikraft/dynamic-apps dynamic-apps\n     ```\n\n  1. Now clone the [`app-elfloader` repository](https://github.com/unikraft/app-elfloader) in the `elfloader/` directory, on the same level with the `dynamic-apps` repository clone:\n\n     ```console\n     git clone https://github.com/unikraft/app-elfloader elfloader\n     ```\n\n     Enter the `elfloader/` directory:\n\n     ```console\n     cd elfloader/\n\n     ls -F\n     ```\n\n     This will show you the contents of the repository:\n\n     ```text\n     arch_prctl.c  brk.c  Config.uk  elf_ctx.c  elf_load.c  elf_prog.h  example/  exportsyms.uk  libelf_helper.h  main.c  Makefile  Makefile.uk  README.md  support/\n     ```\n\n  1. While inside the `elfloader/` directory, clone all required repositories by using the `setup.sh` script:\n\n     ```console\n     ./scripts/setup.sh\n     ```\n\n  1. Use the `tree` command to inspect the contents of the `workdir/` directory.\n     It should print something like this:\n\n     ```console\n     tree -F -L 2 workdir/\n     ```\n\n     The layout of the `workdir/` directory should look something like this:\n\n     ```text\n     workdir/\n     |-- libs/\n     |   |-- lwip/\n     |   |-- libelf/\n     `-- unikraft/\n         |-- arch/\n         |-- Config.uk\n         |-- CONTRIBUTING.md\n         |-- COPYING.md\n         |-- include/\n         |-- lib/\n         |-- Makefile\n         |-- Makefile.uk\n         |-- plat/\n         |-- README.md\n         |-- support/\n         `-- version.mk\n\n     9 directories, 7 files\n     ```\n\n## Scripted Building and Running\n\nTo build and run Unikraft images, it's easiest to generate build and running scripts and use those.\n\nFirst of all, grab the [`generate.py` script](https://github.com/unikraft/app-testing/blob/staging/scripts/generate.py) and place it in the `scripts/` directory by running:\n\n```console\nwget https://raw.githubusercontent.com/unikraft/app-testing/staging/scripts/generate.py -O scripts/generate.py\nchmod a+x scripts/generate.py\n```\n\nNow, run the `generate.py` script.\nYou must run it in the root directory of this repository:\n\n```console\n./scripts/generate.py\n```\n\nRunning the script will generate build and run scripts in the `scripts/build/` and the `scripts/run/` directories:\n\n```text\nscripts/\n|-- build/\n|   |-- kraft-fc-x86_64-initrd-debug.sh*\n|   |-- kraft-fc-x86_64-initrd.sh*\n|   |-- kraft-fc-x86_64-initrd-strace.sh*\n|   |-- kraft-qemu-x86_64-9pfs-debug.sh*\n|   |-- kraft-qemu-x86_64-9pfs.sh*\n|   |-- kraft-qemu-x86_64-9pfs-strace.sh*\n|   |-- kraft-qemu-x86_64-initrd-debug.sh*\n|   |-- kraft-qemu-x86_64-initrd.sh*\n|   |-- kraft-qemu-x86_64-initrd-strace.sh*\n|   |-- make-fc-x86_64-initrd-debug.sh*\n|   |-- make-fc-x86_64-initrd.sh*\n|   |-- make-fc-x86_64-initrd-strace.sh*\n|   |-- make-qemu-x86_64-9pfs-debug.sh*\n|   |-- make-qemu-x86_64-9pfs.sh*\n|   |-- make-qemu-x86_64-9pfs-strace.sh*\n|   |-- make-qemu-x86_64-initrd-debug.sh*\n|   |-- make-qemu-x86_64-initrd.sh*\n|   `-- make-qemu-x86_64-initrd-strace.sh*\n|-- generate.py*\n|-- run/\n|   |-- fc-x86_64-initrd-helloworld-c.json\n|   |-- fc-x86_64-initrd-helloworld-c.sh*\n|   |-- fc-x86_64-initrd-nginx.json\n[...]\n|   |-- kraft-qemu-x86_64-initrd-nginx.sh*\n|   |-- kraft-qemu-x86_64-initrd-sqlite3.sh*\n|   |-- kraft-qemu-x86_64-initrd-strace-helloworld-c.sh*\n|   |-- kraft-qemu-x86_64-initrd-strace-nginx.sh*\n|   |-- kraft-qemu-x86_64-initrd-strace-sqlite3.sh*\n|   |-- qemu-x86_64-9pfs-helloworld-c.sh*\n|   |-- qemu-x86_64-9pfs-nginx.sh*\n|   |-- qemu-x86_64-9pfs-sqlite3.sh*\n|   |-- qemu-x86_64-initrd-helloworld-c.sh*\n|   |-- qemu-x86_64-initrd-nginx.sh*\n|   `-- qemu-x86_64-initrd-sqlite3.sh*\n|-- run.yaml\n`-- setup.sh*\n```\n\nThey are shell scripts, so you can use an editor or a text viewer to check their contents:\n\n```console\ncat scripts/run/kraft-fc-x86_64-initrd-helloworld-c.sh\n```\n\nNow, invoke each script to build and run ELF loader.\nA sample build and run set of commands is:\n\n```console\n./scripts/build/kraft-qemu-x86_64-9pfs-strace.sh\n./scripts/run/kraft-qemu-x86_64-9pfs-strace-helloworld-c.sh\n./scripts/run/kraft-qemu-x86_64-9pfs-strace-nginx.sh\n```\n\nAnother one is:\n\n```console\n./scripts/build/make-qemu-x86_64-initrd.sh\n./scripts/run/qemu-x86_64-initrd-helloworld-c.sh\n./scripts/run/qemu-x86_64-initrd-nginx.sh\n```\n\nNote that Firecracker only works with initrd (not 9pfs).\nAnd Firecracker networking is not yet upstream.\n\n## Detailed Steps\n\n### Configure\n\nConfiguring, building and running a Unikraft application depends on our choice of platform and architecture.\nCurrently, supported platform and architecture for `app-elfloader` are QEMU (KVM), x86_64.\nUse the `.config.elfloader_qemu-x86_64` configuration file together with `make defconfig` to create the configuration file:\n\n```console\nUK_DEFCONFIG=$(pwd)/.config.elfloader_qemu-x86_64 make defconfig\n```\n\nThis results in the creation of the `.config` file:\n\n```console\nls .config\n.config\n```\n\nThe `.config` file will be used in the build step.\n\n### Build\n\nBuilding uses as input the `.config` file from above, and results in a unikernel image as output.\nThe unikernel output image, together with intermediary build files, are stored in the `build/` directory.\n\n#### Clean Up\n\nBefore building after some changes had been made, you may need to clean up the build output.\n\nCleaning up is done with 3 possible commands:\n\n* `make clean`: cleans all actual build output files (binary files, including the unikernel image)\n* `make properclean`: removes the entire `build/` directory\n* `make distclean`: removes the entire `build/` directory **and** the `.config` file\n\nTypically, you would use `make properclean` to remove all build artifacts, but keep the configuration file.\n\n#### QEMU x86_64\n\nBuilding for QEMU x86_64 assumes you did the QEMU x86_64 configuration step above.\nBuild the Unikraft elfloader image for QEMU x86_64 by using the command below:\n\n```console\nmake -j $(nproc)\n```\n\nYou can see a list of all the files processed by the build system:\n\n```text\n[...]\n  LD      elfloader_qemu-x86_64.dbg\n  UKBI    elfloader_qemu-x86_64.dbg.bootinfo\n  SCSTRIP elfloader_qemu-x86_64\n  GZ      elfloader_qemu-x86_64.gz\nmake[1]: Leaving directory '/tmp/apps/app-elfloader/workdir/unikraft'\n```\n\nAt the end of the build command, the `elfloader_qemu-x86_64` unikernel image is generated.\nThis image is to be used in the run step.\n\n## Executing ELF binaries\n\nThe `elfloader` currently supports statically-linked and dynamically-linked applications for Linux on x86_64, as long as they are compiled position independent (PIE).\nIn most cases, such an application can be loaded from any virtual file system that is supported by Unikraft.\nFor example, the application binary can be packaged with a CPIO initramdisk or handed over via a 9pfs host share.\nPlease note that we use 9pfs in the following how-to.\nTo load the application from another file system (e.g., initramdisk), you will need to follow equivalent steps.\n\nBefore we can launch an application we need to prepare a root file system that contains the ELF binary along with its library dependencies.\nYou can use `ldd` (or probably `musl-ldd` for applications linked with [`musl`](https://www.musl-libc.org/)) to list the shared libraries on which the application depends.\nPlease note that the vDSO (here: `linux-vdso.so.1`) is a kernel-provided library that is not present on the host filesystem.\nPlease ignore this file.\n\nFor a helloworld example application (here: [`/example/helloworld`](./example/helloworld), compiled on Debian 11), `ldd` will likely look like the following:\n\n```sh\n$ ldd helloworld\n\tlinux-vdso.so.1 (0x00007ffdd695d000)\n\tlibc.so.6 =\u003e /lib/x86_64-linux-gnu/libc.so.6 (0x00007efed259f000)\n\t/lib64/ld-linux-x86-64.so.2 (0x00007efed2787000)\n```\n\nCopy the library dependencies to the same subdirectories as reported by `ldd`.\nPlease remember to also copy any additional and required configuration files to the root file system.\nIn this example, the populated root filesystem will look like this:\n\n```text\nrootfs/\n├── lib\n│   └── x86_64-linux-gnu\n│       └── libc.so.6\n├── lib64\n│   └── ld-linux-x86-64.so.2\n└── helloworld\n```\n\nBecause the official dynamic loader maps the application and libraries into memory, the `elfloader` unikernel must be configured with `posix-mmap`, `ukvmem`, and `vfscore`.\nFor 9pfs, also make sure that you configured `vfscore` to automatically mount a host shared filesystem: Under `Library Configuration -\u003e vfscore: Configuration` select `Automatically mount a root filesystem`, set `Default root filesystem` to `9PFS`, and ensure that `Default root device` is to `fs0`.\nThis last option simplifies the use of the `-e` parameter of [`qemu-guest`](https://github.com/unikraft/unikraft/tree/staging/support/scripts).\n\nThe application can then be started with:\n\n```sh\n# qemu-guest -k elfloader_kvm-x86_64 -e rootfs/ \\\n            -a \"/helloworld \u003capplication arguments\u003e\"\n```\n\n*NOTE:* This command line example expects that you built your unikernel with `Application Options -\u003e Application name/path via command line` (`APPELFLOADER_CUSTOMAPPNAME`).\n\n*HINT:* Environment variables can be set through `lib/posix-environ` and `lib/uklibparam`.\nFor this purpose, enable `Library Configuration -\u003e posix-environ` and activate `Parse kernel command line arguments`.\nThe variables can be handed over via the kernel command line with the Unikraft library parameter `env.vars`, for example:\n\n```sh\n# qemu-guest -k elfloader_kvm-x86_64 -e rootfs/ \\\n            -a \"env.vars=[ LD_LIBRARY_PATH=/lib LD_SHOW_AUXV=1 ] -- /helloworld \u003capplication arguments\u003e\"\n```\n\n*NOTE:* At the moment, a program exit will not yet cause a shutdown of the elfloader unikernel. You need to manually terminate it.\nIn case of `qemu-guest`, you can use `CTRL` + `C`.\n\n## Debugging\n\n### `strace`-like Output\n\nUnikraft's [`syscall_shim`](https://github.com/unikraft/unikraft/tree/staging/lib/syscall_shim) provides the ability to print a strace-like message for every processed binary system call request on the kernel output.\nThis option can be useful for understanding what code a system call handler returns to the application, and how the application interacts with the kernel.\nThe setting can be found under `Library Configuration -\u003e syscall_shim -\u003e Debugging`: `'strace'-like messages for binary system calls`.\n\n### GNU Debugger (gdb)\n\nIt is possible to debug `elfloader` together with the loaded application, and use the full set of debugging facilities for kernel and application at the same time.\nIn principle, `gdb` must only be made aware of the runtime memory layout of `elfloader` with the loaded application.\nThanks to the single address space layout, we gain easy debugability and full transparency.\n\n#### Static-PIE ELF\n\nAs a first step, `gdb` is started with loading the symbols from the `dbg` image of the `elfloader`.\nWe map the symbols of ELF application with the gdb command `add-symbol-file` by specifying the application (with debug symbols) and the base load address.\nIf `info` messages are enabled in `ukdebug`, this base address will be messaged by the loader like this:\n\n```\nELF program loaded to 0x400101000-0x4001c2a08 (793096 B), entry at 0x40010ad50\n```\n\nTo this address (here: `0x400101000`) you have to add the offset of the `.text` segment.\nYou can use `readelf -S` to find it out. In our example it is `0x92a0` (output shortened):\n\n```\n$ readelf -S helloworld_static\nSection Headers:\n  [Nr] Name              Type             Address           Offset\n       Size              EntSize          Flags  Link  Info  Align\n  [11] .text             PROGBITS         00000000000092a0  000092a0\n       0000000000086eb0  0000000000000000  AX       0     0     32\n```\n\nThe resulting address here is `0x40010a2a0`. The symbols of the static helloworld program can then be loaded from `gdb` with the following command:\n\n```\n(gdb) add-symbol-file -readnow helloworld_static 0x40010A2A0\n```\n\nFrom this point you have symbol resolution in your debugger, for both the Unikraft elfloader and the loaded application.\n\n*NOTE:* You can only set regular breakpoints within the application (`break` with GDB) after it got loaded into memory by elfloader (otherwise they will be ignored).\nThe recommended procedure is:\n\n1. Set a breakpoint just after the application was loaded (e.g., the first system call that the application executes),\n2. Let the execution continue until the breakpoint is reached.\n3. Set the interesting breakpoints within application space.\n\n#### Dynamically-linked ELF\n\nThe principle of runtime address space layout for dynamically linked executables is the same as for statically linked executables.\nThe differences are that we have additionally loaded a dynamic loader together with the application and we have to load the symbols of each dependent dynamic library as well.\nFor this purpose, we recommend to enable `strace`-like output in `syscall_shim` (read subsection: [`strace`-like output](#strace-like-output)).\nIt is the dynamic loader that will for each library:\n\n1. Open the library.\n2. Parse the ELF header.\n3. Memory-map all needed sections into memory.\n4. Close the library file again.\n\nFor our Helloworld example compiled with `glibc`, this looks like the following for libc:\n\n```cpp\nopenat(AT_FDCWD, \"/libc.so.6\", O_RDONLY|O_CLOEXEC) = fd:3\nread(fd:3, \u003cout\u003e\"\\x7FELF\\x02\\x01\\x01\\x03\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\u003e\\x00\\x01\\x00\\x00\\x00\"..., 832) = 832\nfstat(fd:3, va:0x40006ef18) = OK\nmmap(NULL, 1918592, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, fd:3, 0) = va:0x8000005000\nmmap(va:0x8000027000, 1417216, PROT_EXEC|PROT_READ, MAP_PRIVATE|MAP_DENYWRITE|MAP_FIXED, fd:3, 139264) = va:0x8000027000\nmmap(va:0x8000181000, 323584, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE|MAP_FIXED, fd:3, 1556480) = va:0x8000181000\nmmap(va:0x80001d0000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_DENYWRITE|MAP_FIXED, fd:3, 1875968) = va:0x80001d0000\nmmap(va:0x80001d6000, 13952, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, fd:-1, 0) = va:0x80001d6000\nclose(fd:3) = OK\n```\n\nThe virtual address returned by the first `mmap` operation is the virtual base address of the application that we need to note down.\nIn this case, the virtual base address of `libc.so.6` is `0x8000005000`.\nPlease do the same for each dynamically loaded library.\n\nTo load the application and library symbols appropriately, as described in the previous subsection, you must add the segment offset of the `.text` section to the virtual base address.\nThis allows you to load the symbols from `gdb` with `add-symbol-file`.\n\n*NOTE:* Regular breakpoints in shared libraries can only be set after the libraries have been loaded into memory.\nSince these are loaded by the dynamic loader and not directly by the `elfloader`, this is done with `mmap` system calls as shown in the console snippet above.\nThe corresponding `close` system call (`break uk_syscall_r_close`) is a safe place to hop to before setting the actual breakpoints within a shared library.\n\n#### Recommendation: `gdb` Setup Script\n\nBecause of the calculations, we recommend scripting the `gdb` setup so that any subsequent `gdb` debug session will be ready quickly.\nYou can get inspiration from the following `bash` script, which provides a function that automatically determines the `.text` offset and adds it to a given base address.\nThe advantage is that only the base addresses have to be noted from the Unikraft console output.\n\n```bash\n#!/bin/bash\n# Host and port of the GDB server port (qemu)\nGDBSRV=\":1234\"\n\n# Generate a GDB command line for loading the symbols of an ELF executable/\n# shared library.\n# Usage: gdb-add-symbols \"\u003cELF executable/library\u003e\" \\\n#                        \"\u003cbase load address (hex, no leading '0x')\u003e\"\ngdb-add-symbols()\n{\n\tlocal LOAD_ELF=\"$1\"\n\tlocal LOAD_ADDR=\"${2}\"\n\tlocal LOAD_TADDR=\n\tlocal TEXT_OFFSET=\n\n\t# Hacky way to figure out the .text offset\n\tTEXT_OFFSET=$( readelf -S \"${LOAD_ELF}\" | grep '.text' | awk '{ print $5 }' )\n\n\t# Compute offset of .text section with base address\n\tLOAD_TADDR=$( printf 'obase=16;ibase=16;%s+%s\\n' \"${LOAD_ADDR^^}\" \"${TEXT_OFFSET^^}\" | bc )\n\n\t# Generate GDB command\n\tprintf 'add-symbol-file -readnow %s 0x%s' \"${LOAD_ELF}\" \"${LOAD_TADDR}\"\n}\n\n# Connect to $GDBSRV and set up gdb\n# NOTE: The first block of instructions connects to the gdb port of the qemu\n#       process and follows the CPU mode change while the guest is booting.\n#       This is currently a requirement for using gdb with qemu for x86_64. For\n#       other platforms and architectures, this may be different.\n#       This block assumes that the guest is started in paused state (`-P` for\n#       `qemu-guest`).\n# NOTE: Please note that regular breakpoints within the application or a shared\n#       library can only be set after they have been loaded into memory. Usually\n#       this is done by elfloader for the application and dynamic loader or by\n#       the dynamic loader for shared libraries.\n# HINT: You can use the `directory` command to specify additional paths that\n#       `gdb` will use to search for source files.\n#       For example, if you run your dynamically linked application with\n#       Debian's libc, you can install (`apt install glibc-source`) and\n#       extract the glibc sources under /usr/src/glibc.\n#         --eval-command=\"directory /usr/src/glibc/glibc-2.31\"\nexec gdb \\\n\t--eval-command=\"target remote $GDBSRV\" \\\n\t--eval-command=\"hbreak _ukplat_entry\" \\\n\t--eval-command=\"continue\" \\\n\t--eval-command=\"disconnect\" \\\n\t--eval-command=\"set arch i386:x86-64:intel\" \\\n\t--eval-command=\"target remote $GDBSRV\" \\\n\t\\\n\t--eval-command=\"$( gdb-add-symbols \"rootfs/helloworld\" \"8000000000\" )\" \\\n\t--eval-command=\"$( gdb-add-symbols \"rootfs/libc.so.6\" \"8000005000\" )\"\n```\n\n#### Hint: Debug symbols of libraries installed from packages\n\nIf you run your dynamically-linked application with libraries installed via a package manager, you can check if a debug package is also available for installation.\nFor example, Debian provides the debug symbols automatically for `gdb` with the following installation (root privileges required):\n\n```sh\n# apt install libc6-dbg\n```\n\nAs soon as the Debian's libc.so.6 is loaded by `gdb`, the debugger will load the symbols provided by the Debian debug package.\n\n#### Hint: Sources of libraries installed from packages\n\nIf you run your dynamically linked application with libraries installed via a package manager, you can check if a source package is available for installation.\nFor example, the libc sources are available under Debian with the package `glibc-source`. After you extracted the installed sources archive under `/usr/src/glibc`, you can make these sources visible to `gdb` with (given that you use Debian's libc also for the application with `elfloader`):\n\n```\n(gdb) directory /usr/src/glibc/glibc-2.31\n```\n\n#### Hint: Duplicate symbols\n\nCommon symbols like `main` might exist in both the KVM image and the ELF application.\nYou can be specific by referring to their respective source file if debug information is present: `helloworld.c:main` instead of just `main`.\nAlternatively, you can use `info functions [regexp]` to find the address of your symbol.\nFor example, `info function ^main$` prints the address of the `main` function.\nSee [\"Examining the Symbol Table\"] for details.\n\n[\"Examining the Symbol Table\"]: https://sourceware.org/gdb/current/onlinedocs/gdb.html/Symbols.html#Symbols\n","funding_links":[],"categories":["C"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Funikraft%2Fapp-elfloader","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Funikraft%2Fapp-elfloader","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Funikraft%2Fapp-elfloader/lists"}