diff options
author | jhb <jhb@FreeBSD.org> | 2008-01-17 21:43:12 +0000 |
---|---|---|
committer | jhb <jhb@FreeBSD.org> | 2008-01-17 21:43:12 +0000 |
commit | 7f24f24832bc16d75b0f181ff5c1665095ba6a30 (patch) | |
tree | d451a5296bd0713f0b63dcf80b792c60b017a26e /gnu/usr.bin | |
parent | 53e7eb64dff1855d853abb42d08c74b62b3a0245 (diff) | |
download | FreeBSD-src-7f24f24832bc16d75b0f181ff5c1665095ba6a30.zip FreeBSD-src-7f24f24832bc16d75b0f181ff5c1665095ba6a30.tar.gz |
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
Diffstat (limited to 'gnu/usr.bin')
-rw-r--r-- | gnu/usr.bin/gdb/kgdb/kgdb.h | 2 | ||||
-rw-r--r-- | gnu/usr.bin/gdb/kgdb/main.c | 4 | ||||
-rw-r--r-- | gnu/usr.bin/gdb/kgdb/trgt.c | 271 |
3 files changed, 275 insertions, 2 deletions
diff --git a/gnu/usr.bin/gdb/kgdb/kgdb.h b/gnu/usr.bin/gdb/kgdb/kgdb.h index d723a5f..5d0498a 100644 --- a/gnu/usr.bin/gdb/kgdb/kgdb.h +++ b/gnu/usr.bin/gdb/kgdb/kgdb.h @@ -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_ */ diff --git a/gnu/usr.bin/gdb/kgdb/main.c b/gnu/usr.bin/gdb/kgdb/main.c index 12bc3c468..629b9c5 100644 --- a/gnu/usr.bin/gdb/kgdb/main.c +++ b/gnu/usr.bin/gdb/kgdb/main.c @@ -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 @@ out: kgdb_new_objfile_chain(objfile); } -static CORE_ADDR +CORE_ADDR kgdb_parse(const char *exp) { struct cleanup *old_chain; diff --git a/gnu/usr.bin/gdb/kgdb/trgt.c b/gnu/usr.bin/gdb/kgdb/trgt.c index f79e4345..2cbb64e 100644 --- a/gnu/usr.bin/gdb/kgdb/trgt.c +++ b/gnu/usr.bin/gdb/kgdb/trgt.c @@ -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."); } |