mirror of
https://github.com/JakeHillion/drgn.git
synced 2024-12-22 17:23:06 +00:00
libdrgn: linux_kernel: get vmemmap generically
AArch64 has changed the location of vmemmap multiple times, and not all of these can be easily distinguished. Rather than restorting to kernel version checks, this replaces the vmemmap architecture callback with a generic approach that gets the vmemmap address directly from the mem_section table. Signed-off-by: Omar Sandoval <osandov@osandov.com>
This commit is contained in:
parent
a213573b23
commit
b28bd9f0a3
@ -500,46 +500,6 @@ apply_elf_reloc_x86_64(const struct drgn_relocating_section *relocating,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct drgn_error *
|
|
||||||
linux_kernel_get_vmemmap_x86_64(struct drgn_object *ret)
|
|
||||||
{
|
|
||||||
|
|
||||||
struct drgn_error *err;
|
|
||||||
struct drgn_program *prog = drgn_object_program(ret);
|
|
||||||
|
|
||||||
struct drgn_qualified_type qualified_type;
|
|
||||||
err = drgn_program_find_type(prog, "struct page *", NULL,
|
|
||||||
&qualified_type);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
/* If KASLR is enabled, vmemmap is vmemmap_base. */
|
|
||||||
struct drgn_object tmp;
|
|
||||||
drgn_object_init(&tmp, prog);
|
|
||||||
err = drgn_program_find_object(prog, "vmemmap_base", NULL,
|
|
||||||
DRGN_FIND_OBJECT_VARIABLE, &tmp);
|
|
||||||
if (!err) {
|
|
||||||
err = drgn_object_cast(ret, qualified_type, &tmp);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
if (err->code == DRGN_ERROR_LOOKUP)
|
|
||||||
drgn_error_destroy(err);
|
|
||||||
else
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
/* Otherwise, it depends on whether we have 5-level page tables. */
|
|
||||||
uint64_t value;
|
|
||||||
if (prog->vmcoreinfo.pgtable_l5_enabled)
|
|
||||||
value = UINT64_C(0xffd4000000000000);
|
|
||||||
else
|
|
||||||
value = UINT64_C(0xffffea0000000000);
|
|
||||||
err = drgn_object_set_unsigned(ret, qualified_type, value, 0);
|
|
||||||
|
|
||||||
out:
|
|
||||||
drgn_object_deinit(&tmp);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct drgn_error *
|
static struct drgn_error *
|
||||||
linux_kernel_live_direct_mapping_fallback_x86_64(struct drgn_program *prog,
|
linux_kernel_live_direct_mapping_fallback_x86_64(struct drgn_program *prog,
|
||||||
uint64_t *address_ret,
|
uint64_t *address_ret,
|
||||||
@ -694,7 +654,6 @@ const struct drgn_architecture_info arch_info_x86_64 = {
|
|||||||
.linux_kernel_get_initial_registers =
|
.linux_kernel_get_initial_registers =
|
||||||
linux_kernel_get_initial_registers_x86_64,
|
linux_kernel_get_initial_registers_x86_64,
|
||||||
.apply_elf_reloc = apply_elf_reloc_x86_64,
|
.apply_elf_reloc = apply_elf_reloc_x86_64,
|
||||||
.linux_kernel_get_vmemmap = linux_kernel_get_vmemmap_x86_64,
|
|
||||||
.linux_kernel_live_direct_mapping_fallback =
|
.linux_kernel_live_direct_mapping_fallback =
|
||||||
linux_kernel_live_direct_mapping_fallback_x86_64,
|
linux_kernel_live_direct_mapping_fallback_x86_64,
|
||||||
.linux_kernel_pgtable_iterator_create =
|
.linux_kernel_pgtable_iterator_create =
|
||||||
|
@ -57,6 +57,12 @@ struct drgn_error *drgn_program_parse_vmcoreinfo(struct drgn_program *prog,
|
|||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
break;
|
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)"@
|
@case "NUMBER(pgtable_l5_enabled)"@
|
||||||
{
|
{
|
||||||
uint64_t tmp;
|
uint64_t tmp;
|
||||||
@ -87,6 +93,7 @@ struct drgn_error *drgn_program_parse_vmcoreinfo(struct drgn_program *prog,
|
|||||||
return drgn_error_create(DRGN_ERROR_OTHER,
|
return drgn_error_create(DRGN_ERROR_OTHER,
|
||||||
"VMCOREINFO does not contain valid swapper_pg_dir");
|
"VMCOREINFO does not contain valid swapper_pg_dir");
|
||||||
}
|
}
|
||||||
/* KERNELOFFSET, pgtable_l5_enabled, and KERNELPACMASK are optional. */
|
// KERNELOFFSET, LENGTH(mem_section), pgtable_l5_enabled, and
|
||||||
|
// KERNELPACMASK are optional.
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -213,15 +213,124 @@ linux_kernel_get_uts_release(struct drgn_program *prog, struct drgn_object *ret)
|
|||||||
0, 0);
|
0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The vmemmap address can vary depending on architecture, kernel version,
|
||||||
|
// configuration options, and KASLR. However, we can get it generically from the
|
||||||
|
// section_mem_map of any valid mem_section.
|
||||||
|
static struct drgn_error *
|
||||||
|
linux_kernel_get_vmemmap_address(struct drgn_program *prog, uint64_t *ret)
|
||||||
|
{
|
||||||
|
static const uint64_t SECTION_HAS_MEM_MAP = 0x2;
|
||||||
|
static const uint64_t SECTION_MAP_MASK = ~((UINT64_C(1) << 6) - 1);
|
||||||
|
struct drgn_error *err;
|
||||||
|
|
||||||
|
struct drgn_object mem_section, root, section;
|
||||||
|
drgn_object_init(&mem_section, prog);
|
||||||
|
drgn_object_init(&root, prog);
|
||||||
|
drgn_object_init(§ion, prog);
|
||||||
|
|
||||||
|
err = drgn_program_find_object(prog, "vmemmap_populate", NULL,
|
||||||
|
DRGN_FIND_OBJECT_FUNCTION, &mem_section);
|
||||||
|
if (err) {
|
||||||
|
if (err->code == DRGN_ERROR_LOOKUP) {
|
||||||
|
// !CONFIG_SPARSEMEM_VMEMMAP
|
||||||
|
drgn_error_destroy(err);
|
||||||
|
err = &drgn_not_found;
|
||||||
|
}
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = drgn_program_find_object(prog, "mem_section", NULL,
|
||||||
|
DRGN_FIND_OBJECT_VARIABLE, &mem_section);
|
||||||
|
if (err)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
const uint64_t nr_section_roots = prog->vmcoreinfo.mem_section_length;
|
||||||
|
uint64_t sections_per_root;
|
||||||
|
if (drgn_type_kind(mem_section.type) == DRGN_TYPE_ARRAY) {
|
||||||
|
// If !CONFIG_SPARSEMEM_EXTREME, mem_section is
|
||||||
|
// struct mem_section mem_section[NR_SECTION_ROOTS][SECTIONS_PER_ROOT],
|
||||||
|
// and SECTIONS_PER_ROOT is 1.
|
||||||
|
sections_per_root = 1;
|
||||||
|
} else {
|
||||||
|
// If CONFIG_SPARSEMEM_EXTREME, mem_section is
|
||||||
|
// struct mem_section **mem_section, and SECTIONS_PER_ROOT is
|
||||||
|
// PAGE_SIZE / sizeof(struct mem_section).
|
||||||
|
struct drgn_type *mem_section_type = mem_section.type;
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
if (drgn_type_kind(mem_section_type) != DRGN_TYPE_POINTER) {
|
||||||
|
unrecognized_mem_section_type:
|
||||||
|
err = drgn_type_error("mem_section has unrecognized type '%s'",
|
||||||
|
mem_section.type);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
mem_section_type = drgn_type_type(mem_section_type).type;
|
||||||
|
}
|
||||||
|
if (drgn_type_kind(mem_section_type) != DRGN_TYPE_STRUCT)
|
||||||
|
goto unrecognized_mem_section_type;
|
||||||
|
uint64_t sizeof_mem_section = drgn_type_size(mem_section_type);
|
||||||
|
if (sizeof_mem_section == 0)
|
||||||
|
goto unrecognized_mem_section_type;
|
||||||
|
sections_per_root =
|
||||||
|
prog->vmcoreinfo.page_size / sizeof_mem_section;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find a valid section.
|
||||||
|
for (uint64_t i = 0; i < nr_section_roots; i++) {
|
||||||
|
err = drgn_object_subscript(&root, &mem_section, i);
|
||||||
|
if (err)
|
||||||
|
goto out;
|
||||||
|
bool truthy;
|
||||||
|
err = drgn_object_bool(&root, &truthy);
|
||||||
|
if (err)
|
||||||
|
goto out;
|
||||||
|
if (!truthy)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (uint64_t j = 0; j < sections_per_root; j++) {
|
||||||
|
err = drgn_object_subscript(§ion, &root, j);
|
||||||
|
if (err)
|
||||||
|
goto out;
|
||||||
|
err = drgn_object_member(§ion, §ion,
|
||||||
|
"section_mem_map");
|
||||||
|
if (err)
|
||||||
|
goto out;
|
||||||
|
uint64_t section_mem_map;
|
||||||
|
err = drgn_object_read_unsigned(§ion,
|
||||||
|
§ion_mem_map);
|
||||||
|
if (err)
|
||||||
|
goto out;
|
||||||
|
if (section_mem_map & SECTION_HAS_MEM_MAP) {
|
||||||
|
*ret = section_mem_map & SECTION_MAP_MASK;
|
||||||
|
err = NULL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = &drgn_not_found;
|
||||||
|
|
||||||
|
out:
|
||||||
|
drgn_object_deinit(§ion);
|
||||||
|
drgn_object_deinit(&root);
|
||||||
|
drgn_object_deinit(&mem_section);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
static struct drgn_error *linux_kernel_get_vmemmap(struct drgn_program *prog,
|
static struct drgn_error *linux_kernel_get_vmemmap(struct drgn_program *prog,
|
||||||
struct drgn_object *ret)
|
struct drgn_object *ret)
|
||||||
{
|
{
|
||||||
struct drgn_error *err;
|
struct drgn_error *err;
|
||||||
if (prog->vmemmap.kind == DRGN_OBJECT_ABSENT) {
|
if (prog->vmemmap.kind == DRGN_OBJECT_ABSENT) {
|
||||||
if (!prog->has_platform ||
|
uint64_t address;
|
||||||
!prog->platform.arch->linux_kernel_get_vmemmap)
|
err = linux_kernel_get_vmemmap_address(prog, &address);
|
||||||
return &drgn_not_found;
|
if (err)
|
||||||
err = prog->platform.arch->linux_kernel_get_vmemmap(&prog->vmemmap);
|
return err;
|
||||||
|
struct drgn_qualified_type qualified_type;
|
||||||
|
err = drgn_program_find_type(prog, "struct page *", NULL,
|
||||||
|
&qualified_type);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
err = drgn_object_set_unsigned(&prog->vmemmap, qualified_type,
|
||||||
|
address, 0);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -159,7 +159,6 @@ struct drgn_architecture_info {
|
|||||||
struct drgn_error *(*linux_kernel_get_initial_registers)(const struct drgn_object *,
|
struct drgn_error *(*linux_kernel_get_initial_registers)(const struct drgn_object *,
|
||||||
struct drgn_register_state **);
|
struct drgn_register_state **);
|
||||||
apply_elf_reloc_fn *apply_elf_reloc;
|
apply_elf_reloc_fn *apply_elf_reloc;
|
||||||
struct drgn_error *(*linux_kernel_get_vmemmap)(struct drgn_object *);
|
|
||||||
struct drgn_error *(*linux_kernel_live_direct_mapping_fallback)(struct drgn_program *,
|
struct drgn_error *(*linux_kernel_live_direct_mapping_fallback)(struct drgn_program *,
|
||||||
uint64_t *,
|
uint64_t *,
|
||||||
uint64_t *);
|
uint64_t *);
|
||||||
|
@ -161,6 +161,8 @@ struct drgn_program {
|
|||||||
uint64_t kaslr_offset;
|
uint64_t kaslr_offset;
|
||||||
/** Kernel page table. */
|
/** Kernel page table. */
|
||||||
uint64_t swapper_pg_dir;
|
uint64_t swapper_pg_dir;
|
||||||
|
/** Length of mem_section array (i.e., NR_SECTION_ROOTS). */
|
||||||
|
uint64_t mem_section_length;
|
||||||
/** Whether 5-level paging was enabled. */
|
/** Whether 5-level paging was enabled. */
|
||||||
bool pgtable_l5_enabled;
|
bool pgtable_l5_enabled;
|
||||||
/** PAGE_SHIFT of the kernel (derived from PAGE_SIZE). */
|
/** PAGE_SHIFT of the kernel (derived from PAGE_SIZE). */
|
||||||
|
Loading…
Reference in New Issue
Block a user