drgn/libdrgn/drgn_program_parse_vmcoreinfo.inc.strswitch
Stephen Brennan 5b39bfb547 libdrgn: x86_64: avoid recursive address translation for swapper_pg_dir
Most core dumps contain some virtual address mappings: usually at a
minimum, the kernel's direct map is represented in ELF vmcores via a
segment. So normally, drgn can rely on the vmcore to read the virtual
address of swapper_pg_dir. However, some vmcores only contain physical
address information, so when drgn reads memory at swapper_pg_dir, it
needs to first translate that address, thus causing a recursive
translation error like below:

>>> prog["slab_caches"]
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/home/stepbren/repos/drgn/drgn/cli.py", line 141, in _displayhook
    text = value.format_(columns=shutil.get_terminal_size((0, 0)).columns)
_drgn.FaultError: recursive address translation; page table may be missing from core dump: 0xffffffff9662aff8

Debuggers like crash, as well as libkdumpfile, contain fallback code
which can translate swapper_pg_dir in order to bootstrap this address
translation. In fact, the above error does not occur in drgn when using
libkdumpfile. So, let's add this fallback case to drgn as well. Other
architectures will need to have equivalent support added.

Co-authored-by: Illia Ostapyshyn <ostapyshyn@sra.uni-hannover.de>
Signed-off-by: Stephen Brennan <stephen.s.brennan@oracle.com>
2024-05-30 11:56:23 -07:00

121 lines
3.3 KiB
Plaintext

// Copyright (c) Meta Platforms, Inc. and affiliates.
// SPDX-License-Identifier: LGPL-2.1-or-later
static struct drgn_error *parse_vmcoreinfo_u64(const char *value,
const char *newline, int base,
uint64_t *ret)
{
errno = 0;
char *end;
*ret = strtoull(value, &end, base);
if (errno == ERANGE) {
return drgn_error_create(DRGN_ERROR_OVERFLOW,
"number in VMCOREINFO is too large");
} else if (errno || end == value || end != newline) {
return drgn_error_create(DRGN_ERROR_OTHER,
"number in VMCOREINFO is invalid");
}
return NULL;
}
struct drgn_error *drgn_program_parse_vmcoreinfo(struct drgn_program *prog,
const char *desc,
size_t descsz)
{
struct drgn_error *err;
prog->vmcoreinfo.raw_size = descsz;
prog->vmcoreinfo.raw = memdup(desc, descsz);
if (!prog->vmcoreinfo.raw)
return &drgn_enomem;
for (const char *line = desc, *end = &desc[descsz], *newline;
(newline = memchr(line, '\n', end - line));
line = newline + 1) {
const char *equals = memchr(line, '=', newline - line);
if (!equals)
continue;
const char *value = equals + 1;
@memswitch (line, equals - line)@
@case "OSRELEASE"@
if ((size_t)(newline - value) >=
sizeof(prog->vmcoreinfo.osrelease)) {
return drgn_error_create(DRGN_ERROR_OTHER,
"OSRELEASE in VMCOREINFO is too long");
}
memcpy(prog->vmcoreinfo.osrelease, value,
newline - value);
prog->vmcoreinfo.osrelease[newline - value] = '\0';
break;
@case "PAGESIZE"@
err = parse_vmcoreinfo_u64(value, newline, 0,
&prog->vmcoreinfo.page_size);
if (err)
return err;
break;
@case "KERNELOFFSET"@
err = parse_vmcoreinfo_u64(value, newline, 16,
&prog->vmcoreinfo.kaslr_offset);
if (err)
return err;
break;
@case "SYMBOL(swapper_pg_dir)"@
err = parse_vmcoreinfo_u64(value, newline, 16,
&prog->vmcoreinfo.swapper_pg_dir);
if (err)
return err;
break;
@case "LENGTH(mem_section)"@
err = parse_vmcoreinfo_u64(value, newline, 0,
&prog->vmcoreinfo.mem_section_length);
if (err)
return err;
break;
@case "NUMBER(pgtable_l5_enabled)"@
{
uint64_t tmp;
err = parse_vmcoreinfo_u64(value, newline, 0, &tmp);
if (err)
return err;
prog->vmcoreinfo.pgtable_l5_enabled = tmp;
break;
}
@case "NUMBER(phys_base)"@
{
err = parse_vmcoreinfo_u64(value, newline, 0,
&prog->vmcoreinfo.phys_base);;
if (err)
return err;
break;
}
@case "NUMBER(KERNELPACMASK)"@
err = parse_vmcoreinfo_u64(value, newline, 16,
&prog->aarch64_insn_pac_mask);
if (err)
return err;
break;
@case "NUMBER(VA_BITS)"@
err = parse_vmcoreinfo_u64(value, newline, 0,
&prog->vmcoreinfo.va_bits);
if (err)
return err;
break;
@endswitch@
}
if (!prog->vmcoreinfo.osrelease[0]) {
return drgn_error_create(DRGN_ERROR_OTHER,
"VMCOREINFO does not contain valid OSRELEASE");
}
if (!is_power_of_two(prog->vmcoreinfo.page_size)) {
return drgn_error_create(DRGN_ERROR_OTHER,
"VMCOREINFO does not contain valid PAGESIZE");
}
prog->vmcoreinfo.page_shift = ctz(prog->vmcoreinfo.page_size);
if (!prog->vmcoreinfo.swapper_pg_dir) {
return drgn_error_create(DRGN_ERROR_OTHER,
"VMCOREINFO does not contain valid swapper_pg_dir");
}
// Everything else is optional.
return NULL;
}