The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/ksyms/ksyms.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 2008-2009, Stacey Son <sson@freebsd.org>
    5  * All rights reserved.
    6  *
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions
    9  * are met:
   10  * 1. Redistributions of source code must retain the above copyright
   11  *    notice, this list of conditions and the following disclaimer.
   12  * 2. Redistributions in binary form must reproduce the above copyright
   13  *    notice, this list of conditions and the following disclaimer in the
   14  *    documentation and/or other materials provided with the distribution.
   15  *
   16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   26  * SUCH DAMAGE.
   27  *
   28  * $FreeBSD: releng/12.0/sys/dev/ksyms/ksyms.c 326255 2017-11-27 14:52:40Z pfg $
   29  */
   30 
   31 #include <sys/param.h>
   32 #include <sys/systm.h>
   33 #include <sys/kernel.h>
   34 
   35 #include <sys/conf.h>
   36 #include <sys/elf.h>
   37 #include <sys/linker.h>
   38 #include <sys/malloc.h>
   39 #include <sys/mman.h>
   40 #include <sys/module.h>
   41 #include <sys/proc.h>
   42 #include <sys/queue.h>
   43 #include <sys/resourcevar.h>
   44 #include <sys/stat.h>
   45 #include <sys/sx.h>
   46 #include <sys/uio.h>
   47 
   48 #include <machine/elf.h>
   49 
   50 #include <vm/pmap.h>
   51 #include <vm/vm.h>
   52 #include <vm/vm_extern.h>
   53 #include <vm/vm_object.h>
   54 
   55 #include "linker_if.h"
   56 
   57 #define SHDR_NULL       0
   58 #define SHDR_SYMTAB     1
   59 #define SHDR_STRTAB     2
   60 #define SHDR_SHSTRTAB   3
   61 
   62 #define SHDR_NUM        4
   63 
   64 #define STR_SYMTAB      ".symtab"
   65 #define STR_STRTAB      ".strtab"
   66 #define STR_SHSTRTAB    ".shstrtab"
   67 
   68 #define KSYMS_DNAME     "ksyms"
   69 
   70 static d_open_t ksyms_open;
   71 static d_read_t ksyms_read;
   72 static d_mmap_single_t ksyms_mmap_single;
   73 
   74 static struct cdevsw ksyms_cdevsw = {
   75         .d_version =    D_VERSION,
   76         .d_flags =      0,
   77         .d_open =       ksyms_open,
   78         .d_read =       ksyms_read,
   79         .d_mmap_single = ksyms_mmap_single,
   80         .d_name =       KSYMS_DNAME
   81 };
   82 
   83 struct ksyms_softc {
   84         LIST_ENTRY(ksyms_softc) sc_list;
   85         vm_offset_t             sc_uaddr;
   86         size_t                  sc_usize;
   87         vm_object_t             sc_obj;
   88         vm_size_t               sc_objsz;
   89         struct proc            *sc_proc;
   90 };
   91 
   92 static struct sx                 ksyms_mtx;
   93 static struct cdev              *ksyms_dev;
   94 static LIST_HEAD(, ksyms_softc)  ksyms_list = LIST_HEAD_INITIALIZER(ksyms_list);
   95 
   96 static const char       ksyms_shstrtab[] =
   97         "\0" STR_SYMTAB "\0" STR_STRTAB "\0" STR_SHSTRTAB "\0";
   98 
   99 struct ksyms_hdr {
  100         Elf_Ehdr        kh_ehdr;
  101         Elf_Phdr        kh_txtphdr;
  102         Elf_Phdr        kh_datphdr;
  103         Elf_Shdr        kh_shdr[SHDR_NUM];
  104         char            kh_shstrtab[sizeof(ksyms_shstrtab)];
  105 };
  106 
  107 struct tsizes {
  108         size_t          ts_symsz;
  109         size_t          ts_strsz;
  110 };
  111 
  112 struct toffsets {
  113         struct ksyms_softc *to_sc;
  114         vm_offset_t     to_symoff;
  115         vm_offset_t     to_stroff;
  116         unsigned        to_stridx;
  117         size_t          to_resid;
  118 };
  119 
  120 static MALLOC_DEFINE(M_KSYMS, "KSYMS", "Kernel Symbol Table");
  121 
  122 /*
  123  * Get the symbol and string table sizes for a kernel module. Add it to the
  124  * running total.
  125  */
  126 static int
  127 ksyms_size_permod(linker_file_t lf, void *arg)
  128 {
  129         struct tsizes *ts;
  130         const Elf_Sym *symtab;
  131         caddr_t strtab;
  132         long syms;
  133 
  134         ts = arg;
  135 
  136         syms = LINKER_SYMTAB_GET(lf, &symtab);
  137         ts->ts_symsz += syms * sizeof(Elf_Sym);
  138         ts->ts_strsz += LINKER_STRTAB_GET(lf, &strtab);
  139 
  140         return (0);
  141 }
  142 
  143 /*
  144  * For kernel module get the symbol and string table sizes, returning the
  145  * totals in *ts.
  146  */
  147 static void
  148 ksyms_size_calc(struct tsizes *ts)
  149 {
  150 
  151         ts->ts_symsz = 0;
  152         ts->ts_strsz = 0;
  153 
  154         (void)linker_file_foreach(ksyms_size_permod, ts);
  155 }
  156 
  157 static int
  158 ksyms_emit(struct ksyms_softc *sc, void *buf, off_t off, size_t sz)
  159 {
  160         struct iovec iov;
  161         struct uio uio;
  162 
  163         iov.iov_base = buf;
  164         iov.iov_len = sz;
  165         uio.uio_iov = &iov;
  166         uio.uio_iovcnt = 1;
  167         uio.uio_offset = off;
  168         uio.uio_resid = (ssize_t)sz;
  169         uio.uio_segflg = UIO_SYSSPACE;
  170         uio.uio_rw = UIO_WRITE;
  171         uio.uio_td = curthread;
  172 
  173         return (uiomove_object(sc->sc_obj, sc->sc_objsz, &uio));
  174 }
  175 
  176 #define SYMBLKSZ        (256 * sizeof(Elf_Sym))
  177 
  178 /*
  179  * For a kernel module, add the symbol and string tables into the
  180  * snapshot buffer.  Fix up the offsets in the tables.
  181  */
  182 static int
  183 ksyms_add(linker_file_t lf, void *arg)
  184 {
  185         char *buf;
  186         struct ksyms_softc *sc;
  187         struct toffsets *to;
  188         const Elf_Sym *symtab;
  189         Elf_Sym *symp;
  190         caddr_t strtab;
  191         size_t len, numsyms, strsz, symsz;
  192         linker_symval_t symval;
  193         int error, i, nsyms;
  194 
  195         buf = malloc(SYMBLKSZ, M_KSYMS, M_WAITOK);
  196         to = arg;
  197         sc = to->to_sc;
  198 
  199         MOD_SLOCK;
  200         numsyms =  LINKER_SYMTAB_GET(lf, &symtab);
  201         strsz = LINKER_STRTAB_GET(lf, &strtab);
  202         symsz = numsyms * sizeof(Elf_Sym);
  203 
  204         while (symsz > 0) {
  205                 len = min(SYMBLKSZ, symsz);
  206                 bcopy(symtab, buf, len);
  207 
  208                 /*
  209                  * Fix up symbol table for kernel modules:
  210                  *   string offsets need adjusted
  211                  *   symbol values made absolute
  212                  */
  213                 symp = (Elf_Sym *) buf;
  214                 nsyms = len / sizeof(Elf_Sym);
  215                 for (i = 0; i < nsyms; i++) {
  216                         symp[i].st_name += to->to_stridx;
  217                         if (lf->id > 1 && LINKER_SYMBOL_VALUES(lf,
  218                             (c_linker_sym_t)&symtab[i], &symval) == 0) {
  219                                 symp[i].st_value = (uintptr_t)symval.value;
  220                         }
  221                 }
  222 
  223                 if (len > to->to_resid) {
  224                         MOD_SUNLOCK;
  225                         free(buf, M_KSYMS);
  226                         return (ENXIO);
  227                 }
  228                 to->to_resid -= len;
  229                 error = ksyms_emit(sc, buf, to->to_symoff, len);
  230                 to->to_symoff += len;
  231                 if (error != 0) {
  232                         MOD_SUNLOCK;
  233                         free(buf, M_KSYMS);
  234                         return (error);
  235                 }
  236 
  237                 symtab += nsyms;
  238                 symsz -= len;
  239         }
  240         free(buf, M_KSYMS);
  241         MOD_SUNLOCK;
  242 
  243         if (strsz > to->to_resid)
  244                 return (ENXIO);
  245         to->to_resid -= strsz;
  246         error = ksyms_emit(sc, strtab, to->to_stroff, strsz);
  247         to->to_stroff += strsz;
  248         to->to_stridx += strsz;
  249 
  250         return (error);
  251 }
  252 
  253 /*
  254  * Create a single ELF symbol table for the kernel and kernel modules loaded
  255  * at this time. Write this snapshot out in the process address space. Return
  256  * 0 on success, otherwise error.
  257  */
  258 static int
  259 ksyms_snapshot(struct ksyms_softc *sc, struct tsizes *ts)
  260 {
  261         struct toffsets to;
  262         struct ksyms_hdr *hdr;
  263         int error;
  264 
  265         hdr = malloc(sizeof(*hdr), M_KSYMS, M_WAITOK | M_ZERO);
  266 
  267         /*
  268          * Create the ELF header.
  269          */
  270         hdr->kh_ehdr.e_ident[EI_PAD] = 0;
  271         hdr->kh_ehdr.e_ident[EI_MAG0] = ELFMAG0;
  272         hdr->kh_ehdr.e_ident[EI_MAG1] = ELFMAG1;
  273         hdr->kh_ehdr.e_ident[EI_MAG2] = ELFMAG2;
  274         hdr->kh_ehdr.e_ident[EI_MAG3] = ELFMAG3;
  275         hdr->kh_ehdr.e_ident[EI_DATA] = ELF_DATA;
  276         hdr->kh_ehdr.e_ident[EI_OSABI] = ELFOSABI_FREEBSD;
  277         hdr->kh_ehdr.e_ident[EI_CLASS] = ELF_CLASS;
  278         hdr->kh_ehdr.e_ident[EI_VERSION] = EV_CURRENT;
  279         hdr->kh_ehdr.e_ident[EI_ABIVERSION] = 0;
  280         hdr->kh_ehdr.e_type = ET_EXEC;
  281         hdr->kh_ehdr.e_machine = ELF_ARCH;
  282         hdr->kh_ehdr.e_version = EV_CURRENT;
  283         hdr->kh_ehdr.e_entry = 0;
  284         hdr->kh_ehdr.e_phoff = offsetof(struct ksyms_hdr, kh_txtphdr);
  285         hdr->kh_ehdr.e_shoff = offsetof(struct ksyms_hdr, kh_shdr);
  286         hdr->kh_ehdr.e_flags = 0;
  287         hdr->kh_ehdr.e_ehsize = sizeof(Elf_Ehdr);
  288         hdr->kh_ehdr.e_phentsize = sizeof(Elf_Phdr);
  289         hdr->kh_ehdr.e_phnum = 2;       /* Text and Data */
  290         hdr->kh_ehdr.e_shentsize = sizeof(Elf_Shdr);
  291         hdr->kh_ehdr.e_shnum = SHDR_NUM;
  292         hdr->kh_ehdr.e_shstrndx = SHDR_SHSTRTAB;
  293 
  294         /*
  295          * Add both the text and data program headers.
  296          */
  297         hdr->kh_txtphdr.p_type = PT_LOAD;
  298         /* XXX - is there a way to put the actual .text addr/size here? */
  299         hdr->kh_txtphdr.p_vaddr = 0;
  300         hdr->kh_txtphdr.p_memsz = 0;
  301         hdr->kh_txtphdr.p_flags = PF_R | PF_X;
  302 
  303         hdr->kh_datphdr.p_type = PT_LOAD;
  304         /* XXX - is there a way to put the actual .data addr/size here? */
  305         hdr->kh_datphdr.p_vaddr = 0;
  306         hdr->kh_datphdr.p_memsz = 0;
  307         hdr->kh_datphdr.p_flags = PF_R | PF_W | PF_X;
  308 
  309         /*
  310          * Add the section headers: null, symtab, strtab, shstrtab.
  311          */
  312 
  313         /* First section header - null */
  314 
  315         /* Second section header - symtab */
  316         hdr->kh_shdr[SHDR_SYMTAB].sh_name = 1; /* String offset (skip null) */
  317         hdr->kh_shdr[SHDR_SYMTAB].sh_type = SHT_SYMTAB;
  318         hdr->kh_shdr[SHDR_SYMTAB].sh_flags = 0;
  319         hdr->kh_shdr[SHDR_SYMTAB].sh_addr = 0;
  320         hdr->kh_shdr[SHDR_SYMTAB].sh_offset = sizeof(*hdr);
  321         hdr->kh_shdr[SHDR_SYMTAB].sh_size = ts->ts_symsz;
  322         hdr->kh_shdr[SHDR_SYMTAB].sh_link = SHDR_STRTAB;
  323         hdr->kh_shdr[SHDR_SYMTAB].sh_info = ts->ts_symsz / sizeof(Elf_Sym);
  324         hdr->kh_shdr[SHDR_SYMTAB].sh_addralign = sizeof(long);
  325         hdr->kh_shdr[SHDR_SYMTAB].sh_entsize = sizeof(Elf_Sym);
  326 
  327         /* Third section header - strtab */
  328         hdr->kh_shdr[SHDR_STRTAB].sh_name = 1 + sizeof(STR_SYMTAB);
  329         hdr->kh_shdr[SHDR_STRTAB].sh_type = SHT_STRTAB;
  330         hdr->kh_shdr[SHDR_STRTAB].sh_flags = 0;
  331         hdr->kh_shdr[SHDR_STRTAB].sh_addr = 0;
  332         hdr->kh_shdr[SHDR_STRTAB].sh_offset =
  333             hdr->kh_shdr[SHDR_SYMTAB].sh_offset + ts->ts_symsz;
  334         hdr->kh_shdr[SHDR_STRTAB].sh_size = ts->ts_strsz;
  335         hdr->kh_shdr[SHDR_STRTAB].sh_link = 0;
  336         hdr->kh_shdr[SHDR_STRTAB].sh_info = 0;
  337         hdr->kh_shdr[SHDR_STRTAB].sh_addralign = sizeof(char);
  338         hdr->kh_shdr[SHDR_STRTAB].sh_entsize = 0;
  339 
  340         /* Fourth section - shstrtab */
  341         hdr->kh_shdr[SHDR_SHSTRTAB].sh_name = 1 + sizeof(STR_SYMTAB) +
  342             sizeof(STR_STRTAB);
  343         hdr->kh_shdr[SHDR_SHSTRTAB].sh_type = SHT_STRTAB;
  344         hdr->kh_shdr[SHDR_SHSTRTAB].sh_flags = 0;
  345         hdr->kh_shdr[SHDR_SHSTRTAB].sh_addr = 0;
  346         hdr->kh_shdr[SHDR_SHSTRTAB].sh_offset =
  347             offsetof(struct ksyms_hdr, kh_shstrtab);
  348         hdr->kh_shdr[SHDR_SHSTRTAB].sh_size = sizeof(ksyms_shstrtab);
  349         hdr->kh_shdr[SHDR_SHSTRTAB].sh_link = 0;
  350         hdr->kh_shdr[SHDR_SHSTRTAB].sh_info = 0;
  351         hdr->kh_shdr[SHDR_SHSTRTAB].sh_addralign = 0 /* sizeof(char) */;
  352         hdr->kh_shdr[SHDR_SHSTRTAB].sh_entsize = 0;
  353 
  354         /* Copy shstrtab into the header. */
  355         bcopy(ksyms_shstrtab, hdr->kh_shstrtab, sizeof(ksyms_shstrtab));
  356 
  357         to.to_sc = sc;
  358         to.to_symoff = hdr->kh_shdr[SHDR_SYMTAB].sh_offset;
  359         to.to_stroff = hdr->kh_shdr[SHDR_STRTAB].sh_offset;
  360         to.to_stridx = 0;
  361         to.to_resid = sc->sc_objsz - sizeof(struct ksyms_hdr);
  362 
  363         /* emit header */
  364         error = ksyms_emit(sc, hdr, 0, sizeof(*hdr));
  365         free(hdr, M_KSYMS);
  366         if (error != 0)
  367                 return (error);
  368 
  369         /* Add symbol and string tables for each kernel module. */
  370         error = linker_file_foreach(ksyms_add, &to);
  371         if (error != 0)
  372                 return (error);
  373         if (to.to_resid != 0)
  374                 return (ENXIO);
  375         return (0);
  376 }
  377 
  378 static void
  379 ksyms_cdevpriv_dtr(void *data)
  380 {
  381         struct ksyms_softc *sc;
  382         vm_object_t obj;
  383 
  384         sc = (struct ksyms_softc *)data;
  385 
  386         sx_xlock(&ksyms_mtx);
  387         LIST_REMOVE(sc, sc_list);
  388         sx_xunlock(&ksyms_mtx);
  389         obj = sc->sc_obj;
  390         if (obj != NULL)
  391                 vm_object_deallocate(obj);
  392         free(sc, M_KSYMS);
  393 }
  394 
  395 static int
  396 ksyms_open(struct cdev *dev, int flags, int fmt __unused, struct thread *td)
  397 {
  398         struct tsizes ts;
  399         struct ksyms_softc *sc;
  400         vm_size_t elfsz;
  401         int error, try;
  402 
  403         /*
  404          * Limit one open() per process. The process must close()
  405          * before open()'ing again.
  406          */
  407         sx_xlock(&ksyms_mtx);
  408         LIST_FOREACH(sc, &ksyms_list, sc_list) {
  409                 if (sc->sc_proc == td->td_proc) {
  410                         sx_xunlock(&ksyms_mtx);
  411                         return (EBUSY);
  412                 }
  413         }
  414 
  415         sc = malloc(sizeof(*sc), M_KSYMS, M_WAITOK | M_ZERO);
  416         sc->sc_proc = td->td_proc;
  417         LIST_INSERT_HEAD(&ksyms_list, sc, sc_list);
  418         sx_xunlock(&ksyms_mtx);
  419 
  420         error = devfs_set_cdevpriv(sc, ksyms_cdevpriv_dtr);
  421         if (error != 0) {
  422                 ksyms_cdevpriv_dtr(sc);
  423                 return (error);
  424         }
  425 
  426         /*
  427          * MOD_SLOCK doesn't work here (because of a lock reversal with
  428          * KLD_SLOCK).  Therefore, simply try up to 3 times to get a "clean"
  429          * snapshot of the kernel symbol table.  This should work fine in the
  430          * rare case of a kernel module being loaded/unloaded at the same
  431          * time.
  432          */
  433         for (try = 0; try < 3; try++) {
  434                 ksyms_size_calc(&ts);
  435                 elfsz = sizeof(struct ksyms_hdr) + ts.ts_symsz + ts.ts_strsz;
  436 
  437                 sc->sc_obj = vm_object_allocate(OBJT_DEFAULT,
  438                     OFF_TO_IDX(round_page(elfsz)));
  439                 sc->sc_objsz = elfsz;
  440 
  441                 error = ksyms_snapshot(sc, &ts);
  442                 if (error == 0)
  443                         break;
  444 
  445                 vm_object_deallocate(sc->sc_obj);
  446                 sc->sc_obj = NULL;
  447         }
  448         return (error);
  449 }
  450 
  451 static int
  452 ksyms_read(struct cdev *dev, struct uio *uio, int flags __unused)
  453 {
  454         struct ksyms_softc *sc;
  455         int error;
  456 
  457         error = devfs_get_cdevpriv((void **)&sc);
  458         if (error != 0)
  459                 return (error);
  460         return (uiomove_object(sc->sc_obj, sc->sc_objsz, uio));
  461 }
  462 
  463 static int
  464 ksyms_mmap_single(struct cdev *dev, vm_ooffset_t *offset, vm_size_t size,
  465     vm_object_t *objp, int nprot)
  466 {
  467         struct ksyms_softc *sc;
  468         vm_object_t obj;
  469         int error;
  470 
  471         error = devfs_get_cdevpriv((void **)&sc);
  472         if (error != 0)
  473                 return (error);
  474 
  475         if (*offset < 0 || *offset >= round_page(sc->sc_objsz) ||
  476             size > round_page(sc->sc_objsz) - *offset ||
  477             (nprot & ~PROT_READ) != 0)
  478                 return (EINVAL);
  479 
  480         obj = sc->sc_obj;
  481         vm_object_reference(obj);
  482         *objp = obj;
  483         return (0);
  484 }
  485 
  486 static int
  487 ksyms_modevent(module_t mod __unused, int type, void *data __unused)
  488 {
  489         int error;
  490 
  491         error = 0;
  492         switch (type) {
  493         case MOD_LOAD:
  494                 sx_init(&ksyms_mtx, "KSyms mtx");
  495                 ksyms_dev = make_dev(&ksyms_cdevsw, 0, UID_ROOT, GID_WHEEL,
  496                     0400, KSYMS_DNAME);
  497                 break;
  498         case MOD_UNLOAD:
  499                 if (!LIST_EMPTY(&ksyms_list))
  500                         return (EBUSY);
  501                 destroy_dev(ksyms_dev);
  502                 sx_destroy(&ksyms_mtx);
  503                 break;
  504         case MOD_SHUTDOWN:
  505                 break;
  506         default:
  507                 error = EOPNOTSUPP;
  508                 break;
  509         }
  510         return (error);
  511 }
  512 
  513 DEV_MODULE(ksyms, ksyms_modevent, NULL);
  514 MODULE_VERSION(ksyms, 1);

Cache object: 9a3d07d24aa42d87c347fbcde8a90971


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.