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

kl_dwarf.c

/*
 * $Id: kl_dwarf.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: Prashanth Tamraparni (prasht@in.ibm.com)
 * Contributions by SGI
 *
 * Copyright (C) 2004 International Business Machines Corp.
 * Copyright (C) 2004, 2005 Silicon Graphics, Inc. All rights reserved.
 *
 * 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 <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <elf.h>
#include <libelf.h>
#include <dwarf.h>
#include <kl_stringtab.h>
#include <kl_typeinfo.h>
#include <libdwarf.h>

#include <klib.h>
#include <bfd.h>
#include <stab.h>
#include <kl_debug.h>
#include <kl_stabs.h>

#include <kl_dwarfs.h>

#define ENUM_STR     "enum"
#define STRUCT_STR   "struct"
#define UNION_STR    "union" 
#define VOID_STR     "void"
#define CONST_STR    "const"
#define VOLATILE_STR "volatile"
#define PTR_STR      "*"

/* Dwarf typeinformation */
typedef struct dwarf_type_s {
        dbg_type_t  dwarf2;            /* This debug type should be first member */

        Dwarf_Die thisdie;              /* This die */
        Dwarf_Off dieoffset;            /* Offset of this die */
        Dwarf_Off targetoffset;         /* Offset of target die */
        int complete;                   /* 0    - processing incomplete
                                           1    - processing complete */
} dw_type_t;

/* Conventions
      dw_ptr        :   pointer to struct dw_type_t  
      sym_ptr   : pointer to struct dbg_sym_t 
      kltype_ptr:       pointer to struct kltype_t    
*/


/* The typecasts would work becuase the targets 
    are the first members of the structures */

/* Get the pointer to kltype from debug type */
#define KLTYPE_PTR(dw_ptr) ((kltype_t *)(dw_ptr))

/* Global variables */
Elf *g_dwarf_elf_fd;
int g_dwarf_size = 1;
Dwarf_Debug g_dwarf_dbg;
Dwarf_Error g_dwarf_err;

/* Forward declarations */
static void init_dwarf(char *);
static void read_dwarf_dies(void);
static void read_dwarf_dies_recurse(Dwarf_Die);
static void read_dwarf_dies_nonrecurse(Dwarf_Die);

static dw_type_t* alloc_memory(void);
void fill_die(dbg_sym_t *, dw_type_t *, Dwarf_Die);
 
static Dwarf_Die process_die(Dwarf_Die);
static void process_common_die(Dwarf_Die, kltype_t *);
void process_struct_members(kltype_t *);

static void find_realtypes(void);
static kltype_t * find_realtype_from_offset(Dwarf_Off);
void look_for_realtype(kltype_t *); 
static void do_final_operations(kltype_t *);

static void process_array(kltype_t *);
static void find_member_sizes(void);
static int setup_member_size(kltype_t *);
void not_processed (Dwarf_Off , Dwarf_Off); 

static Dwarf_Signed get_constant(Dwarf_Die, Dwarf_Half);
static Dwarf_Die get_ref_die(Dwarf_Die, Dwarf_Half);
static int decode_unsigned_leb128(unsigned char *, int *);
static int get_struct_member_offset(Dwarf_Die);

/* a kltype of 'void' type */
dw_type_t *void_dw_ptr;

/* To insert the symbol in a binary tree */
extern int dbg_insert_sym(dbg_sym_t *);

int dw_open_namelist(char *filename, int flags)
{
      init_dwarf(filename);
      return 0;
}

static void init_dwarf(char *filename)
{
      /* We shall open the object file and get 
          the elf file descriptor */
      
      int fd;
      int archive = 0;
      Elf_Cmd command;

      fd = open(filename, O_RDONLY);
      if(fd < 0) {
            perror("Open on 'namelist' file failed:");
            exit(2);
      }
      
      (void)elf_version(EV_NONE);
      if(elf_version(EV_CURRENT) == EV_NONE) {
            fprintf(stderr,"libelf outdated\n");
            exit(2);
      }
      
      command = ELF_C_READ;
      g_dwarf_elf_fd = elf_begin(fd, command, (Elf*)0);
      
      if(!g_dwarf_elf_fd) {
                fprintf(stderr,"Unable to get valid elf descriptor\n");
                exit(2);
      }

      if(elf_kind(g_dwarf_elf_fd) == ELF_K_AR) {
            archive = 1;
      }
      return;
}

int dw_setup_typeinfo(void)
{
      /* g_dwarf_elf_fd was setup in pass1.
       * Read in Abbrev & DIE content
      */
      int result = 0;
      result = dwarf_elf_init(g_dwarf_elf_fd, DW_DLC_READ, NULL, NULL,
                        &g_dwarf_dbg, &g_dwarf_err);

      if(result == DW_DLV_NO_ENTRY) {
            fprintf(stderr,"Dwarf information not present\n");
            exit(3);
      }
      if(result != DW_DLV_OK) {
            fprintf(stderr,"Error in reading DIE information\n");
            exit(3);
      }

      /* Read DIE information */
      read_dwarf_dies();
      return 1;
}

static void read_dwarf_dies(void)
{
      Dwarf_Die cu_die, child_die;
      Dwarf_Half ver, addrsize;
      Dwarf_Unsigned cu_len, abbroff, nexthdr;
      int result;

      /* Read the CU header and discard */
      result = dwarf_next_cu_header(g_dwarf_dbg, &cu_len, &ver, &abbroff,
                              &addrsize, &nexthdr, &g_dwarf_err);
      if(result != DW_DLV_OK) {
            fprintf(stderr,"Problem reading DIE %d\n",result);
            exit(4);
      }

      /* Setting second parameter to NULL gives us the die of the CU */
      result = dwarf_siblingof(g_dwarf_dbg, NULL, &cu_die, &g_dwarf_err);
      if(result != DW_DLV_OK) {
            fprintf(stderr,"Problem reading DIE %d\n",result);
            exit(4);
      }

      /* We are not interested in CU, read ahead to get the first
          actual die */
      result = dwarf_child(cu_die, &child_die, &g_dwarf_err);
      if(result != DW_DLV_OK) {
            fprintf(stderr,"Problem reading DIE\n");
            exit(4);
      }

      /* All set for a recursive read */
#ifdef NOT
      read_dwarf_dies_recurse(child_die);
#else
      read_dwarf_dies_nonrecurse(child_die);
#endif
      
      /* find the realtypes of dies */
      find_realtypes();

      /* find the size of struct/union members */
      find_member_sizes();
}

typedef struct die_stack_rec {
      struct die_stack_rec    *next;
      struct die_stack_rec    *prev;
      Dwarf_Die          die;
} die_stack_rec_t;

die_stack_rec_t *die_stack =  (die_stack_rec_t *)NULL;
die_stack_rec_t *die_rec_freelist = (die_stack_rec_t *)NULL;
int die_stack_cnt = 0;

void
push_die_stack(die_stack_rec_t **stack, die_stack_rec_t *rec)
{
      die_stack_rec_t *stktop = *stack;

      if (stktop) {
            stktop->next = rec;
            rec->prev = stktop;
            rec->next = (die_stack_rec_t *)NULL;
      } else {
            rec->next = rec->prev = (die_stack_rec_t *)NULL;
      }
      *stack = rec;
      die_stack_cnt++;
}

die_stack_rec_t *
pop_die_stack(die_stack_rec_t **stack)
{
      die_stack_rec_t *rec;

      if ((rec = *stack)) {
            if ((*stack = rec->prev)) {
                  (*stack)->next = (die_stack_rec_t *)NULL;
            }
      }
      return(rec);
}

die_stack_rec_t *
alloc_die_rec(void)
{
      die_stack_rec_t *rec;

      if ((rec = die_rec_freelist)) {
            die_rec_freelist = rec->next;
      } else {
            rec = calloc(1, sizeof(die_stack_rec_t));
      }
      return(rec);
}

void
free_die_rec(die_stack_rec_t *rec)
{
      rec->next = die_rec_freelist;
      die_rec_freelist = rec;
}

void
free_all_die_recs(void)
{
      die_stack_rec_t *rec = die_rec_freelist, *nextrec;

      while (rec) {
            nextrec = rec->next;
            free(rec);
            rec = nextrec;
      }
      die_rec_freelist = (die_stack_rec_t *)NULL;
}

/* We shall process 'thisdie' first, then nonrecursively walk through
 * the rest of the dies in the tree, processing each die as we go.
 */
static void read_dwarf_dies_nonrecurse(Dwarf_Die thisdie)
{
      Dwarf_Die cur_die, child_die, sibling_die, processed_die;
      die_stack_rec_t *rec;
      int result;

      cur_die = thisdie;

process_next_die:

      processed_die = process_die(cur_die);
      
      result = dwarf_child(processed_die, &child_die, &g_dwarf_err);
      if (result == DW_DLV_OK) {
            rec = alloc_die_rec();
            rec->die = processed_die;
            push_die_stack(&die_stack, rec);
            cur_die = child_die;
            goto process_next_die;
      } else if (result == DW_DLV_ERROR) {
            fprintf(stderr,"Problem reading DIE\n");
            exit(5);
      } 

next_sibling:

      /* We come here only if 'processed_die' doesnt have a child 
       */
      result = dwarf_siblingof(g_dwarf_dbg, processed_die, 
            &sibling_die, &g_dwarf_err);
      if (result == DW_DLV_OK)  {
            cur_die = sibling_die;
            goto process_next_die;
      } else if (result == DW_DLV_ERROR) {
            fprintf(stderr,"Problem reading DIE\n");
            exit(5);
      }

      /* We come here only if there aren't any more siblings.
       */
      if ((rec = pop_die_stack(&die_stack))) {
            processed_die = rec->die;
            free_die_rec(rec);
            goto next_sibling;
      }

      /* we're all done...clean up and then return.
       */   
      free_all_die_recs();
      return;
}

/* We shall process 'thisdie' first. Then grab the child of
 * 'thisdie'. If it has a child, recurse on the child die,
 * else get the sibling and recurse on the sibling die.
 * Recursion stops when 'thisdie' doesnt have either a child or
 * a sibling, which essentially means we just processed the last die.
*/
static void read_dwarf_dies_recurse(Dwarf_Die thisdie)
{
      Dwarf_Die child_die, sibling_die, processed_die;
      int result;

      processed_die = process_die(thisdie);
      
      result = dwarf_child(processed_die, &child_die, &g_dwarf_err);
      if(result == DW_DLV_OK) {
            read_dwarf_dies_recurse(child_die);
      }
      else if(result == DW_DLV_ERROR) {
            fprintf(stderr,"Problem reading DIE\n");
            exit(5);
      } 

      /* We come here only if 'processed_die' doesnt have a child */
      result = dwarf_siblingof(g_dwarf_dbg, 
            processed_die, &sibling_die, &g_dwarf_err);
      if(result == DW_DLV_OK)  {
            read_dwarf_dies_recurse(sibling_die);
      }
      else if(result == DW_DLV_ERROR) {
            fprintf(stderr,"Problem reading DIE\n");
            exit(5);
      }
      return;
}


/* Allocate memory for all the required structures, and  
 * fill the available information */
static Dwarf_Die process_die(Dwarf_Die die) 
{
      dbg_sym_t *sym_ptr;
      dw_type_t *dw_ptr;
      kltype_t  *kltype_ptr;

      Dwarf_Half dietag;
      Dwarf_Die last_processed_die = die, sibling_die;
      int result;

      /* Memory allocation */
      if(!(dw_ptr = alloc_memory())) {
            fprintf(stderr,"Memory allocatin failed\n");
            exit(6);
      }

      /* Allocate memory  for symbol structure */
      if(!(sym_ptr = (dbg_sym_t *)calloc(1,sizeof(dbg_sym_t)))) {
            fprintf(stderr,"Memory allocatin failed for symbol structure\n");
            exit(6);
      }

      /* Get kltype pointer */
        kltype_ptr = KLTYPE_PTR(dw_ptr);
      
      /* Check for siblings  */
      result = dwarf_siblingof(g_dwarf_dbg,die, &sibling_die, &g_dwarf_err);
      if(result == DW_DLV_ERROR) {
            fprintf(stderr,"Problem reading DIE\n");
            exit(6);
      }
      else if(result != DW_DLV_NO_ENTRY) {
            dwarf_dealloc(g_dwarf_dbg, sibling_die, DW_DLA_DIE);
      }

      /* Fill die information */
      fill_die(sym_ptr, dw_ptr, die);
 
      /* Read the tag */
      result = dwarf_tag(die, &dietag, &g_dwarf_err);
      if(result != DW_DLV_OK) {
            fprintf(stderr,"Problem reading DIE TAG\n");
            exit(6);
      }

      switch(dietag) {

      case DW_TAG_base_type:{
            
            int encoding;

            kltype_ptr->kl_type = KLT_BASE;
            sym_ptr->sym_type   = DBG_TYPE;     
                  
            /* Name of base type itself is the type */
            strcpy(kltype_ptr->kl_typestr, kltype_ptr->kl_name);
            strcat(kltype_ptr->kl_typestr, " ");

            /* Encoding */
            encoding = get_constant(die, DW_AT_encoding);

            switch(encoding) { 
            
            case DW_ATE_signed:       {kltype_ptr->kl_encoding = ENC_SIGNED; break;}
            case DW_ATE_unsigned:     {kltype_ptr->kl_encoding = ENC_UNSIGNED; break;}
            case DW_ATE_signed_char:  
            case DW_ATE_unsigned_char:{kltype_ptr->kl_encoding = ENC_CHAR; break;}
            case DW_ATE_complex_float:{kltype_ptr->kl_encoding = ENC_FLOAT; break;}
            case DW_ATE_address:      {kltype_ptr->kl_encoding = ENC_ADDRESS; break;}
            default:            {kltype_ptr->kl_encoding = ENC_UNDEFINED;}
            
            } /* end of switch statement */
            break;
      }

      case DW_TAG_enumeration_type: {

            kltype_ptr->kl_type = KLT_ENUMERATION;    
            sym_ptr->sym_type   = DBG_TYPE;     

            /* typestring = "enum <enum name>" */
            strcpy(kltype_ptr->kl_typestr,ENUM_STR);
            if(kltype_ptr->kl_name) {
                  strcat(kltype_ptr->kl_typestr, " ");
                  strcat(kltype_ptr->kl_typestr, kltype_ptr->kl_name);
                  strcat(kltype_ptr->kl_typestr, " ");
            }

            /* Process this die to get the enumators and link them */
            process_common_die(dw_ptr->thisdie, kltype_ptr);      
            break;
      }

      case DW_TAG_structure_type: {

            kltype_ptr->kl_type = KLT_STRUCT;         
            sym_ptr->sym_type   = DBG_TYPE;     

            /* typestring = "struct <struct name>" */
            strcpy(kltype_ptr->kl_typestr, STRUCT_STR);
            if(kltype_ptr->kl_name) {
                  strcat(kltype_ptr->kl_typestr, " ");
                  strcat(kltype_ptr->kl_typestr, kltype_ptr->kl_name);
                  strcat(kltype_ptr->kl_typestr, " ");
            }
      
            /* Process this die to get structure members and link them */           
            process_common_die(dw_ptr->thisdie, kltype_ptr);      
            break;
      }
      
      case DW_TAG_union_type: {

            kltype_ptr->kl_type = KLT_UNION;          
            sym_ptr->sym_type   = DBG_TYPE;     

            /* typestring = "union <union name>" */
            strcpy(kltype_ptr->kl_typestr, UNION_STR);
            if(kltype_ptr->kl_name) {
                  strcat(kltype_ptr->kl_typestr, " ");
                  strcat(kltype_ptr->kl_typestr, kltype_ptr->kl_name);
                  strcat(kltype_ptr->kl_typestr, " ");
            }
      
            /* Process this die to get union members and link them */         
            process_common_die(dw_ptr->thisdie, kltype_ptr);      
            break;
      }

      case DW_TAG_typedef: {

            kltype_ptr->kl_type = KLT_TYPEDEF;  
            sym_ptr->sym_type   = DBG_TYPEDEF;  

            break;
      }
      
      case DW_TAG_array_type: {
            
            kltype_ptr->kl_type = KLT_ARRAY;
            sym_ptr->sym_type   = DBG_TYPE;     
      
            /* Process this die to get array bounds and link them */
            process_common_die(dw_ptr->thisdie, kltype_ptr);
            break;      
      }

      case DW_TAG_pointer_type: {
            
            kltype_ptr->kl_type = KLT_POINTER;
            sym_ptr->sym_type   = DBG_TYPE;     

            if(!dw_ptr->targetoffset) {
                  strcpy(kltype_ptr->kl_typestr, "void *");
                  
                  if(!void_dw_ptr) {
                        /* allocate memory for kltype of 'void' type */ 
                        if((void_dw_ptr = alloc_memory())) {
                              kltype_t* void_kl_ptr = KLTYPE_PTR(void_dw_ptr);
                              strcpy(void_kl_ptr->kl_name, VOID_STR);
                              strcpy(void_kl_ptr->kl_typestr, VOID_STR);
                              void_kl_ptr->kl_type = KLT_BASE;
                        } else {
                              fprintf(stderr,
                                 "Memory allocation for void kltype failed\n");
                              exit(6);
                        }
                  }
      
                  kltype_ptr->kl_realtype = KLTYPE_PTR(void_dw_ptr);
                  /* No further processing required for void pointer */
                  dw_ptr->complete = 1;               
            } else {
                  strcpy(kltype_ptr->kl_typestr, PTR_STR); 
            }
            break;
      }

      case DW_TAG_volatile_type: {

            kltype_ptr->kl_type  = KLT_UNKNOWN;
            sym_ptr->sym_type    = DBG_TYPE;    

            strcpy(kltype_ptr->kl_typestr, VOLATILE_STR);
            break;
      }
      case DW_TAG_const_type: {

            kltype_ptr->kl_type  = KLT_UNKNOWN;
            sym_ptr->sym_type    = DBG_TYPE;    

            strcpy(kltype_ptr->kl_typestr, CONST_STR);
            break;
      }
      case DW_TAG_subroutine_type: {

            kltype_ptr->kl_type  = KLT_FUNCTION;
            sym_ptr->sym_type    = DBG_TYPE;    
            
            if(!dw_ptr->targetoffset) {
                  /* This is a function returning 'void' 
                        - no processing required */
                  strcpy(kltype_ptr->kl_typestr, VOID_STR);
                  dw_ptr->complete = 1;
            }
            break;
      }

      case DW_TAG_formal_parameter: {

            sym_ptr->sym_type     = DBG_TYPE;   
            /* FIXME - Needs to be considered */
            dw_ptr->complete = 1;
            break;
      }
      
      case DW_TAG_variable: {
            
            kltype_ptr->kl_type   = KLT_TYPE;
            sym_ptr->sym_type     = DBG_TYPE;   
            break;
      }
      
      default: { 
            /* Some other type?  */
            /* printf("other TAG:%x %x\n",tag,dw_ptr->dieoffset); */
      }
      } /* End of switch case */

      /* Insert this sym into binary tree */
      if (dbg_insert_sym(sym_ptr)) {
            dbg_free_sym(sym_ptr);
      }
            
      return last_processed_die;
}

/* Memory allocation for dwarf structure */
static dw_type_t* alloc_memory(void) 
{
      dw_type_t *dw_ptr;
      kltype_t *kltype_ptr;
 
      if(!(dw_ptr = (dw_type_t *)calloc(1,sizeof(dw_type_t))))
            return NULL;

        kltype_ptr = KLTYPE_PTR(dw_ptr);

      if(!(kltype_ptr->kl_name = (char *)calloc(1,100))) 
            return NULL;
      if(!(kltype_ptr->kl_typestr = (char *)calloc(1,100)))
            return NULL;
      kltype_ptr->kl_member = NULL;
      kltype_ptr->kl_next = NULL;
      kltype_ptr->kl_realtype = NULL;
      kltype_ptr->kl_indextype = NULL;
      kltype_ptr->kl_elementtype = NULL; 
            
      return dw_ptr;
}

/* Fill the given die with available information */
void fill_die(dbg_sym_t *sym_ptr, dw_type_t *dw_ptr, Dwarf_Die die) 
{
      Dwarf_Off offset;
        Dwarf_Unsigned bytesize;
        Dwarf_Unsigned bitsize;
      Dwarf_Die refdie;
      int result;
      char *diename = NULL;

      kltype_t *kltype_ptr = KLTYPE_PTR(dw_ptr);
      
      /* This die */
        dw_ptr->thisdie = die;
      
      /* back pointer to sym */     
      kltype_ptr->kl_ptr = sym_ptr;

      sym_ptr->sym_kltype = kltype_ptr;
      
      if(!(sym_ptr->sym_name = (char *)calloc(1,100))) {
            fprintf(stderr,"Memory allocation for sym-name failed\n");
            exit(7);
      } 

        /* Store die name */
        result = dwarf_diename(die, &diename, &g_dwarf_err);
      if(result == DW_DLV_ERROR) {
                fprintf(stderr,"Problem reading DIE name\n");
                exit(7);
        } else if (result == DW_DLV_NO_ENTRY) {
            free(kltype_ptr->kl_name);
            free(sym_ptr->sym_name);
            sym_ptr->sym_name = kltype_ptr->kl_name = NULL;
      } else {
                strcpy(kltype_ptr->kl_name, diename);
                strcpy(sym_ptr->sym_name, diename);
                dwarf_dealloc(g_dwarf_dbg, diename, DW_DLA_STRING);
      }

        /* Store die offset */
        result = dwarf_dieoffset(die, &offset, &g_dwarf_err);
        if(result == DW_DLV_ERROR) {
                fprintf(stderr,"Problem reading DIE offset\n");
                exit(7);
        }
        dw_ptr->dieoffset = offset;
      
      /* The die offset is used as the typenum */ 
      sym_ptr->sym_typenum = offset;

        /* Store target die offset */
        if((refdie = get_ref_die(die, DW_AT_type))) {
                dw_ptr->complete = 0;
                result = dwarf_dieoffset(refdie, &offset, &g_dwarf_err);
                if(result == DW_DLV_ERROR) {
                        fprintf(stderr,"Problem reading target DIE offset\n");
                        exit(7);
                }
                dw_ptr->targetoffset = offset;
        } else {
                dw_ptr->complete = 1;
        }

        /* Store size */
        result = dwarf_bytesize(die, &bytesize, &g_dwarf_err);
        if(result == DW_DLV_ERROR) {
                fprintf(stderr,"Problem reading byte size\n");
                exit(7);
        }
        else if(result != DW_DLV_NO_ENTRY) {
                kltype_ptr->kl_size = bytesize;
        }

        /* Checck for bitsize. If NO entry, bitsize=(8*bytesize) */
        result = dwarf_bitsize(die, &bitsize, &g_dwarf_err);
        if(result == DW_DLV_ERROR) {
                fprintf(stderr,"Problem reading bitsize for DIE\n");
                exit(7);
        }
      else if(result != DW_DLV_NO_ENTRY) {
                kltype_ptr->kl_bit_size = bitsize;
      }
      else {
                kltype_ptr->kl_bit_size =(8*(kltype_ptr->kl_size));
      }
      return;
}
      
/* The arrays are to be arranged with each kltype (type KLT_ARRAY 
 * for each dimension) linked with kl_elementype. The kl_elementype 
 * of last dimension points to the typeinfomation of all the 
 * array elements */  
static void process_array(kltype_t *kltype_ptr) 
{
      dw_type_t *dw_ptr = (dw_type_t *)kltype_ptr;
      kltype_t *first_kltype_ptr;
      int size;
      
      if(kltype_ptr && !dw_ptr->complete && 
           (kltype_ptr->kl_type == KLT_ARRAY)) {      

            size = 1;
            first_kltype_ptr = kltype_ptr;
            
            while(kltype_ptr->kl_member) {

                  kltype_t *member;
                  member = kltype_ptr->kl_member;

                  kltype_ptr->kl_low_bounds = member->kl_low_bounds;
                  kltype_ptr->kl_high_bounds = member->kl_high_bounds;  

                  size *= (kltype_ptr->kl_high_bounds - 
                         kltype_ptr->kl_low_bounds + 1);

                  /* Fill the typestring for all array elements 
                      with the typestring of first kltype 
                        processed earlier */ 
                  strcpy(kltype_ptr->kl_typestr, first_kltype_ptr->kl_typestr);
                  
                  kltype_ptr->kl_type = KLT_ARRAY;
                  if(member->kl_member)
                        kltype_ptr->kl_elementtype = kltype_ptr->kl_member;   
                  else 
                        break;      
                  kltype_ptr = kltype_ptr->kl_member;
            }

            /* Link the typeinformation for this last element */
            kltype_ptr->kl_elementtype = first_kltype_ptr->kl_realtype;
                  
            /* If a member is an array of structures, process 
                that structure members */ 
            if(kltype_ptr->kl_elementtype->kl_type == KLT_STRUCT ||           
               kltype_ptr->kl_elementtype->kl_type == KLT_UNION) {            

                  process_struct_members(kltype_ptr->kl_elementtype);   
            }     
            
            first_kltype_ptr->kl_realtype = NULL;
            
            /* size of the array */
            first_kltype_ptr->kl_size = size;

            dw_ptr->complete = 1;
      }

      return;
}

/* Finding realtypes is done in two passes.  
   In the first pass, all types are processed except structure members.
   In the second pass, the structure members are processed */
static void find_realtypes(void) 
{
      /* Realtypes to be found for elements in type tree and typedef tree 
          Last element should be NULL */
      
      kltype_t* klptr_list[]= {kl_first_type(KLT_TYPE),
                         kl_first_type(KLT_TYPEDEF),
                         NULL,
                        };
      int i;
      kltype_t *source;

      i=0;  
      while((source = klptr_list[i++])) {
            for(; source; source = kl_next_type(source)) 
                  
                  look_for_realtype(source);
      }
            
      i=0;  
      while((source = klptr_list[i++])) {
            for(; source; source = kl_next_type(source)) {
                  /* Handle structure types */
                  if(source->kl_type == KLT_STRUCT || 
                     source->kl_type == KLT_UNION)  {
                        process_struct_members(source);

                  /* Handle typedefs to structure types */
                  } else if(source->kl_type == KLT_TYPEDEF) {
                  
                        if(source->kl_realtype->kl_type == KLT_STRUCT || 
                                 source->kl_realtype->kl_type == KLT_UNION ) 
                              
                              process_struct_members(source->kl_realtype);
                  }
            } /* end of for loop */
      } /* end of while loop */ 
}

/* Just find the realtype and return */
void look_for_realtype(kltype_t *src) 
{
      dw_type_t *dw_ptr = (dw_type_t*)src;

      /* Processing complete ?*/    
      if(dw_ptr->complete)  {
            return;
      }     
      /* If there exists target offset and if realtype is not found yet, 
          find the realtype */
      if(!(src->kl_realtype) && (dw_ptr->targetoffset)) {
            src->kl_realtype = 
                  find_realtype_from_offset(dw_ptr->targetoffset);

      }           
      if(src->kl_realtype) {
            /* Complete the final operations */
            do_final_operations(src);     
            dw_ptr->complete = 1;
      } else {
            not_processed(dw_ptr->dieoffset, dw_ptr->targetoffset);
      }     
}

/* Lookup for the symbol in tree given an offset and find its realtype */
static kltype_t * find_realtype_from_offset(Dwarf_Off offset) 
{
      dbg_sym_t *sym_ptr;

      /* Look up for this symbol */ 
      if(( sym_ptr = dbg_find_sym(NULL, DBG_TYPE, offset))) {                       

            /* Fill up the realtype for this symbol */      
            look_for_realtype(sym_ptr->sym_kltype);

            return (sym_ptr->sym_kltype);
      }
      return NULL;
}

/* Find the realtypes for structure members */ 
void process_struct_members(kltype_t *source) 
{
      if(!source)
            return;

      /* For each member, find realtype */      
      kltype_t *member = source->kl_member;     
      for(; member; member = member->kl_member) {     
            
            look_for_realtype(member);
      
            if(member->kl_realtype) {   
                  /* If a structure member is of struct type, 
                      process its members */
                  if(member->kl_realtype->kl_type == KLT_STRUCT || 
                     member->kl_realtype->kl_type == KLT_UNION ) {
                        
                              process_struct_members(member->kl_realtype);    
                  }
            } 
      }
}

/* If something is NOT found */
void not_processed (Dwarf_Off dieoffset, Dwarf_Off targetoffset) 
{
      fprintf(stderr,"not found....");
      fprintf(stderr,"source-off:%"FMT64"x \t dest-off:%"FMT64"x\n", 
            (uint64_t)dieoffset, (uint64_t)targetoffset);
}


/* These are the specific operations to be done for each kltype 
 * so as to match the higher layer code */
static void do_final_operations (kltype_t *kltype_ptr) 
{
      /* If kltype is of type TYPEDEF, its size is same as that of 
          its realtype.  For a typedef to BASE/TYPEDEF, typestring 
          is same as that of BASE/TYPEDEF typestring */
      if(kltype_ptr->kl_type == KLT_TYPEDEF)   {

            kltype_ptr->kl_size = kltype_ptr->kl_realtype->kl_size;

            if((kltype_ptr->kl_realtype->kl_type == KLT_BASE) || 
               (kltype_ptr->kl_realtype->kl_type == KLT_TYPEDEF)) {

                  strcpy(kltype_ptr->kl_typestr, 
                               kltype_ptr->kl_realtype->kl_typestr);    
                  strcat(kltype_ptr->kl_typestr, " ");
            } else {
                  strcpy(kltype_ptr->kl_typestr, kltype_ptr->kl_name);  
                  strcat(kltype_ptr->kl_typestr, " ");
            }
      }

      /* If its a pointer to a function, remove the '*' from typestr. 
          This is handled in a differnt manner by upper layer code */
      if((kltype_ptr->kl_type == KLT_POINTER))
            if((kltype_ptr->kl_realtype) && 
               (kltype_ptr->kl_realtype->kl_type == KLT_FUNCTION))
                  strcpy(kltype_ptr->kl_typestr,"");

      /* If its of type volatile/const, then skip this kltype - these 
          types are not understood by upper layer code */ 

      if((strstr(kltype_ptr->kl_realtype->kl_typestr, VOLATILE_STR)) || 
         (strstr(kltype_ptr->kl_realtype->kl_typestr, CONST_STR)) ) {

            kltype_ptr->kl_realtype = kltype_ptr->kl_realtype->kl_realtype;         
      } 

      /* If type other than TYPEDEF, prefix the typestrings of realtype 
          to this type */
      if(kltype_ptr->kl_type != KLT_TYPEDEF) {
            if((kltype_ptr->kl_realtype) && 
               (strlen(kltype_ptr->kl_realtype->kl_typestr))) {

                  char *temp = (char *)calloc(1,100);
                  if(!temp) {
                        fprintf(stderr,"Memory allocation failed\n");   
                        exit(8);
                  }
                  strcpy(temp, kltype_ptr->kl_realtype->kl_typestr);
                  strcat(temp, kltype_ptr->kl_typestr);
                  strcpy(kltype_ptr->kl_typestr, temp);     

                  free(temp);
            }
      }

      /* If bitsize NOT present (which means bits NOT present),
          bitsize = 8* bytesize */ 
      if(!(kltype_ptr->kl_bit_size))
            kltype_ptr->kl_bit_size = (8*(kltype_ptr->kl_size));


      /* Some specifics for array type */
      if(kltype_ptr->kl_type == KLT_ARRAY) {
            process_array(kltype_ptr);          

      }
}

/* Common processing for enumerators, structures/unions and arrays */
static void process_common_die(Dwarf_Die die, kltype_t *kltype_ptr) 
{
      Dwarf_Die child_die, sibling_die;
      Dwarf_Half dietag, search_tag;
      int result;

      int type = kltype_ptr->kl_type;     

      switch (type) {

            case KLT_ENUMERATION: {
                  search_tag = DW_TAG_enumerator;
                  break;
            }
            case KLT_STRUCT:
            case KLT_UNION: {
                  search_tag = DW_TAG_member;
                  break;
            }
            case KLT_ARRAY: {
                  search_tag = DW_TAG_subrange_type;
                  break;
            }
            default: 
                  return;
      } /* end of swtich */

      /* Check for siblings */
      result = dwarf_siblingof(g_dwarf_dbg, die, &sibling_die, &g_dwarf_err);
      if(result == DW_DLV_ERROR) {
            fprintf(stderr,"Problem reading DIE\n");
            exit(9);
      }
      else if(result == DW_DLV_NO_ENTRY) {
            /* Acceptable */
            return;
      } else {
            dwarf_dealloc(g_dwarf_dbg, sibling_die, DW_DLA_DIE);
      }
      
      result = dwarf_child(die, &child_die, &g_dwarf_err);
      if(result == DW_DLV_ERROR) {
            fprintf(stderr,"Problem reading DIE\n");
            exit(9);
      } else if (result == DW_DLV_NO_ENTRY) 
            /* No children */
            return;

      /* Process children */
      while(1) {
      
      result = dwarf_tag(child_die, &dietag, &g_dwarf_err);
      if(result == DW_DLV_ERROR) {
            fprintf(stderr,"Problem reading DIE TAG\n");
            exit(9);
      }

      if(dietag == search_tag) {

            dbg_sym_t *sym_ptr;
            dw_type_t *dw_ptr;      
            
            /* Allocate the memory */     
            if(!(dw_ptr = alloc_memory())) {
                  fprintf(stderr,"Memory allocatin failed\n");
                  exit(9);
            }
      
            if(!(sym_ptr = (dbg_sym_t *)calloc(1,sizeof(dbg_sym_t)))) {
                  fprintf(stderr,"6.1 Memory allocatin failed\n");
                  exit(9);
            }

            fill_die(sym_ptr, dw_ptr, child_die);


            /* Get member offset if structure member */
            if((type == KLT_STRUCT)) 
                  sym_ptr->sym_kltype->kl_offset = 
                        get_struct_member_offset(child_die);

            /* Get enumeration value */
            if((type == KLT_ENUMERATION))
                  sym_ptr->sym_kltype->kl_value = 
                        get_constant(dw_ptr->thisdie, DW_AT_const_value); 

            if(type == KLT_ARRAY) {
                  /* Read the bounds */
                  sym_ptr->sym_kltype->kl_low_bounds = 
                        get_constant (dw_ptr->thisdie, DW_AT_lower_bound);
                  sym_ptr->sym_kltype->kl_high_bounds = 
                        get_constant (dw_ptr->thisdie, DW_AT_upper_bound); 
                  
                  dw_ptr->complete = 1;
            }
            else  {
                  sym_ptr->sym_kltype->kl_type = KLT_MEMBER;
            }
      
            /* Link */  
            kltype_ptr->kl_member = sym_ptr->sym_kltype; 
            
            kltype_ptr = sym_ptr->sym_kltype;
      }

      result = dwarf_siblingof(g_dwarf_dbg, child_die, &sibling_die, &g_dwarf_err);
      if(result == DW_DLV_ERROR) {
            fprintf(stderr,"Problem reading DIE\n");
            exit(9);
      }
      else if(result == DW_DLV_NO_ENTRY) {
            return;
      }

      dwarf_dealloc(g_dwarf_dbg, child_die, DW_DLA_DIE);
      child_die = sibling_die;

      } /* end of while loop */
}

static Dwarf_Signed get_constant(Dwarf_Die die, Dwarf_Half at)  
{
      Dwarf_Attribute attr;
      Dwarf_Signed value = 0;
      int result;

      result = dwarf_attr(die, at, &attr, &g_dwarf_err);
      if(result == DW_DLV_ERROR) {
            fprintf(stderr,"Problem reading attribute\n");
            exit(10);
      }
      else if(result == DW_DLV_NO_ENTRY) {
            return value;
      }
      result = dwarf_formsdata(attr, &value, &g_dwarf_err);
      if(result == DW_DLV_ERROR) {
#ifdef NOTTHIS
            /* XXX -- This seems a bit harsh...
             */
            fprintf(stderr,"Problem reading values\n");
            exit(10);
#endif
      }
      return value;     
}

/* Lookup the type tree for structures and find its members size */
static void find_member_sizes() 
{
      int index = 0;
      void *tag = NULL;
      kltype_t *kltype_ptr = (kltype_t *)dbg_walk_hash(&index, &tag); 
      kltype_t *member;
      
      while(kltype_ptr) {     
      
            if(kltype_ptr->kl_type == KLT_STRUCT || 
                   kltype_ptr->kl_type == KLT_UNION) {
 
                  member = kltype_ptr->kl_member;
                  while(member) {               
                        
                        g_dwarf_size = 1; 
                        
                        member->kl_size = 
                              setup_member_size(member->kl_realtype);   
                  
                        /* If there NO bitsize (bits not present), 
                              bitsize = 8*bytesize */
                        if(!member->kl_bit_size)
                              member->kl_bit_size = 8 * member->kl_size;
                  
                        member = member->kl_member;
                  }
            }
            kltype_ptr = (kltype_t *)dbg_walk_hash(&index, &tag);
      };
}

/* Recursively calculates the size */
static int setup_member_size(kltype_t *kltype_ptr)
{
      if(!kltype_ptr) 
            return 0;
      switch(kltype_ptr->kl_type) {
      
      case KLT_ARRAY: {
            g_dwarf_size *= kltype_ptr->kl_size;
            while(1) {
                  if(!kltype_ptr->kl_member)
                        break;
                  kltype_ptr = kltype_ptr->kl_member;
            }     
            setup_member_size(kltype_ptr->kl_elementtype);
            break;
      }
      case KLT_TYPEDEF: {
            g_dwarf_size *= kltype_ptr->kl_size;
            break;
      }
      case KLT_POINTER: {
            g_dwarf_size *= kltype_ptr->kl_size;
            break;
      }
      case KLT_UNKNOWN: {
            /* DW_TAG_const_type 
               DW_TAG_volatile_type */
            setup_member_size(kltype_ptr->kl_realtype);
            break;
      }
      case KLT_BASE: {
            g_dwarf_size *= kltype_ptr->kl_size;
            break;
      }
      case KLT_STRUCT:
      case KLT_UNION: {
            g_dwarf_size *= kltype_ptr->kl_size;
            break;
      }
      case KLT_ENUMERATION: {
            g_dwarf_size *= kltype_ptr->kl_size;
            break;
      }
      default: {
            break;
      }
      
      } /* end of switch case */
      
      return g_dwarf_size;
}     

/* Returns the reference die */
static Dwarf_Die get_ref_die(Dwarf_Die die, Dwarf_Half at)
{
      Dwarf_Attribute attr;
      Dwarf_Die refdie;
      Dwarf_Off offset;
      int result;

      result = dwarf_attr(die, at, &attr, &g_dwarf_err);
      if(result == DW_DLV_ERROR) {
            fprintf(stderr,"Problem reading attribute\n");
            exit(11);
      }
      else if(result == DW_DLV_NO_ENTRY) {
            return NULL;
      }
      result = dwarf_formref(attr, &offset, &g_dwarf_err);
      if(result == DW_DLV_ERROR) {
            fprintf(stderr,"Problem reading attribute\n");
            exit(11);
      }
      result = dwarf_offdie(g_dwarf_dbg, offset, &refdie, &g_dwarf_err);      
      if(result == DW_DLV_ERROR) {
            fprintf(stderr,"Problem reading attribute\n");
            exit(11);
      }
      return refdie;
}

static int decode_unsigned_leb128 (unsigned char *data, int *soffset)
{
      Dwarf_Unsigned result = 0;
      unsigned char byte;
      int bytes_read, shift = 0;

      do {
                  byte = *data++;
                  bytes_read++;
                  /* For every byte read (from lower oder), 
                get the lower 7 bits and multiply by 128^x
                where x is 0,1,2...  */  
                  result |= (byte & 0x7f) << shift;
                  shift += 7;
      } while (byte & 0x80);        /* Loop till higher order byte */

      *soffset = result; 
      return bytes_read;
}

static int get_struct_member_offset(Dwarf_Die die) 
{
      Dwarf_Attribute attr;
      Dwarf_Block *ptr_blk;
      Dwarf_Half form_die;
      int soffset = 0;
      unsigned char *start;
        int result;

        result = dwarf_attr(die, DW_AT_data_member_location, &attr, &g_dwarf_err);
        if(result == DW_DLV_ERROR) {
                fprintf(stderr,"Problem reading attribute\n");
                exit(12);
        }
        else if(result == DW_DLV_NO_ENTRY) {
            fprintf(stderr,"No data member location entry\n");
                exit(12);
        }

      /* Get the form of DW_AT_data_member_location (block1/block2/block4/block), 
          length of data and the data itself (Unsigned LEB128 which is the offset */

      result = dwarf_whatform(attr, &form_die, &g_dwarf_err);
      if(result == DW_DLV_ERROR) {
            fprintf(stderr,"Problem reading form\n");
            exit(12);
      }

        result = dwarf_formblock(attr, &ptr_blk, &g_dwarf_err);
        if(result == DW_DLV_ERROR) {
            fprintf(stderr,"ProblemE reading the block\n");
                exit(12);
        }

      start  = (unsigned char*)(ptr_blk->bl_data);
      
      /* point to the actual data by skipping the length */ 
      switch(form_die) {
            case DW_FORM_block1: { start +=1; break; }
            case DW_FORM_block2: { start +=2; break; }
            case DW_FORM_block4: { start +=4; break; }
            case DW_FORM_block : 
                  { start += decode_unsigned_leb128(start, &soffset); }  
      }
      
      /* Gets the offset in soffset */    
      decode_unsigned_leb128(start, &soffset);
      
      dwarf_dealloc(g_dwarf_dbg, ptr_blk, DW_DLA_BLOCK);
      return soffset;
}


Generated by  Doxygen 1.6.0   Back to index