ksyms.c revision 201145
1192859Ssson/*- 2192859Ssson * Copyright (c) 2008-2009, Stacey Son <sson@freebsd.org> 3192859Ssson * All rights reserved. 4192859Ssson * 5192859Ssson * Redistribution and use in source and binary forms, with or without 6192859Ssson * modification, are permitted provided that the following conditions 7192859Ssson * are met: 8192859Ssson * 1. Redistributions of source code must retain the above copyright 9192859Ssson * notice, this list of conditions and the following disclaimer. 10192859Ssson * 2. Redistributions in binary form must reproduce the above copyright 11192859Ssson * notice, this list of conditions and the following disclaimer in the 12192859Ssson * documentation and/or other materials provided with the distribution. 13192859Ssson * 14192859Ssson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15192859Ssson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16192859Ssson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17192859Ssson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18192859Ssson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19192859Ssson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20192859Ssson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21192859Ssson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22192859Ssson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23192859Ssson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24192859Ssson * SUCH DAMAGE. 25192859Ssson * 26192859Ssson * $FreeBSD: head/sys/dev/ksyms/ksyms.c 201145 2009-12-28 22:56:30Z antoine $ 27192859Ssson */ 28192859Ssson 29192859Ssson#include <sys/param.h> 30192859Ssson#include <sys/systm.h> 31192859Ssson#include <sys/kernel.h> 32192859Ssson 33192859Ssson#include <sys/conf.h> 34192859Ssson#include <sys/elf.h> 35192859Ssson#include <sys/ksyms.h> 36192859Ssson#include <sys/linker.h> 37192859Ssson#include <sys/malloc.h> 38192859Ssson#include <sys/mman.h> 39192859Ssson#include <sys/module.h> 40192859Ssson#include <sys/mutex.h> 41192859Ssson#include <sys/proc.h> 42192859Ssson#include <sys/queue.h> 43192859Ssson#include <sys/resourcevar.h> 44192859Ssson#include <sys/stat.h> 45192859Ssson#include <sys/uio.h> 46192859Ssson 47192859Ssson#include <machine/elf.h> 48192859Ssson 49192859Ssson#include <vm/pmap.h> 50192859Ssson#include <vm/vm.h> 51192859Ssson#include <vm/vm_extern.h> 52192859Ssson#include <vm/vm_map.h> 53192859Ssson 54192859Ssson#include "linker_if.h" 55192859Ssson 56192859Ssson#define SHDR_NULL 0 57192859Ssson#define SHDR_SYMTAB 1 58192859Ssson#define SHDR_STRTAB 2 59192859Ssson#define SHDR_SHSTRTAB 3 60192859Ssson 61192859Ssson#define SHDR_NUM 4 62192859Ssson 63192859Ssson#define STR_SYMTAB ".symtab" 64192859Ssson#define STR_STRTAB ".strtab" 65192859Ssson#define STR_SHSTRTAB ".shstrtab" 66192859Ssson 67192859Ssson#define KSYMS_DNAME "ksyms" 68192859Ssson 69192859Sssonstatic d_open_t ksyms_open; 70192859Sssonstatic d_read_t ksyms_read; 71192859Sssonstatic d_close_t ksyms_close; 72192859Sssonstatic d_ioctl_t ksyms_ioctl; 73192859Sssonstatic d_mmap_t ksyms_mmap; 74192859Ssson 75192859Sssonstatic struct cdevsw ksyms_cdevsw = { 76192859Ssson .d_version = D_VERSION, 77192859Ssson .d_flags = D_PSEUDO | D_TRACKCLOSE, 78192859Ssson .d_open = ksyms_open, 79192859Ssson .d_close = ksyms_close, 80192859Ssson .d_read = ksyms_read, 81192859Ssson .d_ioctl = ksyms_ioctl, 82192859Ssson .d_mmap = ksyms_mmap, 83192859Ssson .d_name = KSYMS_DNAME 84192859Ssson}; 85192859Ssson 86192859Sssonstruct ksyms_softc { 87192859Ssson LIST_ENTRY(ksyms_softc) sc_list; 88192859Ssson vm_offset_t sc_uaddr; 89192859Ssson size_t sc_usize; 90192859Ssson pmap_t sc_pmap; 91192859Ssson struct proc *sc_proc; 92192859Ssson}; 93192859Ssson 94192859Sssonstatic struct mtx ksyms_mtx; 95192859Sssonstatic struct cdev *ksyms_dev; 96192859Sssonstatic LIST_HEAD(, ksyms_softc) ksyms_list = 97201145Santoine LIST_HEAD_INITIALIZER(ksyms_list); 98192859Ssson 99192859Sssonstatic const char ksyms_shstrtab[] = 100192859Ssson "\0" STR_SYMTAB "\0" STR_STRTAB "\0" STR_SHSTRTAB "\0"; 101192859Ssson 102192859Sssonstruct ksyms_hdr { 103192859Ssson Elf_Ehdr kh_ehdr; 104192859Ssson Elf_Phdr kh_txtphdr; 105192859Ssson Elf_Phdr kh_datphdr; 106192859Ssson Elf_Shdr kh_shdr[SHDR_NUM]; 107192859Ssson char kh_shstrtab[sizeof(ksyms_shstrtab)]; 108192859Ssson}; 109192859Ssson 110192859Sssonstruct tsizes { 111192859Ssson size_t ts_symsz; 112192859Ssson size_t ts_strsz; 113192859Ssson}; 114192859Ssson 115192859Sssonstruct toffsets { 116192859Ssson vm_offset_t to_symoff; 117192859Ssson vm_offset_t to_stroff; 118192859Ssson unsigned to_stridx; 119192859Ssson size_t to_resid; 120192859Ssson}; 121192859Ssson 122192859Sssonstatic MALLOC_DEFINE(M_KSYMS, "KSYMS", "Kernel Symbol Table"); 123192859Ssson 124192859Ssson/* 125192859Ssson * Get the symbol and string table sizes for a kernel module. Add it to the 126192859Ssson * running total. 127192859Ssson */ 128192859Sssonstatic int 129192859Sssonksyms_size_permod(linker_file_t lf, void *arg) 130192859Ssson{ 131192859Ssson struct tsizes *ts; 132194016Savg const Elf_Sym *symtab; 133192859Ssson caddr_t strtab; 134192859Ssson long syms; 135192859Ssson 136192859Ssson ts = arg; 137192859Ssson 138192859Ssson syms = LINKER_SYMTAB_GET(lf, &symtab); 139192859Ssson ts->ts_symsz += syms * sizeof(Elf_Sym); 140192859Ssson ts->ts_strsz += LINKER_STRTAB_GET(lf, &strtab); 141192859Ssson 142192859Ssson return (0); 143192859Ssson} 144192859Ssson 145192859Ssson/* 146192859Ssson * For kernel module get the symbol and string table sizes, returning the 147192859Ssson * totals in *ts. 148192859Ssson */ 149192859Sssonstatic void 150192859Sssonksyms_size_calc(struct tsizes *ts) 151192859Ssson{ 152192859Ssson ts->ts_symsz = 0; 153192859Ssson ts->ts_strsz = 0; 154192859Ssson 155192859Ssson (void) linker_file_foreach(ksyms_size_permod, ts); 156192859Ssson} 157192859Ssson 158192859Ssson#define KSYMS_EMIT(src, des, sz) do { \ 159192859Ssson copyout(src, (void *)des, sz); \ 160192859Ssson des += sz; \ 161192859Ssson } while (0) 162192859Ssson 163192859Ssson#define SYMBLKSZ 256 * sizeof (Elf_Sym) 164192859Ssson 165192859Ssson/* 166192859Ssson * For a kernel module, add the symbol and string tables into the 167192859Ssson * snapshot buffer. Fix up the offsets in the tables. 168192859Ssson */ 169192859Sssonstatic int 170192859Sssonksyms_add(linker_file_t lf, void *arg) 171192859Ssson{ 172192859Ssson struct toffsets *to; 173194016Savg const Elf_Sym *symtab; 174194016Savg Elf_Sym *symp; 175192859Ssson caddr_t strtab; 176192859Ssson long symsz; 177192859Ssson size_t strsz, numsyms; 178192859Ssson linker_symval_t symval; 179192859Ssson char *buf; 180192859Ssson int i, nsyms, len; 181192859Ssson 182192859Ssson to = arg; 183192859Ssson 184192859Ssson MOD_SLOCK; 185192859Ssson numsyms = LINKER_SYMTAB_GET(lf, &symtab); 186192859Ssson strsz = LINKER_STRTAB_GET(lf, &strtab); 187192859Ssson symsz = numsyms * sizeof(Elf_Sym); 188192859Ssson 189192859Ssson buf = malloc(SYMBLKSZ, M_KSYMS, M_WAITOK); 190192859Ssson 191192859Ssson while (symsz > 0) { 192192859Ssson len = min(SYMBLKSZ, symsz); 193192859Ssson bcopy(symtab, buf, len); 194192859Ssson 195192859Ssson /* 196192859Ssson * Fix up symbol table for kernel modules: 197192859Ssson * string offsets need adjusted 198192859Ssson * symbol values made absolute 199192859Ssson */ 200192859Ssson symp = (Elf_Sym *) buf; 201192859Ssson nsyms = len / sizeof (Elf_Sym); 202192859Ssson for (i = 0; i < nsyms; i++) { 203192859Ssson symp[i].st_name += to->to_stridx; 204192859Ssson if (lf->id > 1 && LINKER_SYMBOL_VALUES(lf, 205192859Ssson (c_linker_sym_t) &symtab[i], &symval) == 0) { 206192859Ssson symp[i].st_value = (uintptr_t) symval.value; 207192859Ssson } 208192859Ssson } 209192859Ssson 210192859Ssson if (len > to->to_resid) { 211192859Ssson MOD_SUNLOCK; 212192859Ssson free(buf, M_KSYMS); 213192859Ssson return (ENXIO); 214192859Ssson } else 215192859Ssson to->to_resid -= len; 216192859Ssson KSYMS_EMIT(buf, to->to_symoff, len); 217192859Ssson 218192859Ssson symtab += nsyms; 219192859Ssson symsz -= len; 220192859Ssson } 221192859Ssson free(buf, M_KSYMS); 222192859Ssson MOD_SUNLOCK; 223192859Ssson 224192859Ssson if (strsz > to->to_resid) 225192859Ssson return (ENXIO); 226192859Ssson else 227192859Ssson to->to_resid -= strsz; 228192859Ssson KSYMS_EMIT(strtab, to->to_stroff, strsz); 229192859Ssson to->to_stridx += strsz; 230192859Ssson 231192859Ssson return (0); 232192859Ssson} 233192859Ssson 234192859Ssson/* 235192859Ssson * Create a single ELF symbol table for the kernel and kernel modules loaded 236192859Ssson * at this time. Write this snapshot out in the process address space. Return 237192859Ssson * 0 on success, otherwise error. 238192859Ssson */ 239192859Sssonstatic int 240192859Sssonksyms_snapshot(struct tsizes *ts, vm_offset_t uaddr, size_t resid) 241192859Ssson{ 242192859Ssson 243192859Ssson struct ksyms_hdr *hdr; 244192859Ssson struct toffsets to; 245192859Ssson int error = 0; 246192859Ssson 247192859Ssson /* Be kernel stack friendly */ 248192859Ssson hdr = malloc(sizeof (*hdr), M_KSYMS, M_WAITOK|M_ZERO); 249192859Ssson 250192859Ssson /* 251192859Ssson * Create the ELF header. 252192859Ssson */ 253192859Ssson hdr->kh_ehdr.e_ident[EI_PAD] = 0; 254192859Ssson hdr->kh_ehdr.e_ident[EI_MAG0] = ELFMAG0; 255192859Ssson hdr->kh_ehdr.e_ident[EI_MAG1] = ELFMAG1; 256192859Ssson hdr->kh_ehdr.e_ident[EI_MAG2] = ELFMAG2; 257192859Ssson hdr->kh_ehdr.e_ident[EI_MAG3] = ELFMAG3; 258192859Ssson hdr->kh_ehdr.e_ident[EI_DATA] = ELF_DATA; 259192859Ssson hdr->kh_ehdr.e_ident[EI_OSABI] = ELFOSABI_FREEBSD; 260192859Ssson hdr->kh_ehdr.e_ident[EI_CLASS] = ELF_CLASS; 261192859Ssson hdr->kh_ehdr.e_ident[EI_VERSION] = EV_CURRENT; 262192859Ssson hdr->kh_ehdr.e_ident[EI_ABIVERSION] = 0; 263192859Ssson hdr->kh_ehdr.e_type = ET_EXEC; 264192859Ssson hdr->kh_ehdr.e_machine = ELF_ARCH; 265192859Ssson hdr->kh_ehdr.e_version = EV_CURRENT; 266192859Ssson hdr->kh_ehdr.e_entry = 0; 267192859Ssson hdr->kh_ehdr.e_phoff = offsetof(struct ksyms_hdr, kh_txtphdr); 268192859Ssson hdr->kh_ehdr.e_shoff = offsetof(struct ksyms_hdr, kh_shdr); 269192859Ssson hdr->kh_ehdr.e_flags = 0; 270192859Ssson hdr->kh_ehdr.e_ehsize = sizeof(Elf_Ehdr); 271192859Ssson hdr->kh_ehdr.e_phentsize = sizeof(Elf_Phdr); 272192859Ssson hdr->kh_ehdr.e_phnum = 2; /* Text and Data */ 273192859Ssson hdr->kh_ehdr.e_shentsize = sizeof(Elf_Shdr); 274192859Ssson hdr->kh_ehdr.e_shnum = SHDR_NUM; 275192859Ssson hdr->kh_ehdr.e_shstrndx = SHDR_SHSTRTAB; 276192859Ssson 277192859Ssson /* 278192859Ssson * Add both the text and data Program headers. 279192859Ssson */ 280192859Ssson hdr->kh_txtphdr.p_type = PT_LOAD; 281192859Ssson /* XXX - is there a way to put the actual .text addr/size here? */ 282192859Ssson hdr->kh_txtphdr.p_vaddr = 0; 283192859Ssson hdr->kh_txtphdr.p_memsz = 0; 284192859Ssson hdr->kh_txtphdr.p_flags = PF_R | PF_X; 285192859Ssson 286192859Ssson hdr->kh_datphdr.p_type = PT_LOAD; 287192859Ssson /* XXX - is there a way to put the actual .data addr/size here? */ 288192859Ssson hdr->kh_datphdr.p_vaddr = 0; 289192859Ssson hdr->kh_datphdr.p_memsz = 0; 290192859Ssson hdr->kh_datphdr.p_flags = PF_R | PF_W | PF_X; 291192859Ssson 292192859Ssson /* 293192859Ssson * Add the Section headers: null, symtab, strtab, shstrtab, 294192859Ssson */ 295192859Ssson 296192859Ssson /* First section header - null */ 297192859Ssson 298192859Ssson /* Second section header - symtab */ 299192859Ssson hdr->kh_shdr[SHDR_SYMTAB].sh_name = 1; /* String offset (skip null) */ 300192859Ssson hdr->kh_shdr[SHDR_SYMTAB].sh_type = SHT_SYMTAB; 301192859Ssson hdr->kh_shdr[SHDR_SYMTAB].sh_flags = 0; 302192859Ssson hdr->kh_shdr[SHDR_SYMTAB].sh_addr = 0; 303192859Ssson hdr->kh_shdr[SHDR_SYMTAB].sh_offset = sizeof(*hdr); 304192859Ssson hdr->kh_shdr[SHDR_SYMTAB].sh_size = ts->ts_symsz; 305192859Ssson hdr->kh_shdr[SHDR_SYMTAB].sh_link = SHDR_STRTAB; 306192859Ssson hdr->kh_shdr[SHDR_SYMTAB].sh_info = ts->ts_symsz / sizeof(Elf_Sym); 307192859Ssson hdr->kh_shdr[SHDR_SYMTAB].sh_addralign = sizeof(long); 308192859Ssson hdr->kh_shdr[SHDR_SYMTAB].sh_entsize = sizeof(Elf_Sym); 309192859Ssson 310192859Ssson /* Third section header - strtab */ 311192859Ssson hdr->kh_shdr[SHDR_STRTAB].sh_name = 1 + sizeof(STR_SYMTAB); 312192859Ssson hdr->kh_shdr[SHDR_STRTAB].sh_type = SHT_STRTAB; 313192859Ssson hdr->kh_shdr[SHDR_STRTAB].sh_flags = 0; 314192859Ssson hdr->kh_shdr[SHDR_STRTAB].sh_addr = 0; 315192859Ssson hdr->kh_shdr[SHDR_STRTAB].sh_offset = 316192859Ssson hdr->kh_shdr[SHDR_SYMTAB].sh_offset + ts->ts_symsz; 317192859Ssson hdr->kh_shdr[SHDR_STRTAB].sh_size = ts->ts_strsz; 318192859Ssson hdr->kh_shdr[SHDR_STRTAB].sh_link = 0; 319192859Ssson hdr->kh_shdr[SHDR_STRTAB].sh_info = 0; 320192859Ssson hdr->kh_shdr[SHDR_STRTAB].sh_addralign = sizeof(char); 321192859Ssson hdr->kh_shdr[SHDR_STRTAB].sh_entsize = 0; 322192859Ssson 323192859Ssson /* Fourth section - shstrtab */ 324192859Ssson hdr->kh_shdr[SHDR_SHSTRTAB].sh_name = 1 + sizeof(STR_SYMTAB) + 325192859Ssson sizeof(STR_STRTAB); 326192859Ssson hdr->kh_shdr[SHDR_SHSTRTAB].sh_type = SHT_STRTAB; 327192859Ssson hdr->kh_shdr[SHDR_SHSTRTAB].sh_flags = 0; 328192859Ssson hdr->kh_shdr[SHDR_SHSTRTAB].sh_addr = 0; 329192859Ssson hdr->kh_shdr[SHDR_SHSTRTAB].sh_offset = 330192859Ssson offsetof(struct ksyms_hdr, kh_shstrtab); 331192859Ssson hdr->kh_shdr[SHDR_SHSTRTAB].sh_size = sizeof(ksyms_shstrtab); 332192859Ssson hdr->kh_shdr[SHDR_SHSTRTAB].sh_link = 0; 333192859Ssson hdr->kh_shdr[SHDR_SHSTRTAB].sh_info = 0; 334192859Ssson hdr->kh_shdr[SHDR_SHSTRTAB].sh_addralign = 0 /* sizeof(char) */; 335192859Ssson hdr->kh_shdr[SHDR_SHSTRTAB].sh_entsize = 0; 336192859Ssson 337192859Ssson /* Copy shstrtab into the header */ 338192859Ssson bcopy(ksyms_shstrtab, hdr->kh_shstrtab, sizeof(ksyms_shstrtab)); 339192859Ssson 340192859Ssson to.to_symoff = uaddr + hdr->kh_shdr[SHDR_SYMTAB].sh_offset; 341192859Ssson to.to_stroff = uaddr + hdr->kh_shdr[SHDR_STRTAB].sh_offset; 342192859Ssson to.to_stridx = 0; 343192859Ssson if (sizeof(struct ksyms_hdr) > resid) { 344192859Ssson free(hdr, M_KSYMS); 345192859Ssson return (ENXIO); 346192859Ssson } 347192859Ssson to.to_resid = resid - sizeof(struct ksyms_hdr); 348192859Ssson 349192859Ssson /* Emit Header */ 350192859Ssson copyout(hdr, (void *)uaddr, sizeof(struct ksyms_hdr)); 351192859Ssson 352192859Ssson free(hdr, M_KSYMS); 353192859Ssson 354192859Ssson /* Add symbol and string tables for each kernelmodule */ 355192859Ssson error = linker_file_foreach(ksyms_add, &to); 356192859Ssson 357192859Ssson if (to.to_resid != 0) 358192859Ssson return (ENXIO); 359192859Ssson 360192859Ssson return (error); 361192859Ssson} 362192859Ssson 363192859Ssson/* 364192859Ssson * Map some anonymous memory in user space of size sz, rounded up to the page 365192859Ssson * boundary. 366192859Ssson */ 367192859Sssonstatic int 368192859Sssonksyms_map(struct thread *td, vm_offset_t *addr, size_t sz) 369192859Ssson{ 370192859Ssson struct vmspace *vms = td->td_proc->p_vmspace; 371192859Ssson int error; 372192859Ssson vm_size_t size; 373192859Ssson 374192859Ssson 375192859Ssson /* 376192859Ssson * Map somewhere after heap in process memory. 377192859Ssson */ 378192859Ssson PROC_LOCK(td->td_proc); 379192859Ssson *addr = round_page((vm_offset_t)vms->vm_daddr + 380192859Ssson lim_max(td->td_proc, RLIMIT_DATA)); 381192859Ssson PROC_UNLOCK(td->td_proc); 382192859Ssson 383192859Ssson /* round size up to page boundry */ 384192859Ssson size = (vm_size_t) round_page(sz); 385192859Ssson 386192859Ssson error = vm_mmap(&vms->vm_map, addr, size, PROT_READ | PROT_WRITE, 387192859Ssson VM_PROT_ALL, MAP_PRIVATE | MAP_ANON, OBJT_DEFAULT, NULL, 0); 388192859Ssson 389192859Ssson return (error); 390192859Ssson} 391192859Ssson 392192859Ssson/* 393192859Ssson * Unmap memory in user space. 394192859Ssson */ 395192859Sssonstatic int 396192859Sssonksyms_unmap(struct thread *td, vm_offset_t addr, size_t sz) 397192859Ssson{ 398192859Ssson vm_map_t map; 399192859Ssson vm_size_t size; 400192859Ssson 401192859Ssson map = &td->td_proc->p_vmspace->vm_map; 402192859Ssson size = (vm_size_t) round_page(sz); 403192859Ssson 404192902Ssson if (!vm_map_remove(map, addr, addr + size)) 405192859Ssson return (EINVAL); 406192859Ssson 407192902Ssson return (0); 408192859Ssson} 409192859Ssson 410192859Sssonstatic void 411192859Sssonksyms_cdevpriv_dtr(void *data) 412192859Ssson{ 413192859Ssson struct ksyms_softc *sc; 414192859Ssson 415192859Ssson sc = (struct ksyms_softc *)data; 416192859Ssson 417192859Ssson mtx_lock(&ksyms_mtx); 418192859Ssson LIST_REMOVE(sc, sc_list); 419192859Ssson mtx_unlock(&ksyms_mtx); 420192859Ssson free(sc, M_KSYMS); 421192859Ssson} 422192859Ssson 423192859Ssson/* ARGSUSED */ 424192859Sssonstatic int 425192859Sssonksyms_open(struct cdev *dev, int flags, int fmt __unused, struct thread *td) 426192859Ssson{ 427192859Ssson struct tsizes ts; 428192859Ssson size_t total_elf_sz; 429192859Ssson int error, try; 430192859Ssson struct ksyms_softc *sc; 431192859Ssson 432192859Ssson /* 433192859Ssson * Limit one open() per process. The process must close() 434192859Ssson * before open()'ing again. 435192859Ssson */ 436192859Ssson mtx_lock(&ksyms_mtx); 437192859Ssson LIST_FOREACH(sc, &ksyms_list, sc_list) { 438192859Ssson if (sc->sc_proc == td->td_proc) { 439192859Ssson mtx_unlock(&ksyms_mtx); 440192859Ssson return (EBUSY); 441192859Ssson } 442192859Ssson } 443192859Ssson 444192859Ssson sc = (struct ksyms_softc *) malloc(sizeof (*sc), M_KSYMS, 445192859Ssson M_NOWAIT|M_ZERO); 446192859Ssson 447192859Ssson if (sc == NULL) { 448192859Ssson mtx_unlock(&ksyms_mtx); 449192859Ssson return (ENOMEM); 450192859Ssson } 451192859Ssson sc->sc_proc = td->td_proc; 452192859Ssson sc->sc_pmap = &td->td_proc->p_vmspace->vm_pmap; 453192859Ssson LIST_INSERT_HEAD(&ksyms_list, sc, sc_list); 454192859Ssson mtx_unlock(&ksyms_mtx); 455192859Ssson 456192859Ssson error = devfs_set_cdevpriv(sc, ksyms_cdevpriv_dtr); 457192859Ssson if (error) 458192859Ssson goto failed; 459192859Ssson 460192859Ssson /* 461192859Ssson * MOD_SLOCK doesn't work here (because of a lock reversal with 462192859Ssson * KLD_SLOCK). Therefore, simply try upto 3 times to get a "clean" 463192859Ssson * snapshot of the kernel symbol table. This should work fine in the 464192859Ssson * rare case of a kernel module being loaded/unloaded at the same 465192859Ssson * time. 466192859Ssson */ 467192859Ssson for(try = 0; try < 3; try++) { 468192859Ssson /* 469192859Ssson * Map a buffer in the calling process memory space and 470192859Ssson * create a snapshot of the kernel symbol table in it. 471192859Ssson */ 472192859Ssson 473192859Ssson /* Compute the size of buffer needed. */ 474192859Ssson ksyms_size_calc(&ts); 475192859Ssson total_elf_sz = sizeof(struct ksyms_hdr) + ts.ts_symsz + 476192859Ssson ts.ts_strsz; 477192859Ssson 478192859Ssson error = ksyms_map(td, &(sc->sc_uaddr), 479192859Ssson (vm_size_t) total_elf_sz); 480192859Ssson if (error) 481192859Ssson break; 482192859Ssson sc->sc_usize = total_elf_sz; 483192859Ssson 484192859Ssson error = ksyms_snapshot(&ts, sc->sc_uaddr, total_elf_sz); 485192859Ssson if (!error) { 486192859Ssson /* Successful Snapshot */ 487192859Ssson return (0); 488192859Ssson } 489192859Ssson 490192859Ssson /* Snapshot failed, unmap the memory and try again */ 491192859Ssson (void) ksyms_unmap(td, sc->sc_uaddr, sc->sc_usize); 492192859Ssson } 493192859Ssson 494192859Sssonfailed: 495192859Ssson ksyms_cdevpriv_dtr(sc); 496192859Ssson return (error); 497192859Ssson} 498192859Ssson 499192859Ssson/* ARGSUSED */ 500192859Sssonstatic int 501192859Sssonksyms_read(struct cdev *dev, struct uio *uio, int flags __unused) 502192859Ssson{ 503192859Ssson int error; 504192859Ssson size_t len, sz; 505192859Ssson struct ksyms_softc *sc; 506192859Ssson off_t off; 507192859Ssson char *buf; 508192859Ssson vm_size_t ubase; 509192859Ssson 510192859Ssson error = devfs_get_cdevpriv((void **)&sc); 511192859Ssson if (error) 512192859Ssson return (error); 513192859Ssson 514192859Ssson off = uio->uio_offset; 515192859Ssson len = uio->uio_resid; 516192859Ssson 517192859Ssson if (off < 0 || off > sc->sc_usize) 518192859Ssson return (EFAULT); 519192859Ssson 520192859Ssson if (len > (sc->sc_usize - off)) 521192859Ssson len = sc->sc_usize - off; 522192859Ssson 523192859Ssson if (len == 0) 524192859Ssson return (0); 525192859Ssson 526192859Ssson /* 527192859Ssson * Since the snapshot buffer is in the user space we have to copy it 528192859Ssson * in to the kernel and then back out. The extra copy saves valuable 529192859Ssson * kernel memory. 530192859Ssson */ 531192859Ssson buf = malloc(PAGE_SIZE, M_KSYMS, M_WAITOK); 532192859Ssson ubase = sc->sc_uaddr + off; 533192859Ssson 534192859Ssson while (len) { 535192859Ssson 536192859Ssson sz = min(PAGE_SIZE, len); 537192859Ssson if (copyin((void *)ubase, buf, sz)) 538192859Ssson error = EFAULT; 539192859Ssson else 540192859Ssson error = uiomove(buf, sz, uio); 541192859Ssson 542192859Ssson if (error) 543192859Ssson break; 544192859Ssson 545192859Ssson len -= sz; 546192859Ssson ubase += sz; 547192859Ssson } 548192859Ssson free(buf, M_KSYMS); 549192859Ssson 550192859Ssson return (error); 551192859Ssson} 552192859Ssson 553192859Ssson/* ARGSUSED */ 554192859Sssonstatic int 555192859Sssonksyms_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int32_t flag __unused, 556193278Sjhb struct thread *td __unused) 557192859Ssson{ 558192859Ssson int error = 0; 559192859Ssson struct ksyms_softc *sc; 560192859Ssson 561192859Ssson error = devfs_get_cdevpriv((void **)&sc); 562192859Ssson if (error) 563192859Ssson return (error); 564192859Ssson 565192859Ssson switch (cmd) { 566192859Ssson case KIOCGSIZE: 567192859Ssson /* 568192859Ssson * Return the size (in bytes) of the symbol table 569192859Ssson * snapshot. 570192859Ssson */ 571192859Ssson *(size_t *)data = sc->sc_usize; 572192859Ssson break; 573192859Ssson 574192859Ssson case KIOCGADDR: 575192859Ssson /* 576192859Ssson * Return the address of the symbol table snapshot. 577192859Ssson * XXX - compat32 version of this? 578192859Ssson */ 579192859Ssson *(void **)data = (void *)sc->sc_uaddr; 580192859Ssson break; 581192859Ssson 582192859Ssson default: 583192859Ssson error = ENOTTY; 584192859Ssson break; 585192859Ssson } 586192859Ssson 587192859Ssson return (error); 588192859Ssson} 589192859Ssson 590192859Ssson/* ARGUSED */ 591192859Sssonstatic int 592192859Sssonksyms_mmap(struct cdev *dev, vm_offset_t offset, vm_paddr_t *paddr, 593192859Ssson int prot __unused) 594192859Ssson{ 595192859Ssson struct ksyms_softc *sc; 596192859Ssson int error; 597192859Ssson 598192859Ssson error = devfs_get_cdevpriv((void **)&sc); 599192859Ssson if (error) 600192859Ssson return (error); 601192859Ssson 602192859Ssson /* 603192859Ssson * XXX mmap() will actually map the symbol table into the process 604192859Ssson * address space again. 605192859Ssson */ 606192859Ssson if (offset > round_page(sc->sc_usize) || 607192859Ssson (*paddr = pmap_extract(sc->sc_pmap, 608192859Ssson (vm_offset_t)sc->sc_uaddr + offset)) == 0) 609192859Ssson return (-1); 610192859Ssson 611192859Ssson return (0); 612192859Ssson} 613192859Ssson 614192859Ssson/* ARGUSED */ 615192859Sssonstatic int 616192859Sssonksyms_close(struct cdev *dev, int flags __unused, int fmt __unused, 617192859Ssson struct thread *td) 618192859Ssson{ 619192859Ssson int error = 0; 620192859Ssson struct ksyms_softc *sc; 621192859Ssson 622192859Ssson error = devfs_get_cdevpriv((void **)&sc); 623192859Ssson if (error) 624192859Ssson return (error); 625192859Ssson 626192859Ssson /* Unmap the buffer from the process address space. */ 627192859Ssson error = ksyms_unmap(td, sc->sc_uaddr, sc->sc_usize); 628192859Ssson 629192859Ssson devfs_clear_cdevpriv(); 630192859Ssson 631192859Ssson return (error); 632192859Ssson} 633192859Ssson 634192859Ssson/* ARGSUSED */ 635192859Sssonstatic int 636192859Sssonksyms_modevent(module_t mod __unused, int type, void *data __unused) 637192859Ssson{ 638192859Ssson int error = 0; 639192859Ssson 640192859Ssson switch (type) { 641192859Ssson case MOD_LOAD: 642192859Ssson mtx_init(&ksyms_mtx, "KSyms mtx", NULL, MTX_DEF); 643192859Ssson ksyms_dev = make_dev(&ksyms_cdevsw, 0, UID_ROOT, GID_WHEEL, 644192859Ssson 0444, KSYMS_DNAME); 645192859Ssson break; 646192859Ssson 647192859Ssson case MOD_UNLOAD: 648192859Ssson if (!LIST_EMPTY(&ksyms_list)) 649192859Ssson return (EBUSY); 650192859Ssson destroy_dev(ksyms_dev); 651192859Ssson mtx_destroy(&ksyms_mtx); 652192859Ssson break; 653192859Ssson 654192859Ssson case MOD_SHUTDOWN: 655192859Ssson break; 656192859Ssson 657192859Ssson default: 658192859Ssson error = EOPNOTSUPP; 659192859Ssson break; 660192859Ssson } 661192859Ssson return (error); 662192859Ssson} 663192859Ssson 664192859SssonDEV_MODULE(ksyms, ksyms_modevent, NULL); 665192859SssonMODULE_VERSION(ksyms, 1); 666