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/emulation/ndis/subr_pe.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  * Copyright (c) 2003
    3  *      Bill Paul <wpaul@windriver.com>.  All rights reserved.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions
    7  * are met:
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice, this list of conditions and the following disclaimer.
   10  * 2. Redistributions in binary form must reproduce the above copyright
   11  *    notice, this list of conditions and the following disclaimer in the
   12  *    documentation and/or other materials provided with the distribution.
   13  * 3. All advertising materials mentioning features or use of this software
   14  *    must display the following acknowledgement:
   15  *      This product includes software developed by Bill Paul.
   16  * 4. Neither the name of the author nor the names of any co-contributors
   17  *    may be used to endorse or promote products derived from this software
   18  *    without specific prior written permission.
   19  *
   20  * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
   21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   23  * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
   24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
   30  * THE POSSIBILITY OF SUCH DAMAGE.
   31  *
   32  * $FreeBSD: src/sys/compat/ndis/subr_pe.c,v 1.15 2009/11/02 11:07:42 rpaulo Exp $
   33  */
   34 
   35 /*
   36  * This file contains routines for relocating and dynamically linking
   37  * executable object code files in the Windows(r) PE (Portable Executable)
   38  * format. In Windows, anything with a .EXE, .DLL or .SYS extention is
   39  * considered an executable, and all such files have some structures in
   40  * common. The PE format was apparently based largely on COFF but has
   41  * mutated significantly over time. We are mainly concerned with .SYS files,
   42  * so this module implements only enough routines to be able to parse the
   43  * headers and sections of a .SYS object file and perform the necessary
   44  * relocations and jump table patching to allow us to call into it
   45  * (and to have it call back to us). Note that while this module
   46  * can handle fixups for imported symbols, it knows nothing about
   47  * exporting them.
   48  */
   49 
   50 #include <sys/param.h>
   51 #include <sys/types.h>
   52 #include <sys/errno.h>
   53 #ifdef _KERNEL
   54 #include <sys/systm.h>
   55 #else
   56 #include <stdio.h>
   57 #include <stddef.h>
   58 #include <stdlib.h>
   59 #include <unistd.h>
   60 #include <string.h>
   61 #define kprintf         printf  /* ndiscvt(8) uses this file */
   62 #endif
   63 
   64 #include <emulation/ndis/pe_var.h>
   65 
   66 static vm_offset_t pe_functbl_match(image_patch_table *, char *);
   67 
   68 /*
   69  * Check for an MS-DOS executable header. All Windows binaries
   70  * have a small MS-DOS executable prepended to them to print out
   71  * the "This program requires Windows" message. Even .SYS files
   72  * have this header, in spite of the fact that you're can't actually
   73  * run them directly.
   74  */
   75 
   76 int
   77 pe_get_dos_header(vm_offset_t imgbase, image_dos_header *hdr)
   78 {
   79         uint16_t                signature;
   80 
   81         if (imgbase == 0 || hdr == NULL)
   82                 return (EINVAL);
   83 
   84         signature = *(uint16_t *)imgbase;
   85         if (signature != IMAGE_DOS_SIGNATURE)
   86                 return (ENOEXEC);
   87 
   88         bcopy ((char *)imgbase, (char *)hdr, sizeof(image_dos_header));
   89 
   90         return (0);
   91 }
   92 
   93 /*
   94  * Verify that this image has a Windows NT PE signature.
   95  */
   96 
   97 int
   98 pe_is_nt_image(vm_offset_t imgbase)
   99 {
  100         uint32_t                signature;
  101         image_dos_header        *dos_hdr;
  102 
  103         if (imgbase == 0)
  104                 return (EINVAL);
  105 
  106         signature = *(uint16_t *)imgbase;
  107         if (signature == IMAGE_DOS_SIGNATURE) {
  108                 dos_hdr = (image_dos_header *)imgbase;
  109                 signature = *(uint32_t *)(imgbase + dos_hdr->idh_lfanew);
  110                 if (signature == IMAGE_NT_SIGNATURE)
  111                         return (0);
  112         }
  113 
  114         return (ENOEXEC);
  115 }
  116 
  117 /*
  118  * Return a copy of the optional header. This contains the
  119  * executable entry point and the directory listing which we
  120  * need to find the relocations and imports later.
  121  */
  122 
  123 int
  124 pe_get_optional_header(vm_offset_t imgbase, image_optional_header *hdr)
  125 {
  126         image_dos_header        *dos_hdr;
  127         image_nt_header         *nt_hdr;
  128 
  129         if (imgbase == 0 || hdr == NULL)
  130                 return (EINVAL);
  131 
  132         if (pe_is_nt_image(imgbase))
  133                 return (EINVAL);
  134 
  135         dos_hdr = (image_dos_header *)(imgbase);
  136         nt_hdr = (image_nt_header *)(imgbase + dos_hdr->idh_lfanew);
  137 
  138         bcopy ((char *)&nt_hdr->inh_optionalhdr, (char *)hdr,
  139             nt_hdr->inh_filehdr.ifh_optionalhdrlen);
  140 
  141         return (0);
  142 }
  143 
  144 /*
  145  * Return a copy of the file header. Contains the number of
  146  * sections in this image.
  147  */
  148 
  149 int
  150 pe_get_file_header(vm_offset_t imgbase, image_file_header *hdr)
  151 {
  152         image_dos_header        *dos_hdr;
  153         image_nt_header         *nt_hdr;
  154 
  155         if (imgbase == 0 || hdr == NULL)
  156                 return (EINVAL);
  157 
  158         if (pe_is_nt_image(imgbase))
  159                 return (EINVAL);
  160 
  161         dos_hdr = (image_dos_header *)imgbase;
  162         nt_hdr = (image_nt_header *)(imgbase + dos_hdr->idh_lfanew);
  163 
  164         /*
  165          * Note: the size of the nt_header is variable since it
  166          * can contain optional fields, as indicated by ifh_optionalhdrlen.
  167          * However it happens we're only interested in fields in the
  168          * non-variant portion of the nt_header structure, so we don't
  169          * bother copying the optional parts here.
  170          */
  171 
  172         bcopy ((char *)&nt_hdr->inh_filehdr, (char *)hdr,
  173             sizeof(image_file_header));
  174 
  175         return (0);
  176 }
  177 
  178 /*
  179  * Return the header of the first section in this image (usually
  180  * .text).
  181  */
  182 
  183 int
  184 pe_get_section_header(vm_offset_t imgbase, image_section_header *hdr)
  185 {
  186         image_dos_header        *dos_hdr;
  187         image_nt_header         *nt_hdr;
  188         image_section_header    *sect_hdr;
  189 
  190         if (imgbase == 0 || hdr == NULL)
  191                 return (EINVAL);
  192 
  193         if (pe_is_nt_image(imgbase))
  194                 return (EINVAL);
  195 
  196         dos_hdr = (image_dos_header *)imgbase;
  197         nt_hdr = (image_nt_header *)(imgbase + dos_hdr->idh_lfanew);
  198         sect_hdr = IMAGE_FIRST_SECTION(nt_hdr);
  199 
  200         bcopy ((char *)sect_hdr, (char *)hdr, sizeof(image_section_header));
  201 
  202         return (0);
  203 }
  204 
  205 /*
  206  * Return the number of sections in this executable, or 0 on error.
  207  */
  208 
  209 int
  210 pe_numsections(vm_offset_t imgbase)
  211 {
  212         image_file_header       file_hdr;
  213 
  214         if (pe_get_file_header(imgbase, &file_hdr))
  215                 return (0);
  216 
  217         return (file_hdr.ifh_numsections);
  218 }
  219 
  220 /*
  221  * Return the base address that this image was linked for.
  222  * This helps us calculate relocation addresses later.
  223  */
  224 
  225 vm_offset_t
  226 pe_imagebase(vm_offset_t imgbase)
  227 {
  228         image_optional_header   optional_hdr;
  229 
  230         if (pe_get_optional_header(imgbase, &optional_hdr))
  231                 return (0);
  232 
  233         return (optional_hdr.ioh_imagebase);
  234 }
  235 
  236 /*
  237  * Return the offset of a given directory structure within the
  238  * image. Directories reside within sections.
  239  */
  240 
  241 vm_offset_t
  242 pe_directory_offset(vm_offset_t imgbase, uint32_t diridx)
  243 {
  244         image_optional_header   opt_hdr;
  245         vm_offset_t             dir;
  246 
  247         if (pe_get_optional_header(imgbase, &opt_hdr))
  248                 return (0);
  249 
  250         if (diridx >= opt_hdr.ioh_rva_size_cnt)
  251                 return (0);
  252 
  253         dir = opt_hdr.ioh_datadir[diridx].idd_vaddr;
  254 
  255         return (pe_translate_addr(imgbase, dir));
  256 }
  257 
  258 vm_offset_t
  259 pe_translate_addr(vm_offset_t imgbase, vm_offset_t rva)
  260 {
  261         image_optional_header   opt_hdr;
  262         image_section_header    *sect_hdr;
  263         image_dos_header        *dos_hdr;
  264         image_nt_header         *nt_hdr;
  265         int                     i = 0, sections, fixedlen;
  266 
  267         if (pe_get_optional_header(imgbase, &opt_hdr))
  268                 return (0);
  269 
  270         sections = pe_numsections(imgbase);
  271 
  272         dos_hdr = (image_dos_header *)imgbase;
  273         nt_hdr = (image_nt_header *)(imgbase + dos_hdr->idh_lfanew);
  274         sect_hdr = IMAGE_FIRST_SECTION(nt_hdr);
  275 
  276         /*
  277          * The test here is to see if the RVA falls somewhere
  278          * inside the section, based on the section's start RVA
  279          * and its length. However it seems sometimes the
  280          * virtual length isn't enough to cover the entire
  281          * area of the section. We fudge by taking into account
  282          * the section alignment and rounding the section length
  283          * up to a page boundary.
  284          */
  285         while (i++ < sections) {
  286                 fixedlen = sect_hdr->ish_misc.ish_vsize;
  287                 fixedlen += ((opt_hdr.ioh_sectalign - 1) -
  288                     sect_hdr->ish_misc.ish_vsize) &
  289                     (opt_hdr.ioh_sectalign - 1);
  290                 if (sect_hdr->ish_vaddr <= (uint32_t)rva &&
  291                     (sect_hdr->ish_vaddr + fixedlen) >
  292                     (uint32_t)rva)
  293                         break;
  294                 sect_hdr++;
  295         }
  296 
  297         if (i > sections)
  298                 return (0);
  299 
  300         return ((vm_offset_t)(imgbase + rva - sect_hdr->ish_vaddr +
  301             sect_hdr->ish_rawdataaddr));
  302 }
  303 
  304 /*
  305  * Get the section header for a particular section. Note that
  306  * section names can be anything, but there are some standard
  307  * ones (.text, .data, .rdata, .reloc).
  308  */
  309 
  310 int
  311 pe_get_section(vm_offset_t imgbase, image_section_header *hdr,
  312     const char *name)
  313 {
  314         image_dos_header        *dos_hdr;
  315         image_nt_header         *nt_hdr;
  316         image_section_header    *sect_hdr;
  317 
  318         int                     i, sections;
  319 
  320         if (imgbase == 0 || hdr == NULL)
  321                 return (EINVAL);
  322 
  323         if (pe_is_nt_image(imgbase))
  324                 return (EINVAL);
  325 
  326         sections = pe_numsections(imgbase);
  327 
  328         dos_hdr = (image_dos_header *)imgbase;
  329         nt_hdr = (image_nt_header *)(imgbase + dos_hdr->idh_lfanew);
  330         sect_hdr = IMAGE_FIRST_SECTION(nt_hdr);
  331 
  332         for (i = 0; i < sections; i++) {
  333                 if (!strcmp ((char *)&sect_hdr->ish_name, name)) {
  334                         bcopy((char *)sect_hdr, (char *)hdr,
  335                             sizeof(image_section_header));
  336                         return (0);
  337                 } else
  338                         sect_hdr++;
  339         }
  340 
  341         return (ENOEXEC);
  342 }
  343 
  344 /*
  345  * Apply the base relocations to this image. The relocation table
  346  * resides within the .reloc section. Relocations are specified in
  347  * blocks which refer to a particular page. We apply the relocations
  348  * one page block at a time.
  349  */
  350 
  351 int
  352 pe_relocate(vm_offset_t imgbase)
  353 {
  354         image_section_header    sect;
  355         image_base_reloc        *relhdr;
  356         uint16_t                rel, *sloc;
  357         vm_offset_t             base;
  358         vm_size_t               delta;
  359         uint32_t                *lloc;
  360         uint64_t                *qloc;
  361         int                     i, count;
  362         vm_offset_t             txt;
  363 
  364         base = pe_imagebase(imgbase);
  365         pe_get_section(imgbase, &sect, ".text");
  366         txt = pe_translate_addr(imgbase, sect.ish_vaddr);
  367         delta = (uint32_t)(txt) - base - sect.ish_vaddr;
  368 
  369         pe_get_section(imgbase, &sect, ".reloc");
  370 
  371         relhdr = (image_base_reloc *)(imgbase + sect.ish_rawdataaddr);
  372 
  373         do {
  374                 count = (relhdr->ibr_blocksize -
  375                     (sizeof(uint32_t) * 2)) / sizeof(uint16_t);
  376                 for (i = 0; i < count; i++) {
  377                         rel = relhdr->ibr_rel[i];
  378                         switch (IMR_RELTYPE(rel)) {
  379                         case IMAGE_REL_BASED_ABSOLUTE:
  380                                 break;
  381                         case IMAGE_REL_BASED_HIGHLOW:
  382                                 lloc = (uint32_t *)pe_translate_addr(imgbase,
  383                                     relhdr->ibr_vaddr + IMR_RELOFFSET(rel));
  384                                 *lloc = pe_translate_addr(imgbase,
  385                                     (*lloc - base));
  386                                 break;
  387                         case IMAGE_REL_BASED_HIGH:
  388                                 sloc = (uint16_t *)pe_translate_addr(imgbase,
  389                                     relhdr->ibr_vaddr + IMR_RELOFFSET(rel));
  390                                 *sloc += (delta & 0xFFFF0000) >> 16;
  391                                 break;
  392                         case IMAGE_REL_BASED_LOW:
  393                                 sloc = (uint16_t *)pe_translate_addr(imgbase,
  394                                     relhdr->ibr_vaddr + IMR_RELOFFSET(rel));
  395                                 *sloc += (delta & 0xFFFF);
  396                                 break;
  397                         case IMAGE_REL_BASED_DIR64:
  398                                 qloc = (uint64_t *)pe_translate_addr(imgbase,
  399                                     relhdr->ibr_vaddr + IMR_RELOFFSET(rel));
  400                                 *qloc = pe_translate_addr(imgbase,
  401                                     (*qloc - base));
  402                                 break;
  403 
  404                         default:
  405                                 kprintf("[%d]reloc type: %d\n", i,
  406                                     IMR_RELTYPE(rel));
  407                                 break;
  408                         }
  409                 }
  410                 relhdr = (image_base_reloc *)((vm_offset_t)relhdr +
  411                     relhdr->ibr_blocksize);
  412         } while (relhdr->ibr_blocksize);
  413 
  414         return (0);
  415 }
  416 
  417 /*
  418  * Return the import descriptor for a particular module. An image
  419  * may be linked against several modules, typically HAL.dll, ntoskrnl.exe
  420  * and NDIS.SYS. For each module, there is a list of imported function
  421  * names and their addresses.
  422  *
  423  * Note: module names are case insensitive!
  424  */
  425 
  426 int
  427 pe_get_import_descriptor(vm_offset_t imgbase, image_import_descriptor *desc,
  428     char *module)
  429 {       
  430         vm_offset_t             offset;
  431         image_import_descriptor *imp_desc;
  432         char                    *modname;
  433 
  434         if (imgbase == 0 || module == NULL || desc == NULL)
  435                 return (EINVAL);
  436 
  437         offset = pe_directory_offset(imgbase, IMAGE_DIRECTORY_ENTRY_IMPORT);
  438         if (offset == 0)
  439                 return (ENOENT);
  440 
  441         imp_desc = (void *)offset;
  442 
  443         while (imp_desc->iid_nameaddr) {
  444                 modname = (char *)pe_translate_addr(imgbase,
  445                     imp_desc->iid_nameaddr);
  446                 if (!strncasecmp(module, modname, strlen(module))) {
  447                         bcopy((char *)imp_desc, (char *)desc,
  448                             sizeof(image_import_descriptor));
  449                         return (0);
  450                 }
  451                 imp_desc++;
  452         }
  453 
  454         return (ENOENT);
  455 }
  456 
  457 int
  458 pe_get_messagetable(vm_offset_t imgbase, message_resource_data **md)
  459 {
  460         image_resource_directory        *rdir, *rtype;
  461         image_resource_directory_entry  *dent, *dent2;
  462         image_resource_data_entry       *rent;
  463         vm_offset_t             offset;
  464         int                     i;
  465 
  466         if (imgbase == 0)
  467                 return (EINVAL);
  468 
  469         offset = pe_directory_offset(imgbase, IMAGE_DIRECTORY_ENTRY_RESOURCE);
  470         if (offset == 0)
  471                 return (ENOENT);
  472 
  473         rdir = (image_resource_directory *)offset;
  474 
  475         dent = (image_resource_directory_entry *)(offset +
  476             sizeof(image_resource_directory));
  477 
  478         for (i = 0; i < rdir->ird_id_entries; i++){
  479                 if (dent->irde_name != RT_MESSAGETABLE) {
  480                         dent++;
  481                         continue;
  482                 }
  483                 dent2 = dent;
  484                 while (dent2->irde_dataoff & RESOURCE_DIR_FLAG) {
  485                         rtype = (image_resource_directory *)(offset +
  486                             (dent2->irde_dataoff & ~RESOURCE_DIR_FLAG));
  487                         dent2 = (image_resource_directory_entry *)
  488                             ((uintptr_t)rtype +
  489                              sizeof(image_resource_directory));
  490                 }
  491                 rent = (image_resource_data_entry *)(offset +
  492                     dent2->irde_dataoff);
  493                 *md = (message_resource_data *)pe_translate_addr(imgbase,
  494                     rent->irde_offset);
  495                 return (0);
  496         }
  497 
  498         return (ENOENT);
  499 }
  500 
  501 int
  502 pe_get_message(vm_offset_t imgbase, uint32_t id, char **str, int *len,
  503     uint16_t *flags)
  504 {
  505         message_resource_data   *md = NULL;
  506         message_resource_block  *mb;
  507         message_resource_entry  *me;
  508         uint32_t                i;
  509 
  510         pe_get_messagetable(imgbase, &md);
  511 
  512         if (md == NULL)
  513                 return (ENOENT);
  514 
  515         mb = (message_resource_block *)((uintptr_t)md +
  516             sizeof(message_resource_data));
  517 
  518         for (i = 0; i < md->mrd_numblocks; i++) {
  519                 if (id >= mb->mrb_lowid && id <= mb->mrb_highid) {
  520                         me = (message_resource_entry *)((uintptr_t)md +
  521                             mb->mrb_entryoff);
  522                         for (i = id - mb->mrb_lowid; i > 0; i--)
  523                                 me = (message_resource_entry *)((uintptr_t)me +
  524                                     me->mre_len);
  525                         *str = me->mre_text;
  526                         *len = me->mre_len;
  527                         *flags = me->mre_flags;
  528                         return (0);
  529                 }
  530                 mb++;
  531         }
  532 
  533         return (ENOENT);
  534 }
  535 
  536 /*
  537  * Find the function that matches a particular name. This doesn't
  538  * need to be particularly speedy since it's only run when loading
  539  * a module for the first time.
  540  */
  541 
  542 static vm_offset_t
  543 pe_functbl_match(image_patch_table *functbl, char *name)
  544 {
  545         image_patch_table       *p;
  546 
  547         if (functbl == NULL || name == NULL)
  548                 return (0);
  549 
  550         p = functbl;
  551 
  552         while (p->ipt_name != NULL) {
  553                 if (!strcmp(p->ipt_name, name))
  554                         return ((vm_offset_t)p->ipt_wrap);
  555                 p++;
  556         }
  557         kprintf("no match for %s\n", name);
  558 
  559         /*
  560          * Return the wrapper pointer for this routine.
  561          * For x86, this is the same as the funcptr.
  562          * For amd64, this points to a wrapper routine
  563          * that does calling convention translation and
  564          * then invokes the underlying routine.
  565          */
  566         return ((vm_offset_t)p->ipt_wrap);
  567 }
  568 
  569 /*
  570  * Patch the imported function addresses for a given module.
  571  * The caller must specify the module name and provide a table
  572  * of function pointers that will be patched into the jump table.
  573  * Note that there are actually two copies of the jump table: one
  574  * copy is left alone. In a .SYS file, the jump tables are usually
  575  * merged into the INIT segment.
  576  */
  577 
  578 int
  579 pe_patch_imports(vm_offset_t imgbase, char *module, image_patch_table *functbl)
  580 {
  581         image_import_descriptor imp_desc;
  582         char                    *fname;
  583         vm_offset_t             *nptr, *fptr;
  584         vm_offset_t             func;
  585 
  586         if (imgbase == 0 || module == NULL || functbl == NULL)
  587                 return (EINVAL);
  588 
  589         if (pe_get_import_descriptor(imgbase, &imp_desc, module))
  590                 return (ENOEXEC);
  591 
  592         nptr = (vm_offset_t *)pe_translate_addr(imgbase,
  593             imp_desc.iid_import_name_table_addr);
  594         fptr = (vm_offset_t *)pe_translate_addr(imgbase,
  595             imp_desc.iid_import_address_table_addr);
  596 
  597         while (nptr != NULL && pe_translate_addr(imgbase, *nptr)) {
  598                 fname = (char *)pe_translate_addr(imgbase, (*nptr) + 2);
  599                 func = pe_functbl_match(functbl, fname);
  600                 if (func)
  601                         *fptr = func;
  602 #ifdef notdef
  603                 if (*fptr == 0)
  604                         return (ENOENT);
  605 #endif
  606                 nptr++;
  607                 fptr++;
  608         }
  609 
  610         return (0);
  611 }

Cache object: 23ba578277a68c6793197bfc5f4050aa


[ 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.