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.
This commit is contained in:
Omar Sandoval 2019-05-13 15:46:54 -07:00
parent f11e030aaa
commit ed6a6f0b3e
5 changed files with 86 additions and 30 deletions

View File

@ -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

View File

@ -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. */

View File

@ -1,11 +1,13 @@
// Copyright 2018-2019 - Omar Sandoval
// SPDX-License-Identifier: GPL-3.0+
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#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;

View File

@ -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;

View File

@ -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 {