summaryrefslogtreecommitdiffstats
path: root/scripts/ksymoops/ksymoops.c
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/ksymoops/ksymoops.c')
-rw-r--r--scripts/ksymoops/ksymoops.c678
1 files changed, 678 insertions, 0 deletions
diff --git a/scripts/ksymoops/ksymoops.c b/scripts/ksymoops/ksymoops.c
new file mode 100644
index 000000000..0cd65795b
--- /dev/null
+++ b/scripts/ksymoops/ksymoops.c
@@ -0,0 +1,678 @@
+/*
+ ksymoops.c.
+
+ Read a kernel Oops file and make the best stab at converting the code to
+ instructions and mapping stack values to kernel symbols.
+
+ Copyright Keith Owens <kaos@ocs.com.au>.
+ Released under the GNU Public Licence, Version 2.
+*/
+
+#define VERSION "0.6e"
+
+/*
+
+ Tue Jan 5 19:26:02 EST 1999
+ Version 0.6e
+ Added to kernel.
+
+ Mon Jan 4 09:48:13 EST 1999
+ Version 0.6d
+ Add ARM support.
+
+ Thu Nov 26 16:37:46 EST 1998
+ Version 0.6c
+ Typo in oops_code.
+ Add -c option.
+ Add -1 option.
+ Report if options were specified or defaulted.
+
+ Fri Nov 6 10:38:42 EST 1998
+ Version 0.6b
+ Remove false warnings when comparing ksyms and lsmod.
+
+ Tue Nov 3 23:33:04 EST 1998
+ Version 0.6a
+ Performance inprovements.
+
+ Tue Nov 3 02:31:01 EST 1998
+ Version 0.6
+ Read lsmod (/proc/modules).
+ Ignore addresses 0-4095 when mapping address to symbol.
+ Discard default objects if -o specified.
+ Oops file must be regular.
+ Add "invalid operand" to Oops_print.
+ Move "Using_Version" copy to map.c.
+ Add Makefile defaults for vmlinux, ksyms, objects, System.map, lsmod.
+ Minor adjustment to re for ppc.
+ Minor adjustment to re for objdump lines with <_EIP+xxx>.
+ Convert from a.out to bfd, using same format as ksymoops.
+ Added MIPS.
+ PPC handling based on patches by "Ryan Nielsen" <ran@krazynet.com>
+
+ Wed Oct 28 23:14:55 EST 1998
+ Version 0.5
+ No longer read vmlinux by default, it only duplicates System.map.
+
+ Wed Oct 28 13:47:38 EST 1998
+ Version 0.4
+ Split into separate sources.
+
+ Mon Oct 26 00:01:47 EST 1998
+ Version 0.3c
+ Add alpha (arm) processing.
+
+ Mon Oct 26 00:01:47 EST 1998
+ Version 0.3b
+ Add sparc processing.
+ Handle kernel symbol versions.
+
+ Fri Oct 23 13:11:20 EST 1998
+ Version 0.3
+ Add -follow to find command for people who use symlinks to modules.
+ Add Version_ checking.
+
+ Thu Oct 22 22:28:30 EST 1998
+ Version 0.2.
+ Generalise text prefix handling.
+ Handle messages on Code: line.
+ Format addresses with leading zeroes.
+ Minor bug fixes.
+
+ Wed Oct 21 23:28:48 EST 1998
+ Version 0.1. Rewrite from scratch in C.
+
+ CREDITS.
+ Oops disassembly based on ksymoops.cc,
+ Copyright (C) 1995 Greg McGary <gkm@magilla.cichlid.com>
+ m68k code based on ksymoops.cc changes by
+ Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>
+ */
+
+#include "ksymoops.h"
+#include <ctype.h>
+#include <errno.h>
+#include <malloc.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/utsname.h>
+
+char *prefix;
+char *path_nm = "/usr/bin/nm"; /* env KSYMOOPS_NM */
+char *path_find = "/usr/bin/find"; /* env KSYMOOPS_FIND */
+char *path_objdump = "/usr/bin/objdump"; /* env KSYMOOPS_OBJDUMP */
+int debug = 0;
+int errors = 0;
+int warnings = 0;
+
+SYMBOL_SET ss_vmlinux;
+SYMBOL_SET ss_ksyms_base;
+SYMBOL_SET *ss_ksyms_module;
+int ss_ksyms_modules;
+SYMBOL_SET ss_lsmod;
+SYMBOL_SET *ss_object;
+int ss_objects;
+SYMBOL_SET ss_system_map;
+
+SYMBOL_SET ss_merged; /* merged map with info from all sources */
+SYMBOL_SET ss_Version; /* Version_ numbers where available */
+
+/* Regular expression stuff */
+
+regex_t re_nm;
+regmatch_t *re_nm_pmatch;
+regex_t re_bracketed_address;
+regmatch_t *re_bracketed_address_pmatch;
+regex_t re_unbracketed_address;
+regmatch_t *re_unbracketed_address_pmatch;
+
+static void usage(void)
+{
+ fprintf(stderr, "Version " VERSION "\n");
+ fprintf(stderr, "usage: %s\n", prefix);
+ fprintf(stderr,
+ "\t\t[-v vmlinux]\tWhere to read vmlinux\n"
+ "\t\t[-V]\t\tNo vmlinux is available\n"
+ "\t\t[-o object_dir]\tDirectory containing modules\n"
+ "\t\t[-O]\t\tNo modules is available\n"
+ "\t\t[-k ksyms]\tWhere to read ksyms\n"
+ "\t\t[-K]\t\tNo ksyms is available\n"
+ "\t\t[-l lsmod]\tWhere to read lsmod\n"
+ "\t\t[-L]\t\tNo lsmod is available\n"
+ "\t\t[-m system.map]\tWhere to read System.map\n"
+ "\t\t[-M]\t\tNo System.map is available\n"
+ "\t\t[-s save.map]\tSave consolidated map\n"
+ "\t\t[-d]\t\tIncrease debug level by 1\n"
+ "\t\t[-h]\t\tPrint help text\n"
+ "\t\t[-c code_bytes]\tHow many bytes in each unit of code\n"
+ "\t\t[-1]\t\tOne shot toggle (exit after first Oops)\n"
+ "\t\t<Oops.file\tOops report to decode\n"
+ "\n"
+ "\t\tAll flags can occur more than once. With the exception "
+ "of -o\n"
+ "\t\tand -d which are cumulative, the last occurrence of each "
+ "flag is\n"
+ "\t\tused. Note that \"-v my.vmlinux -V\" will be taken as "
+ "\"No vmlinux\n"
+ "\t\tavailable\" but \"-V -v my.vmlinux\" will read "
+ "my.vmlinux. You\n"
+ "\t\twill be warned about such combinations.\n"
+ "\n"
+ "\t\tEach occurrence of -d increases the debug level.\n"
+ "\n"
+ "\t\tEach -o flag can refer to a directory or to a single "
+ "object\n"
+ "\t\tfile. If a directory is specified then all *.o files in "
+ "that\n"
+ "\t\tdirectory and its subdirectories are assumed to be "
+ "modules.\n"
+ "\n"
+ "\t\tIf any of the vmlinux, object_dir, ksyms or system.map "
+ "options\n"
+ "\t\tcontain the string *r (*m, *n, *s) then it is replaced "
+ "at run\n"
+ "\t\ttime by the current value of `uname -r` (-m, -n, -s).\n"
+ "\n"
+ "\t\tThe defaults can be changed in the Makefile, current "
+ "defaults\n"
+ "\t\tare\n\n"
+ "\t\t\t"
+#ifdef DEF_VMLINUX
+ "-v " DEF_LINUX
+#else
+ "-V"
+#endif
+ "\n"
+ "\t\t\t"
+#ifdef DEF_OBJECTS
+ "-o " DEF_OBJECTS
+#else
+ "-O"
+#endif
+ "\n"
+ "\t\t\t"
+#ifdef DEF_KSYMS
+ "-k " DEF_KSYMS
+#else
+ "-K"
+#endif
+ "\n"
+ "\t\t\t"
+#ifdef DEF_LSMOD
+ "-l " DEF_LSMOD
+#else
+ "-L"
+#endif
+ "\n"
+ "\t\t\t"
+#ifdef DEF_MAP
+ "-m " DEF_MAP
+#else
+ "-M"
+#endif
+ "\n"
+ "\t\t\t-c %d\n" /* DEF_CODE_BYTES */
+ "\t\t\tOops report is read from stdin\n"
+ "\n",
+ DEF_CODE_BYTES
+ );
+}
+
+/* Check if possibly conflicting options were specified */
+static void multi_opt(int specl, int specu, char type, const char *using)
+{
+ if (specl && specu) {
+ fprintf(stderr,
+ "Warning - you specified both -%c and -%c. Using '",
+ type, toupper(type));
+ ++warnings;
+ if (using) {
+ fprintf(stderr, "-%c %s", type, using);
+ if (type == 'o')
+ fprintf(stderr, " ...");
+ fprintf(stderr, "'\n");
+ }
+ else
+ fprintf(stderr, "-%c'\n", toupper(type));
+ }
+ else if (specl > 1 && type != 'o') {
+ fprintf(stderr,
+ "Warning - you specified -%c more than once. "
+ "Using '-%c %s'\n",
+ type, type, using);
+ ++warnings;
+ }
+ else if (specu > 1) {
+ fprintf(stderr,
+ "Warning - you specified -%c more than once. "
+ "Second and subsequent '-%c' ignored\n",
+ toupper(type), toupper(type));
+ ++warnings;
+ }
+}
+
+/* If a name contains *r (*m, *n, *s), replace with the current value of
+ * `uname -r` (-m, -n, -s). Actually uses uname system call rather than the
+ * uname command but the result is the same.
+ */
+static void convert_uname(char **name)
+{
+ char *p, *newname, *oldname, *replacement;
+ unsigned len;
+ int free_oldname = 0;
+ static char procname[] = "convert_uname";
+
+ if (!*name)
+ return;
+
+ while ((p = strchr(*name, '*'))) {
+ struct utsname buf;
+ int i = uname(&buf);
+ if (debug)
+ fprintf(stderr, "DEBUG: %s %s in\n", procname, *name);
+ if (i) {
+ fprintf(stderr,
+ "%s: uname failed, %s will not be processed\n",
+ prefix, *name);
+ perror(prefix);
+ ++errors;
+ return;
+ }
+ switch (*(p+1)) {
+ case 'r':
+ replacement = buf.release;
+ break;
+ case 'm':
+ replacement = buf.machine;
+ break;
+ case 'n':
+ replacement = buf.nodename;
+ break;
+ case 's':
+ replacement = buf.sysname;
+ break;
+ default:
+ fprintf(stderr,
+ "%s: invalid replacement character '*%c' "
+ "in %s\n",
+ prefix, *(p+1), *name);
+ ++errors;
+ return;
+ }
+ len = strlen(*name)-2+strlen(replacement)+1;
+ if (!(newname = malloc(len)))
+ malloc_error(procname);
+ strncpy(newname, *name, (p-*name));
+ strcpy(newname+(p-*name), replacement);
+ strcpy(newname+(p-*name)+strlen(replacement), p+2);
+ p = newname+(p-*name)+strlen(replacement); /* no rescan */
+ oldname = *name;
+ *name = newname;
+ if (free_oldname)
+ free(oldname);
+ free_oldname = 1;
+ if (debug)
+ fprintf(stderr, "DEBUG: %s %s out\n", procname, *name);
+ }
+ return;
+}
+
+/* Report if the option was specified or defaulted */
+static void spec_or_default(int spec, int *some_spec) {
+ if (spec) {
+ printf(" (specified)\n");
+ if (some_spec)
+ *some_spec = 1;
+ }
+ else
+ printf(" (default)\n");
+}
+
+/* Parse the options. Verbose but what's new with getopt? */
+static void parse(int argc,
+ char **argv,
+ char **vmlinux,
+ char ***object,
+ int *objects,
+ char **ksyms,
+ char **lsmod,
+ char **system_map,
+ char **save_system_map,
+ char ***filename,
+ int *filecount,
+ int *spec_h,
+ int *code_bytes,
+ int *one_shot
+ )
+{
+ int spec_v = 0, spec_V = 0;
+ int spec_o = 0, spec_O = 0;
+ int spec_k = 0, spec_K = 0;
+ int spec_l = 0, spec_L = 0;
+ int spec_m = 0, spec_M = 0;
+ int spec_s = 0;
+ int spec_c = 0;
+
+ int c, i, some_spec = 0;
+ char *p;
+
+ while ((c = getopt(argc, argv, "v:Vo:Ok:Kl:Lm:Ms:dhc:1")) != EOF) {
+ if (debug && c != 'd')
+ fprintf(stderr, "DEBUG: getopt '%c' '%s'\n", c, optarg);
+ switch(c) {
+ case 'v':
+ *vmlinux = optarg;
+ ++spec_v;
+ break;
+ case 'V':
+ *vmlinux = NULL;
+ ++spec_V;
+ break;
+ case 'o':
+ if (!spec_o) {
+ /* First -o, discard default value(s) */
+ for (i = 0; i < *objects; ++i)
+ free((*object)[i]);
+ free(*object);
+ *object = NULL;
+ *objects = 0;
+ }
+ *object = realloc(*object,
+ ((*objects)+1)*sizeof(**object));
+ if (!*object)
+ malloc_error("object");
+ if (!(p = strdup(optarg)))
+ malloc_error("strdup -o");
+ else {
+ (*object)[(*objects)++] = p;
+ ++spec_o;
+ }
+ break;
+ case 'O':
+ ++spec_O;
+ for (i = 0; i < *objects; ++i)
+ free((*object)[i]);
+ free(*object);
+ *object = NULL;
+ *objects = 0;
+ break;
+ case 'k':
+ *ksyms = optarg;
+ ++spec_k;
+ break;
+ case 'K':
+ *ksyms = NULL;
+ ++spec_K;
+ break;
+ case 'l':
+ *lsmod = optarg;
+ ++spec_l;
+ break;
+ case 'L':
+ *lsmod = NULL;
+ ++spec_L;
+ break;
+ case 'm':
+ *system_map = optarg;
+ ++spec_m;
+ break;
+ case 'M':
+ *system_map = NULL;
+ ++spec_M;
+ break;
+ case 's':
+ *save_system_map = optarg;
+ ++spec_s;
+ break;
+ case 'd':
+ ++debug;
+ break;
+ case 'h':
+ usage();
+ ++*spec_h;
+ break;
+ case 'c':
+ ++spec_c;
+ errno = 0;
+ *code_bytes = strtoul(optarg, &p, 10);
+ /* Oops_code_values assumes that code_bytes is a
+ * multiple of 2.
+ */
+ if (!*optarg || *p || errno ||
+ (*code_bytes != 1 &&
+ *code_bytes != 2 &&
+ *code_bytes != 4 &&
+ *code_bytes != 8)) {
+ fprintf(stderr,
+ "%s Invalid value for -c '%s'\n",
+ prefix, optarg);
+ ++errors;
+ if (errno)
+ perror(" ");
+ *code_bytes = DEF_CODE_BYTES;
+ }
+ break;
+ case '1':
+ *one_shot = !*one_shot;
+ break;
+ case '?':
+ usage();
+ exit(2);
+ }
+ }
+
+ *filecount = argc - optind;
+ *filename = argv + optind;
+
+ /* Expand any requests for the current uname values */
+ convert_uname(vmlinux);
+ if (*objects) {
+ for (i = 0; i < *objects; ++i)
+ convert_uname(*object+i);
+ }
+ convert_uname(ksyms);
+ convert_uname(lsmod);
+ convert_uname(system_map);
+
+ /* Check for multiple options specified */
+ multi_opt(spec_v, spec_V, 'v', *vmlinux);
+ multi_opt(spec_o, spec_O, 'o', *object ? **object : NULL);
+ multi_opt(spec_k, spec_K, 'k', *ksyms);
+ multi_opt(spec_l, spec_L, 'l', *lsmod);
+ multi_opt(spec_m, spec_M, 'm', *system_map);
+
+ printf("Options used:");
+ if (*vmlinux)
+ printf(" -v %s", *vmlinux);
+ else
+ printf(" -V");
+ spec_or_default(spec_v || spec_V, &some_spec);
+
+ printf(" ");
+ if (*objects) {
+ for (i = 0; i < *objects; ++i)
+ printf(" -o %s", (*object)[i]);
+ }
+ else
+ printf(" -O");
+ spec_or_default(spec_o || spec_O, &some_spec);
+
+ printf(" ");
+ if (*ksyms)
+ printf(" -k %s", *ksyms);
+ else
+ printf(" -K");
+ spec_or_default(spec_k || spec_K, &some_spec);
+
+ printf(" ");
+ if (*lsmod)
+ printf(" -l %s", *lsmod);
+ else
+ printf(" -L");
+ spec_or_default(spec_l || spec_L, &some_spec);
+
+ printf(" ");
+ if (*system_map)
+ printf(" -m %s", *system_map);
+ else
+ printf(" -M");
+ spec_or_default(spec_m || spec_M, &some_spec);
+
+ printf(" ");
+ printf(" -c %d", *code_bytes);
+ spec_or_default(spec_c, NULL);
+
+ if (*one_shot) {
+ printf(" ");
+ printf(" -1");
+ }
+
+ printf("\n");
+
+ if (!some_spec) {
+ printf(
+"You did not tell me where to find symbol information. I will assume\n"
+"that the log matches the kernel and modules that are running right now\n"
+"and I'll use the default options above for symbol resolution.\n"
+"If the current kernel and/or modules do not match the log, you can get\n"
+"more accurate output by telling me the kernel version and where to find\n"
+"map, modules, ksyms etc. ksymoops -h explains the options.\n"
+ "\n");
+ ++warnings;
+ }
+}
+
+/* Read environment variables */
+static void read_env(const char *external, char **internal)
+{
+ char *p;
+ if ((p = getenv(external))) {
+ *internal = p;
+ if (debug)
+ fprintf(stderr,
+ "DEBUG: env override %s=%s\n",
+ external, *internal);
+ }
+ else {
+ if (debug)
+ fprintf(stderr,
+ "DEBUG: env default %s=%s\n",
+ external, *internal);
+ }
+}
+
+
+int main(int argc, char **argv)
+{
+ char *vmlinux = NULL;
+ char **object = NULL;
+ int objects = 0;
+ char *ksyms = NULL;
+ char *lsmod = NULL;
+ char *system_map = NULL;
+ char *save_system_map = NULL;
+ char **filename;
+ int filecount = 0;
+ int spec_h = 0; /* -h was specified */
+ int code_bytes = DEF_CODE_BYTES;
+ int one_shot = 0;
+ int i, ret;
+
+ prefix = *argv;
+ setvbuf(stdout, NULL, _IONBF, 0);
+
+#ifdef DEF_VMLINUX
+ vmlinux = DEF_LINUX;
+#endif
+#ifdef DEF_OBJECTS
+ {
+ char *p;
+ object = realloc(object, (objects+1)*sizeof(*object));
+ if (!object)
+ malloc_error("DEF_OBJECTS");
+ if (!(p = strdup(DEF_OBJECTS)))
+ malloc_error("DEF_OBJECTS");
+ else
+ object[objects++] = p;
+ }
+#endif
+#ifdef DEF_KSYMS
+ ksyms = DEF_KSYMS;
+#endif
+#ifdef DEF_LSMOD
+ lsmod = DEF_LSMOD;
+#endif
+#ifdef DEF_MAP
+ system_map = DEF_MAP;
+#endif
+
+ parse(argc,
+ argv,
+ &vmlinux,
+ &object,
+ &objects,
+ &ksyms,
+ &lsmod,
+ &system_map,
+ &save_system_map,
+ &filename,
+ &filecount,
+ &spec_h,
+ &code_bytes,
+ &one_shot
+ );
+
+ if (spec_h && filecount == 0)
+ return(0); /* just the help text */
+
+ if (errors)
+ return(1);
+
+ if (debug)
+ fprintf(stderr, "DEBUG: level %d\n", debug);
+
+ read_env("KSYMOOPS_NM", &path_nm);
+ read_env("KSYMOOPS_FIND", &path_find);
+ read_env("KSYMOOPS_OBJDUMP", &path_objdump);
+
+ re_compile_common();
+ ss_init_common();
+
+ read_vmlinux(vmlinux);
+ read_ksyms(ksyms);
+ /* No point in reading modules unless ksyms shows modules loaded */
+ if (ss_ksyms_modules) {
+ expand_objects(object, objects);
+ for (i = 0; i < ss_objects; ++i)
+ read_object(ss_object[i].source, i);
+ }
+ else if (objects)
+ printf("No modules in ksyms, skipping objects\n");
+ /* No point in reading lsmod without ksyms */
+ if (ss_ksyms_modules || ss_ksyms_base.used)
+ read_lsmod(lsmod);
+ else if (lsmod)
+ printf("No ksyms, skipping lsmod\n");
+ read_system_map(system_map);
+ merge_maps(save_system_map);
+
+ /* After all that work, it is finally time to read the Oops report */
+ ret = Oops_read(filecount, filename, code_bytes, one_shot);
+
+ if (warnings || errors) {
+ printf("\n");
+ if (warnings)
+ printf("%d warning%s ",
+ warnings, warnings == 1 ? "" : "s");
+ if (warnings && errors)
+ printf("and ");
+ if (errors)
+ printf("%d error%s ", errors, errors == 1 ? "" : "s");
+ printf("issued. Results may not be reliable.\n");
+ if (!ret)
+ return(1);
+ }
+
+ return(ret);
+}