Skip to content
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

Add VMCOREINFO to Linux special objects #316

Merged
merged 2 commits into from
Aug 19, 2023
Merged
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
19 changes: 19 additions & 0 deletions docs/advanced_usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -191,3 +191,22 @@ core dumps. These special objects include:
resorting to architecture-specific logic.

This is *not* available without debugging information.

``VMCOREINFO``
Object type: ``const char []``

This is the data contained in the vmcoreinfo note, which is present either
as an ELF note in ``/proc/kcore`` or ELF vmcores, or as a special data
section in kdump-formatted vmcores. The vmcoreinfo note contains critical
data necessary for interpreting the kernel image, such as KASLR offsets and
data structure locations.

In the Linux kernel, this data is normally stored in a variable called
``vmcoreinfo_data``. However, drgn reads this information from ELF note or
from the diskdump header. It is possible (in rare cases, usually with
vmcores created by hypervisors) for a vmcore to contain vmcoreinfo which
differs from the data in ``vmcoreinfo_data``, so it is important to
distinguish the contents. For that reason, we use the name ``VMCOREINFO`` to
distinguish it from the kernel variable ``vmcoreinfo_data``.

This is available without debugging information.
5 changes: 5 additions & 0 deletions libdrgn/drgn_program_parse_vmcoreinfo.inc.strswitch
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ struct drgn_error *drgn_program_parse_vmcoreinfo(struct drgn_program *prog,
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) {
Expand Down
3 changes: 3 additions & 0 deletions libdrgn/kdump.c
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,9 @@ struct drgn_error *drgn_program_set_kdump(struct drgn_program *prog)
err_platform:
prog->has_platform = had_platform;
err:
// Reset anything we parsed from vmcoreinfo
free(prog->vmcoreinfo.raw);
memset(&prog->vmcoreinfo, 0, sizeof(prog->vmcoreinfo));
kdump_free(ctx);
return err;
}
Expand Down
20 changes: 20 additions & 0 deletions libdrgn/linux_kernel.c
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,26 @@ static struct drgn_error *linux_kernel_get_jiffies(struct drgn_program *prog,
return err;
}

static struct drgn_error *
linux_kernel_get_vmcoreinfo(struct drgn_program *prog, struct drgn_object *ret)
{
struct drgn_error *err;
struct drgn_qualified_type qualified_type;
err = drgn_program_find_primitive_type(prog,
DRGN_C_TYPE_CHAR,
&qualified_type.type);
if (err)
return err;
qualified_type.qualifiers = DRGN_QUALIFIER_CONST;
err = drgn_array_type_create(prog, qualified_type, prog->vmcoreinfo.raw_size,
&drgn_language_c, &qualified_type.type);
if (err)
return err;
qualified_type.qualifiers = 0;
return drgn_object_set_from_buffer(ret, qualified_type, prog->vmcoreinfo.raw,
prog->vmcoreinfo.raw_size, 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.
Expand Down
4 changes: 4 additions & 0 deletions libdrgn/linux_kernel_object_find.inc.strswitch
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ struct drgn_error *linux_kernel_object_find(const char *name, size_t name_len,
if (flags & DRGN_FIND_OBJECT_CONSTANT)
return linux_kernel_get_uts_release(prog, ret);
break;
@case "VMCOREINFO"@
if (flags & DRGN_FIND_OBJECT_CONSTANT)
return linux_kernel_get_vmcoreinfo(prog, ret);
break;
@case "jiffies"@
if (flags & DRGN_FIND_OBJECT_VARIABLE)
return linux_kernel_get_jiffies(prog, ret);
Expand Down
2 changes: 2 additions & 0 deletions libdrgn/program.c
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ void drgn_program_deinit(struct drgn_program *prog)
drgn_memory_reader_deinit(&prog->reader);

free(prog->file_segments);
free(prog->vmcoreinfo.raw);

#ifdef WITH_LIBKDUMPFILE
if (prog->kdump_ctx)
Expand Down Expand Up @@ -581,6 +582,7 @@ drgn_program_set_core_dump(struct drgn_program *prog, const char *path)
out_notes:
// Reset anything we parsed from ELF notes.
prog->aarch64_insn_pac_mask = 0;
free(prog->vmcoreinfo.raw);
memset(&prog->vmcoreinfo, 0, sizeof(prog->vmcoreinfo));
out_platform:
prog->has_platform = had_platform;
Expand Down
4 changes: 4 additions & 0 deletions libdrgn/program.h
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,10 @@ struct drgn_program {
bool pgtable_l5_enabled;
/** PAGE_SHIFT of the kernel (derived from PAGE_SIZE). */
int page_shift;

/** The original vmcoreinfo data, to expose as an object */
char *raw;
size_t raw_size;
} vmcoreinfo;
/*
* Difference between a virtual address in the direct mapping and the
Expand Down
43 changes: 43 additions & 0 deletions tests/linux_kernel/test_special_objects.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
# SPDX-License-Identifier: LGPL-2.1-or-later

import os

import drgn
from tests.linux_kernel import LinuxKernelTestCase


class TestUts(LinuxKernelTestCase):
def test_uts_release(self):
self.assertEqual(
self.prog["UTS_RELEASE"].string_().decode(), os.uname().release
)

def test_uts_release_no_debug_info(self):
prog = drgn.Program()
prog.set_kernel()
self.assertEqual(prog["UTS_RELEASE"].string_().decode(), os.uname().release)


class TestVmcoreinfo(LinuxKernelTestCase):
def test_vmcoreinfo(self):
vmcoreinfo_data = dict(
line.split("=", 1)
for line in self.prog["VMCOREINFO"].string_().decode().strip().split("\n")
)
self.assertEqual(
int(vmcoreinfo_data["SYMBOL(init_uts_ns)"], 16),
self.prog.symbol("init_uts_ns").address,
)

def test_vmcoreinfo_no_debug_info(self):
prog = drgn.Program()
prog.set_kernel()
vmcoreinfo_data = dict(
line.split("=", 1)
for line in prog["VMCOREINFO"].string_().decode().strip().split("\n")
)
self.assertEqual(
vmcoreinfo_data["OSRELEASE"],
os.uname().release,
)
19 changes: 0 additions & 19 deletions tests/linux_kernel/test_uts.py

This file was deleted.