Skip to content

RFC Support dumps without VMCOREINFO #470

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 20 additions & 3 deletions libdrgn/debug_info.c
Original file line number Diff line number Diff line change
Expand Up @@ -1228,9 +1228,9 @@ static bool drgn_module_elf_file_bias(struct drgn_module *module,
SWITCH_ENUM(module->kind) {
case DRGN_MODULE_MAIN:
if (prog->flags & DRGN_PROGRAM_IS_LINUX_KERNEL) {
*ret = prog->vmcoreinfo.kaslr_offset;
*ret = prog->ktext_offset;
drgn_log_debug(prog,
"got bias 0x%" PRIx64 " from VMCOREINFO",
"got bias 0x%" PRIx64 " of kernel",
*ret);
return true;
} else {
Expand Down Expand Up @@ -1376,6 +1376,21 @@ drgn_module_maybe_use_elf_file(struct drgn_module *module,
goto unused;
}

/* This may be initialized by drgn_find_ktext of existing vmcore */
if (prog->ktext_mapped) {
uint64_t s, e;
/* Dummy load of ELF under the assumption that
* _text is always on the lowest address. */
if (!drgn_elf_file_address_range(file, &s, &e)) {
drgn_log_warning(prog, "Failed ktext_offset calculation.");
goto noktext_offset;
}

prog->ktext_offset = prog->ktext_mapped - s;
drgn_log_info(prog, "Calculated ktext_offset=%lx", prog->ktext_offset);
}

noktext_offset:
uint64_t bias;
if (!drgn_module_elf_file_bias(module, file, &bias)) {
err = NULL;
Expand Down Expand Up @@ -5049,14 +5064,16 @@ load_debug_info_try_provided_vmlinux(struct drgn_module *module,
release_len) == 0) {
drgn_log_debug(prog, "%s: %s Linux version matches",
module->name, file->path);
} else {
} else if (strlen(prog->vmcoreinfo.osrelease) > 0) {
drgn_log_debug(prog,
"%s: %s Linux version (%.*s) does not match",
module->name, file->path,
release_len > INT_MAX
? INT_MAX : (int)release_len,
release);
continue;
} else {
drgn_log_debug(prog, "Linux version in VMCOREINFO not found");
}

if (!it.entry->matched) {
Expand Down
11 changes: 7 additions & 4 deletions libdrgn/drgn_program_parse_vmcoreinfo.inc.strswitch
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ struct drgn_error *drgn_program_parse_vmcoreinfo(struct drgn_program *prog,
break;
@case "KERNELOFFSET"@
err = parse_vmcoreinfo_u64(value, newline, 16,
&prog->vmcoreinfo.kaslr_offset);
&prog->ktext_offset);
if (err)
return err;
break;
Expand Down Expand Up @@ -159,18 +159,21 @@ struct drgn_error *drgn_program_parse_vmcoreinfo(struct drgn_program *prog,
@endswitch@
}
if (!prog->vmcoreinfo.osrelease[0]) {
return drgn_error_create(DRGN_ERROR_OTHER,
drgn_log_error(prog,
"VMCOREINFO does not contain valid OSRELEASE");
// XXX make users of osrelease more robust
}
ignore_broken_vmcoreinfo_build_id(prog);
if (!is_power_of_two(prog->vmcoreinfo.page_size)) {
return drgn_error_create(DRGN_ERROR_OTHER,
drgn_log_error(prog,
"VMCOREINFO does not contain valid PAGESIZE");
// XXX make users of osrelease more robust || assume page_size
}
prog->vmcoreinfo.page_shift = ctz(prog->vmcoreinfo.page_size);
if (!prog->vmcoreinfo.swapper_pg_dir) {
return drgn_error_create(DRGN_ERROR_OTHER,
drgn_log_error(prog,
"VMCOREINFO does not contain valid swapper_pg_dir");
// XXX make users of swapper_pg_dir more robust
}
// Everything else is optional.
return NULL;
Expand Down
66 changes: 58 additions & 8 deletions libdrgn/kdump.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef WITH_LIBKDUMPFILE
#include <libkdumpfile/addrxlat.h>
#endif

#include "linux_kernel.h"
#include "log.h"
#include "plugins.h"
#include "program.h" // IWYU pragma: associated
#include "util.h"
Expand Down Expand Up @@ -125,6 +129,53 @@ static struct drgn_error *drgn_read_kdump(void *buf, uint64_t address,
return NULL;
}

static struct drgn_error *drgn_find_ktext(struct drgn_program *prog, kdump_ctx_t *ctx)
{
struct drgn_error *err;
kdump_status ks;
addrxlat_sys_t *sys;
addrxlat_map_t *map;
addrxlat_addr_t addr;
const addrxlat_range_t *range;
size_t i, n;
bool found = false;

ks = kdump_get_addrxlat(ctx, NULL, &sys);
if (ks != KDUMP_OK)
return drgn_error_format(DRGN_ERROR_OTHER, "Cannot setup addrxlat");

map = addrxlat_sys_get_map(sys, ADDRXLAT_SYS_MAP_KV_PHYS);
if (!map) {
err = drgn_error_format(DRGN_ERROR_OTHER, "Cannot get addrxlat map");
goto err;
}

n = addrxlat_map_len(map);
addr = 0;
range = addrxlat_map_ranges(map);
for (i = 0; i < n; ++i) {
if (range->meth == ADDRXLAT_SYS_METH_KTEXT) {
prog->ktext_mapped = addr;
found = true;
drgn_log_debug(prog, "addrxlat found ktext at %x", addr);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see a warning in openscanhub:

Error: COMPILER_WARNING: [#def1]
python-drgn-0.0.30+57.g5dce6d2f-build/drgn-0.0.30+57.g5dce6d2f/libdrgn/kdump.c:13: included_from: Included from here.
python-drgn-0.0.30+57.g5dce6d2f-build/drgn-0.0.30+57.g5dce6d2f/libdrgn/kdump.c: scope_hint: In function 'drgn_find_ktext'
python-drgn-0.0.30+57.g5dce6d2f-build/drgn-0.0.30+57.g5dce6d2f/libdrgn/kdump.c:160:46: warning[-Wformat=]: format '%x' expects argument of type 'unsigned int', but argument 5 has type 'addrxlat_addr_t' {aka 'long unsigned int'}
#  160 |                         drgn_log_debug(prog, "addrxlat found ktext at %x", addr);
#      |                                              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~  ~~~~
#      |                                                                            |
#      |                                                                            addrxlat_addr_t {aka long unsigned int}
python-drgn-0.0.30+57.g5dce6d2f-build/drgn-0.0.30+57.g5dce6d2f/libdrgn/log.h:46:70: note: in definition of macro 'drgn_log'
#   46 | #define drgn_log(level, prog, ...) drgn_error_log(level, prog, NULL, __VA_ARGS__)
#      |                                                                      ^~~~~~~~~~~
python-drgn-0.0.30+57.g5dce6d2f-build/drgn-0.0.30+57.g5dce6d2f/libdrgn/kdump.c:160:25: note: in expansion of macro 'drgn_log_debug'
#  160 |                         drgn_log_debug(prog, "addrxlat found ktext at %x", addr);
#      |                         ^~~~~~~~~~~~~~
python-drgn-0.0.30+57.g5dce6d2f-build/drgn-0.0.30+57.g5dce6d2f/libdrgn/kdump.c:160:72: note: format string is defined here
#  160 |                         drgn_log_debug(prog, "addrxlat found ktext at %x", addr);
#      |                                                                       ~^
#      |                                                                        |
#      |                                                                        unsigned int
#      |                                                                       %lx
#  158|   			prog->ktext_mapped = addr;
#  159|   			found = true;
#  160|-> 			drgn_log_debug(prog, "addrxlat found ktext at %x", addr);
#  161|   			break;
#  162|

Shall this be fixed before merging?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall this be fixed before merging?

Much more needs to be fixed in this branch 😊 Let me mark it a draft.

break;
}

addr += range->endoff + 1;
++range;
}
if (!found) {
err = drgn_error_format(DRGN_ERROR_OTHER,
"addrxlat cannot locate KTEXT");
goto err;
}
return NULL;

err:
addrxlat_sys_decref(sys);
return err;
}

struct drgn_error *drgn_program_set_kdump(struct drgn_program *prog)
{
struct drgn_error *err;
Expand Down Expand Up @@ -221,20 +272,19 @@ struct drgn_error *drgn_program_set_kdump(struct drgn_program *prog)
#endif
} else {
#if KDUMPFILE_VERSION >= KDUMPFILE_MKVER(0, 4, 1)
char *vmcoreinfo;
char *vmcoreinfo = NULL;
#else
const char *vmcoreinfo;
#endif
ks = kdump_vmcoreinfo_raw(ctx, &vmcoreinfo);
if (ks != KDUMP_OK) {
err = drgn_error_format(DRGN_ERROR_OTHER,
"kdump_vmcoreinfo_raw: %s",
kdump_get_err(ctx));
goto err;
if (ks == KDUMP_OK) {
err = drgn_program_parse_vmcoreinfo(prog, vmcoreinfo,
strlen(vmcoreinfo) + 1);
} else {
drgn_log_warning(prog, "No vmcoreinfo (%s), relying on addrxlat and debuginfo", kdump_get_err(ctx));
err = drgn_find_ktext(prog, ctx);
}

err = drgn_program_parse_vmcoreinfo(prog, vmcoreinfo,
strlen(vmcoreinfo) + 1);
/*
* As of libkdumpfile 0.4.1, the string returned by
* kdump_vmcoreinfo_raw() needs to be freed.
Expand Down
1 change: 1 addition & 0 deletions libdrgn/linux_kernel.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include "helpers.h"
#include "hexlify.h"
#include "io.h"
#include "log.h"
#include "linux_kernel.h"
#include "log.h"
#include "platform.h"
Expand Down
18 changes: 2 additions & 16 deletions libdrgn/program.c
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,6 @@ drgn_program_set_core_dump_fd_internal(struct drgn_program *prog, int fd,
size_t phnum, i;
size_t num_file_segments, j;
bool have_phys_addrs = false;
bool have_qemu_note = false;
const char *vmcoreinfo_note = NULL;
size_t vmcoreinfo_size = 0;
bool have_nt_taskstruct = false, is_proc_kcore;
Expand Down Expand Up @@ -454,10 +453,6 @@ drgn_program_set_core_dump_fd_internal(struct drgn_program *prog, int fd,
*/
have_phys_addrs = true;
have_vmcoreinfo = true;
} else if (nhdr.n_namesz == sizeof("QEMU") &&
memcmp(name, "QEMU",
sizeof("QEMU")) == 0) {
have_qemu_note = true;
}
}
}
Expand All @@ -480,7 +475,7 @@ drgn_program_set_core_dump_fd_internal(struct drgn_program *prog, int fd,
is_proc_kcore = false;
}

if (have_vmcoreinfo && !is_proc_kcore) {
if (!is_proc_kcore) { /* libkdumpfile for kernel ELF vmcores */
char *env;

/* Use libkdumpfile for ELF vmcores if it was requested. */
Expand Down Expand Up @@ -631,7 +626,7 @@ drgn_program_set_core_dump_fd_internal(struct drgn_program *prog, int fd,
j++;
}
}
if (vmcoreinfo_note && !prog->vmcoreinfo.raw) {
if (vmcoreinfo_note && !prog->vmcoreinfo.raw) { /* override vmcoreinfo from ELF */
err = drgn_program_parse_vmcoreinfo(prog, vmcoreinfo_note,
vmcoreinfo_size);
if (err)
Expand All @@ -651,15 +646,6 @@ drgn_program_set_core_dump_fd_internal(struct drgn_program *prog, int fd,
prog->core = NULL;
} else if (have_vmcoreinfo) {
prog->flags |= DRGN_PROGRAM_IS_LINUX_KERNEL;
} else if (have_qemu_note) {
err = drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT,
"unrecognized QEMU memory dump; "
"for Linux guests, run QEMU with '-device vmcoreinfo', "
"compile the kernel with CONFIG_FW_CFG_SYSFS and CONFIG_KEXEC, "
"and load the qemu_fw_cfg kernel module "
"before dumping the guest memory "
"(requires Linux >= 4.17 and QEMU >= 2.11)");
goto out_segments;
}
if (prog->flags & DRGN_PROGRAM_IS_LINUX_KERNEL) {
err = drgn_program_finish_set_kernel(prog);
Expand Down
16 changes: 16 additions & 0 deletions libdrgn/program.h
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ struct drgn_program {
*
* This is non-zero if kernel address space
* layout randomization (KASLR) is enabled.
* XXX unused
*/
uint64_t kaslr_offset;
/** Kernel page table. */
Expand Down Expand Up @@ -237,6 +238,21 @@ struct drgn_program {
* Whether @ref drgn_program::mod_text has been cached.
*/
bool mod_text_cached;
/**
* The offset from the compiled address of the
* kernel image to its actual address in memory.
*
* This is non-zero if kernel address space
* layout randomization (KASLR) is enabled.
*/
uint64_t ktext_offset;
/**
* ktext_mapped = &_text + ktext_offset
* where
* &_text is ELF before loading
* ktext_mapped is from vmcore's mapping probing
*/
uint64_t ktext_mapped;
/*
* Whether we are currently in address translation. Used
* to prevent address translation from recursing.
Expand Down
2 changes: 1 addition & 1 deletion libdrgn/python/helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ PyObject *drgnpy_linux_helper_kaslr_offset(PyObject *self, PyObject *arg)
Program *prog = (Program *)arg;
if (!(prog->prog.flags & DRGN_PROGRAM_IS_LINUX_KERNEL))
return PyErr_Format(PyExc_ValueError, "not Linux kernel");
return PyLong_FromUint64(prog->prog.vmcoreinfo.kaslr_offset);
return PyLong_FromUint64(prog->prog.ktext_offset);
}

PyObject *drgnpy_linux_helper_pgtable_l5_enabled(PyObject *self, PyObject *arg)
Expand Down
Loading