mirror of
https://github.com/systemd/systemd
synced 2026-04-12 01:55:10 +02:00
Compare commits
2 Commits
ff97eb4aac
...
da2862ef06
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
da2862ef06 | ||
|
|
948d085e89 |
@ -330,3 +330,43 @@ To debug systemd components other than PID 1, set "program" to the full path of
|
|||||||
debug and set "processId" to "${command:pickProcess}". Now, when starting the debugger, VSCode will ask you
|
debug and set "processId" to "${command:pickProcess}". Now, when starting the debugger, VSCode will ask you
|
||||||
the PID of the process you want to debug. Run `systemctl show --property MainPID --value <component>` in the
|
the PID of the process you want to debug. Run `systemctl show --property MainPID --value <component>` in the
|
||||||
container to figure out the PID and enter it when asked and VSCode will attach to that process instead.
|
container to figure out the PID and enter it when asked and VSCode will attach to that process instead.
|
||||||
|
|
||||||
|
# Debugging systemd-boot
|
||||||
|
|
||||||
|
During boot, systemd-boot and the stub loader will output a message like `systemd-boot@0x0A,0x0B`,
|
||||||
|
providing the location of the text and data sections. These location can then be used to attach
|
||||||
|
to a QEMU session (provided it was run with `-s`) with these gdb commands:
|
||||||
|
|
||||||
|
```
|
||||||
|
(gdb) file build/src/boot/efi/systemd-bootx64.efi
|
||||||
|
(gdb) add-symbol-file build/src/boot/efi/systemd_boot.so 0x0A -s .data 0x0B
|
||||||
|
(gdb) set architecture i386:x86-64
|
||||||
|
(gdb) target remote :1234
|
||||||
|
```
|
||||||
|
|
||||||
|
This process can be automated by using the `debug-sd-boot.sh` script in the tools folder. If run
|
||||||
|
without arguments it will provide usage information.
|
||||||
|
|
||||||
|
If the debugger is too slow to attach to examine an early boot code passage, we can uncomment the
|
||||||
|
call to `debug_break()` inside of `efi_main()`. As soon as the debugger has control we can then run
|
||||||
|
`set variable wait = 0` or `return` to continue. Once the debugger has attached, setting breakpoints
|
||||||
|
will work like usual.
|
||||||
|
|
||||||
|
To debug systemd-boot in an IDE such as VSCode we can use a launch configuration like this:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "systemd-boot",
|
||||||
|
"type": "cppdbg",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${workspaceFolder}/build/src/boot/efi/systemd-bootx64.efi",
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"MIMode": "gdb",
|
||||||
|
"miDebuggerServerAddress": ":1234",
|
||||||
|
"setupCommands": [
|
||||||
|
{ "text": "shell mkfifo /tmp/sdboot.{in,out}" },
|
||||||
|
{ "text": "shell qemu-system-x86_64 [...] -s -serial pipe:/tmp/sdboot" },
|
||||||
|
{ "text": "shell ${workspaceFolder}/tools/debug-sd-boot.sh ${workspaceFolder}/build/src/boot/efi/systemd-bootx64.efi /tmp/sdboot.out systemd-boot.gdb" },
|
||||||
|
{ "text": "source /tmp/systemd-boot.gdb" },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|||||||
@ -2353,6 +2353,9 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
|
|||||||
|
|
||||||
InitializeLib(image, sys_table);
|
InitializeLib(image, sys_table);
|
||||||
init_usec = time_usec();
|
init_usec = time_usec();
|
||||||
|
debug_hook(L"systemd-boot");
|
||||||
|
/* Uncomment the next line if you need to wait for debugger. */
|
||||||
|
// debug_break();
|
||||||
|
|
||||||
err = BS->OpenProtocol(image,
|
err = BS->OpenProtocol(image,
|
||||||
&LoadedImageProtocol,
|
&LoadedImageProtocol,
|
||||||
|
|||||||
@ -181,6 +181,9 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
|
|||||||
EFI_STATUS err;
|
EFI_STATUS err;
|
||||||
|
|
||||||
InitializeLib(image, sys_table);
|
InitializeLib(image, sys_table);
|
||||||
|
debug_hook(L"systemd-stub");
|
||||||
|
/* Uncomment the next line if you need to wait for debugger. */
|
||||||
|
// debug_break();
|
||||||
|
|
||||||
err = BS->OpenProtocol(
|
err = BS->OpenProtocol(
|
||||||
image,
|
image,
|
||||||
|
|||||||
@ -738,3 +738,20 @@ UINT64 get_os_indications_supported(void) {
|
|||||||
|
|
||||||
return osind;
|
return osind;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef EFI_DEBUG
|
||||||
|
__attribute__((noinline)) void debug_break(void) {
|
||||||
|
/* This is a poor programmer's breakpoint to wait until a debugger
|
||||||
|
* has attached to us. Just "set variable wait = 0" or "return" to continue. */
|
||||||
|
volatile BOOLEAN wait = TRUE;
|
||||||
|
while (wait)
|
||||||
|
/* Prefer asm based stalling so that gdb has a source location to present. */
|
||||||
|
#if defined(__i386__) || defined(__x86_64__)
|
||||||
|
asm volatile("pause");
|
||||||
|
#elif defined(__aarch64__)
|
||||||
|
asm volatile("wfi");
|
||||||
|
#else
|
||||||
|
BS->Stall(5000);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
@ -159,3 +159,13 @@ static inline void *PHYSICAL_ADDRESS_TO_POINTER(EFI_PHYSICAL_ADDRESS addr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
UINT64 get_os_indications_supported(void);
|
UINT64 get_os_indications_supported(void);
|
||||||
|
|
||||||
|
#ifdef EFI_DEBUG
|
||||||
|
void debug_break(void);
|
||||||
|
extern UINT8 _text, _data;
|
||||||
|
/* Report the relocated position of text and data sections so that a debugger
|
||||||
|
* can attach to us. See debug-sd-boot.sh for how this can be done. */
|
||||||
|
# define debug_hook(identity) Print(identity L"@0x%x,0x%x\n", &_text, &_data)
|
||||||
|
#else
|
||||||
|
# define debug_hook(identity)
|
||||||
|
#endif
|
||||||
|
|||||||
@ -26,6 +26,7 @@
|
|||||||
#include "random-util.h"
|
#include "random-util.h"
|
||||||
#include "string-util.h"
|
#include "string-util.h"
|
||||||
#include "sync-util.h"
|
#include "sync-util.h"
|
||||||
|
#include "sha256.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "xattr-util.h"
|
#include "xattr-util.h"
|
||||||
|
|
||||||
@ -106,9 +107,11 @@ static int run(int argc, char *argv[]) {
|
|||||||
_cleanup_close_ int seed_fd = -1, random_fd = -1;
|
_cleanup_close_ int seed_fd = -1, random_fd = -1;
|
||||||
bool read_seed_file, write_seed_file, synchronous;
|
bool read_seed_file, write_seed_file, synchronous;
|
||||||
_cleanup_free_ void* buf = NULL;
|
_cleanup_free_ void* buf = NULL;
|
||||||
|
struct sha256_ctx hash_state;
|
||||||
|
uint8_t hash[32];
|
||||||
size_t buf_size;
|
size_t buf_size;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
ssize_t k;
|
ssize_t k, l;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
log_setup();
|
log_setup();
|
||||||
@ -242,6 +245,16 @@ static int run(int argc, char *argv[]) {
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
log_error_errno(r, "Failed to write seed to /dev/urandom: %m");
|
log_error_errno(r, "Failed to write seed to /dev/urandom: %m");
|
||||||
}
|
}
|
||||||
|
/* If we're going to later write out a seed file, initialize a hash state with
|
||||||
|
* the contents of the seed file we just read, so that the new one can't regress
|
||||||
|
* in entropy. */
|
||||||
|
if (write_seed_file) {
|
||||||
|
sha256_init_ctx(&hash_state);
|
||||||
|
if (k < 0)
|
||||||
|
k = 0;
|
||||||
|
sha256_process_bytes(&k, sizeof(k), &hash_state);
|
||||||
|
sha256_process_bytes(buf, k, &hash_state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (write_seed_file) {
|
if (write_seed_file) {
|
||||||
@ -277,6 +290,17 @@ static int run(int argc, char *argv[]) {
|
|||||||
"Got EOF while reading from /dev/urandom.");
|
"Got EOF while reading from /dev/urandom.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If we previously read in a seed file, then hash the new seed into the old one,
|
||||||
|
* and replace the last 32 bytes of the seed with the hash output, so that the
|
||||||
|
* new seed file can't regress in entropy. */
|
||||||
|
if (read_seed_file) {
|
||||||
|
sha256_process_bytes(&k, sizeof(k), &hash_state);
|
||||||
|
sha256_process_bytes(buf, k, &hash_state);
|
||||||
|
sha256_finish_ctx(&hash_state, hash);
|
||||||
|
l = MIN(k, 32);
|
||||||
|
memcpy((uint8_t *)buf + k - l, hash, l);
|
||||||
|
}
|
||||||
|
|
||||||
r = loop_write(seed_fd, buf, (size_t) k, false);
|
r = loop_write(seed_fd, buf, (size_t) k, false);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to write new random seed file: %m");
|
return log_error_errno(r, "Failed to write new random seed file: %m");
|
||||||
|
|||||||
85
tools/debug-sd-boot.sh
Executable file
85
tools/debug-sd-boot.sh
Executable file
@ -0,0 +1,85 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
if [[ $# -lt 2 ]]; then
|
||||||
|
echo "Usage: ${0} TARGET INPUT [GDBSCRIPT]"
|
||||||
|
echo "Debug systemd-boot/stub in QEMU."
|
||||||
|
echo
|
||||||
|
echo "TARGET should point to the EFI binary to be examined inside the"
|
||||||
|
echo "build directory (systemd-boot\$ARCH.efi or linux\$arch.efi.stub)."
|
||||||
|
echo
|
||||||
|
echo "INPUT should point to the QEMU serial output pipe. This is used to"
|
||||||
|
echo "extract the location of the symbols. For this to work, QEMU must"
|
||||||
|
echo "be run with '-s -serial pipe:PATH'. Note that QEMU will append"
|
||||||
|
echo ".in/.out to the path, while this script expects the out pipe directly."
|
||||||
|
echo
|
||||||
|
echo "If GDBSCRIPT is empty, gdb is run directly attached to the boot"
|
||||||
|
echo "loader, otherwise a script is generated in the given path that allows"
|
||||||
|
echo "attaching manually like this:"
|
||||||
|
echo " (gdb) source GDBSCRIPT"
|
||||||
|
echo " (gdb) target remote :1234"
|
||||||
|
echo
|
||||||
|
echo "Exmaple usage:"
|
||||||
|
echo " mkfifo /tmp/sdboot.{in,out}"
|
||||||
|
echo " qemu-system-x86_64 [...] -s -serial pipe:/tmp/sdboot"
|
||||||
|
echo " ./tools/debug-sd-boot.sh ./build/src/boot/efi/systemd-bootx64.efi \\"
|
||||||
|
echo " /tmp/sdboot.out"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
binary=$(realpath "${1}")
|
||||||
|
if [[ "${1}" =~ systemd-boot([[:alnum:]]+).efi ]]; then
|
||||||
|
target="systemd-boot"
|
||||||
|
symbols=$(realpath "$(dirname "${1}")/systemd_boot.so")
|
||||||
|
elif [[ "${1}" =~ linux([[:alnum:]]+).efi.stub ]]; then
|
||||||
|
target="systemd-stub"
|
||||||
|
symbols=$(realpath "$(dirname "${1}")/linux${BASH_REMATCH[1]}.elf.stub")
|
||||||
|
else
|
||||||
|
echo "Cannot detect EFI binary '${1}'."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "${BASH_REMATCH[1]}" in
|
||||||
|
ia32) arch="i386";;
|
||||||
|
x64) arch="i386:x86-64";;
|
||||||
|
aa64) arch="aarch64";;
|
||||||
|
arm|riscv64) arch="${BASH_REMATCH[1]}";;
|
||||||
|
*)
|
||||||
|
echo "Unknown EFI arch '${BASH_REMATCH[1]}'."
|
||||||
|
exit 1
|
||||||
|
esac
|
||||||
|
|
||||||
|
# system-boot will print out a line like this to inform us where gdb is supposed to
|
||||||
|
# look for .text and .data section:
|
||||||
|
# systemd-boot@0x0,0x0
|
||||||
|
while read -r line; do
|
||||||
|
if [[ "${line}" =~ ${target}@(0x[[:xdigit:]]+),(0x[[:xdigit:]]+) ]]; then
|
||||||
|
text="${BASH_REMATCH[1]}"
|
||||||
|
data="${BASH_REMATCH[2]}"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done < "${2}"
|
||||||
|
|
||||||
|
if [[ -z "${text}" || -z "${data}" ]]; then
|
||||||
|
echo "Could not determine text and data location."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "${3}" ]]; then
|
||||||
|
gdb_script=$(mktemp /tmp/debug-sd-boot.XXXXXX.gdb)
|
||||||
|
trap 'rm -f "${gdb_script}"' EXIT
|
||||||
|
else
|
||||||
|
gdb_script="${3}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "file ${binary}
|
||||||
|
add-symbol-file ${symbols} ${text} -s .data ${data}
|
||||||
|
set architecture ${arch}" > "${gdb_script}"
|
||||||
|
|
||||||
|
if [[ -z "${3}" ]]; then
|
||||||
|
gdb -x "${gdb_script}" -ex "target remote :1234"
|
||||||
|
else
|
||||||
|
echo "GDB script written to '${gdb_script}'."
|
||||||
|
fi
|
||||||
Loading…
x
Reference in New Issue
Block a user