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

Robert Watson rwatson at FreeBSD.org
Tue May 26 22:49:44 UTC 2009


On Tue, 26 May 2009, Stacey Son wrote:

>  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)

One downside to the once-per-process limitation is that it means a library 
*and* an application using it can't both use ksyms at once.  Obviously, not a 
limitation for the current use, but if we start to grow more consumers it 
might become one.

Robert N M Watson
Computer Laboratory
University of Cambridge

>
> 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-head mailing list