rsyslog/plugins/imklog/ksym_mod.c
Rainer Gerhards f8dff16a4a cleanup of imklog + addtl. config directives
- implemented $KLogInternalMsgFacility config directive
- implemented $KLogPermitNonKernelFacility config directive
- modified internal interfaces
2008-04-14 14:44:32 +02:00

484 lines
15 KiB
C

/*
* 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));
fclose(ksyms);
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);
}