Skip to content

Commit 7a9fad0

Browse files
committed
libdrgn: move _vmemmap() to object finder
Similarly to PAGE_OFFSET, vmemmap makes more sense as part of the Linux kernel object finder than an internal helper. While we're here, let's fix the definition for 5-level page tables. This only matters for kernels with commit 77ef56e4f0fb ("x86: Enable 5-level paging support via CONFIG_X86_5LEVEL=y") but without eedb92abb9bb ("x86/mm: Make virtual memory layout dynamic for CONFIG_X86_5LEVEL=y") (namely, v4.14, v4.15, and v4.16); since v4.17, 5-level page table support enables KASLR.
1 parent 5ac95e4 commit 7a9fad0

File tree

5 files changed

+61
-12
lines changed

5 files changed

+61
-12
lines changed

drgn/helpers/linux/mm.py

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,22 +35,13 @@ def pgtable_l5_enabled(prog):
3535
return _linux_helper_pgtable_l5_enabled(prog)
3636

3737

38-
def _vmemmap(prog):
39-
try:
40-
# KASAN
41-
return cast("struct page *", prog["vmemmap_base"])
42-
except KeyError:
43-
# x86-64
44-
return Object(prog, "struct page *", value=0xFFFFEA0000000000)
45-
46-
4738
def for_each_page(prog):
4839
"""
4940
Iterate over all pages in the system.
5041
5142
:return: Iterator of ``struct page *`` objects.
5243
"""
53-
vmemmap = _vmemmap(prog)
44+
vmemmap = prog["vmemmap"]
5445
for i in range(prog["max_pfn"]):
5546
yield vmemmap + i
5647

@@ -61,7 +52,7 @@ def page_to_pfn(page):
6152
6253
Get the page frame number (PFN) of a page.
6354
"""
64-
return cast("unsigned long", page - _vmemmap(page.prog_))
55+
return cast("unsigned long", page - page.prog_["vmemmap"])
6556

6657

6758
def pfn_to_page(prog_or_pfn, pfn=None):
@@ -76,7 +67,7 @@ def pfn_to_page(prog_or_pfn, pfn=None):
7667
pfn = prog_or_pfn
7768
else:
7869
prog = prog_or_pfn
79-
return _vmemmap(prog) + pfn
70+
return prog["vmemmap"] + pfn
8071

8172

8273
def virt_to_pfn(prog_or_addr, addr=None):

libdrgn/arch_x86_64.c.in

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,39 @@ out:
344344
return err;
345345
}
346346

347+
static struct drgn_error *
348+
linux_kernel_get_vmemmap_x86_64(struct drgn_program *prog, uint64_t *ret)
349+
{
350+
351+
struct drgn_error *err;
352+
struct drgn_object obj;
353+
354+
/* If KASLR is enabled, vmemmap is vmemmap_base. */
355+
drgn_object_init(&obj, prog);
356+
err = drgn_program_find_object(prog, "vmemmap_base", NULL,
357+
DRGN_FIND_OBJECT_VARIABLE, &obj);
358+
if (!err) {
359+
err = drgn_object_read_unsigned(&obj, ret);
360+
goto out;
361+
}
362+
if (err->code == DRGN_ERROR_LOOKUP) {
363+
drgn_error_destroy(err);
364+
err = NULL;
365+
} else {
366+
goto out;
367+
}
368+
369+
/* Otherwise, it depends on whether we have 5-level page tables. */
370+
if (prog->vmcoreinfo.pgtable_l5_enabled)
371+
*ret = UINT64_C(0xffd4000000000000);
372+
else
373+
*ret = UINT64_C(0xffffea0000000000);
374+
375+
out:
376+
drgn_object_deinit(&obj);
377+
return err;
378+
}
379+
347380
const struct drgn_architecture_info arch_info_x86_64 = {
348381
ARCHITECTURE_INFO,
349382
.default_flags = (DRGN_PLATFORM_IS_64_BIT |
@@ -352,4 +385,5 @@ const struct drgn_architecture_info arch_info_x86_64 = {
352385
.num_frame_registers = ARRAY_SIZE(frame_registers_x86_64),
353386
.linux_kernel_set_initial_registers = linux_kernel_set_initial_registers_x86_64,
354387
.linux_kernel_get_page_offset = linux_kernel_get_page_offset_x86_64,
388+
.linux_kernel_get_vmemmap = linux_kernel_get_vmemmap_x86_64,
355389
};

libdrgn/linux_kernel.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,26 @@ struct drgn_error *linux_kernel_object_find(const char *name, size_t name_len,
318318
prog->vmcoreinfo.osrelease,
319319
0, 0,
320320
DRGN_PROGRAM_ENDIAN);
321+
} else if (name_len == strlen("vmemmap") &&
322+
memcmp(name, "vmemmap", name_len) == 0) {
323+
if (!prog->vmemmap) {
324+
if (!prog->has_platform ||
325+
!prog->platform.arch->linux_kernel_get_vmemmap)
326+
return &drgn_not_found;
327+
err = prog->platform.arch->linux_kernel_get_vmemmap(prog,
328+
&prog->vmemmap);
329+
if (err) {
330+
prog->vmemmap = 0;
331+
return err;
332+
}
333+
}
334+
335+
err = drgn_program_find_type(prog, "struct page *",
336+
NULL, &qualified_type);
337+
if (err)
338+
return err;
339+
return drgn_object_set_unsigned(ret, qualified_type,
340+
prog->vmemmap, 0);
321341
}
322342
}
323343
return &drgn_not_found;

libdrgn/platform.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ struct drgn_architecture_info {
3737
const struct drgn_object *);
3838
struct drgn_error *(*linux_kernel_get_page_offset)(struct drgn_program *,
3939
uint64_t *);
40+
struct drgn_error *(*linux_kernel_get_vmemmap)(struct drgn_program *,
41+
uint64_t *);
4042
};
4143

4244
static inline const struct drgn_register *

libdrgn/program.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ struct drgn_program {
7171
struct vmcoreinfo vmcoreinfo;
7272
/* Cached PAGE_OFFSET. */
7373
uint64_t page_offset;
74+
/* Cached vmemmap. */
75+
uint64_t vmemmap;
7476
#ifdef WITH_LIBKDUMPFILE
7577
kdump_ctx_t *kdump_ctx;
7678
#endif

0 commit comments

Comments
 (0)