diff options
Diffstat (limited to 'plugins/imklog/ksym_mod.c')
-rw-r--r-- | plugins/imklog/ksym_mod.c | 482 |
1 files changed, 482 insertions, 0 deletions
diff --git a/plugins/imklog/ksym_mod.c b/plugins/imklog/ksym_mod.c new file mode 100644 index 00000000..6e48e89e --- /dev/null +++ b/plugins/imklog/ksym_mod.c @@ -0,0 +1,482 @@ +/* + * ksym_mod.c - functions for building symbol lookup tables for klogd + * Copyright (c) 1995, 1996 Dr. G.W. Wettstein <greg@wind.rmcc.com> + * Copyright (c) 1996 Enjellic Systems Development + * Copyright (c) 1998-2007 Martin Schulze <joey@infodrom.org> + * Copyright (C) 2007-2008 Rainer Gerhards <rgerhards@adiscon.com> + * + * This file is part of rsyslog. + * + * Rsyslog is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Rsyslog is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>. + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. +*/ + +/* + * This file implements functions which are useful for building + * a symbol lookup table based on the in kernel symbol table + * maintained by the Linux kernel. + * + * Proper logging of kernel panics generated by loadable modules + * tends to be difficult. Since the modules are loaded dynamically + * their addresses are not known at kernel load time. A general + * protection fault (Oops) cannot be properly deciphered with + * classic methods using the static symbol map produced at link time. + * + * One solution to this problem is to have klogd attempt to translate + * addresses from module when the fault occurs. By referencing the + * the kernel symbol table proper resolution of these symbols is made + * possible. + * + * At least that is the plan. + * + * Wed Aug 21 09:20:09 CDT 1996: Dr. Wettstein + * The situation where no module support has been compiled into a + * kernel is now detected. An informative message is output indicating + * that the kernel has no loadable module support whenever kernel + * module symbols are loaded. + * + * An informative message is printed indicating the number of kernel + * modules and the number of symbols loaded from these modules. + * + * Sun Jun 15 16:23:29 MET DST 1997: Michael Alan Dorman + * Some more glibc patches made by <mdorman@debian.org>. + * + * Sat Jan 10 15:00:18 CET 1998: Martin Schulze <joey@infodrom.north.de> + * Fixed problem with klogd not being able to be built on a kernel + * newer than 2.1.18. It was caused by modified structures + * inside the kernel that were included. I have worked in a + * patch from Alessandro Suardi <asuardi@uninetcom.it>. + * + * Sun Jan 25 20:57:34 CET 1998: Martin Schulze <joey@infodrom.north.de> + * Another patch for Linux/alpha by Christopher C Chimelis + * <chris@classnet.med.miami.edu>. + * + * Thu Mar 19 23:39:29 CET 1998: Manuel Rodrigues <pmanuel@cindy.fe.up.pt> + * Changed lseek() to llseek() in order to support > 2GB address + * space which provided by kernels > 2.1.70. + * + * Mon Apr 13 18:18:45 CEST 1998: Martin Schulze <joey@infodrom.north.de> + * Removed <sys/module.h> as it's no longer part of recent glibc + * versions. Added prototyp for llseek() which has been + * forgotton in <unistd.h> from glibc. Added more log + * information if problems occurred while reading a system map + * file, by submission from Mark Simon Phillips <M.S.Phillips@nortel.co.uk>. + * + * Sun Jan 3 18:38:03 CET 1999: Martin Schulze <joey@infodrom.north.de> + * Corrected return value of AddModule if /dev/kmem can't be + * loaded. This will prevent klogd from segfaulting if /dev/kmem + * is not available. Patch from Topi Miettinen <tom@medialab.sonera.net>. + * + * Tue Sep 12 23:11:13 CEST 2000: Martin Schulze <joey@infodrom.ffis.de> + * Changed llseek() to lseek64() in order to skip a libc warning. + */ + + +/* Includes. */ +#include "config.h" +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <unistd.h> +#include <signal.h> +#include <string.h> +#include <errno.h> +#include <sys/fcntl.h> +#include <sys/stat.h> +#if !defined(__GLIBC__) +#include <linux/time.h> +#include <linux/module.h> +#else /* __GLIBC__ */ +#include "module.h" +#endif /* __GLIBC__ */ +#include <stdarg.h> +#include <paths.h> +#include <linux/version.h> + +#include "rsyslog.h" +#include "imklog.h" +#include "ksyms.h" + +#define KSYMS "/proc/kallsyms" + +static int num_modules = 0; +struct Module *sym_array_modules = (struct Module *) 0; + +static int have_modules = 0; + + +/* Function prototypes. */ +static void FreeModules(void); +static int AddSymbol(const char *); +struct Module *AddModule(const char *); +static int symsort(const void *, const void *); + +/* Imported from ksym.c */ +extern int num_syms; + + +/************************************************************************** + * Function: InitMsyms + * + * Purpose: This function is responsible for building a symbol + * table which can be used to resolve addresses for + * loadable modules. + * + * Arguements: Void + * + * Return: A boolean return value is assumed. + * + * A false value indicates that something went wrong. + * + * True if loading is successful. + **************************************************************************/ +extern int InitMsyms(void) +{ + + auto int rtn, + tmp; + FILE *ksyms; + char buf[128]; + char *p; + + /* Initialize the kernel module symbol table. */ + FreeModules(); + + ksyms = fopen(KSYMS, "r"); + + if ( ksyms == NULL ) { + if ( errno == ENOENT ) + imklogLogIntMsg(LOG_INFO, "No module symbols loaded - " + "kernel modules not enabled.\n"); + else + imklogLogIntMsg(LOG_ERR, "Error loading kernel symbols " \ + "- %s\n", strerror(errno)); + return(0); + } + + dbgprintf("Loading kernel module symbols - Source: %s\n", KSYMS); + + while ( fgets(buf, sizeof(buf), ksyms) != NULL ) { + if (num_syms > 0 && index(buf, '[') == NULL) + continue; + + p = index(buf, ' '); + + if ( p == NULL ) + continue; + + if ( buf[strlen(buf)-1] == '\n' ) + buf[strlen(buf)-1] = '\0'; + /* overlong lines will be ignored above */ + + AddSymbol(buf); + } + + if(ksyms != NULL) + fclose(ksyms); + + have_modules = 1; + + /* Sort the symbol tables in each module. */ + for (rtn = tmp = 0; tmp < num_modules; ++tmp) { + rtn += sym_array_modules[tmp].num_syms; + if ( sym_array_modules[tmp].num_syms < 2 ) + continue; + qsort(sym_array_modules[tmp].sym_array, \ + sym_array_modules[tmp].num_syms, \ + sizeof(struct sym_table), symsort); + } + + if ( rtn == 0 ) + imklogLogIntMsg(LOG_INFO, "No module symbols loaded."); + else + imklogLogIntMsg(LOG_INFO, "Loaded %d %s from %d module%s", rtn, \ + (rtn == 1) ? "symbol" : "symbols", \ + num_modules, (num_modules == 1) ? "." : "s."); + + return(1); +} + + +static int symsort(const void *p1, const void *p2) +{ + auto const struct sym_table *sym1 = p1, + *sym2 = p2; + + if ( sym1->value < sym2->value ) + return(-1); + if ( sym1->value == sym2->value ) + return(0); + return(1); +} + + +extern void DeinitMsyms(void) +{ + FreeModules(); +} + + +/************************************************************************** + * Function: FreeModules + * + * Purpose: This function is used to free all memory which has been + * allocated for the modules and their symbols. + * + * Arguements: None specified. + * + * Return: void + **************************************************************************/ +static void FreeModules() +{ + auto int nmods, + nsyms; + auto struct Module *mp; + + /* Check to see if the module symbol tables need to be cleared. */ + have_modules = 0; + if ( num_modules == 0 ) + return; + + if ( sym_array_modules == NULL ) + return; + + for (nmods = 0; nmods < num_modules; ++nmods) { + mp = &sym_array_modules[nmods]; + if ( mp->num_syms == 0 ) + continue; + + for (nsyms= 0; nsyms < mp->num_syms; ++nsyms) + free(mp->sym_array[nsyms].name); + free(mp->sym_array); + if ( mp->name != NULL ) + free(mp->name); + } + + free(sym_array_modules); + sym_array_modules = (struct Module *) 0; + num_modules = 0; + return; +} + + +/************************************************************************** + * Function: AddModule + * + * Purpose: This function is responsible for adding a module to + * the list of currently loaded modules. + * + * Arguments: (const char *) module + * + * module:-> The name of the module. + * + * Return: struct Module * + **************************************************************************/ + +struct Module *AddModule(module) + const char *module; +{ + struct Module *mp; + + if ( num_modules == 0 ) { + sym_array_modules = (struct Module *)malloc(sizeof(struct Module)); + + if ( sym_array_modules == NULL ) + { + imklogLogIntMsg(LOG_WARNING, "Cannot allocate Module array.\n"); + return NULL; + } + mp = sym_array_modules; + } else { + /* Allocate space for the module. */ + mp = (struct Module *) \ + realloc(sym_array_modules, \ + (num_modules+1) * sizeof(struct Module)); + + if ( mp == NULL ) + { + imklogLogIntMsg(LOG_WARNING, "Cannot allocate Module array.\n"); + return NULL; + } + + sym_array_modules = mp; + mp = &sym_array_modules[num_modules]; + } + + num_modules++; + mp->sym_array = NULL; + mp->num_syms = 0; + + if ( module != NULL ) + mp->name = strdup(module); + else + mp->name = NULL; + + return mp; +} + + +/************************************************************************** + * Function: AddSymbol + * + * Purpose: This function is responsible for adding a symbol name + * and its address to the symbol table. + * + * Arguements: const char * + * + * Return: int + * + * A boolean value is assumed. True if the addition is + * successful. False if not. + **************************************************************************/ +static int AddSymbol(line) + const char *line; +{ + char *module; + unsigned long address; + char *p; + static char *lastmodule = NULL; + struct Module *mp; + + module = index(line, '['); + + if ( module != NULL ) { + p = index(module, ']'); + if ( p != NULL ) + *p = '\0'; + p = module++; + while ( isspace(*(--p)) ) + /*SKIP*/; + *(++p) = '\0'; + } + + p = index(line, ' '); + + if ( p == NULL ) + return(0); + + *p = '\0'; + + address = strtoul(line, (char **) 0, 16); + + p += 3; + + if ( num_modules == 0 || + ( lastmodule == NULL && module != NULL ) || + ( module == NULL && lastmodule != NULL) || + ( module != NULL && strcmp(module, lastmodule))) { + mp = AddModule(module); + + if ( mp == NULL ) + return(0); + } else + mp = &sym_array_modules[num_modules-1]; + + lastmodule = mp->name; + + /* Allocate space for the symbol table entry. */ + mp->sym_array = (struct sym_table *) realloc(mp->sym_array, \ + (mp->num_syms+1) * sizeof(struct sym_table)); + + if ( mp->sym_array == (struct sym_table *) 0 ) + return(0); + + mp->sym_array[mp->num_syms].name = strdup(p); + if ( mp->sym_array[mp->num_syms].name == (char *) 0 ) + return(0); + + /* Stuff interesting information into the module. */ + mp->sym_array[mp->num_syms].value = address; + ++mp->num_syms; + + return(1); +} + + + +/************************************************************************** + * Function: LookupModuleSymbol + * + * Purpose: Find the symbol which is related to the given address from + * a kernel module. + * + * Arguements: (long int) value, (struct symbol *) sym + * + * value:-> The address to be located. + * + * sym:-> A pointer to a structure which will be + * loaded with the symbol's parameters. + * + * Return: (char *) + * + * If a match cannot be found a diagnostic string is printed. + * If a match is found the pointer to the symbolic name most + * closely matching the address is returned. + **************************************************************************/ +extern char * LookupModuleSymbol(value, sym) + unsigned long value; + struct symbol *sym; +{ + auto int nmod, + nsym; + auto struct sym_table *last; + auto struct Module *mp; + static char ret[100]; + + sym->size = 0; + sym->offset = 0; + if ( num_modules == 0 ) + return((char *) 0); + + for (nmod = 0; nmod < num_modules; ++nmod) { + mp = &sym_array_modules[nmod]; + + /* + * Run through the list of symbols in this module and + * see if the address can be resolved. + */ + for(nsym = 1, last = &mp->sym_array[0]; + nsym < mp->num_syms; + ++nsym) { + if ( mp->sym_array[nsym].value > value ) + { + if ( sym->size == 0 || + (value - last->value) < sym->offset || + ( (sym->offset == (value - last->value)) && + (mp->sym_array[nsym].value-last->value) < sym->size ) ) + { + sym->offset = value - last->value; + sym->size = mp->sym_array[nsym].value - \ + last->value; + ret[sizeof(ret)-1] = '\0'; + if ( mp->name == NULL ) + snprintf(ret, sizeof(ret)-1, + "%s", last->name); + else + snprintf(ret, sizeof(ret)-1, + "%s:%s", mp->name, last->name); + } + break; + } + last = &mp->sym_array[nsym]; + } + } + + if ( sym->size > 0 ) + return(ret); + + /* It has been a hopeless exercise. */ + return((char *) 0); +} |