Date: Tue, 26 May 2009 23:49:42 +0100 (BST) From: Robert Watson <rwatson@FreeBSD.org> To: Stacey Son <sson@FreeBSD.org> Cc: svn-src-head@freebsd.org, svn-src-all@freebsd.org, src-committers@freebsd.org Subject: Re: svn commit: r192859 - in head: share/man/man4 sys/conf sys/dev/ksyms sys/kern sys/modules sys/modules/ksyms sys/sys Message-ID: <alpine.BSF.2.00.0905262348390.23177@fledge.watson.org> In-Reply-To: <200905262139.n4QLd9pI074530@svn.freebsd.org> References: <200905262139.n4QLd9pI074530@svn.freebsd.org>
next in thread | previous in thread | raw e-mail | index | archive | help
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@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@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@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 *** >
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?alpine.BSF.2.00.0905262348390.23177>