mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-05 09:14:03 +00:00
Add a new 'add-kld <kld>' command to kgdb to make it easier to analyze
crash dumps with kernel modules. The command is basically a wrapper around add-symbol-file except that it uses the kernel linker data structures and the ELF section headers of the kld to calculate the section addresses add-symbol-file needs. The 'kld' parameter may either be an absolute path or a relative path. kgdb looks for the kld in several locations checking for variants with ".symbols" or ".debug" suffixes in each location. The first location it tries is just opening the specified path (this handles absolute paths and looks for the kld relative to the current directory otherwise). Next it tries to find the module in the same directory of the kernel image being used. If that fails it extracts the kern.module_path from the kernel being debugged and looks in each of those paths. The upshot is that for the common cases of debugging /boot/kernel/kernel where the module is in either /boot/kernel or /boot/modules one can merely do 'add-kld foo.ko'. MFC after: 1 week
This commit is contained in:
parent
8ec3680eb5
commit
933785a027
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=175416
@ -32,6 +32,7 @@
|
||||
struct thread_info;
|
||||
|
||||
extern kvm_t *kvm;
|
||||
extern char *kernel;
|
||||
|
||||
struct kthr {
|
||||
struct kthr *next;
|
||||
@ -63,5 +64,6 @@ struct kthr *kgdb_thr_select(struct kthr *);
|
||||
char *kgdb_thr_extra_thread_info(int);
|
||||
|
||||
uintptr_t kgdb_lookup(const char *sym);
|
||||
CORE_ADDR kgdb_parse(const char *exp);
|
||||
|
||||
#endif /* _KGDB_H_ */
|
||||
|
@ -75,7 +75,7 @@ static int dumpnr;
|
||||
static int verbose;
|
||||
|
||||
static char crashdir[PATH_MAX];
|
||||
static char *kernel;
|
||||
char *kernel;
|
||||
static char *remote;
|
||||
static char *vmcore;
|
||||
|
||||
@ -178,7 +178,7 @@ kgdb_new_objfile(struct objfile *objfile)
|
||||
kgdb_new_objfile_chain(objfile);
|
||||
}
|
||||
|
||||
static CORE_ADDR
|
||||
CORE_ADDR
|
||||
kgdb_parse(const char *exp)
|
||||
{
|
||||
struct cleanup *old_chain;
|
||||
|
@ -29,9 +29,12 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/user.h>
|
||||
#include <libgen.h>
|
||||
#include <kvm.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <defs.h>
|
||||
#include <command.h>
|
||||
@ -40,6 +43,9 @@ __FBSDID("$FreeBSD$");
|
||||
#include <inferior.h>
|
||||
#include <regcache.h>
|
||||
#include <target.h>
|
||||
#include <objfiles.h>
|
||||
#include <gdbcore.h>
|
||||
#include <language.h>
|
||||
|
||||
#include "kgdb.h"
|
||||
|
||||
@ -199,6 +205,268 @@ kgdb_set_tid_cmd (char *arg, int from_tty)
|
||||
kgdb_switch_to_thread(thr);
|
||||
}
|
||||
|
||||
static int
|
||||
kld_ok (char *path)
|
||||
{
|
||||
struct stat sb;
|
||||
|
||||
if (stat(path, &sb) == 0 && S_ISREG(sb.st_mode))
|
||||
return (1);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Look for a matching file in the following order:
|
||||
* - filename + ".symbols" (e.g. foo.ko.symbols)
|
||||
* - filename + ".debug" (e.g. foo.ko.debug)
|
||||
* - filename (e.g. foo.ko)
|
||||
* - dirname(kernel) + filename + ".symbols" (e.g. /boot/kernel/foo.ko.symbols)
|
||||
* - dirname(kernel) + filename + ".debug" (e.g. /boot/kernel/foo.ko.debug)
|
||||
* - dirname(kernel) + filename (e.g. /boot/kernel/foo.ko)
|
||||
* - iterate over each path in the module path looking for:
|
||||
* - dir + filename + ".symbols" (e.g. /boot/modules/foo.ko.symbols)
|
||||
* - dir + filename + ".debug" (e.g. /boot/modules/foo.ko.debug)
|
||||
* - dir + filename (e.g. /boot/modules/foo.ko)
|
||||
*/
|
||||
static int
|
||||
find_kld_path (char *filename, char *path, size_t path_size)
|
||||
{
|
||||
CORE_ADDR module_path_addr;
|
||||
char module_path[PATH_MAX];
|
||||
char *kernel_dir, *module_dir, *cp;
|
||||
|
||||
snprintf(path, path_size, "%s.symbols", filename);
|
||||
if (kld_ok(path))
|
||||
return (1);
|
||||
snprintf(path, path_size, "%s.debug", filename);
|
||||
if (kld_ok(path))
|
||||
return (1);
|
||||
snprintf(path, path_size, "%s", filename);
|
||||
if (kld_ok(path))
|
||||
return (1);
|
||||
kernel_dir = dirname(kernel);
|
||||
if (kernel_dir != NULL) {
|
||||
snprintf(path, path_size, "%s/%s.symbols", kernel_dir,
|
||||
filename);
|
||||
if (kld_ok(path))
|
||||
return (1);
|
||||
snprintf(path, path_size, "%s/%s.debug", kernel_dir, filename);
|
||||
if (kld_ok(path))
|
||||
return (1);
|
||||
snprintf(path, path_size, "%s/%s", kernel_dir, filename);
|
||||
if (kld_ok(path))
|
||||
return (1);
|
||||
}
|
||||
module_path_addr = kgdb_parse("linker_path");
|
||||
if (module_path_addr != 0 &&
|
||||
kvm_read(kvm, module_path_addr, module_path, sizeof(module_path)) ==
|
||||
sizeof(module_path)) {
|
||||
module_path[PATH_MAX - 1] = '\0';
|
||||
cp = module_path;
|
||||
while ((module_dir = strsep(&cp, ";")) != NULL) {
|
||||
snprintf(path, path_size, "%s/%s.symbols", module_dir,
|
||||
filename);
|
||||
if (kld_ok(path))
|
||||
return (1);
|
||||
snprintf(path, path_size, "%s/%s.debug", module_dir,
|
||||
filename);
|
||||
if (kld_ok(path))
|
||||
return (1);
|
||||
snprintf(path, path_size, "%s/%s", module_dir,
|
||||
filename);
|
||||
if (kld_ok(path))
|
||||
return (1);
|
||||
}
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read a kernel pointer given a KVA in 'address'.
|
||||
*/
|
||||
static CORE_ADDR
|
||||
read_pointer (CORE_ADDR address)
|
||||
{
|
||||
union {
|
||||
uint32_t d32;
|
||||
uint64_t d64;
|
||||
} val;
|
||||
|
||||
switch (TARGET_PTR_BIT) {
|
||||
case 32:
|
||||
if (kvm_read(kvm, address, &val.d32, sizeof(val.d32)) !=
|
||||
sizeof(val.d32))
|
||||
return (0);
|
||||
return (val.d32);
|
||||
case 64:
|
||||
if (kvm_read(kvm, address, &val.d64, sizeof(val.d64)) !=
|
||||
sizeof(val.d64))
|
||||
return (0);
|
||||
return (val.d64);
|
||||
default:
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to find this kld in the kernel linker's list of linker files.
|
||||
*/
|
||||
static int
|
||||
find_kld_address (char *arg, CORE_ADDR *address)
|
||||
{
|
||||
CORE_ADDR kld, filename_addr;
|
||||
CORE_ADDR off_address, off_filename, off_next;
|
||||
char kld_filename[PATH_MAX];
|
||||
char *filename;
|
||||
size_t filelen;
|
||||
|
||||
/* Compute offsets of relevant members in struct linker_file. */
|
||||
off_address = kgdb_parse("&((struct linker_file *)0)->address");
|
||||
off_filename = kgdb_parse("&((struct linker_file *)0)->filename");
|
||||
off_next = kgdb_parse("&((struct linker_file *)0)->link.tqe_next");
|
||||
if (off_address == 0 || off_filename == 0 || off_next == 0)
|
||||
return (0);
|
||||
|
||||
filename = basename(arg);
|
||||
filelen = strlen(filename) + 1;
|
||||
kld = kgdb_parse("linker_files.tqh_first");
|
||||
while (kld != 0) {
|
||||
/* Try to read this linker file's filename. */
|
||||
filename_addr = read_pointer(kld + off_filename);
|
||||
if (filename_addr == 0)
|
||||
goto next_kld;
|
||||
if (kvm_read(kvm, filename_addr, kld_filename, filelen) !=
|
||||
filelen)
|
||||
goto next_kld;
|
||||
|
||||
/* Compare this kld's filename against our passed in name. */
|
||||
if (kld_filename[filelen - 1] != '\0')
|
||||
goto next_kld;
|
||||
if (strcmp(kld_filename, filename) != 0)
|
||||
goto next_kld;
|
||||
|
||||
/*
|
||||
* We found a match, use its address as the base
|
||||
* address if we can read it.
|
||||
*/
|
||||
*address = read_pointer(kld + off_address);
|
||||
if (*address == 0)
|
||||
return (0);
|
||||
return (1);
|
||||
|
||||
next_kld:
|
||||
kld = read_pointer(kld + off_next);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
add_section(struct section_addr_info *section_addrs, int *sect_indexp,
|
||||
char *name, CORE_ADDR address)
|
||||
{
|
||||
int sect_index;
|
||||
|
||||
sect_index = *sect_indexp;
|
||||
section_addrs->other[sect_index].name = name;
|
||||
section_addrs->other[sect_index].addr = address;
|
||||
printf_unfiltered("\t%s_addr = %s\n", name,
|
||||
local_hex_string(address));
|
||||
sect_index++;
|
||||
*sect_indexp = sect_index;
|
||||
}
|
||||
|
||||
static void
|
||||
kgdb_add_kld_cmd (char *arg, int from_tty)
|
||||
{
|
||||
struct section_addr_info *section_addrs;
|
||||
struct cleanup *cleanup;
|
||||
char path[PATH_MAX];
|
||||
asection *sect;
|
||||
CORE_ADDR base_addr;
|
||||
bfd *bfd;
|
||||
CORE_ADDR text_addr, data_addr, bss_addr, rodata_addr;
|
||||
int sect_count, sect_index;
|
||||
|
||||
if (!find_kld_path(arg, path, sizeof(path))) {
|
||||
error("unable to locate kld");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!find_kld_address(arg, &base_addr)) {
|
||||
error("unable to find kld in kernel");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Open the kld and find the offsets of the various sections. */
|
||||
bfd = bfd_openr(path, gnutarget);
|
||||
if (bfd == NULL) {
|
||||
error("\"%s\": can't open: %s", path,
|
||||
bfd_errmsg(bfd_get_error()));
|
||||
return;
|
||||
}
|
||||
cleanup = make_cleanup_bfd_close(bfd);
|
||||
|
||||
if (!bfd_check_format(bfd, bfd_object)) {
|
||||
do_cleanups(cleanup);
|
||||
error("\%s\": not an object file", path);
|
||||
return;
|
||||
}
|
||||
|
||||
data_addr = bss_addr = rodata_addr = 0;
|
||||
sect = bfd_get_section_by_name (bfd, ".text");
|
||||
if (sect == NULL) {
|
||||
do_cleanups(cleanup);
|
||||
error("\"%s\": can't find text section", path);
|
||||
return;
|
||||
}
|
||||
text_addr = bfd_get_section_vma(bfd, sect);
|
||||
sect_count = 1;
|
||||
|
||||
/* Save the offsets of relevant sections. */
|
||||
sect = bfd_get_section_by_name (bfd, ".data");
|
||||
if (sect != NULL) {
|
||||
data_addr = bfd_get_section_vma(bfd, sect);
|
||||
sect_count++;
|
||||
}
|
||||
|
||||
sect = bfd_get_section_by_name (bfd, ".bss");
|
||||
if (sect != NULL) {
|
||||
bss_addr = bfd_get_section_vma(bfd, sect);
|
||||
sect_count++;
|
||||
}
|
||||
|
||||
sect = bfd_get_section_by_name (bfd, ".rodata");
|
||||
if (sect != NULL) {
|
||||
rodata_addr = bfd_get_section_vma(bfd, sect);
|
||||
sect_count++;
|
||||
}
|
||||
|
||||
do_cleanups(cleanup);
|
||||
|
||||
printf_unfiltered("add symbol table from file \"%s\" at\n", path);
|
||||
|
||||
/* Build a section table for symbol_file_add(). */
|
||||
section_addrs = alloc_section_addr_info(sect_count);
|
||||
cleanup = make_cleanup(xfree, section_addrs);
|
||||
sect_index = 0;
|
||||
add_section(section_addrs, §_index, ".text", base_addr + text_addr);
|
||||
if (data_addr != 0)
|
||||
add_section(section_addrs, §_index, ".data",
|
||||
base_addr + data_addr);
|
||||
if (bss_addr != 0)
|
||||
add_section(section_addrs, §_index, ".bss",
|
||||
base_addr + bss_addr);
|
||||
if (rodata_addr != 0)
|
||||
add_section(section_addrs, §_index, ".rodata",
|
||||
base_addr + rodata_addr);
|
||||
|
||||
symbol_file_add(path, from_tty, section_addrs, 0, OBJF_USERLOADED);
|
||||
|
||||
reinit_frame_cache();
|
||||
|
||||
do_cleanups(cleanup);
|
||||
}
|
||||
|
||||
void
|
||||
kgdb_target(void)
|
||||
{
|
||||
@ -236,4 +504,7 @@ kgdb_target(void)
|
||||
"Set current process context");
|
||||
add_com ("tid", class_obscure, kgdb_set_tid_cmd,
|
||||
"Set current thread context");
|
||||
add_com ("add-kld", class_files, kgdb_add_kld_cmd,
|
||||
"Usage: add-kld FILE\n\
|
||||
Load the symbols from the kernel loadable module FILE.");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user