svn commit: r192859 - in head: share/man/man4 sys/conf sys/dev/ksyms sys/kern sys/modules sys/modules/ksyms sys/sys

Stacey Son sson at FreeBSD.org
Tue May 26 21:39:11 UTC 2009


Author: sson
Date: Tue May 26 21:39:09 2009
New Revision: 192859
URL: http://svn.freebsd.org/changeset/base/192859

Log:
  Add the ksyms(4) pseudo driver.  The ksyms driver allows a process to
  get a quick snapshot of the kernel's symbol table including the symbols
  from any loaded modules (the symbols are all merged into one symbol
  table).  Unlike like other implementations, this ksyms driver maps
  memory in the process memory space to store the snapshot at the time
  /dev/ksyms is opened.  It also checks to see if the process has already
  a snapshot open and won't allow it to open /dev/ksyms it again until it
  closes first.  This prevents kernel and process memory from being
  exhausted.  Note that /dev/ksyms is used by the lockstat(1) command.
  
  Reviewed by:	gallatin kib (freebsd-arch)
  Approved by:	gnn (mentor)

Added:
  head/share/man/man4/ksyms.4   (contents, props changed)
  head/sys/dev/ksyms/
  head/sys/dev/ksyms/ksyms.c   (contents, props changed)
  head/sys/modules/ksyms/
  head/sys/modules/ksyms/Makefile   (contents, props changed)
  head/sys/sys/ksyms.h   (contents, props changed)
Modified:
  head/share/man/man4/Makefile
  head/sys/conf/NOTES
  head/sys/conf/files
  head/sys/kern/link_elf.c
  head/sys/kern/link_elf_obj.c
  head/sys/kern/linker_if.m
  head/sys/modules/Makefile

Modified: head/share/man/man4/Makefile
==============================================================================
--- head/share/man/man4/Makefile	Tue May 26 21:34:43 2009	(r192858)
+++ head/share/man/man4/Makefile	Tue May 26 21:39:09 2009	(r192859)
@@ -160,6 +160,7 @@ MAN=	aac.4 \
 	kbdmux.4 \
 	keyboard.4 \
 	kld.4 \
+	ksyms.4 \
 	ktr.4 \
 	kue.4 \
 	lagg.4 \

Added: head/share/man/man4/ksyms.4
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/share/man/man4/ksyms.4	Tue May 26 21:39:09 2009	(r192859)
@@ -0,0 +1,158 @@
+.\" Copyright (c) 2008-2009 Stacey Son <sson at freebsd.org> 
+.\"	The Regents of the University of California.  All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"	This product includes software developed by the University of
+.\"	California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd April 5, 2009
+.Dt KSYMS 4
+.Os
+.Sh NAME
+.Nm ksyms
+.Nd kernel symbol table interface
+.Sh SYNOPSIS
+.Cd "device ksyms"
+.Sh DESCRIPTION
+The
+.Pa /dev/ksyms
+character device provides a read-only interface to a snapshot of the kernel 
+symbol table.  The in-kernel symbol manager is designed to be able to handle
+many types of symbols tables, however, only 
+.Xr elf 5
+symbol tables are supported by this device.  The ELF format image contains two
+sections: a symbol table and a corresponding string table.  
+.Bl -tag -width indent -offset indent
+.It Dv Symbol Table
+The SYMTAB section contains the symbol table entries present in the current 
+running kernel, including the symbol table entries of any loaded modules. The
+symbols are ordered by the kernel module load time starting with kernel file
+symbols first, followed by the first loaded module's symbols and so on.
+.It Dv String Table
+The STRTAB section contains the symbol name strings from the kernel and any
+loaded modules that the symbol table entries reference.
+.El
+.Pp
+Elf formated symbol table data read from the
+.Pa /dev/ksyms
+file represents the state of the kernel at the time when the device is opened.
+Since
+.Pa /dev/ksyms
+has no text or data, most of the fields are initialized to NULL.
+The 
+.Nm 
+driver does not block the loading or unloading of modules into the kernel
+while the 
+.Pa /dev/ksyms
+file is open but may contain stale data.
+.Sh IOCTLS
+The
+.Xr ioctl 2
+command codes below are defined in
+.Aq Pa sys/ksyms.h .
+.Pp
+The (third) argument to the
+.Xr ioctl 2
+should be a pointer to the type indicated.
+.Bl -tag -width indent -offset indent
+.It Dv KIOCGSIZE (size_t)
+Returns the total size of the current symbol table.
+This can be used when allocating a buffer to make a copy of
+the kernel symbol table.
+.It Dv KIOCGADDR (void *)
+Returns the address of the kernel symbol table mapped in
+the process memory.  
+.El
+.Sh FILES
+.Bl -tag -width /dev/ksymsX
+.It Pa /dev/ksyms
+.El
+.Sh ERRORS
+An 
+.Xr open 2
+of
+.Pa /dev/ksyms
+will fail if: 
+.Bl -tag -width Er
+.It Bq Er EBUSY
+The device is already open.  A process must close 
+.Pa /dev/ksyms
+before it can be opened again. 
+.It Bq Er ENOMEM
+There is a resource shortage in the kernel.
+.It Bq Er ENXIO
+The driver was unsuccessful in creating a snapshot of the kernel symbol
+table.  This may occur if the kernel was in the process of loading or
+unloading a module. 
+.El
+.Sh SEE ALSO
+.Xr ioctl 2 ,
+.Xr nlist 3 ,
+.Xr elf 5 ,
+.Xr kldload 8
+.Sh HISTORY
+A
+.Nm
+device exists in many different operating systems.
+This implementation is similar in function to the Solaris and NetBSD
+.Nm
+driver.
+.Pp
+The
+.Nm
+driver first appeared in
+.Fx 8.0
+to support 
+.Xr lockstat 1 .
+.Sh BUGS
+Because files can be dynamically linked into the kernel at any time the symbol 
+information can vary.  When you open the
+.Pa /dev/ksyms
+file, you have access to an ELF image which represents a snapshot of the state of the kernel symbol information at that instant in time. Keeping the device open does not block the loading or unloading of kernel modules.  To get a new snapshot you must close and re-open the device.
+.Pp
+A process is only allowed to open the
+.Pa /dev/ksyms
+file once at a time.  The process must close the
+.Pa /dev/ksyms
+before it is allowed to open it again.
+.Pp 
+The
+.Nm
+driver uses the calling process' memory address space to store the snapshot.  
+.Xr ioctl 2
+can be used to get the memory address where the symbol table is stored to
+save kernel memory. 
+.Xr mmap 2
+may also be used but it will map it to another address. 
+.Sh AUTHORS
+The 
+.Nm
+driver was written by 
+.An Stacey Son
+.Aq sson at freebsd.org .

Modified: head/sys/conf/NOTES
==============================================================================
--- head/sys/conf/NOTES	Tue May 26 21:34:43 2009	(r192858)
+++ head/sys/conf/NOTES	Tue May 26 21:39:09 2009	(r192859)
@@ -1080,6 +1080,9 @@ device		random
 # The system memory devices; /dev/mem, /dev/kmem
 device		mem
 
+# The kernel symbol table device; /dev/ksyms
+device		ksyms
+
 # Optional character code conversion support with LIBICONV.
 # Each option requires their base file system and LIBICONV.
 options 	CD9660_ICONV

Modified: head/sys/conf/files
==============================================================================
--- head/sys/conf/files	Tue May 26 21:34:43 2009	(r192858)
+++ head/sys/conf/files	Tue May 26 21:39:09 2009	(r192859)
@@ -1085,6 +1085,7 @@ dev/joy/joy.c			optional joy
 dev/joy/joy_isa.c		optional joy isa
 dev/joy/joy_pccard.c		optional joy pccard
 dev/kbdmux/kbdmux.c		optional kbdmux
+dev/ksyms/ksyms.c		optional ksyms
 dev/le/am7990.c			optional le
 dev/le/am79900.c		optional le
 dev/le/if_le_pci.c		optional le pci

Added: head/sys/dev/ksyms/ksyms.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/dev/ksyms/ksyms.c	Tue May 26 21:39:09 2009	(r192859)
@@ -0,0 +1,678 @@
+/*-
+ * Copyright (c) 2008-2009, Stacey Son <sson at freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+
+#include <sys/conf.h>
+#include <sys/elf.h>
+#include <sys/ksyms.h>
+#include <sys/linker.h>
+#include <sys/malloc.h>
+#include <sys/mman.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <sys/resourcevar.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+
+#include <machine/elf.h>
+
+#include <vm/pmap.h>
+#include <vm/vm.h>
+#include <vm/vm_extern.h>
+#include <vm/vm_map.h>
+
+#include "linker_if.h"
+
+#define SHDR_NULL	0
+#define SHDR_SYMTAB	1
+#define SHDR_STRTAB	2
+#define SHDR_SHSTRTAB	3
+
+#define SHDR_NUM	4
+
+#define STR_SYMTAB	".symtab"
+#define STR_STRTAB	".strtab"
+#define STR_SHSTRTAB	".shstrtab" 
+
+#define KSYMS_DNAME	"ksyms"
+
+static	d_open_t 	ksyms_open;
+static	d_read_t	ksyms_read;
+static	d_close_t	ksyms_close;
+static	d_ioctl_t	ksyms_ioctl;
+static	d_mmap_t	ksyms_mmap;
+
+static struct cdevsw ksyms_cdevsw = {
+    .d_version	=	D_VERSION,
+    .d_flags	=	D_PSEUDO | D_TRACKCLOSE,
+    .d_open	=	ksyms_open,
+    .d_close	=	ksyms_close,
+    .d_read	=	ksyms_read,
+    .d_ioctl	=	ksyms_ioctl,
+    .d_mmap	=	ksyms_mmap,
+    .d_name	=	KSYMS_DNAME	
+};
+
+struct ksyms_softc {
+	LIST_ENTRY(ksyms_softc)	sc_list;
+	vm_offset_t 		sc_uaddr;
+	size_t 			sc_usize;
+	pmap_t			sc_pmap;
+	struct proc	       *sc_proc;
+};
+
+static struct mtx 		 ksyms_mtx;
+static struct cdev 		*ksyms_dev;
+static LIST_HEAD(, ksyms_softc)	 ksyms_list = 
+	LIST_HEAD_INITIALIZER(&ksyms_list);
+
+static const char 	ksyms_shstrtab[] = 
+	"\0" STR_SYMTAB "\0" STR_STRTAB "\0" STR_SHSTRTAB "\0";
+
+struct ksyms_hdr {
+	Elf_Ehdr	kh_ehdr;
+	Elf_Phdr	kh_txtphdr;
+	Elf_Phdr	kh_datphdr;
+	Elf_Shdr	kh_shdr[SHDR_NUM];
+	char		kh_shstrtab[sizeof(ksyms_shstrtab)];
+};
+	
+struct tsizes {
+	size_t		ts_symsz;
+	size_t		ts_strsz;
+};
+
+struct toffsets {
+	vm_offset_t	to_symoff;
+	vm_offset_t	to_stroff;
+	unsigned	to_stridx;
+	size_t		to_resid;
+};
+
+static MALLOC_DEFINE(M_KSYMS, "KSYMS", "Kernel Symbol Table");
+
+/*
+ * Get the symbol and string table sizes for a kernel module. Add it to the
+ * running total. 
+ */
+static int
+ksyms_size_permod(linker_file_t lf, void *arg)
+{
+	struct tsizes *ts;
+	Elf_Sym *symtab;
+	caddr_t strtab;
+	long syms;
+	
+	ts = arg;
+    
+	syms = LINKER_SYMTAB_GET(lf, &symtab);
+	ts->ts_symsz += syms * sizeof(Elf_Sym);
+	ts->ts_strsz += LINKER_STRTAB_GET(lf, &strtab);
+	
+	return (0);
+}
+
+/*
+ * For kernel module get the symbol and string table sizes, returning the
+ * totals in *ts. 
+ */
+static void 
+ksyms_size_calc(struct tsizes *ts)
+{
+	ts->ts_symsz = 0;
+	ts->ts_strsz = 0;
+    
+	(void) linker_file_foreach(ksyms_size_permod, ts);
+}
+
+#define KSYMS_EMIT(src, des, sz) do {				\
+		copyout(src, (void *)des, sz);			\
+		des += sz;					\
+	} while (0)
+
+#define SYMBLKSZ	256 * sizeof (Elf_Sym)
+
+/*
+ * For a kernel module, add the symbol and string tables into the
+ * snapshot buffer.  Fix up the offsets in the tables.
+ */
+static int
+ksyms_add(linker_file_t lf, void *arg)
+{
+	struct toffsets *to;
+	Elf_Sym *symtab, *symp;
+	caddr_t strtab;
+	long symsz;
+	size_t strsz, numsyms;
+	linker_symval_t symval;
+	char *buf;
+	int i, nsyms, len;
+	
+	to = arg;
+    
+	MOD_SLOCK;
+	numsyms =  LINKER_SYMTAB_GET(lf, &symtab);
+	strsz = LINKER_STRTAB_GET(lf, &strtab);
+	symsz = numsyms * sizeof(Elf_Sym);
+	
+	buf = malloc(SYMBLKSZ, M_KSYMS, M_WAITOK);
+	
+	while (symsz > 0) {
+		len = min(SYMBLKSZ, symsz);
+		bcopy(symtab, buf, len);
+
+		/* 
+		 * Fix up symbol table for kernel modules: 
+		 *   string offsets need adjusted 
+		 *   symbol values made absolute
+		 */
+		symp = (Elf_Sym *) buf;
+		nsyms = len / sizeof (Elf_Sym);
+		for (i = 0; i < nsyms; i++) {
+			symp[i].st_name += to->to_stridx;
+			if (lf->id > 1 && LINKER_SYMBOL_VALUES(lf, 
+				(c_linker_sym_t) &symtab[i], &symval) == 0) {
+				symp[i].st_value = (uintptr_t) symval.value;
+			}
+		}
+
+		if (len > to->to_resid) { 
+			MOD_SUNLOCK;
+			free(buf, M_KSYMS);
+			return (ENXIO);
+		} else
+			to->to_resid -= len;
+		KSYMS_EMIT(buf, to->to_symoff, len);
+
+		symtab += nsyms;
+		symsz -= len;
+	}
+	free(buf, M_KSYMS);
+	MOD_SUNLOCK;
+	
+	if (strsz > to->to_resid)
+		return (ENXIO);
+	else
+		to->to_resid -= strsz;
+	KSYMS_EMIT(strtab, to->to_stroff, strsz);
+	to->to_stridx += strsz;
+	
+	return (0);
+}
+
+/*
+ * Create a single ELF symbol table for the kernel and kernel modules loaded
+ * at this time. Write this snapshot out in the process address space. Return
+ * 0 on success, otherwise error.
+ */
+static int
+ksyms_snapshot(struct tsizes *ts, vm_offset_t uaddr, size_t resid)
+{
+
+	struct ksyms_hdr *hdr;
+	struct toffsets	 to;
+	int error = 0;
+
+	/* Be kernel stack friendly */
+	hdr = malloc(sizeof (*hdr), M_KSYMS, M_WAITOK|M_ZERO);
+
+	/* 
+	 * Create the ELF header. 
+	 */
+	hdr->kh_ehdr.e_ident[EI_PAD] = 0;
+	hdr->kh_ehdr.e_ident[EI_MAG0] = ELFMAG0;
+	hdr->kh_ehdr.e_ident[EI_MAG1] = ELFMAG1;
+	hdr->kh_ehdr.e_ident[EI_MAG2] = ELFMAG2;
+	hdr->kh_ehdr.e_ident[EI_MAG3] = ELFMAG3;
+	hdr->kh_ehdr.e_ident[EI_DATA] = ELF_DATA;
+	hdr->kh_ehdr.e_ident[EI_OSABI] = ELFOSABI_FREEBSD;
+	hdr->kh_ehdr.e_ident[EI_CLASS] = ELF_CLASS;
+	hdr->kh_ehdr.e_ident[EI_VERSION] = EV_CURRENT;
+	hdr->kh_ehdr.e_ident[EI_ABIVERSION] = 0;
+	hdr->kh_ehdr.e_type = ET_EXEC;
+	hdr->kh_ehdr.e_machine = ELF_ARCH;
+	hdr->kh_ehdr.e_version = EV_CURRENT;
+	hdr->kh_ehdr.e_entry = 0;
+	hdr->kh_ehdr.e_phoff = offsetof(struct ksyms_hdr, kh_txtphdr);
+	hdr->kh_ehdr.e_shoff = offsetof(struct ksyms_hdr, kh_shdr);
+	hdr->kh_ehdr.e_flags = 0;
+	hdr->kh_ehdr.e_ehsize = sizeof(Elf_Ehdr);
+	hdr->kh_ehdr.e_phentsize = sizeof(Elf_Phdr);
+	hdr->kh_ehdr.e_phnum = 2;	/* Text and Data */ 
+	hdr->kh_ehdr.e_shentsize = sizeof(Elf_Shdr);
+	hdr->kh_ehdr.e_shnum = SHDR_NUM;
+	hdr->kh_ehdr.e_shstrndx = SHDR_SHSTRTAB;
+
+	/* 
+	 * Add both the text and data Program headers. 
+	 */
+	hdr->kh_txtphdr.p_type = PT_LOAD;
+	/* XXX - is there a way to put the actual .text addr/size here? */
+	hdr->kh_txtphdr.p_vaddr = 0;  
+	hdr->kh_txtphdr.p_memsz = 0; 
+	hdr->kh_txtphdr.p_flags = PF_R | PF_X;
+    
+	hdr->kh_datphdr.p_type = PT_LOAD;
+	/* XXX - is there a way to put the actual .data addr/size here? */
+	hdr->kh_datphdr.p_vaddr = 0; 
+	hdr->kh_datphdr.p_memsz = 0; 
+	hdr->kh_datphdr.p_flags = PF_R | PF_W | PF_X;
+
+	/* 
+	 * Add the Section headers: null, symtab, strtab, shstrtab, 
+	 */
+
+	/* First section header - null */
+	
+	/* Second section header - symtab */
+	hdr->kh_shdr[SHDR_SYMTAB].sh_name = 1; /* String offset (skip null) */
+	hdr->kh_shdr[SHDR_SYMTAB].sh_type = SHT_SYMTAB;
+	hdr->kh_shdr[SHDR_SYMTAB].sh_flags = 0;
+	hdr->kh_shdr[SHDR_SYMTAB].sh_addr = 0;
+	hdr->kh_shdr[SHDR_SYMTAB].sh_offset = sizeof(*hdr);
+	hdr->kh_shdr[SHDR_SYMTAB].sh_size = ts->ts_symsz;
+	hdr->kh_shdr[SHDR_SYMTAB].sh_link = SHDR_STRTAB;	
+	hdr->kh_shdr[SHDR_SYMTAB].sh_info = ts->ts_symsz / sizeof(Elf_Sym); 
+	hdr->kh_shdr[SHDR_SYMTAB].sh_addralign = sizeof(long);
+	hdr->kh_shdr[SHDR_SYMTAB].sh_entsize = sizeof(Elf_Sym);
+
+	/* Third section header - strtab */
+	hdr->kh_shdr[SHDR_STRTAB].sh_name = 1 + sizeof(STR_SYMTAB); 
+	hdr->kh_shdr[SHDR_STRTAB].sh_type = SHT_STRTAB;
+	hdr->kh_shdr[SHDR_STRTAB].sh_flags = 0;
+	hdr->kh_shdr[SHDR_STRTAB].sh_addr = 0;
+	hdr->kh_shdr[SHDR_STRTAB].sh_offset = 
+	    hdr->kh_shdr[SHDR_SYMTAB].sh_offset + ts->ts_symsz;	
+	hdr->kh_shdr[SHDR_STRTAB].sh_size = ts->ts_strsz;
+	hdr->kh_shdr[SHDR_STRTAB].sh_link = 0;
+	hdr->kh_shdr[SHDR_STRTAB].sh_info = 0;
+	hdr->kh_shdr[SHDR_STRTAB].sh_addralign = sizeof(char);
+	hdr->kh_shdr[SHDR_STRTAB].sh_entsize = 0;
+	
+	/* Fourth section - shstrtab */
+	hdr->kh_shdr[SHDR_SHSTRTAB].sh_name = 1 + sizeof(STR_SYMTAB) +
+	    sizeof(STR_STRTAB);
+	hdr->kh_shdr[SHDR_SHSTRTAB].sh_type = SHT_STRTAB;
+	hdr->kh_shdr[SHDR_SHSTRTAB].sh_flags = 0;
+	hdr->kh_shdr[SHDR_SHSTRTAB].sh_addr = 0;
+	hdr->kh_shdr[SHDR_SHSTRTAB].sh_offset = 
+	    offsetof(struct ksyms_hdr, kh_shstrtab);
+	hdr->kh_shdr[SHDR_SHSTRTAB].sh_size = sizeof(ksyms_shstrtab);
+	hdr->kh_shdr[SHDR_SHSTRTAB].sh_link = 0;
+	hdr->kh_shdr[SHDR_SHSTRTAB].sh_info = 0;
+	hdr->kh_shdr[SHDR_SHSTRTAB].sh_addralign = 0 /* sizeof(char) */;
+	hdr->kh_shdr[SHDR_SHSTRTAB].sh_entsize = 0;
+
+	/* Copy shstrtab into the header */
+	bcopy(ksyms_shstrtab, hdr->kh_shstrtab, sizeof(ksyms_shstrtab));
+	
+	to.to_symoff = uaddr + hdr->kh_shdr[SHDR_SYMTAB].sh_offset;
+	to.to_stroff = uaddr + hdr->kh_shdr[SHDR_STRTAB].sh_offset;
+	to.to_stridx = 0;
+	if (sizeof(struct ksyms_hdr) > resid) {
+		free(hdr, M_KSYMS);
+		return (ENXIO);
+	}
+	to.to_resid = resid - sizeof(struct ksyms_hdr);
+
+	/* Emit Header */
+	copyout(hdr, (void *)uaddr, sizeof(struct ksyms_hdr));
+	
+	free(hdr, M_KSYMS);
+
+	/* Add symbol and string tables for each kernelmodule */
+	error = linker_file_foreach(ksyms_add, &to); 
+
+	if (to.to_resid != 0)
+		return (ENXIO);
+
+	return (error);
+}
+
+/*
+ * Map some anonymous memory in user space of size sz, rounded up to the page
+ * boundary.
+ */
+static int
+ksyms_map(struct thread *td, vm_offset_t *addr, size_t sz)
+{
+	struct vmspace *vms = td->td_proc->p_vmspace;
+	int error;
+	vm_size_t size;
+
+	
+	/* 
+	 * Map somewhere after heap in process memory.
+	 */
+	PROC_LOCK(td->td_proc);
+	*addr = round_page((vm_offset_t)vms->vm_daddr + 
+	    lim_max(td->td_proc, RLIMIT_DATA));
+	PROC_UNLOCK(td->td_proc);
+
+	/* round size up to page boundry */
+	size = (vm_size_t) round_page(sz);
+    
+	error = vm_mmap(&vms->vm_map, addr, size, PROT_READ | PROT_WRITE, 
+	    VM_PROT_ALL, MAP_PRIVATE | MAP_ANON, OBJT_DEFAULT, NULL, 0);
+	
+	return (error);
+}
+
+/*
+ * Unmap memory in user space.
+ */
+static int
+ksyms_unmap(struct thread *td, vm_offset_t addr, size_t sz)
+{
+	vm_map_t map;
+	int error;
+	vm_size_t size;
+    
+	map = &td->td_proc->p_vmspace->vm_map;
+
+	size = (vm_size_t) round_page(sz);	
+
+	/* check for address wrap-around */
+	if (addr + size < addr || addr < vm_map_min(map) || 
+	    addr + size > vm_map_max(map))
+		return (EINVAL);
+
+	vm_map_lock(map);
+	/* make sure the pages are mapped */
+	if (!vm_map_check_protection(map, addr, addr + size, VM_PROT_NONE)) {
+		vm_map_unlock(map);
+		return (EINVAL);
+	}
+
+	error = vm_map_delete(map, addr, addr + size);
+	vm_map_unlock(map);
+
+	return (error);
+}
+
+static void
+ksyms_cdevpriv_dtr(void *data)
+{
+	struct ksyms_softc *sc;
+
+	sc = (struct ksyms_softc *)data; 
+
+	mtx_lock(&ksyms_mtx);
+	LIST_REMOVE(sc, sc_list);
+	mtx_unlock(&ksyms_mtx);
+	free(sc, M_KSYMS);
+}
+
+/* ARGSUSED */
+static int
+ksyms_open(struct cdev *dev, int flags, int fmt __unused, struct thread *td)
+{
+	struct tsizes ts;
+	size_t total_elf_sz;
+	int error, try;
+	struct ksyms_softc *sc;
+	
+	/* 
+	 *  Limit one open() per process. The process must close()
+	 *  before open()'ing again.
+	 */ 
+	mtx_lock(&ksyms_mtx);
+	LIST_FOREACH(sc, &ksyms_list, sc_list) {
+		if (sc->sc_proc == td->td_proc) {
+			mtx_unlock(&ksyms_mtx);
+			return (EBUSY);
+		}
+	}
+
+	sc = (struct ksyms_softc *) malloc(sizeof (*sc), M_KSYMS, 
+	    M_NOWAIT|M_ZERO);
+
+	if (sc == NULL) {
+		mtx_unlock(&ksyms_mtx);
+		return (ENOMEM);
+	}
+	sc->sc_proc = td->td_proc;
+	sc->sc_pmap = &td->td_proc->p_vmspace->vm_pmap;
+	LIST_INSERT_HEAD(&ksyms_list, sc, sc_list);
+	mtx_unlock(&ksyms_mtx);
+
+	error = devfs_set_cdevpriv(sc, ksyms_cdevpriv_dtr);
+	if (error) 
+		goto failed;
+
+	/*
+	 * MOD_SLOCK doesn't work here (because of a lock reversal with 
+	 * KLD_SLOCK).  Therefore, simply try upto 3 times to get a "clean"
+	 * snapshot of the kernel symbol table.  This should work fine in the
+	 * rare case of a kernel module being loaded/unloaded at the same
+	 * time. 
+	 */
+	for(try = 0; try < 3; try++) {
+		/*
+	 	* Map a buffer in the calling process memory space and
+	 	* create a snapshot of the kernel symbol table in it.
+	 	*/
+	
+		/* Compute the size of buffer needed. */
+		ksyms_size_calc(&ts);
+		total_elf_sz = sizeof(struct ksyms_hdr) + ts.ts_symsz + 
+			ts.ts_strsz; 
+
+		error = ksyms_map(td, &(sc->sc_uaddr), 
+				(vm_size_t) total_elf_sz);
+		if (error)
+			break;
+		sc->sc_usize = total_elf_sz;	
+
+		error = ksyms_snapshot(&ts, sc->sc_uaddr, total_elf_sz); 
+		if (!error)  {
+			/* Successful Snapshot */
+			return (0); 
+		}
+		
+		/* Snapshot failed, unmap the memory and try again */ 
+		(void) ksyms_unmap(td, sc->sc_uaddr, sc->sc_usize);
+	}
+
+failed:
+	ksyms_cdevpriv_dtr(sc);
+	return (error);
+}
+
+/* ARGSUSED */
+static int
+ksyms_read(struct cdev *dev, struct uio *uio, int flags __unused)
+{
+	int error;
+	size_t len, sz;
+	struct ksyms_softc *sc;
+	off_t off;
+	char *buf;
+	vm_size_t ubase;
+    
+	error = devfs_get_cdevpriv((void **)&sc);
+	if (error)
+		return (error);
+    
+	off = uio->uio_offset;
+	len = uio->uio_resid;	
+    
+	if (off < 0 || off > sc->sc_usize)
+		return (EFAULT);
+
+	if (len > (sc->sc_usize - off))
+		len = sc->sc_usize - off;
+
+	if (len == 0)
+		return (0);
+
+	/*
+	 * Since the snapshot buffer is in the user space we have to copy it
+	 * in to the kernel and then back out.  The extra copy saves valuable
+	 * kernel memory.
+	 */
+	buf = malloc(PAGE_SIZE, M_KSYMS, M_WAITOK);
+	ubase = sc->sc_uaddr + off;
+
+	while (len) {
+
+		sz = min(PAGE_SIZE, len);
+		if (copyin((void *)ubase, buf, sz))
+			error = EFAULT;	
+		else
+			error = uiomove(buf, sz, uio);
+		
+		if (error)
+			break;
+	
+		len -= sz;
+		ubase += sz;
+	}
+	free(buf, M_KSYMS);
+
+	return (error);
+}
+
+/* ARGSUSED */
+static int
+ksyms_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int32_t flag __unused,
+    d_thread_t *td __unused)
+{
+	int error = 0;
+	struct ksyms_softc *sc;
+
+	error = devfs_get_cdevpriv((void **)&sc);
+	if (error)
+		return (error);
+
+	switch (cmd) {
+	case KIOCGSIZE:
+		/* 
+		 * Return the size (in bytes) of the symbol table
+		 * snapshot.
+		 */
+		*(size_t *)data = sc->sc_usize;
+		break;
+		
+	case KIOCGADDR:
+		/*
+		 * Return the address of the symbol table snapshot.
+		 * XXX - compat32 version of this?
+		 */
+		*(void **)data = (void *)sc->sc_uaddr;
+		break;
+		
+	default:
+		error = ENOTTY;
+		break;
+	}
+
+	return (error);
+}
+
+/* ARGUSED */
+static int
+ksyms_mmap(struct cdev *dev, vm_offset_t offset, vm_paddr_t *paddr,
+		int prot __unused)
+{
+    	struct ksyms_softc *sc;
+	int error;
+
+	error = devfs_get_cdevpriv((void **)&sc);
+	if (error)
+		return (error);
+
+	/*
+	 * XXX mmap() will actually map the symbol table into the process
+	 * address space again.
+	 */
+	if (offset > round_page(sc->sc_usize) || 
+	    (*paddr = pmap_extract(sc->sc_pmap, 
+	    (vm_offset_t)sc->sc_uaddr + offset)) == 0) 
+		return (-1);
+
+	return (0);
+}
+
+/* ARGUSED */
+static int
+ksyms_close(struct cdev *dev, int flags __unused, int fmt __unused,
+		struct thread *td)
+{
+	int error = 0;
+	struct ksyms_softc *sc;
+	
+	error = devfs_get_cdevpriv((void **)&sc);
+	if (error)
+		return (error);
+
+	/* Unmap the buffer from the process address space. */
+	error = ksyms_unmap(td, sc->sc_uaddr, sc->sc_usize);
+
+	devfs_clear_cdevpriv();
+
+	return (error);
+}
+
+/* ARGSUSED */
+static int
+ksyms_modevent(module_t mod __unused, int type, void *data __unused)
+{
+	int error = 0;
+
+	switch (type) {
+	case MOD_LOAD:
+		mtx_init(&ksyms_mtx, "KSyms mtx", NULL, MTX_DEF);
+		ksyms_dev = make_dev(&ksyms_cdevsw, 0, UID_ROOT, GID_WHEEL,
+		    0444, KSYMS_DNAME);
+		break;
+
+	case MOD_UNLOAD:
+		if (!LIST_EMPTY(&ksyms_list))
+			return (EBUSY);
+		destroy_dev(ksyms_dev);
+		mtx_destroy(&ksyms_mtx);
+		break;
+
+	case MOD_SHUTDOWN:
+		break;
+
+	default:
+		error = EOPNOTSUPP;
+		break;
+	}
+	return (error);
+}
+
+DEV_MODULE(ksyms, ksyms_modevent, NULL);
+MODULE_VERSION(ksyms, 1);

Modified: head/sys/kern/link_elf.c
==============================================================================
--- head/sys/kern/link_elf.c	Tue May 26 21:34:43 2009	(r192858)
+++ head/sys/kern/link_elf.c	Tue May 26 21:39:09 2009	(r192859)
@@ -137,6 +137,8 @@ static int	link_elf_each_function_nameva
 				linker_function_nameval_callback_t,
 				void *);
 static void	link_elf_reloc_local(linker_file_t);
+static long	link_elf_symtab_get(linker_file_t, const Elf_Sym **);
+static long	link_elf_strtab_get(linker_file_t, caddr_t *);
 static Elf_Addr	elf_lookup(linker_file_t lf, Elf_Size symidx, int deps);
 
 static kobj_method_t link_elf_methods[] = {
@@ -151,6 +153,8 @@ static kobj_method_t link_elf_methods[] 
     KOBJMETHOD(linker_each_function_name, link_elf_each_function_name),
     KOBJMETHOD(linker_each_function_nameval, link_elf_each_function_nameval),
     KOBJMETHOD(linker_ctf_get,          link_elf_ctf_get),
+    KOBJMETHOD(linker_symtab_get, 	link_elf_symtab_get),
+    KOBJMETHOD(linker_strtab_get, 	link_elf_strtab_get),
     { 0, 0 }
 };
 
@@ -1390,3 +1394,29 @@ link_elf_reloc_local(linker_file_t lf)
 	}
     }
 }
+
+static long
+link_elf_symtab_get(linker_file_t lf, const Elf_Sym **symtab)
+{
+    elf_file_t ef = (elf_file_t)lf;
+    
+    *symtab = ef->ddbsymtab;
+    
+    if (*symtab == NULL)
+        return (0);
+
+    return (ef->ddbsymcnt);
+}
+    
+static long
+link_elf_strtab_get(linker_file_t lf, caddr_t *strtab)
+{
+    elf_file_t ef = (elf_file_t)lf;
+
+    *strtab = ef->ddbstrtab;
+
+    if (*strtab == NULL)
+        return (0);
+
+    return (ef->ddbstrcnt);
+}

Modified: head/sys/kern/link_elf_obj.c
==============================================================================
--- head/sys/kern/link_elf_obj.c	Tue May 26 21:34:43 2009	(r192858)
+++ head/sys/kern/link_elf_obj.c	Tue May 26 21:39:09 2009	(r192859)
@@ -140,6 +140,8 @@ static int	link_elf_each_function_nameva
 				linker_function_nameval_callback_t,
 				void *);
 static void	link_elf_reloc_local(linker_file_t);
+static long	link_elf_symtab_get(linker_file_t, Elf_Sym **);
+static long	link_elf_strtab_get(linker_file_t, caddr_t *);
 
 static Elf_Addr elf_obj_lookup(linker_file_t lf, Elf_Size symidx, int deps);
 
@@ -155,6 +157,8 @@ static kobj_method_t link_elf_methods[] 
 	KOBJMETHOD(linker_each_function_name,	link_elf_each_function_name),
 	KOBJMETHOD(linker_each_function_nameval, link_elf_each_function_nameval),
 	KOBJMETHOD(linker_ctf_get,		link_elf_ctf_get),
+	KOBJMETHOD(linker_symtab_get, 		link_elf_symtab_get),
+	KOBJMETHOD(linker_strtab_get, 		link_elf_strtab_get),
 	{ 0, 0 }
 };
 
@@ -1286,3 +1290,29 @@ link_elf_reloc_local(linker_file_t lf)
 		}
 	}
 }
+
+static long
+link_elf_symtab_get(linker_file_t lf, Elf_Sym **symtab)
+{
+    elf_file_t ef = (elf_file_t)lf;
+    
+    *symtab = ef->ddbsymtab;
+    
+    if (*symtab == NULL)
+        return (0);
+
+    return (ef->ddbsymcnt);
+}
+    
+static long
+link_elf_strtab_get(linker_file_t lf, caddr_t *strtab)
+{
+    elf_file_t ef = (elf_file_t)lf;
+
+    *strtab = ef->ddbstrtab;
+
+    if (*strtab == NULL)
+        return (0);
+
+    return (ef->ddbstrcnt);
+}

Modified: head/sys/kern/linker_if.m
==============================================================================
--- head/sys/kern/linker_if.m	Tue May 26 21:34:43 2009	(r192858)
+++ head/sys/kern/linker_if.m	Tue May 26 21:39:09 2009	(r192859)
@@ -105,6 +105,24 @@ METHOD int ctf_get {
 };
 
 #
+# Get the symbol table, returning it in **symtab.  Return the 
+# number of symbols, otherwise zero.
+#
+METHOD long symtab_get {
+	linker_file_t	file;
+	Elf_Sym		**symtab;
+};
+
+#
+# Get the string table, returning it in *strtab.  Return the
+# size (in bytes) of the string table, otherwise zero.
+#
+METHOD long strtab_get {
+	linker_file_t	file;
+	caddr_t		*strtab;
+};
+
+#
 # Load a file, returning the new linker_file_t in *result.  If
 # the class does not recognise the file type, zero should be

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-src-all mailing list