2 ksym.c - functions for kernel address->symbol translation
3 Copyright (c) 1995, 1996 Dr. G.W. Wettstein <greg@wind.rmcc.com>
4 Copyright (c) 1996 Enjellic Systems Development
5 Copyright (c) 1997-2007 Martin Schulze <joey@infodrom.org>
7 This file is part of the sysklogd package, a kernel and system log daemon.
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 * This file contains functions which handle the translation of kernel
26 * numeric addresses into symbols for the klogd utility.
28 * Sat Oct 28 09:00:14 CDT 1995: Dr. Wettstein
31 * Fri Nov 24 12:50:52 CST 1995: Dr. Wettstein
32 * Added VERBOSE_DEBUGGING define to make debugging output more
35 * Added support for verification of the loaded kernel symbols. If
36 * no version information can be be found in the mapfile a warning
37 * message is issued but translation will still take place. This
38 * will be the default case if kernel versions < 1.3.43 are used.
40 * If the symbols in the mapfile are of the same version as the kernel
41 * that is running an informative message is issued. If the symbols
42 * in the mapfile do not match the current kernel version a warning
43 * message is issued and translation is disabled.
45 * Wed Dec 6 16:14:11 CST 1995: Dr. Wettstein
46 * Added /boot/System.map to the list of symbol maps to search for.
47 * Also made this map the first item in the search list. I am open
48 * to CONSTRUCTIVE suggestions for any additions or corrections to
49 * the list of symbol maps to search for. Be forewarned that the
50 * list in use is the consensus agreement between myself, Linus and
51 * some package distributers. It is a given that no list will suit
52 * everyone's taste. If you have rabid concerns about the list
53 * please feel free to edit the system_maps array and compile your
56 * Added support for searching of the list of symbol maps. This
57 * allows support for access to multiple symbol maps. The theory
58 * behind this is that a production kernel may have a system map in
59 * /boot/System.map. If a test kernel is booted this system map
60 * would be skipped in favor of one found in /usr/src/linux.
62 * Thu Jan 18 11:18:31 CST 1996: Dr. Wettstein
63 * Added patch from beta-testers to allow for reading of both
64 * ELF and a.out map files.
66 * Wed Aug 21 09:15:49 CDT 1996: Dr. Wettstein
67 * Reloading of kernel module symbols is now turned on by the
68 * SetParanoiaLevel function. The default behavior is to NOT reload
69 * the kernel module symbols when a protection fault is detected.
71 * Added support for freeing of the current kernel module symbols.
72 * This was necessary to support reloading of the kernel module symbols.
74 * When a matching static symbol table is loaded the kernel version
77 * Mon Jun 9 17:12:42 CST 1997: Martin Schulze
78 * Added #1 and #2 to some error messages in order to being able
79 * to divide them (ulmo@Q.Net)
81 * Fri Jun 13 10:50:23 CST 1997: Martin Schulze
82 * Changed definition of LookupSymbol to non-static because it is
83 * used in klogd.c, too.
85 * Fri Jan 9 23:00:08 CET 1998: Martin Schulze <joey@infodrom.north.de>
86 * Fixed bug that caused klogd to die if there is no System.map available.
88 * Sun 29 Mar 18:14:07 BST 1998: Mark Simon Phillips <M.S.Phillips@nortel.co.uk>
89 * Switched to fgets() as gets() is not buffer overrun secure.
91 * Mon Apr 13 18:18:45 CEST 1998: Martin Schulze <joey@infodrom.north.de>
92 * Modified loop for detecting the correct system map. Now it won't
93 * stop if a file has been found but doesn't contain the correct map.
94 * Special thanks go go Mark Simon Phillips for the hint.
96 * Mon Oct 12 00:42:30 CEST 1998: Martin Schulze <joey@infodrom.north.de>
97 * Modified CheckVersion()
98 * . Use shift to decode the kernel version
99 * . Compare integers of kernel version
100 * . extract major.minor.patch from utsname.release via sscanf()
101 * The reason lays in possible use of kernel flavours which
102 * modify utsname.release but no the Version_ symbol.
104 * Sun Feb 21 22:27:49 EST 1999: Keith Owens <kaos@ocs.com.au>
105 * Fixed bug that caused klogd to die if there is no sym_array available.
107 * Tue Sep 12 23:48:12 CEST 2000: Martin Schulze <joey@infodrom.ffis.de>
108 * Close symbol file in InitKsyms() when an error occurred.
110 * Thu Apr 29 18:07:16 CEST 2004: Dmitry Levin <ldv@altlinux.org>
111 * Close file descriptor in FindSymbolFile() in order not to leak
114 * Fri Jul 16 08:32:49 CEST 2004: Ulf Härnhammar <Ulf.Harnhammar.9485@student.uu.se>
115 * Added boundary check for fscanf() in InitKsyms() and
116 * CheckMapVersion() to prevent an unintended crash when reading
117 * an incorrect System.map.
119 * Mon May 28 08:27:51 CEST 2007: Martin Schulze <joey@infodrom.org>
120 * Added back /usr/src/linux/System.map as fall-back location.
122 * Thu May 31 16:56:26 CEST 2007: Martin Schulze <joey@infodrom.org>
123 * Improved symbol lookup, since symbols are spread over the entire
124 * address space. Return the symbol that fits best instead of
132 #include <sys/utsname.h>
137 #define VERBOSE_DEBUGGING 0
141 static int i_am_paranoid = 0;
142 static char vstring[12];
143 static struct sym_table *sym_array = (struct sym_table *) 0;
145 static char *system_maps[] =
149 "/usr/src/linux/System.map",
160 extern int debugging;
164 /* Function prototypes. */
165 static char * FindSymbolFile(void);
166 static int AddSymbol(unsigned long, char*);
167 static void FreeSymbols(void);
168 static int CheckVersion(char *);
169 static int CheckMapVersion(char *);
172 /**************************************************************************
173 * Function: InitKsyms
175 * Purpose: This function is responsible for initializing and loading
176 * the data tables used by the kernel address translations.
178 * Arguments: (char *) mapfile
180 * mapfile:-> A pointer to a complete path
181 * specification of the file containing
182 * the kernel map to use.
186 * A boolean style context is returned. The return value will
187 * be true if initialization was successful. False if not.
188 **************************************************************************/
190 extern int InitKsyms(mapfile)
198 auto int version = 0;
200 auto unsigned long int address;
205 /* Check and make sure that we are starting with a clean slate. */
211 * Search for and open the file containing the kernel symbols.
213 if ( mapfile != (char *) 0 )
215 if ( (sym_file = fopen(mapfile, "r")) == (FILE *) 0 )
217 Syslog(LOG_WARNING, "Cannot open map file: %s.", \
224 if ( (mapfile = FindSymbolFile()) == (char *) 0 )
226 Syslog(LOG_WARNING, "Cannot find map file.");
228 fputs("Cannot find map file.\n", stderr);
232 if ( (sym_file = fopen(mapfile, "r")) == (FILE *) 0 )
234 Syslog(LOG_WARNING, "Cannot open map file.");
236 fputs("Cannot open map file.\n", stderr);
243 * Read the kernel symbol table file and add entries for each
244 * line. I suspect that the use of fscanf is not really in vogue
245 * but it was quick and dirty and IMHO suitable for fixed format
246 * data such as this. If anybody doesn't agree with this please
247 * e-mail me a diff containing a parser with suitable political
250 while ( !feof(sym_file) )
252 if ( fscanf(sym_file, "%lx %c %511s\n", &address, &type, sym)
255 Syslog(LOG_ERR, "Error in symbol table input (#1).");
259 if ( VERBOSE_DEBUGGING && debugging )
260 fprintf(stderr, "Address: %lx, Type: %c, Symbol: %s\n",
263 if ( AddSymbol(address, sym) == 0 )
265 Syslog(LOG_ERR, "Error adding symbol - %s.", sym);
271 version = CheckVersion(sym);
275 Syslog(LOG_INFO, "Loaded %d symbols from %s.", num_syms, mapfile);
279 Syslog(LOG_WARNING, "Symbols do not match kernel version.");
284 Syslog(LOG_WARNING, "Cannot verify that symbols match " \
289 Syslog(LOG_INFO, "Symbols match kernel version %s.", vstring);
298 /**************************************************************************
299 * Function: FindSymbolFile
301 * Purpose: This function is responsible for encapsulating the search
302 * for a valid symbol file. Encapsulating the search for
303 * the map file in this function allows an intelligent search
304 * process to be implemented.
306 * The list of symbol files will be searched until either a
307 * symbol file is found whose version matches the currently
308 * executing kernel or the end of the list is encountered. If
309 * the end of the list is encountered the first available
310 * symbol file is returned to the caller.
312 * This strategy allows klogd to locate valid symbol files
313 * for both a production and an experimental kernel. For
314 * example a map for a production kernel could be installed
315 * in /boot. If an experimental kernel is loaded the map
316 * in /boot will be skipped and the map in /usr/src/linux would
317 * be used if its version number matches the executing kernel.
319 * Arguments: None specified.
323 * If a valid system map cannot be located a null pointer
324 * is returned to the caller.
326 * If the search is succesful a pointer is returned to the
327 * caller which points to the name of the file containing
328 * the symbol table to be used.
329 **************************************************************************/
331 static char * FindSymbolFile()
334 auto char *file = (char *) 0,
337 auto struct utsname utsname;
338 static char symfile[100];
340 auto FILE *sym_file = (FILE *) 0;
342 if ( uname(&utsname) < 0 )
344 Syslog(LOG_ERR, "Cannot get kernel version information.");
349 fputs("Searching for symbol map.\n", stderr);
351 for (mf = system_maps; *mf != (char *) 0 && file == (char *) 0; ++mf)
354 sprintf (symfile, "%s-%s", *mf, utsname.release);
356 fprintf(stderr, "Trying %s.\n", symfile);
357 if ( (sym_file = fopen(symfile, "r")) != (FILE *) 0 ) {
358 if (CheckMapVersion(symfile) == 1)
362 if (sym_file == (FILE *) 0 || file == (char *) 0) {
363 sprintf (symfile, "%s", *mf);
365 fprintf(stderr, "Trying %s.\n", symfile);
366 if ( (sym_file = fopen(symfile, "r")) != (FILE *) 0 ) {
367 if (CheckMapVersion(symfile) == 1)
376 * At this stage of the game we are at the end of the symbol
380 fprintf(stderr, "End of search list encountered.\n");
385 /**************************************************************************
386 * Function: CheckVersion
388 * Purpose: This function is responsible for determining whether or
389 * the system map being loaded matches the version of the
390 * currently running kernel.
392 * The kernel version is checked by examing a variable which
393 * is of the form: _Version_66347 (a.out) or Version_66437 (ELF).
395 * The suffix of this variable is the current kernel version
396 * of the kernel encoded in base 256. For example the
397 * above variable would be decoded as:
399 * (66347 = 1*65536 + 3*256 + 43 = 1.3.43)
401 * (Insert appropriate deities here) help us if Linus ever
402 * needs more than 255 patch levels to get a kernel out the
405 * Arguments: (char *) version
407 * version:-> A pointer to the string which
408 * is to be decoded as a kernel
413 * -1:-> The currently running kernel version does
414 * not match this version string.
416 * 0:-> The string is not a kernel version variable.
418 * 1:-> The executing kernel is of the same version
419 * as the version string.
420 **************************************************************************/
422 static int CheckVersion(version)
435 auto struct utsname utsname;
438 static char *prefix = { "Version_" };
441 /* Early return if there is no hope. */
442 if ( strncmp(version, prefix, strlen(prefix)) == 0 /* ELF */ ||
444 strncmp(++version, prefix, strlen(prefix)) == 0 ) /* a.out */ )
451 * Since the symbol looks like a kernel version we can start
452 * things out by decoding the version string into its component
455 vnum = atoi(version + strlen(prefix));
456 patch = vnum & 0x000000FF;
457 minor = (vnum >> 8) & 0x000000FF;
458 major = (vnum >> 16) & 0x000000FF;
460 fprintf(stderr, "Version string = %s, Major = %d, " \
461 "Minor = %d, Patch = %d.\n", version +
462 strlen(prefix), major, minor, \
464 sprintf(vstring, "%d.%d.%d", major, minor, patch);
468 * We should now have the version string in the vstring variable in
469 * the same format that it is stored in by the kernel. We now
470 * ask the kernel for its version information and compare the two
471 * values to determine if our system map matches the kernel
474 if ( uname(&utsname) < 0 )
476 Syslog(LOG_ERR, "Cannot get kernel version information.");
480 fprintf(stderr, "Comparing kernel %s with symbol table %s.\n",\
481 utsname.release, vstring);
483 if ( sscanf (utsname.release, "%d.%d.%d", &major, &minor, &patch) < 3 )
485 Syslog(LOG_ERR, "Kernel send bogus release string `%s'.",
490 /* Compute the version code from data sent by the kernel */
491 kvnum = (major << 16) | (minor << 8) | patch;
503 /**************************************************************************
504 * Function: CheckMapVersion
506 * Purpose: This function is responsible for determining whether or
507 * the system map being loaded matches the version of the
508 * currently running kernel. It uses CheckVersion as
511 * Arguments: (char *) fname
513 * fname:-> A pointer to the string which
514 * references the system map file to
519 * -1:-> The currently running kernel version does
520 * not match the version in the given file.
522 * 0:-> No system map file or no version information.
524 * 1:-> The executing kernel is of the same version
525 * as the version of the map file.
526 **************************************************************************/
528 static int CheckMapVersion(fname)
536 auto unsigned long int address;
540 if ( (sym_file = fopen(fname, "r")) != (FILE *) 0 ) {
542 * At this point a map file was successfully opened. We
543 * now need to search this file and look for version
546 Syslog(LOG_INFO, "Inspecting %s", fname);
549 while ( !feof(sym_file) && (version == 0) )
551 if ( fscanf(sym_file, "%lx %c %511s\n", &address, \
554 Syslog(LOG_ERR, "Error in symbol table input (#2).");
558 if ( VERBOSE_DEBUGGING && debugging )
559 fprintf(stderr, "Address: %lx, Type: %c, " \
560 "Symbol: %s\n", address, type, sym);
562 version = CheckVersion(sym);
569 Syslog(LOG_ERR, "Symbol table has incorrect " \
570 "version number.\n");
575 fprintf(stderr, "No version information " \
580 fprintf(stderr, "Found table with " \
581 "matching version number.\n");
592 /**************************************************************************
593 * Function: AddSymbol
595 * Purpose: This function is responsible for adding a symbol name
596 * and its address to the symbol table.
598 * Arguments: (unsigned long) address, (char *) symbol
602 * A boolean value is assumed. True if the addition is
603 * successful. False if not.
604 **************************************************************************/
606 static int AddSymbol(address, symbol)
608 unsigned long address;
613 /* Allocate the the symbol table entry. */
614 sym_array = (struct sym_table *) realloc(sym_array, (num_syms+1) * \
615 sizeof(struct sym_table));
616 if ( sym_array == (struct sym_table *) 0 )
619 /* Then the space for the symbol. */
620 sym_array[num_syms].name = (char *) malloc(strlen(symbol)*sizeof(char)\
622 if ( sym_array[num_syms].name == (char *) 0 )
625 sym_array[num_syms].value = address;
626 strcpy(sym_array[num_syms].name, symbol);
632 /**************************************************************************
633 * Function: LookupSymbol
635 * Purpose: Find the symbol which is related to the given kernel
638 * Arguments: (long int) value, (struct symbol *) sym
640 * value:-> The address to be located.
642 * sym:-> A pointer to a structure which will be
643 * loaded with the symbol's parameters.
647 * If a match cannot be found a diagnostic string is printed.
648 * If a match is found the pointer to the symbolic name most
649 * closely matching the address is returned.
650 **************************************************************************/
652 char * LookupSymbol(value, sym)
664 struct symbol ksym, msym;
669 last = sym_array[0].name;
672 if ( value < sym_array[0].value )
675 for(lp = 0; lp <= num_syms; ++lp)
677 if ( sym_array[lp].value > value )
679 ksym.offset = value - sym_array[lp-1].value;
680 ksym.size = sym_array[lp].value - \
681 sym_array[lp-1].value;
684 last = sym_array[lp].name;
687 name = LookupModuleSymbol(value, &msym);
689 if ( ksym.offset == 0 && msym.offset == 0 )
694 if ( ksym.offset == 0 || msym.offset < 0 ||
695 (ksym.offset > 0 && ksym.offset < msym.offset) )
697 sym->offset = ksym.offset;
698 sym->size = ksym.size;
703 sym->offset = msym.offset;
704 sym->size = msym.size;
713 /**************************************************************************
714 * Function: FreeSymbols
716 * Purpose: This function is responsible for freeing all memory which
717 * has been allocated to hold the static symbol table. It
718 * also initializes the symbol count and in general prepares
719 * for a re-read of a static symbol table.
724 **************************************************************************/
726 static void FreeSymbols()
731 /* Free each piece of memory allocated for symbol names. */
732 for(lp = 0; lp < num_syms; ++lp)
733 free(sym_array[lp].name);
735 /* Whack the entire array and initialize everything. */
737 sym_array = (struct sym_table *) 0;
744 /**************************************************************************
745 * Function: LogExpanded
747 * Purpose: This function is responsible for logging a kernel message
748 * line after all potential numeric kernel addresses have
749 * been resolved symolically.
751 * Arguments: (char *) line, (char *) el
753 * line:-> A pointer to the buffer containing the kernel
754 * message to be expanded and logged.
756 * el:-> A pointer to the buffer into which the expanded
757 * kernel line will be written.
760 **************************************************************************/
762 extern char * ExpandKadds(line, el)
776 auto unsigned long int value;
778 auto struct symbol sym;
784 * This is as handy a place to put this as anyplace.
786 * Since the insertion of kernel modules can occur in a somewhat
787 * dynamic fashion we need some mechanism to insure that the
788 * kernel symbol tables get read just prior to when they are
791 * To accomplish this we look for the Oops string and use its
792 * presence as a signal to load the module symbols.
794 * This is not the best solution of course, especially if the
795 * kernel is rapidly going out to lunch. What really needs to
796 * be done is to somehow generate a callback from the
797 * kernel whenever a module is loaded or unloaded. I am
800 if ( i_am_paranoid &&
801 (strstr(line, "Oops:") != (char *) 0) && !InitMsyms() )
802 Syslog(LOG_WARNING, "Cannot load kernel module symbols.\n");
806 * Early return if there do not appear to be any kernel
807 * messages in this line.
809 if ( (num_syms == 0) ||
810 (kp = strstr(line, "[<")) == (char *) 0 )
816 /* Loop through and expand all kernel messages. */
822 /* Now poised at a kernel delimiter. */
823 if ( (kp = strstr(sl, ">]")) == (char *) 0 )
829 strncpy(num,sl+1,kp-sl-1);
831 value = strtoul(num, (char **) 0, 16);
832 if ( (symbol = LookupSymbol(value, &sym)) == (char *) 0 )
836 elp += strlen(symbol);
838 fprintf(stderr, "Symbol: %s = %lx = %s, %x/%d\n", \
840 (sym.size==0) ? symbol+1 : symbol, \
841 sym.offset, sym.size);
848 elp += sprintf(elp, "+0x%x/0x%02x", sym.offset, sym.size);
850 strncat(elp, kp, value);
853 if ( (kp = strstr(sl, "[<")) == (char *) 0 )
856 while ( kp != (char *) 0);
859 fprintf(stderr, "Expanded line: %s\n", el);
864 /**************************************************************************
865 * Function: SetParanoiaLevel
867 * Purpose: This function is an interface function for setting the
868 * mode of loadable module symbol lookups. Probably overkill
869 * but it does slay another global variable.
871 * Arguments: (int) level
873 * level:-> The amount of paranoia which is to be
874 * present when resolving kernel exceptions.
876 **************************************************************************/
878 extern void SetParanoiaLevel(level)
883 i_am_paranoid = level;
889 * Setting the -DTEST define enables the following code fragment to
890 * be compiled. This produces a small standalone program which will
891 * echo the standard input of the process to stdout while translating
892 * all numeric kernel addresses into their symbolic equivalent.
898 extern int main(int, char **);
901 extern int main(int argc, char *argv[])
903 auto char line[1024], eline[2048];
908 if ( !InitKsyms((char *) 0) )
910 fputs("ksym: Error loading system map.\n", stderr);
914 while ( !feof(stdin) )
916 fgets(line, sizeof(line), stdin);
917 if (line[strlen(line)-1] == '\n') line[strlen(line)-1] = '\0'; /* Trash NL char */
918 memset(eline, '\0', sizeof(eline));
919 ExpandKadds(line, eline);
920 fprintf(stdout, "%s\n", eline);
927 extern void Syslog(int priority, char *fmt, ...)
933 fprintf(stdout, "Pr: %d, ", priority);
934 vfprintf(stdout, fmt, ap);