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

kl_module.c

/*
 * $Id: kl_module.c,v 1.2 2005/02/23 01:09:12 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>

#define _DBG_COMPONENT KL_DBGCOMP_MODULE

//static kl_modinfo_t *ksym_modinfo = NULL;
kl_modinfo_t *ksym_modinfo = NULL;

int kl_get_modname_2_6(char **, void *);

/*
 * get name from struct module
 */
int
kl_get_modname(char **name, void *module)
{
      void *dump_page;
      kaddr_t name_addr;

      dump_page = kl_alloc_block(KL_PAGE_SIZE, K_TEMP);
      if (dump_page) {
            name_addr = kl_kaddr(module, "module", "name");
            GET_BLOCK(name_addr, KL_PAGE_SIZE, dump_page);
            if((KL_ERROR == KLE_INVALID_MAPPING) ||
               (KL_ERROR == KLE_PAGE_NOT_PRESENT)){
                  kl_trace1(0, "Ignoring PAGE_NOT_PRESENT "
                          "and INVALID_MAPPING errors.\n");
                  kl_reset_error();
            }
      }

      if(*(char*) dump_page == '\000') {
            *name = NULL;
      } else {
            *name = strdup(dump_page);
      }
      kl_free_block(dump_page);
      return(0);
}

/*
 * kl_get_module()
 *
 * reads struct module from dump, returns either (in the given priority)
 *    - struct for module named modname (modname!=NULL)
 *    - struct for module where vaddr points to (modname==NULL, vaddr!=0)
 *    - struct for first module in module_list (modname==NULL, vaddr==0)
 * function allocates memory if *ptr_module==NULL,
 * this memory has to be released in the calling function using kl_free_block()
 */
int
kl_get_module(char *modname, kaddr_t *vaddr, void **ptr_module)
{
      syment_t *sym_module_list = NULL;
      void *dump_page = NULL;
      int mod_found = 0;
      kaddr_t dump_modname = 0;
      kaddr_t addr_mod = 0;
      size_t size=0;

      kl_reset_error();


      if(!*vaddr) {
            if(!(sym_module_list = kl_lkup_symname("module_list"))){
                  KL_ERROR = KLE_NO_MODULE_LIST;
                  return(1);
            }
            addr_mod = KL_VREAD_PTR(sym_module_list->s_addr);
      } else {
            addr_mod = *vaddr;
      }

      dump_page = kl_alloc_block(KL_PAGE_SIZE, K_TEMP);
      if (KL_ERROR) {
            return(1);
      }

      if(modname){
            if(!strcmp(modname, KL_KERNEL_MODULE)){
                  sym_module_list = kl_lkup_symname(KL_KERNEL_MODULE);
                  addr_mod = sym_module_list->s_addr;
                  modname=NULL;
            } else {
                  sym_module_list = kl_lkup_symname("module_list");
                  addr_mod = KL_VREAD_PTR(sym_module_list->s_addr);
            }
      }

      if(modname) {
            while(addr_mod){
                  if(kl_get_structure(addr_mod, "module", 
                                  &size, ptr_module)){
                        kl_free_block(dump_page);
                        return(1);
                  }
                  *vaddr= addr_mod;
                  dump_modname = kl_kaddr(*ptr_module, "module", "name");
                  GET_BLOCK(dump_modname, KL_PAGE_SIZE, dump_page);
                  if(!strcmp(modname, (char *)dump_page)) {
                        mod_found = 1;
                        break;
                  }
                  addr_mod = kl_kaddr(*ptr_module, "module", "next");
            }
      } else {
            if(kl_get_structure(addr_mod, "module",
                            &size, ptr_module)){
                  kl_free_block(dump_page);
                  return(1);
            }
            mod_found = 1;
            *vaddr= addr_mod;
      }

      kl_free_block(dump_page);
      if(!mod_found){
            return(1);
      }
      return(0);
}

/*
 *  create new modinfo for module dump_module in dump
 */
int
kl_new_modinfo(kl_modinfo_t **modinfo, void *dump_module)
{
#define __KSYM_SZ 512
      char      ksym[__KSYM_SZ];

      if(!(*modinfo = calloc(1, sizeof(kl_modinfo_t)))){
            /* XXX out of mem */
            return(1);
      }

      if(KL_LINUX_RELEASE < LINUX_2_6_0)
            kl_get_modname(&(*modinfo)->modname, dump_module);
      else
            kl_get_modname_2_6(&(*modinfo)->modname, dump_module);
      if((*modinfo)->modname){
            sprintf(ksym, "__insmod_%s_O", (*modinfo)->modname);
            (*modinfo)->ksym_object = strdup(ksym);
            sprintf(ksym, "__insmod_%s_S.text", (*modinfo)->modname);
            (*modinfo)->ksym_text_sec = strdup(ksym);
            sprintf(ksym, "__insmod_%s_S.data", (*modinfo)->modname);
            (*modinfo)->ksym_data_sec = strdup(ksym);
            sprintf(ksym, "__insmod_%s_S.rodata", (*modinfo)->modname);
            (*modinfo)->ksym_rodata_sec = strdup(ksym);
            sprintf(ksym, "__insmod_%s_S.bss", (*modinfo)->modname);
            (*modinfo)->ksym_bss_sec = strdup(ksym);
      }
      return(0);
}

/*
 * free kl_modinfo_t
 */
void
kl_free_modinfo(kl_modinfo_t **modinfo)
{
      if(*modinfo){
            free((*modinfo)->modname);
            free((*modinfo)->ksym_object);
            free((*modinfo)->ksym_text_sec);
            free((*modinfo)->ksym_data_sec);
            free((*modinfo)->ksym_rodata_sec);
            free((*modinfo)->ksym_bss_sec);
            free((*modinfo)->object_file);
            free(*modinfo);
            *modinfo = NULL;
      }
      return;
}

/*
 * search modinfo for given modname
 */
kl_modinfo_t *
kl_lkup_modinfo(char *modname)
{
      kl_modinfo_t *tmp;

      for(tmp=ksym_modinfo; tmp != NULL; tmp = tmp->next){
            if(!tmp->modname){
                  continue;
            }
            if(!(strcmp(tmp->modname, modname))){
                  return(tmp);
            }
      }
      return(NULL);
}

/*
 * compare ksym with "__insmod"  symbols for this module;
 * set some members in modinfo struct
 */
int
kl_set_modinfo(kaddr_t value, char *ksym, kl_modinfo_t *modinfo)
{
      if(!(modinfo->modname)){
            return(0);
      }
      if((!modinfo->header) &&
         (!(strncmp(ksym, modinfo->ksym_object,
                  strlen(modinfo->ksym_object))))){
            modinfo->header = value;
            free(modinfo->ksym_object);
            modinfo->ksym_object = strdup(ksym);
      }
      if((!modinfo->text_sec) &&
         (!(strncmp(ksym, modinfo->ksym_text_sec,
                  strlen(modinfo->ksym_text_sec))))){
            modinfo->text_sec = value;
            free(modinfo->ksym_text_sec);
            modinfo->ksym_text_sec = strdup(ksym);
      }
      if((!modinfo->data_sec) &&
         (!(strncmp(ksym, modinfo->ksym_data_sec,
                  strlen(modinfo->ksym_data_sec))))){
            modinfo->data_sec = value;
            free(modinfo->ksym_data_sec);
            modinfo->ksym_data_sec = strdup(ksym);
      }
      if((!modinfo->rodata_sec) &&
         (!(strncmp(ksym, modinfo->ksym_rodata_sec,
                  strlen(modinfo->ksym_rodata_sec))))){
            modinfo->rodata_sec = value;
            free(modinfo->ksym_rodata_sec);
            modinfo->ksym_rodata_sec = strdup(ksym);
      }
      if((!modinfo->bss_sec) &&
         (!(strncmp(ksym, modinfo->ksym_bss_sec,
                  strlen(modinfo->ksym_bss_sec))))){
            modinfo->bss_sec = value;
            free(modinfo->ksym_bss_sec);
            modinfo->ksym_bss_sec = strdup(ksym);
      }
      return(0);
}


/*
 * set remaining members in modinfo struct
 */
int
kl_complete_modinfo(kl_modinfo_t *modinfo)
{
      char *s1, *s2;

      if(!(modinfo->modname)){
            return(0);
      }

      if(!(modinfo->object_file = calloc(1, __KSYM_SZ))){
            /* out of memory */
            return(1);
      }
      
      /* search for beginning of object_name */
      if(!(s1 = strstr(modinfo->ksym_object, "_O/"))){
            /* substring not found */
            return(1);
      }
      s1 += 2; /* file name starts at '/' */
      /* search for suffix of object_name */
      if(!(s2 = strstr(modinfo->ksym_object, ".o_M"))){
            /* substring not found */
            return(1);
      }
      s2 += 2; /* file name ends after suffix */
      /* add 1 for terminating NULL */
      strncpy(modinfo->object_file, s1, s2-s1+1);
      modinfo->object_file[s2-s1] = 0;

      /* set last modification time and kernel version */
      sscanf(s2, "_M%"FMT64"x_V%u", &modinfo->mtime, &modinfo->version);
       
      /* set ELF section offsets
       */
      sscanf(modinfo->ksym_text_sec, "_L%"FMT64"u", &modinfo->text_len);
      sscanf(modinfo->ksym_data_sec, "_L%"FMT64"u", &modinfo->data_len);
      sscanf(modinfo->ksym_rodata_sec, "_L%"FMT64"u", &modinfo->rodata_len);
      sscanf(modinfo->ksym_bss_sec, "_L%"FMT64"u", &modinfo->bss_len);

      return(0);
}

/*
 * load ksyms from dump into a KLIB symbol table
 * Note: mapfile is NULL for table of ksyms
 * rc: 0 - success, 1 - failed, 2 - maplist for ksyms already loaded
 */
int
kl_load_ksyms(int flag)
{
      int i, dot_cnt = 0;
      size_t nsyms = 0, size_modsym = 0;
      void *dump_module = NULL, *dump_modsym = NULL, *dump_name = NULL;
      kaddr_t next_module = 0, syms = 0;
      kaddr_t value;
      kaddr_t name;
      symtab_t *stp;
      syment_t *cur_syment = NULL;
      syment_t *syment_list = NULL;
      maplist_t *ml;
      kl_modinfo_t* modinfo;
      kl_modinfo_t* prev_modinfo = NULL;
      
      for(ml=STP; ml!=NULL; ml=ml->next){
            if((ml->maplist_type == SYM_MAP_KSYM)){
                  kl_trace1(0, "Symbol table for ksyms "
                          "already loaded\n");
                  return(2);
            }
      }

      if( !(ml = (maplist_t*)calloc(1, sizeof(maplist_t)))){
            /* XXX out of memory */
            return(1);
      }

      stp = (symtab_t *)calloc(1, sizeof(symtab_t));
      
      dump_name = kl_alloc_block(KL_SYMBOL_NAME_LEN, K_TEMP);
      if(KL_ERROR){
            return(1);
      }

      kl_free_modinfo(&ksym_modinfo);
      do{
            if(kl_get_module(NULL, &next_module, &dump_module)){
                  kl_free_block(dump_name);
                  kl_free_block(dump_module);
                  kl_free_block(dump_modsym);
                  free(stp);
                  return(1);
            }

            kl_new_modinfo(&modinfo, dump_module);
            if(prev_modinfo){
                  prev_modinfo->next = modinfo;
            } else {
                  ksym_modinfo = modinfo;
            }

            syms=kl_kaddr(dump_module, "module", "syms");
            nsyms=KL_UINT(dump_module, "module", "nsyms");
            for(i=0; i<nsyms; i++){
                  if(kl_get_structure(syms, "module_symbol" ,
                                  &size_modsym, &dump_modsym)){
                        kl_free_block(dump_name);
                        kl_free_block(dump_module);
                        kl_free_block(dump_modsym);
                        free(stp);
                        return(1);
                  }
                  value=KL_UINT(dump_modsym, "module_symbol", "value");
                  name=kl_kaddr(dump_modsym, "module_symbol", "name");
                  memset(dump_name, 0, KL_SYMBOL_NAME_LEN);
                  GET_BLOCK(name, KL_SYMBOL_NAME_LEN, dump_name);
                  if (!flag && ((dot_cnt++ % 1000) == 0)) {
                        KL_MSG(".");
                  }

                  kl_set_modinfo(value, dump_name, modinfo);
                  if (cur_syment) {
                        cur_syment->s_next = kl_alloc_syment(value,
                                                  SYM_KSYM,
                                                  dump_name);
                        cur_syment = cur_syment->s_next;
                  } else {
                        syment_list = kl_alloc_syment(value, SYM_KSYM,
                                             dump_name);
                        cur_syment = syment_list;
                  }
                  stp->symcnt++;

                  syms += size_modsym;
            }
            kl_complete_modinfo(modinfo);
            prev_modinfo = modinfo;
            next_module = kl_kaddr(dump_module, "module", "next");
      } while(next_module);

      kl_insert_symbols(stp, syment_list);

      ml->syminfo=stp;
      ml->next=STP->next;
      ml->maplist_type=SYM_MAP_KSYM;
      STP->next=ml;

      kl_free_block(dump_name);
      kl_free_block(dump_module);
      kl_free_block(dump_modsym);
      return(0);
}

/*
 * automatically load type info and symbol info for kernel modules
 */
int
kl_autoload_module_info(char *moddir)
{
      kl_modinfo_t *modinfo;

      for(modinfo = ksym_modinfo; 
          (modinfo != NULL) && (modinfo->modname != NULL);
          modinfo = modinfo->next){
            if(kl_load_module_sym(modinfo->modname, NULL, moddir)){
                  if(KL_ERROR == KLE_MAP_FILE_PRESENT){
                        KL_MSG("Symbol table already loaded for "
                               "module %s\n", modinfo->modname);
                  } else {
                        return(1);
                  }
            }
      }
      return(0);
}

/*
 * load symbol info for one kernel module
 */
int
kl_load_module_sym(char *modname, char *file, char *moddir)
{
      kl_modinfo_t *modinfo;
      maplist_t *ml, *last_ml;

      for(ml=STP; ml!=NULL; ml=ml->next){
            if((ml->maplist_type == SYM_MAP_MODULE) &&
               !strcmp(modname, ml->modname)){
                  KL_ERROR = KLE_MAP_FILE_PRESENT;
                  return(1);
            }
            last_ml = ml;
      }

      if(!(modinfo = kl_lkup_modinfo(modname))){
            kl_trace1(0, "Could not find modinfo for module %s\n",
                    modname);
            return(1);
      }

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

      ml->modname=strdup(modname);
      ml->maplist_type=SYM_MAP_MODULE;

      if(file){
            /* load symbols from file */
            ml->mapfile = strdup(file);
      } else {
            /* load symbols from original object file ... */
            if(moddir){
                  /* ... but change beginning of path */
                  char *s1 = strstr(modinfo->object_file,
                                "/lib/modules");
                  if(s1){
                        ml->mapfile = calloc(1, strlen(moddir) + 
                                         strlen(modinfo->
                                              object_file));
                        strcat(ml->mapfile, moddir);
                        strcat(ml->mapfile + strlen(ml->mapfile),
                               modinfo->object_file +
                               strlen("/lib/modules"));
                  }
            } else {
                  ml->mapfile = strdup(modinfo->object_file);
            }
      }

      kl_trace1(0, "Loading symbols from file: %s.\n", ml->mapfile);
      if(kl_read_bfd_syminfo(ml)){
            /* now treat as ascii map file */
            if((KL_ERROR == KLE_ARCHIVE_FILE) ||
               (kl_read_syminfo(ml))){
                  kl_free_maplist(ml);
                  return(1);
            }
      }

      if(STP){
            last_ml->next=ml;
      }else{
            STP=ml;
      }
      return(0);
}

/*
 * unload KLIB symbol table of ksyms
 * rc: 0 - success, 1 - no maplist for ksyms
 */
int
kl_unload_ksyms(void)
{
      maplist_t *ml, *prev_ml;
      int rc=1;

      for(prev_ml = NULL, ml = STP; ml != NULL; prev_ml = ml, ml = ml->next){
            if(ml->maplist_type == SYM_MAP_KSYM){
                  if(prev_ml){
                        prev_ml->next=ml->next;
                        kl_free_maplist(ml);
                        ml=prev_ml;
                  } else {
                        STP=ml->next;
                        kl_free_maplist(ml);
                        ml=STP;
                  }
                  rc = 0;
                  break;
            }
      }
      return(rc);
}

/*
 * if modname == NULL delete all maplist_t structs assigned to any module
 * otherwise delete maplist_t assigned to specified module
 * rc: 0 - success, 1 - no symbol table for loaded for that/any module
 */
int
kl_unload_module_sym(char *modname)
{
      maplist_t *ml, *prev_ml;
      int rc = 1;

      for(prev_ml = NULL, ml = STP; ml != NULL; prev_ml = ml, ml = ml->next){
            if(ml->maplist_type == SYM_MAP_MODULE) {
                  /* free only specified maplist */
                  if(modname &&
                     strcmp(modname, ml->modname)){
                        continue;
                  }
                  if(prev_ml){
                        prev_ml->next=ml->next;
                        kl_free_maplist(ml);
                        ml=prev_ml;
                  } else {
                        STP=ml->next;
                        kl_free_maplist(ml);
                        ml=STP;
                  }
                  rc = 0;
            }
      }
      return(rc);
}

Generated by  Doxygen 1.6.0   Back to index