From eb83d5117572e94431163ce97833b234b3bdfbf3 Mon Sep 17 00:00:00 2001 From: Stephen Brennan Date: Thu, 29 Jun 2023 16:28:12 -0700 Subject: [PATCH] Add VMCOREINFO to special Linux Kernel objects For Python-based object, type, and symbol finders, the vmcoreinfo is a critical source of information. It can contain addresses necessary for loading certain information (such as kallsyms). Expose this information as a special object. Signed-off-by: Stephen Brennan --- docs/advanced_usage.rst | 19 ++++++++++++++++++ ...rgn_program_parse_vmcoreinfo.inc.strswitch | 5 +++++ libdrgn/kdump.c | 3 +++ libdrgn/linux_kernel.c | 20 +++++++++++++++++++ .../linux_kernel_object_find.inc.strswitch | 4 ++++ libdrgn/program.c | 2 ++ libdrgn/program.h | 4 ++++ 7 files changed, 57 insertions(+) diff --git a/docs/advanced_usage.rst b/docs/advanced_usage.rst index 2b05a319..ce941c5f 100644 --- a/docs/advanced_usage.rst +++ b/docs/advanced_usage.rst @@ -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. diff --git a/libdrgn/drgn_program_parse_vmcoreinfo.inc.strswitch b/libdrgn/drgn_program_parse_vmcoreinfo.inc.strswitch index 8db5795e..14659945 100644 --- a/libdrgn/drgn_program_parse_vmcoreinfo.inc.strswitch +++ b/libdrgn/drgn_program_parse_vmcoreinfo.inc.strswitch @@ -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) { diff --git a/libdrgn/kdump.c b/libdrgn/kdump.c index 1ac8c6f2..5e822b7e 100644 --- a/libdrgn/kdump.c +++ b/libdrgn/kdump.c @@ -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; } diff --git a/libdrgn/linux_kernel.c b/libdrgn/linux_kernel.c index 96557dfe..1b3080e1 100644 --- a/libdrgn/linux_kernel.c +++ b/libdrgn/linux_kernel.c @@ -248,6 +248,26 @@ out: 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. diff --git a/libdrgn/linux_kernel_object_find.inc.strswitch b/libdrgn/linux_kernel_object_find.inc.strswitch index 686b70bb..78f48342 100644 --- a/libdrgn/linux_kernel_object_find.inc.strswitch +++ b/libdrgn/linux_kernel_object_find.inc.strswitch @@ -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); diff --git a/libdrgn/program.c b/libdrgn/program.c index 73519b4e..52430348 100644 --- a/libdrgn/program.c +++ b/libdrgn/program.c @@ -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) @@ -581,6 +582,7 @@ out_segments: 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; diff --git a/libdrgn/program.h b/libdrgn/program.h index 4b8954c5..7b870dc5 100644 --- a/libdrgn/program.h +++ b/libdrgn/program.h @@ -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