From ed6a6f0b3e450fb4bfbc19162612a9ecafd7fec8 Mon Sep 17 00:00:00 2001 From: Omar Sandoval Date: Mon, 13 May 2019 15:46:54 -0700 Subject: [PATCH] libdrgn: get module section address from sysfs when possible In the running kernel, we don't have to walk the list of modules and module sections, since we can just look it up directly in sysfs. --- docs/api_reference.rst | 4 ++++ libdrgn/drgn.h | 2 ++ libdrgn/kmod.c | 50 ++++++++++++++++++++++++++++++++++++++ libdrgn/program.c | 54 +++++++++++++++++++++++------------------- libdrgn/program.h | 6 ----- 5 files changed, 86 insertions(+), 30 deletions(-) diff --git a/docs/api_reference.rst b/docs/api_reference.rst index 58217120..b878e5b2 100644 --- a/docs/api_reference.rst +++ b/docs/api_reference.rst @@ -280,6 +280,10 @@ Programs The program is the Linux kernel. + .. attribute:: IS_RUNNING_KERNEL + + The program is the running operating system kernel. + .. class:: Architecture ``Architecture`` is an :class:`enum.Flag` of flags describing the target diff --git a/libdrgn/drgn.h b/libdrgn/drgn.h index fc65fe5f..2d710f6e 100644 --- a/libdrgn/drgn.h +++ b/libdrgn/drgn.h @@ -856,6 +856,8 @@ struct drgn_program; enum drgn_program_flags { /** The program is the Linux kernel. */ DRGN_PROGRAM_IS_LINUX_KERNEL = (1 << 0), + /** The program is the running operating system kernel. */ + DRGN_PROGRAM_IS_RUNNING_KERNEL = (1 << 1), }; /** Target architecture of a @ref drgn_program. */ diff --git a/libdrgn/kmod.c b/libdrgn/kmod.c index d9068cb3..2402ace1 100644 --- a/libdrgn/kmod.c +++ b/libdrgn/kmod.c @@ -1,11 +1,13 @@ // Copyright 2018-2019 - Omar Sandoval // SPDX-License-Identifier: GPL-3.0+ +#include #include #include #include "internal.h" #include "kmod.h" +#include "program.h" struct drgn_error * kernel_module_iterator_init(struct kernel_module_iterator *it, @@ -88,6 +90,43 @@ kernel_module_iterator_next(struct kernel_module_iterator *it) return NULL; } +struct drgn_error * +kernel_module_section_address_from_sysfs(struct drgn_program *prog, + const char *module_name, + const char *section_name, + uint64_t *ret) +{ + struct drgn_error *err; + FILE *file; + char *path; + + if (asprintf(&path, "/sys/module/%s/sections/%s", module_name, + section_name) == -1) { + return &drgn_enomem; + } + file = fopen(path, "r"); + if (!file) { + if (errno == ENOENT) { + err = drgn_error_format(DRGN_ERROR_LOOKUP, + "%s is not loaded", + module_name); + } else { + err = drgn_error_create_os(errno, path, "fopen"); + } + goto out_path; + } + if (fscanf(file, "%" SCNx64, ret) != 1) { + err = drgn_error_format(DRGN_ERROR_OTHER, "could not parse %s", + path); + } else { + err = NULL; + } + fclose(file); +out_path: + free(path); + return err; +} + static struct drgn_error *find_section_address(struct drgn_object *mod, const char *section_name, uint64_t *ret) @@ -156,6 +195,17 @@ struct drgn_error *kernel_module_section_address(struct drgn_program *prog, struct drgn_error *err; struct kernel_module_iterator it; + /* + * For the running kernel, we can take a shortcut by looking at sysfs. + * Otherwise, we have to walk the list of modules in the kernel. + */ + if (prog->flags & DRGN_PROGRAM_IS_RUNNING_KERNEL) { + return kernel_module_section_address_from_sysfs(prog, + module_name, + section_name, + ret); + } + err = kernel_module_iterator_init(&it, prog); if (err) return err; diff --git a/libdrgn/program.c b/libdrgn/program.c index a8212093..4619a130 100644 --- a/libdrgn/program.c +++ b/libdrgn/program.c @@ -627,7 +627,7 @@ drgn_program_set_core_dump(struct drgn_program *prog, const char *path) size_t phnum, i, mappings_capacity = 0; bool have_non_zero_phys_addr = false; struct drgn_memory_file_segment *current_file_segment; - bool have_nt_taskstruct = false, have_vmcoreinfo = false; + bool have_nt_taskstruct = false, have_vmcoreinfo = false, is_proc_kcore; err = drgn_program_check_initialized(prog); if (err) @@ -785,20 +785,10 @@ drgn_program_set_core_dump(struct drgn_program *prog, const char *path) resize_array(&prog->mappings, prog->num_mappings); } - if (have_nt_taskstruct && !have_vmcoreinfo) { + if (have_nt_taskstruct) { /* - * Before Linux kernel commit 23c85094fe18 ("proc/kcore: add - * vmcoreinfo note to /proc/kcore") (in v4.19), /proc/kcore - * didn't have a VMCOREINFO note. However, it has always had an - * NT_TASKSTRUCT note. If this is a file in /proc with the - * NT_TASKSTRUCT note, then it's probably /proc/kcore, and we - * need to try to get vmcoreinfo elsewhere. - * - * Since Linux kernel commit 464920104bf7 ("/proc/kcore: update - * physical address for kcore ram and text") (in v4.11), we can - * read from the physical address of vmcoreinfo exported in - * sysfs. Before that, p_paddr in /proc/kcore is always zero, so - * we have to use a hackier fallback. + * If the core file has an NT_TASKSTRUCT note and is in /proc, + * then it's probably /proc/kcore. */ struct statfs fs; @@ -807,21 +797,37 @@ drgn_program_set_core_dump(struct drgn_program *prog, const char *path) if (err) goto out_mappings; } - if (fs.f_type == 0x9fa0 /* PROC_SUPER_MAGIC */) { - if (have_non_zero_phys_addr) { - err = read_vmcoreinfo_from_sysfs(&prog->reader, - &prog->vmcoreinfo); - } else { - err = get_fallback_vmcoreinfo(&prog->vmcoreinfo); - } - if (err) - goto out_mappings; - have_vmcoreinfo = true; + is_proc_kcore = fs.f_type == 0x9fa0; /* PROC_SUPER_MAGIC */ + } else { + is_proc_kcore = false; + } + + if (!have_vmcoreinfo && is_proc_kcore) { + /* + * Before Linux kernel commit 23c85094fe18 ("proc/kcore: add + * vmcoreinfo note to /proc/kcore") (in v4.19), /proc/kcore + * didn't have a VMCOREINFO note. Since Linux kernel commit + * 464920104bf7 ("/proc/kcore: update physical address for kcore + * ram and text") (in v4.11), we can read from the physical + * address of vmcoreinfo exported in sysfs. Before that, p_paddr + * in /proc/kcore is always zero, so we have to use a hackier + * fallback. + */ + if (have_non_zero_phys_addr) { + err = read_vmcoreinfo_from_sysfs(&prog->reader, + &prog->vmcoreinfo); + } else { + err = get_fallback_vmcoreinfo(&prog->vmcoreinfo); } + if (err) + goto out_mappings; + have_vmcoreinfo = true; } if (have_vmcoreinfo) prog->flags |= DRGN_PROGRAM_IS_LINUX_KERNEL; + if (is_proc_kcore) + prog->flags |= DRGN_PROGRAM_IS_RUNNING_KERNEL; drgn_program_update_arch(prog, arch); return NULL; diff --git a/libdrgn/program.h b/libdrgn/program.h index 952a562e..33dfc578 100644 --- a/libdrgn/program.h +++ b/libdrgn/program.h @@ -62,12 +62,6 @@ struct file_mapping { uint64_t file_offset; }; -struct drgn_cleanup { - void (*cb)(void *); - void *arg; - struct drgn_cleanup *next; -}; - struct drgn_dwarf_info_cache; struct drgn_program {