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

kl_new_module.c

/*
 * $Id: kl_new_module.c,v 1.1 2004/12/21 23:26:20 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
 * Modified for supporting new module mechanism in 2.5.x Linux Kernel
 *  by Fleming Feng (fleming.feng@intel.com)
 *
 * Copyright (C) 1999 - 2002 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>
 * Copyright (C) 2003, Intel China Software Lab, Intel Corporation.
 *
 * 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 <linux/stddef.h>

#define MODULE_NAME_LEN (64 - sizeof(unsigned long))
#define _DBG_COMPONENT KL_DBGCOMP_MODULE

extern kl_modinfo_t *ksym_modinfo;
static syment_t * sym_module = NULL;

/*
 * get name from struct module
 */
int 
kl_get_modname_2_6(char **name, void *module)
{
      char modname[MODULE_NAME_LEN];

      memcpy(modname, (char*)module + kl_member_offset("module","name"), MODULE_NAME_LEN);

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

/* 
 * kl_get_module_2_6_kaddr()
 * 
 * get the address of next struct module from address of module list. 
 * Do remember to check if the list is empty. If it is, then 
 * return NULL. 
 */
static kaddr_t 
kl_get_next_module_kaddr_2_6(kaddr_t list_addr)
{
      void *list = NULL;
      kaddr_t next;
      size_t list_head_size = 0;

      kl_get_structure(list_addr, "list_head", &list_head_size, &list);
      if(!list)
            return ((kaddr_t)NULL);
      next = kl_kaddr(list, "list_head", "next");
      if(next == sym_module->s_addr){
            /* endof the module list! */
            kl_free_block(list);
            return ((kaddr_t)NULL);
      }
      else {
            next = next - kl_member_offset("module","list");
            kl_free_block(list);
            return next;
      }
}

/*
 * kl_get_module_2_6()
 *
 * 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()
 * This function has to be called with 'vaddr' = NULL the first time!
 * rc:0 - success, 1 - Error, 2 - no module found
 */
int
kl_get_module_2_6(char *modname, kaddr_t *vaddr, void **ptr_module)
{
      int mod_found = 0;
      kaddr_t addr_mod = 0;
      size_t size=0;

      kl_reset_error();

      if(!*vaddr) {
            addr_mod = kl_get_next_module_kaddr_2_6(sym_module->s_addr);
      } else {
            addr_mod = *vaddr;
      }

      if(!addr_mod)
            return(2); /* no module found */
      
      if (KL_ERROR) {
            return(1);
      }

      if(modname) {
            while(addr_mod){
                  if(kl_get_structure(addr_mod, "module", 
                                  &size, ptr_module)){
                        return(1);
                  }
                  
                  if(!strcmp(modname, ((char*)*ptr_module) + kl_member_offset("module","name"))){
                        mod_found = 1;
                        break;
                  }
                  addr_mod = kl_get_next_module_kaddr_2_6(addr_mod + kl_member_offset("module","list"));
            }
      } else {
            if(kl_get_structure(addr_mod, "module",
                            &size, ptr_module)){
                  return(1);
            }
            mod_found = 1;
      }

      if(!mod_found){
            return(1);
      }
      *vaddr= addr_mod;
      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
 */
static int
load_ksyms_2_6(int flag)
{
      int i, dot_cnt = 0;
      int nsyms = 0;
      size_t 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;
      int rc;
      
      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)))){
            KL_ERROR = KLE_NO_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){
            free(ml);
            return(1);
      }

      kl_free_modinfo(&ksym_modinfo);
      do{
            rc = kl_get_module_2_6(NULL, &next_module, &dump_module);
            if(rc){
                  free(stp);
                  if(rc == 2){
                         /* No module found */
                        rc = 0;
                        goto out;
                  } else {
                        rc = 1;
                        goto out;
                  }
            }

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

            nsyms=KL_UINT(dump_module, "module", "num_syms");
            syms = kl_kaddr(dump_module, "module","syms");
            /* XXX maybe implement also gpl_syms in the future */

            for(i=0; i<nsyms; i++){
                  if(kl_get_structure(syms, "kernel_symbol",&size_modsym, &dump_modsym)){
                        free(stp);
                        rc = 1;
                        goto out;
                  }
                  value=KL_UINT(dump_modsym, "kernel_symbol", "value");
                  name=kl_kaddr(dump_modsym, "kernel_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(".");
                  }

                  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_get_next_module_kaddr_2_6(next_module + kl_member_offset("module","list"));
      } 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;
      rc = 0;

out:
      if(dump_name)
            kl_free_block(dump_name);
      if(dump_module)
            kl_free_block(dump_module);
      if(dump_modsym)
            kl_free_block(dump_modsym);
      return(rc);
}


/*
 * load ksyms from dump into a KLIB symbol table
 * This function is used, if the kernel is compiled with CONFIG_KALLSYMS
 * Note: mapfile is NULL for table of ksyms
 * rc: 0 - success, 1 - failed, 2 - maplist for ksyms already loaded
 */
static int
load_kallsyms_2_6(int flag)
{
      int i, dot_cnt = 0;
      int nsyms = 0;
      void *dump_module = NULL;
      void* elf_sym = NULL;
      char *dump_name = NULL;
      kaddr_t next_module = 0;
      kaddr_t value;
      kaddr_t name;
      kaddr_t mod_init_start, mod_init_size;
      kaddr_t mod_core_start, mod_core_size;
      int sym_type;
      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;
      int rc;
      char* elf_sym_type;
      size_t elf_sym_size = 0;

      if(KL_PTRSZ == 32){
            elf_sym_type = "elf32_sym";
      } else if(KL_PTRSZ == 64){
            elf_sym_type = "elf64_sym";
      } else {
            rc = 1;
            goto out;
      }

      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)))){
            KL_ERROR = KLE_NO_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){
            free(ml);
            return(1);
      }

      kl_free_modinfo(&ksym_modinfo);
      do{
            kaddr_t sym_entry,str_tab;

            rc = kl_get_module_2_6(NULL, &next_module, &dump_module);
            if(rc){
                  free(stp);
                  if(rc == 2){
                         /* No module found */
                        rc = 0;
                        goto out;
                  } else {
                        rc = 1;
                        goto out;
                  }
            }

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


            nsyms = kl_kaddr(dump_module, "module","num_symtab");
            sym_entry = kl_kaddr(dump_module,"module","symtab");
            str_tab   = kl_kaddr(dump_module,"module","strtab");
            mod_init_start = kl_kaddr(dump_module,"module","module_init");
            mod_init_size = kl_kaddr(dump_module,"module","init_text_size");
            mod_core_start = kl_kaddr(dump_module,"module","module_core");
            mod_core_size = kl_kaddr(dump_module,"module","core_text_size");

            for(i=0; i<nsyms; i++){
                  if(kl_get_structure(sym_entry,elf_sym_type,&elf_sym_size,&elf_sym)){
                        free(stp);
                        rc = 1;
                        goto out;
                  }
                  value=kl_kaddr(elf_sym,elf_sym_type,"st_value");
                  name =KL_UINT(elf_sym,elf_sym_type,"st_name");
                  memset(dump_name, 0, KL_SYMBOL_NAME_LEN);
                  GET_BLOCK(str_tab + name, KL_SYMBOL_NAME_LEN, dump_name);
                  // fprintf(stderr,"- %s\t%x\n",dump_name,value);
                  if (!flag && ((dot_cnt++ % 1000) == 0)) {
                        KL_MSG(".");
                  }

                  if((*dump_name) && (value != 0)){
                        sym_type = SYM_KSYM;
                        if ((value >= mod_init_start &&
                            value < mod_init_start + mod_init_size) ||
                            (value >= mod_core_start &&
                            value < mod_core_start + mod_core_size)) {
                            sym_type = SYM_KSYM_TEXT;
                        }
                        if (cur_syment) {
                              cur_syment->s_next = kl_alloc_syment(value,
                                                      sym_type,
                                                      dump_name);
                              cur_syment = cur_syment->s_next;
                        } else {
                              syment_list = kl_alloc_syment(value, 
                                                      sym_type,
                                                dump_name);
                              cur_syment = syment_list;
                        } 
                        stp->symcnt++;
                  }

                  sym_entry += elf_sym_size;
            }
            kl_complete_modinfo(modinfo);
            prev_modinfo = modinfo;
            next_module = kl_get_next_module_kaddr_2_6(next_module + kl_member_offset("module","list"));
      } 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;
      rc = 0;

out:
      if(dump_name)
            kl_free_block(dump_name);
      if(dump_module)
            kl_free_block(dump_module);
      if(elf_sym)
            kl_free_block(elf_sym);
      return(rc);
}

/*
 * 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_2_6(int flag)
{
      if(!sym_module){
            if(!(sym_module = kl_lkup_symname("modules"))){
                  KL_ERROR = KLE_NO_MODULE_LIST;
                  return(1);
            }           
      }
      if(kl_is_member("module","symtab"))
            return load_kallsyms_2_6(flag);
      else
            return load_ksyms_2_6(flag);
}

Generated by  Doxygen 1.6.0   Back to index