Document changes
[infodrom/sysklogd] / ksym.c
1 /*
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>
6
7     This file is part of the sysklogd package, a kernel and system log daemon.
8
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.
13
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.
18
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.
22 */
23
24 /*
25  * This file contains functions which handle the translation of kernel
26  * numeric addresses into symbols for the klogd utility.
27  *
28  * Sat Oct 28 09:00:14 CDT 1995:  Dr. Wettstein
29  *      Initial Version.
30  *
31  * Fri Nov 24 12:50:52 CST 1995:  Dr. Wettstein
32  *      Added VERBOSE_DEBUGGING define to make debugging output more
33  *      manageable.
34  *
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.
39  *
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.
44  *
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
54  *      own binaries.
55  *
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.
61  *
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.
65  *
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.
70  *
71  *      Added support for freeing of the current kernel module symbols.
72  *      This was necessary to support reloading of the kernel module symbols.
73  *
74  *      When a matching static symbol table is loaded the kernel version
75  *      number is printed.
76  *
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)
80  *
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.
84  *
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.
87  *
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.
90  *
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.
95  *
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.
103  *
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.
106  *
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.
109  *
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
112  *      file descriptors.
113  *
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.
118  *
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.
121  *
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
125  *      the first hit.
126  */
127
128
129 /* Includes. */
130 #include <stdlib.h>
131 #include <malloc.h>
132 #include <sys/utsname.h>
133 #include "klogd.h"
134 #include "ksyms.h"
135 #include "module.h"
136
137 #define VERBOSE_DEBUGGING 0
138
139
140 int num_syms = 0;
141 static int i_am_paranoid = 0;
142 static char vstring[12];
143 static struct sym_table *sym_array = (struct sym_table *) 0;
144
145 static char *system_maps[] =
146 {
147         "/boot/System.map",
148         "/System.map",
149         "/usr/src/linux/System.map",
150 #if defined(TEST)
151         "./System.map",
152 #endif
153         (char *) 0
154 };
155
156
157 #if defined(TEST)
158 int debugging;
159 #else
160 extern int debugging;
161 #endif
162
163
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 *);
170
171
172 /**************************************************************************
173  * Function:    InitKsyms
174  *
175  * Purpose:     This function is responsible for initializing and loading
176  *              the data tables used by the kernel address translations.
177  *
178  * Arguments:   (char *) mapfile
179  *
180  *                      mapfile:->      A pointer to a complete path
181  *                                      specification of the file containing
182  *                                      the kernel map to use.
183  *
184  * Return:      int
185  *
186  *              A boolean style context is returned.  The return value will
187  *              be true if initialization was successful.  False if not.
188  **************************************************************************/
189
190 extern int InitKsyms(mapfile)
191
192         char *mapfile;
193
194 {
195         auto char       type,
196                         sym[512];
197
198         auto int version = 0;
199
200         auto unsigned long int address;
201
202         auto FILE *sym_file;
203
204
205         /* Check and make sure that we are starting with a clean slate. */
206         if ( num_syms > 0 )
207                 FreeSymbols();
208
209
210         /*
211          * Search for and open the file containing the kernel symbols.
212          */
213         if ( mapfile != (char *) 0 )
214         {
215                 if ( (sym_file = fopen(mapfile, "r")) == (FILE *) 0 )
216                 {
217                         Syslog(LOG_WARNING, "Cannot open map file: %s.", \
218                                mapfile);
219                         return(0);
220                 }
221         }
222         else
223         {
224                 if ( (mapfile = FindSymbolFile()) == (char *) 0 ) 
225                 {
226                         Syslog(LOG_WARNING, "Cannot find map file.");
227                         if ( debugging )
228                                 fputs("Cannot find map file.\n", stderr);
229                         return(0);
230                 }
231                 
232                 if ( (sym_file = fopen(mapfile, "r")) == (FILE *) 0 )
233                 {
234                         Syslog(LOG_WARNING, "Cannot open map file.");
235                         if ( debugging )
236                                 fputs("Cannot open map file.\n", stderr);
237                         return(0);
238                 }
239         }
240         
241
242         /*
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
248          * correctness -- GW.
249          */
250         while ( !feof(sym_file) )
251         {
252                 if ( fscanf(sym_file, "%lx %c %511s\n", &address, &type, sym)
253                     != 3 )
254                 {
255                         Syslog(LOG_ERR, "Error in symbol table input (#1).");
256                         fclose(sym_file);
257                         return(0);
258                 }
259                 if ( VERBOSE_DEBUGGING && debugging )
260                         fprintf(stderr, "Address: %lx, Type: %c, Symbol: %s\n",
261                                 address, type, sym);
262
263                 if ( AddSymbol(address, sym) == 0 )
264                 {
265                         Syslog(LOG_ERR, "Error adding symbol - %s.", sym);
266                         fclose(sym_file);
267                         return(0);
268                 }
269
270                 if ( version == 0 )
271                         version = CheckVersion(sym);
272         }
273         
274
275         Syslog(LOG_INFO, "Loaded %d symbols from %s.", num_syms, mapfile);
276         switch ( version )
277         {
278             case -1:
279                 Syslog(LOG_WARNING, "Symbols do not match kernel version.");
280                 num_syms = 0;
281                 break;
282
283             case 0:
284                 Syslog(LOG_WARNING, "Cannot verify that symbols match " \
285                        "kernel version.");
286                 break;
287                 
288             case 1:
289                 Syslog(LOG_INFO, "Symbols match kernel version %s.", vstring);
290                 break;
291         }
292                 
293         fclose(sym_file);
294         return(1);
295 }
296
297
298 /**************************************************************************
299  * Function:    FindSymbolFile
300  *
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.
305  *
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.
311  *
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.
318  *
319  * Arguments:   None specified.
320  *
321  * Return:      char *
322  *
323  *              If a valid system map cannot be located a null pointer
324  *              is returned to the caller.
325  *
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  **************************************************************************/
330
331 static char * FindSymbolFile()
332
333 {
334         auto char       *file = (char *) 0,
335                         **mf = system_maps;
336
337         auto struct utsname utsname;
338         static char symfile[100];
339
340         auto FILE *sym_file = (FILE *) 0;
341
342         if ( uname(&utsname) < 0 )
343         {
344                 Syslog(LOG_ERR, "Cannot get kernel version information.");
345                 return(0);
346         }
347
348         if ( debugging )
349                 fputs("Searching for symbol map.\n", stderr);
350         
351         for (mf = system_maps; *mf != (char *) 0 && file == (char *) 0; ++mf)
352         {
353  
354                 sprintf (symfile, "%s-%s", *mf, utsname.release);
355                 if ( debugging )
356                         fprintf(stderr, "Trying %s.\n", symfile);
357                 if ( (sym_file = fopen(symfile, "r")) != (FILE *) 0 ) {
358                         if (CheckMapVersion(symfile) == 1)
359                                 file = symfile;
360                         fclose (sym_file);
361                 }
362                 if (sym_file == (FILE *) 0 || file == (char *) 0) {
363                         sprintf (symfile, "%s", *mf);
364                         if ( debugging )
365                                 fprintf(stderr, "Trying %s.\n", symfile);
366                         if ( (sym_file = fopen(symfile, "r")) != (FILE *) 0 ) {
367                                 if (CheckMapVersion(symfile) == 1)
368                                         file = symfile;
369                                 fclose (sym_file);
370                         }
371                 }
372
373         }
374
375         /*
376          * At this stage of the game we are at the end of the symbol
377          * tables.
378          */
379         if ( debugging )
380                 fprintf(stderr, "End of search list encountered.\n");
381         return(file);
382 }
383
384
385 /**************************************************************************
386  * Function:    CheckVersion
387  *
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.
391  *
392  *              The kernel version is checked by examing a variable which
393  *              is of the form: _Version_66347 (a.out) or Version_66437 (ELF).
394  *
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:
398  *
399  *                      (66347 = 1*65536 + 3*256 + 43 = 1.3.43)
400  *
401  *              (Insert appropriate deities here) help us if Linus ever
402  *              needs more than 255 patch levels to get a kernel out the
403  *              door... :-)
404  *
405  * Arguments:   (char *) version
406  *
407  *                      version:->      A pointer to the string which
408  *                                      is to be decoded as a kernel
409  *                                      version variable.
410  *
411  * Return:      int
412  *
413  *                     -1:->    The currently running kernel version does
414  *                              not match this version string.
415  *
416  *                      0:->    The string is not a kernel version variable.
417  *
418  *                      1:->    The executing kernel is of the same version
419  *                              as the version string.
420  **************************************************************************/
421
422 static int CheckVersion(version)
423
424         char *version;
425         
426
427 {
428         auto int        vnum,
429                         major,
430                         minor,
431                         patch;
432
433 #ifndef TESTING
434         int kvnum;
435         auto struct utsname utsname;
436 #endif
437
438         static char *prefix = { "Version_" };
439
440
441         /* Early return if there is no hope. */
442         if ( strncmp(version, prefix, strlen(prefix)) == 0  /* ELF */ ||
443            (*version == '_' &&
444                 strncmp(++version, prefix, strlen(prefix)) == 0 ) /* a.out */ )
445                 ;
446         else
447                 return(0);
448
449
450         /*
451          * Since the symbol looks like a kernel version we can start
452          * things out by decoding the version string into its component
453          * parts.
454          */
455         vnum = atoi(version + strlen(prefix));
456         patch = vnum & 0x000000FF;
457         minor = (vnum >> 8) & 0x000000FF;
458         major = (vnum >> 16) & 0x000000FF;
459         if ( debugging )
460                 fprintf(stderr, "Version string = %s, Major = %d, " \
461                        "Minor = %d, Patch = %d.\n", version +
462                        strlen(prefix), major, minor, \
463                        patch);
464         sprintf(vstring, "%d.%d.%d", major, minor, patch);
465
466 #ifndef TESTING
467         /*
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
472          * version level.
473          */
474         if ( uname(&utsname) < 0 )
475         {
476                 Syslog(LOG_ERR, "Cannot get kernel version information.");
477                 return(0);
478         }
479         if ( debugging )
480                 fprintf(stderr, "Comparing kernel %s with symbol table %s.\n",\
481                        utsname.release, vstring);
482
483         if ( sscanf (utsname.release, "%d.%d.%d", &major, &minor, &patch) < 3 )
484         {
485                 Syslog(LOG_ERR, "Kernel send bogus release string `%s'.",
486                        utsname.release);
487                 return(0);
488         }
489
490         /* Compute the version code from data sent by the kernel */
491         kvnum = (major << 16) | (minor << 8) | patch;
492
493         /* Failure. */
494         if ( vnum != kvnum )
495                 return(-1);
496
497         /* Success. */
498 #endif
499         return(1);
500 }
501
502
503 /**************************************************************************
504  * Function:    CheckMapVersion
505  *
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
509  *              backend.
510  *
511  * Arguments:   (char *) fname
512  *
513  *                      fname:->        A pointer to the string which
514  *                                      references the system map file to
515  *                                      be used.
516  *
517  * Return:      int
518  *
519  *                     -1:->    The currently running kernel version does
520  *                              not match the version in the given file.
521  *
522  *                      0:->    No system map file or no version information.
523  *
524  *                      1:->    The executing kernel is of the same version
525  *                              as the version of the map file.
526  **************************************************************************/
527
528 static int CheckMapVersion(fname)
529
530         char *fname;
531         
532
533 {
534         int     version;
535         FILE    *sym_file;
536         auto unsigned long int address;
537         auto char       type,
538                         sym[512];
539
540         if ( (sym_file = fopen(fname, "r")) != (FILE *) 0 ) {
541                 /*
542                  * At this point a map file was successfully opened.  We
543                  * now need to search this file and look for version
544                  * information.
545                  */
546                 Syslog(LOG_INFO, "Inspecting %s", fname);
547
548                 version = 0;
549                 while ( !feof(sym_file) && (version == 0) )
550                 {
551                         if ( fscanf(sym_file, "%lx %c %511s\n", &address, \
552                                     &type, sym) != 3 )
553                         {
554                                 Syslog(LOG_ERR, "Error in symbol table input (#2).");
555                                 fclose(sym_file);
556                                 return(0);
557                         }
558                         if ( VERBOSE_DEBUGGING && debugging )
559                                 fprintf(stderr, "Address: %lx, Type: %c, " \
560                                     "Symbol: %s\n", address, type, sym);
561
562                         version = CheckVersion(sym);
563                 }
564                 fclose(sym_file);
565
566                 switch ( version )
567                 {
568                     case -1:
569                         Syslog(LOG_ERR, "Symbol table has incorrect " \
570                                 "version number.\n");
571                         break;
572                         
573                     case 0:
574                         if ( debugging )
575                                 fprintf(stderr, "No version information " \
576                                         "found.\n");
577                         break;
578                     case 1:
579                         if ( debugging )
580                                 fprintf(stderr, "Found table with " \
581                                         "matching version number.\n");
582                         break;
583                 }
584
585                 return(version);
586         }
587
588         return(0);
589 }
590
591         
592 /**************************************************************************
593  * Function:    AddSymbol
594  *
595  * Purpose:     This function is responsible for adding a symbol name
596  *              and its address to the symbol table.
597  *
598  * Arguments:   (unsigned long) address, (char *) symbol
599  *
600  * Return:      int
601  *
602  *              A boolean value is assumed.  True if the addition is
603  *              successful.  False if not.
604  **************************************************************************/
605
606 static int AddSymbol(address, symbol)
607
608         unsigned long address;
609         
610         char *symbol;
611         
612 {
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 )
617                 return(0);
618
619         /* Then the space for the symbol. */
620         sym_array[num_syms].name = (char *) malloc(strlen(symbol)*sizeof(char)\
621                                                    + 1);
622         if ( sym_array[num_syms].name == (char *) 0 )
623                 return(0);
624         
625         sym_array[num_syms].value = address;
626         strcpy(sym_array[num_syms].name, symbol);
627         ++num_syms;
628         return(1);
629 }
630
631
632 /**************************************************************************
633  * Function:    LookupSymbol
634  *
635  * Purpose:     Find the symbol which is related to the given kernel
636  *              address.
637  *
638  * Arguments:   (long int) value, (struct symbol *) sym
639  *
640  *              value:->        The address to be located.
641  * 
642  *              sym:->          A pointer to a structure which will be
643  *                              loaded with the symbol's parameters.
644  *
645  * Return:      (char *)
646  *
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  **************************************************************************/
651
652 char * LookupSymbol(value, sym)
653
654         unsigned long value;
655
656         struct symbol *sym;
657         
658 {
659         auto int lp;
660         
661         auto char *last;
662         auto char *name;
663
664         struct symbol ksym, msym;
665
666         if (!sym_array)
667                 return((char *) 0);
668
669         last = sym_array[0].name;
670         ksym.offset = 0;
671         ksym.size = 0;
672         if ( value < sym_array[0].value )
673                 return((char *) 0);
674         
675         for(lp = 0; lp <= num_syms; ++lp)
676         {
677                 if ( sym_array[lp].value > value )
678                 {               
679                         ksym.offset = value - sym_array[lp-1].value;
680                         ksym.size = sym_array[lp].value - \
681                                 sym_array[lp-1].value;
682                         break;
683                 }
684                 last = sym_array[lp].name;
685         }
686
687         name = LookupModuleSymbol(value, &msym);
688
689         if ( ksym.offset == 0 && msym.offset == 0 )
690         {
691                 return((char *) 0);
692         }
693
694         if ( ksym.offset == 0 || msym.offset < 0 ||
695              (ksym.offset > 0 && ksym.offset < msym.offset) )
696         {
697                 sym->offset = ksym.offset;
698                 sym->size = ksym.size;
699                 return(last);
700         }
701         else
702         {
703                 sym->offset = msym.offset;
704                 sym->size = msym.size;
705                 return(name);
706         }
707
708
709         return((char *) 0);
710 }
711
712
713 /**************************************************************************
714  * Function:    FreeSymbols
715  *
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.
720  *
721  * Arguments:  void
722  *
723  * Return:      void
724  **************************************************************************/
725
726 static void FreeSymbols()
727
728 {
729         auto int lp;
730
731         /* Free each piece of memory allocated for symbol names. */
732         for(lp = 0; lp < num_syms; ++lp)
733                 free(sym_array[lp].name);
734
735         /* Whack the entire array and initialize everything. */
736         free(sym_array);
737         sym_array = (struct sym_table *) 0;
738         num_syms = 0;
739
740         return;
741 }
742
743
744 /**************************************************************************
745  * Function:    LogExpanded
746  *
747  * Purpose:     This function is responsible for logging a kernel message
748  *              line after all potential numeric kernel addresses have
749  *              been resolved symolically.
750  *
751  * Arguments:   (char *) line, (char *) el
752  *
753  *              line:-> A pointer to the buffer containing the kernel
754  *                      message to be expanded and logged.
755  *
756  *              el:->   A pointer to the buffer into which the expanded
757  *                      kernel line will be written.
758  *
759  * Return:      void
760  **************************************************************************/
761
762 extern char * ExpandKadds(line, el)
763
764         char *line;
765
766         char *el;
767         
768 {
769         auto char       dlm,
770                         *kp,
771                         *sl = line,
772                         *elp = el,
773                         *symbol;
774
775         char num[15];
776         auto unsigned long int value;
777
778         auto struct symbol sym;
779
780         sym.offset = 0;
781         sym.size = 0;
782
783         /*
784          * This is as handy a place to put this as anyplace.
785          *
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
789          * needed.
790          *
791          * To accomplish this we look for the Oops string and use its
792          * presence as a signal to load the module symbols.
793          *
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
798          * open for patches.
799          */
800         if ( i_am_paranoid &&
801              (strstr(line, "Oops:") != (char *) 0) && !InitMsyms() )
802                 Syslog(LOG_WARNING, "Cannot load kernel module symbols.\n");
803         
804
805         /*
806          * Early return if there do not appear to be any kernel
807          * messages in this line.
808          */
809         if ( (num_syms == 0) ||
810              (kp = strstr(line, "[<")) == (char *) 0 )
811         {
812                 strcpy(el, line);
813                 return(el);
814         }
815
816         /* Loop through and expand all kernel messages. */
817         do
818         {
819                 while ( sl < kp+1 )
820                         *elp++ = *sl++;
821
822                 /* Now poised at a kernel delimiter. */
823                 if ( (kp = strstr(sl, ">]")) == (char *) 0 )
824                 {
825                         strcpy(el, sl);
826                         return(el);
827                 }
828                 dlm = *kp;
829                 strncpy(num,sl+1,kp-sl-1);
830                 num[kp-sl-1] = '\0';
831                 value = strtoul(num, (char **) 0, 16);
832                 if ( (symbol = LookupSymbol(value, &sym)) == (char *) 0 )
833                         symbol = sl;
834                         
835                 strcat(elp, symbol);
836                 elp += strlen(symbol);
837                 if ( debugging )
838                         fprintf(stderr, "Symbol: %s = %lx = %s, %x/%d\n", \
839                                 sl+1, value, \
840                                 (sym.size==0) ? symbol+1 : symbol, \
841                                 sym.offset, sym.size);
842
843                 value = 2;
844                 if ( sym.size != 0 )
845                 {
846                         --value;
847                         ++kp;
848                         elp += sprintf(elp, "+0x%x/0x%02x", sym.offset, sym.size);
849                 }
850                 strncat(elp, kp, value);
851                 elp += value;
852                 sl = kp + value;
853                 if ( (kp = strstr(sl, "[<")) == (char *) 0 )
854                         strcat(elp, sl);
855         }
856         while ( kp != (char *) 0);
857                 
858         if ( debugging )
859                 fprintf(stderr, "Expanded line: %s\n", el);
860         return(el);
861 }
862
863
864 /**************************************************************************
865  * Function:    SetParanoiaLevel
866  *
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.
870  *
871  * Arguments:   (int) level
872  *
873  *              level:->        The amount of paranoia which is to be
874  *                              present when resolving kernel exceptions.
875  * Return:      void
876  **************************************************************************/
877
878 extern void SetParanoiaLevel(level)
879
880         int level;
881
882 {
883         i_am_paranoid = level;
884         return;
885 }
886
887
888 /*
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.
893  */
894 #if defined(TEST)
895
896 #include <stdarg.h>
897
898 extern int main(int, char **);
899
900
901 extern int main(int argc, char *argv[])
902 {
903         auto char line[1024], eline[2048];
904
905         debugging = 1;
906         
907         
908         if ( !InitKsyms((char *) 0) )
909         {
910                 fputs("ksym: Error loading system map.\n", stderr);
911                 return(1);
912         }
913
914         while ( !feof(stdin) )
915         {
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);
921         }
922         
923
924         return(0);
925 }
926
927 extern void Syslog(int priority, char *fmt, ...)
928
929 {
930         va_list ap;
931
932         va_start(ap, fmt);
933         fprintf(stdout, "Pr: %d, ", priority);
934         vfprintf(stdout, fmt, ap);
935         va_end(ap);
936         fputc('\n', stdout);
937
938         return;
939 }
940 #endif
941
942 /*
943  * Local variables:
944  *  c-indent-level: 8
945  *  c-basic-offset: 8
946  *  tab-width: 8
947  * End:
948  */