Logo Search packages:      
Sourcecode: lcrash version File versions  Download package

klib.c

/*
 * $Id: klib.c,v 1.4 2005/03/02 21:38:01 tjm Exp $
 *
 * This file is part of libklib.
 * A library which provides access to Linux system kernel dumps.
 *
 * Created by Silicon Graphics, Inc.
 * Contributions by IBM, NEC, and others
 *
 * Copyright (C) 1999 - 2005 Silicon Graphics, Inc. 
 *    All rights reserved.
 * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation
 * Copyright 2000 Junichi Nomura, NEC Solutions <j-nomura@ce.jp.nec.com>
 *
 * This code is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version. See the file COPYING for more
 * information.
 */

#include <klib.h>
#include <sys/stat.h>
#include <unistd.h>

/* Some global variables
 */
klib_t   *KLP = (klib_t *)NULL;
k_error_t klib_error = 0;

/*
 * status_msg()
 */
void
status_msg(int flags, const char *fmt, ...)
{
      va_list args;

      if (!(flags & KL_SILENT_FLG)) {
            va_start(args, fmt);
            fprintf(KL_ERRORFP, fmt, args);
            va_end(args);
      }
}

/*
 * kl_reset_error()
 */
void
kl_reset_error(void)
{
      klib_error = 0;
}

/*
 * kl_print_error()
 */
void
kl_print_error(void)
{
      int ecode;

      ecode = klib_error & 0xffffffff;
      switch(ecode) {

            /** General klib error codes
             **/
            case KLE_NO_MEMORY:
                  fprintf(KL_ERRORFP, "insufficient memory");
                  break;
            case KLE_OPEN_ERROR:
                  fprintf(KL_ERRORFP, 
                        "unable to open file: %s", strerror(errno));
                  break;
            case KLE_ZERO_BLOCK:
                  fprintf(KL_ERRORFP, 
                        "tried to allocate a zero-sized block");
                  break;
            case KLE_INVALID_VALUE:
                  fprintf(KL_ERRORFP, "invalid input value");
                  break;
            case KLE_NULL_BUFF:
                  fprintf(KL_ERRORFP, "NULL buffer pointer");
                  break;
            case KLE_ZERO_SIZE:
                  fprintf(KL_ERRORFP, "zero sized block requested");
                  break;
            case KLE_ACTIVE:
                  fprintf(KL_ERRORFP, 
                        "operation not supported on a live system");
                  break;
            case KLE_UNSUPPORTED_ARCH:
                  fprintf(KL_ERRORFP, 
                        "unsupported architecture");
                  break;
            case KLE_MISC_ERROR:
                  fprintf(KL_ERRORFP, "KLIB error");  
                  break;
            case KLE_NOT_SUPPORTED:
                  fprintf(KL_ERRORFP, "operation not supported"); 
                  break;
            case KLE_UNKNOWN_ERROR:
                  fprintf(KL_ERRORFP, "unknown error");     
                  break;

            /** memory error codes
             **/
            case KLE_BAD_MAP_FILE:
                  fprintf(KL_ERRORFP, "bad map file");      
                  break;
            case KLE_BAD_DUMP:
                  fprintf(KL_ERRORFP, "bad dump file");     
                  break;
            case KLE_BAD_DUMPTYPE:
                  fprintf(KL_ERRORFP, "bad dumptype");      
                  break;
            case KLE_INVALID_LSEEK:
                  fprintf(KL_ERRORFP, "lseek error"); 
                  break;
            case KLE_INVALID_READ:
                  fprintf(KL_ERRORFP, "not found in dump file");  
                  break;
            case KLE_BAD_KERNINFO:
                  fprintf(KL_ERRORFP, "bad kerninfo struct");     
                  break;
            case KLE_INVALID_PADDR:
                  fprintf(KL_ERRORFP, "invalid physical address");      
                  break;
            case KLE_INVALID_VADDR:
                  fprintf(KL_ERRORFP, "invalid virtual address"); 
                  break;
            case KLE_INVALID_VADDR_ALIGN:
                  fprintf(KL_ERRORFP, "invalid vaddr alignment"); 
                  break;
            case KLE_INVALID_MAPPING:
                  fprintf(KL_ERRORFP, "invalid address mapping"); 
                  break;
            case KLE_PAGE_NOT_PRESENT:
                  fprintf(KL_ERRORFP, "page not present");  
                  break;
            case KLE_BAD_ELF_FILE:
                  fprintf(KL_ERRORFP, "bad elf file");
                  break;
            case KLE_ARCHIVE_FILE:
                  fprintf(KL_ERRORFP, "archive file");
                  break;
            case KLE_MAP_FILE_PRESENT:
                  fprintf(KL_ERRORFP, "map file present");
                  break;
            case KLE_BAD_MAP_FILENAME:
                  fprintf(KL_ERRORFP, "bad map filename");
                  break;
            case KLE_BAD_DUMP_FILENAME:
                  fprintf(KL_ERRORFP, "bad dump filename");
                  break;
            case KLE_BAD_NAMELIST_FILE:
                  fprintf(KL_ERRORFP, "bad namelist file");
                  break;
            case KLE_BAD_NAMELIST_FILENAME:
                  fprintf(KL_ERRORFP, "bad namelist filename");
                  break;

            /** symbol error codes
             **/
            case KLE_NO_SYMTAB:
                  fprintf(KL_ERRORFP, "no symtab");   
                  break;
            case KLE_NO_SYMBOLS:
                  fprintf(KL_ERRORFP, "no symbol information");   
                  break;
            case KLE_NO_MODULE_LIST:
                  fprintf(KL_ERRORFP, "kernel without module support"); 
                  break;

            /** kernel data error codes
             **/
            case KLE_INVALID_KERNELSTACK:
                  fprintf(KL_ERRORFP, "invalid kernel stack");    
                  break;
            case KLE_INVALID_STRUCT_SIZE:
                  fprintf(KL_ERRORFP, "invalid struct size");     
                  break;
            case KLE_BEFORE_RAM_OFFSET:
                  fprintf(KL_ERRORFP, 
                        "physical address proceeds start of RAM");
                  break;
            case KLE_AFTER_MAXPFN:
                  fprintf(KL_ERRORFP, "PFN exceeds maximum PFN");
                  break;
            case KLE_AFTER_PHYSMEM:
                  fprintf(KL_ERRORFP, "address exceeds physical memory");
                  break;
            case KLE_AFTER_MAXMEM:
                  fprintf(KL_ERRORFP, 
                        "address exceeds maximum physical address");
                  break;
            case KLE_PHYSMEM_NOT_INSTALLED:
                  fprintf(KL_ERRORFP, "physical memory not installed");
                  break;
            case KLE_NO_DEFTASK:
                  fprintf(KL_ERRORFP, "default task not set");
                  break;
            case KLE_PID_NOT_FOUND:
                  fprintf(KL_ERRORFP, "PID not found");
                  break;
            case KLE_DEFTASK_NOT_ON_CPU:
                  fprintf(KL_ERRORFP, 
                        "default task not running on a cpu");
                  break;
            case KLE_NO_CURCPU:
                  fprintf(KL_ERRORFP, 
                        "current cpu could not be determined");
                  break;

            case KLE_KERNEL_MAGIC_MISMATCH:
                  fprintf(KL_ERRORFP, "kernel_magic mismatch "
                        "of map and memory image");
                  break;

            case KLE_INVALID_DUMP_HEADER:
                  fprintf(KL_ERRORFP, "invalid dump header in dump");
                  break;

            case KLE_DUMP_INDEX_CREATION:
                  fprintf(KL_ERRORFP, "cannot create index file");
                  break;

            case KLE_DUMP_HEADER_ONLY:
                  fprintf(KL_ERRORFP, "dump only has a dump header");
                  break;

            case KLE_NO_END_SYMBOL:
                  fprintf(KL_ERRORFP, "no _end symbol in kernel");
                  break;

            case KLE_NO_CPU:
                  fprintf(KL_ERRORFP, "CPU not installed");
                  break;

            default:
                  break;
      }     
      fprintf(KL_ERRORFP, "\n");
}

/*
 * kl_alloc_klib()
 */
static klib_t *
kl_alloc_klib(void)
{
      klib_t *klp;

      if (!(klp = (klib_t *)calloc(1, sizeof(klib_t)))) {
            KL_ERROR = KLE_NO_MEMORY;     
      }
      return(klp);
}

/*
 * kl_free_klib()
 */
void
kl_free_klib(klib_t *klp)
{
      free(klp->host);
      if (klp->dump) {
            kl_free_dumpinfo(klp->dump);
      }
      if (STP) {
            kl_free_syminfo(NULL); /* free complete list, STP points to */
      }
      if (klp->kerntypes){
            free(klp->kerntypes);
      }
      free(klp);
}

/*
 * _kl_find_dump_version()
 */
#define VV_SEARCH_PREFIX "__linux_compile_version_id__"
#define VV_BUF_SIZE 0x4000 
#define VV_END_ADDR 0x1000000 /* 16 MB */

static int _kl_find_dump_version(char* version)
{
      uint64_t end_addr,i,j;
      int rc = 1;
      char buf[VV_BUF_SIZE];

      version[0]=0;
      if(!KL_DUMP_HEADER){
            /* E.g live System */
            end_addr = VV_END_ADDR;
            kl_reset_error();
      } else {
            end_addr = MIN(KL_DUMP_HEADER->memory_size,VV_END_ADDR);
      }

      /* now grep in dump for version string */
      for(i = 0; i < end_addr; i+=(VV_BUF_SIZE-strlen(VV_SEARCH_PREFIX))){
            kl_readmem(i,VV_BUF_SIZE,buf);
            if(KL_ERROR){
                  kl_reset_error();
                  goto out;
            }
            for(j=0;j<(VV_BUF_SIZE-strlen(VV_SEARCH_PREFIX));j++){
                  if(strncmp(buf+j,VV_SEARCH_PREFIX,
                              strlen(VV_SEARCH_PREFIX)) == 0){
                        kl_readmem(i+j,VV_BUF_SIZE,buf);
                        strncpy(version,buf,
                              MIN(KL_SYMBOL_NAME_LEN,VV_BUF_SIZE));
                        rc = 0;
                        goto out;
                  }
            }
      }
out:
      return rc;
}

/*
 * _kl_check_inputfiles()
 */
static void _kl_check_inputfiles(void)
{
      syment_t *sq_head;
      int  candtcnt = 0, candt_maxlen;
      char version_str[KL_SYMBOL_NAME_LEN];
      char map_str[KL_SYMBOL_NAME_LEN] = VV_SEARCH_PREFIX;
      char dump_str[KL_SYMBOL_NAME_LEN];
      char type_str[KL_SYMBOL_NAME_LEN];
      uint64_t addr;

      bzero(dump_str,KL_SYMBOL_NAME_LEN);
      bzero(type_str,KL_SYMBOL_NAME_LEN);
      
      /* Get version symbol (System.map) */

      sq_head = kl_get_similar_name(VV_SEARCH_PREFIX, version_str, 
            &candtcnt, &candt_maxlen);        
      if (candtcnt == 0) {
            /* Probably old system.map with no special version symbol */
            kl_reset_error();
            return;
      } else if (candtcnt == 1) {

            /* get version string (dump) */

            strcat(map_str,version_str);
            addr=KL_VREAD_PTR(sq_head->s_addr);
            GET_BLOCK(addr, strlen(sq_head->s_name) + 1, dump_str);
            if(strncmp(dump_str,VV_SEARCH_PREFIX,
                        strlen(VV_SEARCH_PREFIX)) != 0){
                  _kl_find_dump_version(dump_str);
            }
            if(KL_ERROR){
                  kl_print_error();
                  return;
            }

            /* get version type (Kerntypes) */

            if(kl_get_first_similar_typedef(VV_SEARCH_PREFIX,type_str) == 0){
                  type_str[strlen(type_str)-2] = 0; /* remove '_t' */
            } else {
                  type_str[0] = 0;
            }

            if((strncmp(map_str, dump_str, strlen(map_str)) == 0) &&
               (strncmp(dump_str, type_str, strlen(map_str)) == 0)){
                  fprintf(kl_stdout,"\tVersion of map,dump and types:\n");
                  fprintf(kl_stdout,"\t\t%s\n",map_str + strlen(VV_SEARCH_PREFIX));
            } else {
                  fprintf(kl_stdout,"\tINFO: Version mismatch of map, dump and types:\n"); 
                  if(!map_str[0])
                        fprintf(kl_stdout,"\t\tmap  : No version info found!\n");
                  else
                        fprintf(kl_stdout,"\t\tmap  : %s\n",map_str + strlen(VV_SEARCH_PREFIX));
                  if(!dump_str[0])
                        fprintf(kl_stdout,"\t\tdump : No version info found!\n");
                  else
                        fprintf(kl_stdout,"\t\tdump : %s\n",dump_str + strlen(VV_SEARCH_PREFIX));
                  if(!type_str[0])
                        fprintf(kl_stdout,"\t\ttypes: No version info found!\n");
                  else
                        fprintf(kl_stdout,"\t\ttypes: %s\n",type_str + strlen(VV_SEARCH_PREFIX));
            }
      } else { /* candtcnt is 2 or more */
            fprintf(kl_stdout,"\tWARNING: Multiple version symbols found in System.map!\n");
      }
      fflush(kl_stdout);
}

#define DEFAULT_MAPFILE   "/boot/System.map"
#define DEFAULT_KERNTYPES "/boot/Kerntypes"
#define DEFAULT_DUMPFILE  "/dev/mem"
#define MAX_FILENAME      256    /* maximum argument size */

/* 
 * kl_get_live_filenames()
 */
int
kl_get_live_filenames(char *map, char *dump, char *namelist)
{
      struct utsname utsbuf;

      if (!map || !dump || !namelist) {
            KL_ERROR = KLE_NULL_POINTER;
            return(1);
      }

      if (uname(&utsbuf) < 0) {
            fprintf(KL_ERRORFP,"Could not determine release number of "
                  "the running kernel.\n");
            utsbuf.release[0] = '\0';
      }
      strcpy(map, DEFAULT_MAPFILE);
      if (utsbuf.release[0]) {
            strcat(map, "-");
            strcat(map, utsbuf.release);
      }       
      strcpy(namelist, DEFAULT_KERNTYPES);
      if (utsbuf.release[0]) {
            strcat(namelist, "-");
            strcat(namelist, utsbuf.release);
      }
      strcpy(dump, DEFAULT_DUMPFILE);
      return(0);
}

static int
_valid_filenames(char *map, char *dump, char *namelist)
{
      struct stat buf;

      /* First, make sure we actually have all three filesnames (paths)
       */
      if (!map || (map[0] == 0) || stat((const char *)map, &buf)) {
            KL_ERROR = KLE_BAD_MAP_FILENAME;
      } else if (!dump || (dump[0] == 0) || stat((const char *)dump, &buf)) {
            KL_ERROR = KLE_BAD_DUMP_FILENAME;
      } else if (!namelist || (namelist[0] == 0) || 
                  stat((const char *)namelist, &buf)) {
            KL_ERROR = KLE_BAD_NAMELIST_FILENAME;
      } 
      if (KL_ERROR) {
            return(0);
      }
      return(1);
}

/*
 * kl_init_klib()
 */
int
kl_init_klib(char *map_path, char *dump_path, char *namelist_path,
           int dump_arch, int flags)
{
      int filenames_alloced = 0;
      kaddr_t kval;
      syment_t *_end, *kernel_magic;
      char *map = NULL, *dump = NULL, *namelist = NULL;

      /* If all filenames are NULL, then set up for live
       * system analysis.
       */
      status_msg(flags, "\n\tVerifying filenames...");
      if (!map_path && !dump_path && !namelist_path) {
            map = calloc(MAX_FILENAME, 1);
            dump = calloc(MAX_FILENAME, 1);
            namelist = calloc(MAX_FILENAME, 1);
            filenames_alloced++;
            if (!map || !dump || !namelist) {
                  KL_ERROR = KLE_NULL_POINTER;
                  goto out;
            }
            if (kl_get_live_filenames(map, dump, namelist)) {
                  status_msg(flags, " Failed.\n");
                  goto out;
            }
      } else {
            map = map_path;
            dump = dump_path;
            namelist = namelist_path;
      }

      /* Make sure the filenames we have are valid
       */
      if (!_valid_filenames(map, dump, namelist)) {
            status_msg(flags, " Failed.\n");
            goto out;
      }
      status_msg(flags, " Done.\n");

      if (!(KLP = kl_alloc_klib())) {
            goto out;
      }

      /* Set up host architecture specific data
       */
      status_msg(flags, "\tInitializing host information...");
      if(kl_setup_hostinfo()) {
            kl_free_klib(KLP);
            status_msg(flags, " Failed.\n");
            goto out;
      } else {
            status_msg(flags, " Done.\n");
      }

      /* Setup dump access information
       */ 
      status_msg(flags, "\tInitializing dump information...");
      if(kl_setup_dumpinfo(map, dump, flags)) {
            kl_free_klib(KLP);
            status_msg(flags, " Failed.\n");
            goto out;
      } else {
            status_msg(flags, " Done.\n");
      }               
out:
      /* Free the filenames if they were allocated earlier...
       */
      if (filenames_alloced) {
            if (map) {
                  free(map);
            }
            if (dump) {
                  free(dump);
            }
            if (namelist) {
                  free(namelist);
            }
      }
      if (KL_ERROR) {
            return(1);
      }

      /* Open the dump
       */
      status_msg(flags, "\tOpening dump for access...");    
      if(kl_open_dump()) {
            kl_free_klib(KLP);
            status_msg(flags, " Failed.\n");
            return(1);
      } else {
            status_msg(flags, " Done.\n");
      }

      /* Read in the dump header (dumps only)
         */
      if (CORE_IS_DUMP) {
            status_msg(flags, "\tReading in the dump header...");
            if(kl_read_dump_header()) {
                  if (KL_ERROR == KLE_LIVE_SYSTEM) {
                        kl_reset_error();
                  } else {
                        kl_free_klib(KLP);
                        status_msg(flags, " Failed.\n");
                        goto out;
                  }
            } else {
                  status_msg(flags, " Done.\n");
            }

      }

      status_msg(flags, "\tDetermining dump architecture...");
      if (kl_set_dumparch(dump_arch)) {
            kl_free_klib(KLP);
            status_msg(flags, " Failed.\n");
            goto out;
      } else {
            status_msg(flags, " Done.\n");
      }

      /* Load type information from Kerntypes (or whatever object file
       * containing stabs or Dwarf info is passed).
       */                                                   
      status_msg(flags, "\tLoading kernel type information ...");
      if (namelist) {
            KLP->kerntypes = (char *)strdup(namelist);
            if (KLP->kerntypes == NULL) {
                  KL_ERROR = KLE_NO_MEMORY;
                  status_msg(flags, " Failed.\n");
                  return(1);
            }
      }
      if (!namelist || !KLP->kerntypes[0]) {
            KL_ERROR = KLE_BAD_NAMELIST_FILENAME;
                status_msg(flags, " Failed.\n");
                return(1);

      }
      if (kl_open_namelist(KLP->kerntypes, ST_DEFAULT)) {
            status_msg(flags, " Failed.\n");
            return(1);
      } else {
            status_msg(flags, " Done.\n");
      }

      /* Set up the rest of the dump access stuff
       */
      status_msg(flags, "\tSetting up for dump access...");
      if (kl_setup_dumpaccess(flags)) {
            kl_free_klib(KLP);
            status_msg(flags, " Failed.\n");
            return(1);
      } else {
            status_msg(flags, " Done.\n");
      }

      /* Load symbol information from System.map
       */
      status_msg(flags, "\tLoading kernel symbol information ...");
      if (kl_load_sym(map)) {
            kl_free_klib(KLP);
            status_msg(flags, " Failed.\n");
            return(1);
      } else {
            status_msg(flags, " Done.\n");
      }

      /* Initialize kernel virtual to physical memory translator.
       */
      status_msg(flags, "\tInitialize virtop address translator...");
      if (KL_INIT_VIRTOP()) {
            kl_free_klib(KLP);
            status_msg(flags, " Failed.\n");
            return(1);
      } else {
            status_msg(flags, " Done.\n");
      }
      status_msg(flags, "\tInitialize dump specific data ...");
      if(kl_set_dumpinfo(map, dump, dump_arch, flags)) {
            kl_free_klib(KLP);
            status_msg(flags, " Failed.\n");
            return(1);
      } else {
            status_msg(flags, " Done.\n");
      }

      /* get kernel release of linux system where dump was generated.
       * If system crashed early during boot the release info was
       * possibly not written to memory!
       */
      if (!(KLP->dump->mem.linux_release = kl_linux_release())) {
            kl_free_klib(KLP);
            return(1);
      }

      _kl_check_inputfiles(); 

      /* verify, that dump, map and kerntypes belong together
       */
      if(!(flags & (KL_FAILSAFE_FLG | KL_NOVERIFY_FLG))){
            /* Make sure the kernel_magic and _end flags match up, if
             * they exist.
             */
            if (!(kernel_magic = kl_lkup_symname("kernel_magic"))) {
                  /* ignore -- check isn't in this kernel */
                  KL_ERROR = 0;
            } else {
                  /* we can't ignore this failure -- it's fatal */
                  if (!(_end = kl_lkup_symname("_end"))) {
                        KL_ERROR = KLE_NO_END_SYMBOL;
                        kl_free_klib(KLP);
                        return(1);
                  }

                  /* if the value doesn't exist, we're screwed */
                  if (!(kval = kl_kaddr_to_ptr(kernel_magic->s_addr))) {
                        kl_free_klib(KLP);
                        return(1);
                  }

                  /* create an option to ignore this in the future */
                  if (_end->s_addr != kval) {
                        KL_ERROR = KLE_KERNEL_MAGIC_MISMATCH;
                        kl_free_klib(KLP);
                        return(1);
                  }
            }


            /* read kernel symbols from dump, if corresponding kernel was
             * built with module support
             */
            status_msg(flags, "\tLoading ksyms from dump ...");
            if(KL_LINUX_RELEASE < LINUX_2_6_0) {
                  if (kl_load_ksyms(0)){
                        fprintf(KL_ERRORFP, " Failed.\n");
                        if(KL_ERROR == KLE_NO_MODULE_LIST) {
                              status_msg(flags," \t\tReason: ");
                              kl_print_error();
                              kl_reset_error();
                        }
                  } else {
                        fprintf(KL_ERRORFP, " Done.\n");
                  }
            }
            else{
                  if (kl_load_ksyms_2_6(0)) {
                        status_msg(flags, " Failed.\n");
                        if(KL_ERROR == KLE_NO_MODULE_LIST) {
                              status_msg(flags," \t\tReason: ");
                              kl_print_error();
                              kl_reset_error();
                        }
                  } else {
                        status_msg(flags, " Done.\n");
                  }
            }
      } /* if(!(flags & (KL_FAILSAFE_FLG | KL_NOVERIFY_FLG))){ */
      fflush(KL_ERRORFP);
      return(0);
}

Generated by  Doxygen 1.6.0   Back to index