diff --git a/docs/advanced_usage.rst b/docs/advanced_usage.rst index 2b05a3191..2d98044c0 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/kallsyms`` or ELF vmcores, or as a special data + section in diskdump-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 8db5795e8..fef209960 100644 --- a/libdrgn/drgn_program_parse_vmcoreinfo.inc.strswitch +++ b/libdrgn/drgn_program_parse_vmcoreinfo.inc.strswitch @@ -23,6 +23,12 @@ struct drgn_error *drgn_program_parse_vmcoreinfo(struct drgn_program *prog, size_t descsz) { struct drgn_error *err; + + prog->vmcoreinfo.data_size = descsz; + prog->vmcoreinfo.data = memdup(desc, descsz); + if (!prog->vmcoreinfo.data) { + 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 1ac8c6f2f..785715fa2 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.data); + 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 7d7f8d8de..63a7de36d 100644 --- a/libdrgn/linux_kernel.c +++ b/libdrgn/linux_kernel.c @@ -250,6 +250,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.data_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.data, + prog->vmcoreinfo.data_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 686b70bb9..053405dd0 100644 --- a/libdrgn/linux_kernel_object_find.inc.strswitch +++ b/libdrgn/linux_kernel_object_find.inc.strswitch @@ -33,6 +33,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_vmemmap(prog, ret); break; + @case "VMCOREINFO"@ + if (flags & DRGN_FIND_OBJECT_CONSTANT) + return linux_kernel_get_vmcoreinfo(prog, ret); + break; @endswitch@ } return &drgn_not_found; diff --git a/libdrgn/program.c b/libdrgn/program.c index affc6d8a3..89fd4cad9 100644 --- a/libdrgn/program.c +++ b/libdrgn/program.c @@ -140,6 +140,7 @@ void drgn_program_deinit(struct drgn_program *prog) drgn_memory_reader_deinit(&prog->reader); free(prog->file_segments); + free(prog->vmcoreinfo.data); #ifdef WITH_LIBKDUMPFILE if (prog->kdump_ctx) @@ -576,6 +577,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.data); 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 57270eef2..f2e915e4c 100644 --- a/libdrgn/program.h +++ b/libdrgn/program.h @@ -171,6 +171,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 *data; + size_t data_size; } vmcoreinfo; /* * Difference between a virtual address in the direct mapping and the