[BACK]Return to kdbm_debugtypes.c CVS log [TXT][DIR] Up to [Development] / linux-2.6-xfs / kdb / modules

File: [Development] / linux-2.6-xfs / kdb / modules / kdbm_debugtypes.c (download)

Revision 1.1, Mon Aug 4 17:03:13 2008 UTC (9 years, 2 months ago) by lachlan.longdrop.melbourne.sgi.com
Branch: MAIN
CVS Tags: HEAD

Merge up to 2.6.26
Merge of 2.6.x-xfs-melb:linux:31804b by kenmcd.

/* this one has some additional address validation - untested */
/*
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 *
 * Copyright (c) 2008 Silicon Graphics, Inc.  All Rights Reserved.
 */

/*
 *
 * Most of this code is borrowed and adapted from the lkcd command "lcrash"
 * and its supporting libarary.
 *
 * This module provides kdb commands for casting memory structures.
 * It loads symbolic debugging info (provided from lcrash -o), and provides
 *  "print" "px", "pd"
 * (this information originally comes from the lcrash "kerntypes" file)
 *
 * A key here is tacking a file of debug info onto this module, for
 * load with it at insmod time.
 *
 * Careful of porting the klib KL_XXX functions (they call thru a jump table
 * that we don't use here)
 *
 * Usage:
 *  in order for the insmod kdbm_debugtypes.ko to succeed in loading types
 *  you must first use  lcrash -t kerntypes.xxxx -o debug_info
 *  and echo debug_info > /proc/kdb/debug_info_name
 */

#define VMALLOC_START_IA64 0xa000000200000000
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kdb.h>
#include <linux/kdbprivate.h>
#include <linux/fs.h>
#include <asm/processor.h>
#include <asm/uaccess.h>
#include <asm/fcntl.h>
#include <linux/vmalloc.h>
#include <linux/ctype.h>
#include <linux/file.h>
#include <linux/err.h>
#include "lcrash/klib.h"
#include "lcrash/kl_stringtab.h"
#include "lcrash/kl_btnode.h"
#include "lcrash/lc_eval.h"

MODULE_AUTHOR("SGI");
MODULE_DESCRIPTION("Load symbolic debugging information");
MODULE_LICENSE("GPL");

#undef next_node /* collision with nodemask.h */
static char	*stringstorage, **stringp_array;
static void	*filestorage;
static long	num_strings, num_kltypes, num_dsyms, stringstorage_size;
extern int	have_debug_file;
extern dbg_sym_t *types_tree_head;
extern dbg_sym_t *typedefs_tree_head;
extern kltype_t	 *kltype_array;
extern dbg_sym_t *dsym_types_array;
extern dbg_sym_t *type_tree;
extern dbg_sym_t *typedef_tree;

/*
 * use a pointer's value as an index in the stringp_array (num_strings) and
 * translate it to string address
 *
 * Return 0 for success, 1 for failure
 */
static int
index_to_char_ptr(char **ptrp)
{
	long	i;

	i = (long)*ptrp;
	/* we use a value of -1 to mean this was a null pointer */
	if (i == -1) {
		*ptrp = NULL;
		return 0;
	}
	if (i > num_strings-1) {
		printk("Could not translate character string index %#lx\n", i);
		return 1;
	}
	*ptrp = *(stringp_array+i);
	return 0;
}

/*
 * use a pointer's value as an index in the kltype_array (num_kltypes) and
 * translate it to the kltype_t address
 *
 * return 0 for success, 1 for failure
 */
static int
index_to_kltype_ptr(kltype_t **ptrp)
{
	long	i;

	i = (long)*ptrp;
	/* we use a value of -1 to mean this was a null pointer */
	if (i == -1) {
		*ptrp = NULL;
		return 0;
	}
	if (i > num_kltypes-1) {
		printk("Could not translate kl_type string index %#lx\n", i);
		return 1;
	}
	*ptrp = kltype_array+i;
	return 0;
}

/*
 * look up a pointer in the dsym_types_array (num_dsyms) and
 * translate it to the index in the array
 *
 * return 0 for success, 1 for failure
 */
static int
index_to_dbg_ptr(dbg_sym_t **ptrp)
{
	long	i;

	i = (long)*ptrp;
	/* we use a value of -1 to mean this was a null pointer */
	if (i == -1) {
		*ptrp = NULL;
		return 0;
	}
	if (i > num_dsyms-1) {
		printk("Could not translate dbg_sym_t index %#lx\n", i);
		return 1;
	}
	*ptrp = dsym_types_array+i;
	return 0;
}


/*
 * Work on the image of the file built by lcrash.
 * Unpack the strings, and resolve the pointers in the arrays of kltype_t's
 * and dbg_sym_t's to pointers.
 *
 * see lcrash's lib/libklib/kl_debug.c, which generates this file
 *
 * Return the pointers to the heads of the two binary trees by means of
 * pointer arguments.
 *
 * Return 0 for sucess, 1 for any error.
 */
static int
trans_file_image(void *file_storage, long file_size,  dbg_sym_t **type_treepp,
		dbg_sym_t **typedef_treepp)
{
	int		len;
	long		i, section_size, *lp, element_size;
	long		head_types_tree, head_typedefs_tree;
	char		*ptr, *stringsection, *kltypesection, *dbgsection;
	void		*kltypestorage, *dbgstorage;
	kltype_t	*klp;
	dbg_sym_t	*dbgp;

	/* 1) the strings */
	lp = (long *)file_storage;
	stringsection = (char *)lp;
	section_size = *lp++;
	num_strings = *lp++;
	lp++; /* element size does not apply the strings section */

	stringstorage_size = section_size - (3*sizeof(long));
	stringstorage = (char *)lp;

	stringp_array = (char **)vmalloc(num_strings * sizeof(char *));
	if (! stringp_array) {
		printk("vmalloc of %ld string pointers failed\n", num_strings);
		return 1;
	}
	ptr = stringstorage;
	for (i=0; i<num_strings; i++) {
		*(stringp_array+i) = ptr;
		len = strlen(ptr) + 1;
		ptr += len;
	}

	/* 2) the kltypes */
	kltypesection = (char *)(stringsection + section_size);
	lp = (long *)kltypesection;
	section_size = *lp++;
	num_kltypes = *lp++;
	element_size = *lp++;
	/* sanity check: */
	if (element_size != sizeof(kltype_t)) {
		printk("size of kltype_t:%ld does not match\n", element_size);
		goto bad;
	}
	kltypestorage = (void *)lp;
	kltype_array = (kltype_t *)kltypestorage;

	/* 3) the dbg_sym_t's */
	dbgsection = (char *)kltypesection + section_size;
	lp = (long *)dbgsection;
	section_size = *lp++;
	/* sanity check: */
	if ((dbgsection + section_size) != ((char *)file_storage+file_size)) {
		printk("dbg_sym_ts do not end at end of file\n");
		goto bad;
	}
	num_dsyms = *lp++;
	element_size = *lp++;
	/* sanity check: */
	if (element_size != sizeof(dbg_sym_t)) {
		printk("kdb: size of dbg_sym_t does not match lkcd\'s\n");
		goto bad;
	}

	/* two special words ahead of the structures themselves */
	head_types_tree = *lp++;
	head_typedefs_tree = *lp++;

	dbgstorage = (void *)lp;
	dsym_types_array = (dbg_sym_t *)dbgstorage;

	/* return the heads of the two binary trees */
	*type_treepp = dsym_types_array+head_types_tree;
	*typedef_treepp = dsym_types_array+head_typedefs_tree;

	/* translate the indices in our our array of kltype_t's to pointers */
	/*  (see write_kltype() for the fields that can be translated) */
	klp = kltype_array;
	for (i=0; i<num_kltypes; i++, klp++) {
		if (index_to_char_ptr(&klp->kl_name))
			goto bad;
		if (index_to_char_ptr(&klp->kl_typestr))
			goto bad;
		if (index_to_kltype_ptr(&klp->kl_member))
			goto bad;
		if (index_to_kltype_ptr(&klp->kl_next))
			goto bad;
		if (index_to_kltype_ptr(&klp->kl_realtype))
			goto bad;
		if (index_to_kltype_ptr(&klp->kl_indextype))
			goto bad;
		if (index_to_kltype_ptr(&klp->kl_elementtype))
			goto bad;
		if (index_to_dbg_ptr((dbg_sym_t **)&klp->kl_ptr))
			goto bad;
	}

	/* translate the indices in our our array of dbg_sym_t's to pointers */
	/*  (see write_dbgtype() for the fields that can be translated) */
	dbgp = dsym_types_array;
	for (i=0; i<num_dsyms; i++, dbgp++) {
		if (index_to_char_ptr(&dbgp->sym_bt.bt_key))
			goto bad;
		if (index_to_dbg_ptr((dbg_sym_t **)&dbgp->sym_bt.bt_left))
			goto bad;
		if (index_to_dbg_ptr((dbg_sym_t **)&dbgp->sym_bt.bt_right))
			goto bad;
		if (index_to_dbg_ptr((dbg_sym_t **)&dbgp->sym_bt.bt_parent))
			goto bad;
		if (index_to_dbg_ptr((dbg_sym_t **)&dbgp->sym_next))
			goto bad;
		if (index_to_dbg_ptr((dbg_sym_t **)&dbgp->sym_link))
			goto bad;
		if (index_to_kltype_ptr(&dbgp->sym_kltype))
			goto bad;
	}

	vfree(stringp_array);
	return 0;
bad:
	printk("trans_file_image() returning an error\n");
	vfree(stringp_array);
	return 1;
}

/* there is /proc interface to this string */
extern char kdb_debug_info_filename[];
/*
 * This is the module initialization function.
 */
static int __init
kdbm_debuginfo_init(void)
{
	int		len;
	long		ret, file_size;
	ssize_t		sizeread;
	mm_segment_t	fs;
	struct file	*file;
	loff_t		inode_size, pos;

	len = strlen(kdb_debug_info_filename);
	if (!len) {
		printk("kdb: no file name in /proc/kdb/debug_info_name\n");
		return -ENODEV;
	}

	fs = get_fs();     /* save previous value of address limits */
	set_fs (get_ds()); /* use kernel limit */

        file = filp_open(kdb_debug_info_filename, O_RDONLY, 0);
	if (IS_ERR(file)) {
		set_fs(fs);
		printk (
		  "kdb: open of %s (from /proc/kdb/debug_info_name) failed\n",
			kdb_debug_info_filename);
		return -ENODEV;
	}
	if (!file->f_op || (!file->f_op->read && !file->f_op->llseek)) {
		printk ("file has no operation for read or seek\n");
		set_fs(fs);
		return -ENODEV;
	}
	inode_size = file->f_dentry->d_inode->i_size;

	/*
	 * File has a header word on it that contains the size of the
	 * file.  We don't need it, but can use it as a sanity check.
	 */
	pos = 0;
	sizeread = file->f_op->read(file, (char *)&file_size,
					sizeof(file_size), &pos);
	if (sizeread != sizeof(file_size)) {
		printk("could not read %d bytes from %s\n",
			(int)sizeof(file_size), kdb_debug_info_filename);
		ret = filp_close(file, NULL);
		set_fs(fs);
		return -ENODEV;
	}
	if (inode_size != file_size) {
		printk("file says %ld, inode says %lld\n",
				file_size, inode_size);
		ret = filp_close(file, NULL);
		set_fs(fs);
		return -ENODEV;
	}

	/* space for the rest of the file: */
	file_size -= sizeof(long);
	filestorage = (void *)vmalloc(file_size);

	pos = sizeof(file_size); /* position after the header word */
	sizeread = file->f_op->read(file, (char *)filestorage,
                                        file_size, &pos);
	if (sizeread != file_size) {
		printk("could not read %ld bytes from %s\n",
			file_size, kdb_debug_info_filename);
		ret = filp_close(file, NULL);
		set_fs(fs);
		vfree (filestorage);
		return -ENODEV;
	}

	ret = filp_close(file, NULL);
	set_fs(fs); /* restore address limits before returning to user space */

	if (trans_file_image(filestorage, file_size, &types_tree_head,
						     &typedefs_tree_head)){
		vfree (filestorage);
		return -ENODEV;
	}
	printk("kdbm_debuginfo loaded %s\n", kdb_debug_info_filename);
	/* set the lcrash code's binary tree head nodes */
	type_tree = types_tree_head;
	typedef_tree = typedefs_tree_head;

	have_debug_file = 1;

	return 0;
}

/*
 * This is the module exit function.
 */
static void __exit
kdbm_debuginfo_exit(void)
{
	printk("kdbm_debuginfo unloaded %s\n", kdb_debug_info_filename);
	vfree (filestorage);
	have_debug_file = 0;
	return;
}

module_init(kdbm_debuginfo_init);
module_exit(kdbm_debuginfo_exit);