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 <s...@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 s...@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 <s...@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 ***