oops, forgot a mention. Last commit:
[dragonfly.git] / sys / emulation / ndis / subr_pe.c
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  * $DragonFly: src/sys/emulation/ndis/subr_pe.c,v 1.1 2004/07/29 20:51:34 dillon Exp $
33  * $FreeBSD: src/sys/compat/ndis/subr_pe.c,v 1.7 2004/01/13 22:49:45 obrien Exp $
34  */
35
36 /*
37  * This file contains routines for relocating and dynamically linking
38  * executable object code files in the Windows(r) PE (Portable Executable)
39  * format. In Windows, anything with a .EXE, .DLL or .SYS extention is
40  * considered an executable, and all such files have some structures in
41  * common. The PE format was apparently based largely on COFF but has
42  * mutated significantly over time. We are mainly concerned with .SYS files,
43  * so this module implements only enough routines to be able to parse the
44  * headers and sections of a .SYS object file and perform the necessary
45  * relocations and jump table patching to allow us to call into it
46  * (and to have it call back to us). Note that while this module
47  * can handle fixups for imported symbols, it knows nothing about
48  * exporting them.
49  */
50
51 #include <sys/param.h>
52 #include <sys/types.h>
53 #include <sys/errno.h>
54 #ifdef _KERNEL
55 #include <sys/systm.h>
56 #else
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <unistd.h>
60 #include <string.h>
61 #endif
62
63 #include "regcall.h"
64 #include "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(imgbase, hdr)
78         vm_offset_t             imgbase;
79         image_dos_header        *hdr;
80 {
81         uint16_t                signature;
82
83         if (imgbase == 0 || hdr == NULL)
84                 return (EINVAL);
85
86         signature = *(uint16_t *)imgbase;
87         if (signature != IMAGE_DOS_SIGNATURE)
88                 return (ENOEXEC);
89
90         bcopy ((char *)imgbase, (char *)hdr, sizeof(image_dos_header));
91
92         return(0);
93 }
94
95 /*
96  * Verify that this image has a Windows NT PE signature.
97  */
98
99 int
100 pe_is_nt_image(imgbase)
101         vm_offset_t             imgbase;
102 {
103         uint32_t                signature;
104         image_dos_header        *dos_hdr;
105
106         if (imgbase == 0)
107                 return (EINVAL);
108
109         signature = *(uint16_t *)imgbase;
110         if (signature == IMAGE_DOS_SIGNATURE) {
111                 dos_hdr = (image_dos_header *)imgbase;
112                 signature = *(uint32_t *)(imgbase + dos_hdr->idh_lfanew);
113                 if (signature == IMAGE_NT_SIGNATURE)
114                         return(0);
115         }
116
117         return(ENOEXEC);
118 }
119
120 /*
121  * Return a copy of the optional header. This contains the
122  * executable entry point and the directory listing which we
123  * need to find the relocations and imports later.
124  */
125
126 int
127 pe_get_optional_header(imgbase, hdr)
128         vm_offset_t             imgbase;
129         image_optional_header   *hdr;
130 {
131         image_dos_header        *dos_hdr;
132         image_nt_header         *nt_hdr;
133
134         if (imgbase == 0 || hdr == NULL)
135                 return(EINVAL);
136
137         if (pe_is_nt_image(imgbase))
138                 return (EINVAL);
139
140         dos_hdr = (image_dos_header *)(imgbase);
141         nt_hdr = (image_nt_header *)(imgbase + dos_hdr->idh_lfanew);
142
143         bcopy ((char *)&nt_hdr->inh_optionalhdr, (char *)hdr,
144             sizeof(image_optional_header));
145
146         return(0);
147 }
148
149 /*
150  * Return a copy of the file header. Contains the number of
151  * sections in this image.
152  */
153
154 int
155 pe_get_file_header(imgbase, hdr)
156         vm_offset_t             imgbase;
157         image_file_header       *hdr;
158 {
159         image_dos_header        *dos_hdr;
160         image_nt_header         *nt_hdr;
161
162         if (imgbase == 0 || hdr == NULL)
163                 return(EINVAL);
164
165         if (pe_is_nt_image(imgbase))
166                 return (EINVAL);
167
168         dos_hdr = (image_dos_header *)imgbase;
169         nt_hdr = (image_nt_header *)(imgbase + dos_hdr->idh_lfanew);
170
171         bcopy ((char *)&nt_hdr->inh_filehdr, (char *)hdr,
172             sizeof(image_file_header));
173
174         return(0);
175 }
176
177 /*
178  * Return the header of the first section in this image (usually
179  * .text).
180  */
181
182 int
183 pe_get_section_header(imgbase, hdr)
184         vm_offset_t             imgbase;
185         image_section_header    *hdr;
186 {
187         image_dos_header        *dos_hdr;
188         image_nt_header         *nt_hdr;
189         image_section_header    *sect_hdr;
190
191         if (imgbase == 0 || hdr == NULL)
192                 return(EINVAL);
193
194         if (pe_is_nt_image(imgbase))
195                 return (EINVAL);
196
197         dos_hdr = (image_dos_header *)imgbase;
198         nt_hdr = (image_nt_header *)(imgbase + dos_hdr->idh_lfanew);
199         sect_hdr = (image_section_header *)((vm_offset_t)nt_hdr +
200             sizeof(image_nt_header));
201
202         bcopy ((char *)sect_hdr, (char *)hdr, sizeof(image_section_header));
203
204         return(0);
205 }
206
207 /*
208  * Return the number of sections in this executable, or 0 on error.
209  */
210
211 int
212 pe_numsections(imgbase)
213         vm_offset_t             imgbase;
214 {
215         image_file_header       file_hdr;
216
217         if (pe_get_file_header(imgbase, &file_hdr))
218                 return(0);
219
220         return (file_hdr.ifh_numsections);
221 }
222
223 /*
224  * Return the base address that this image was linked for.
225  * This helps us calculate relocation addresses later.
226  */
227
228 vm_offset_t
229 pe_imagebase(imgbase)
230         vm_offset_t             imgbase;
231 {
232         image_optional_header   optional_hdr;
233
234         if (pe_get_optional_header(imgbase, &optional_hdr))
235                 return(0);
236
237         return (optional_hdr.ioh_imagebase);
238 }
239
240 /*
241  * Return the offset of a given directory structure within the
242  * image. Directories reside within sections.
243  */
244
245 vm_offset_t
246 pe_directory_offset(imgbase, diridx)
247         vm_offset_t             imgbase;
248         uint32_t                diridx;
249 {
250         image_optional_header   opt_hdr;
251         vm_offset_t             dir;
252
253         if (pe_get_optional_header(imgbase, &opt_hdr))
254                 return(0);
255
256         if (diridx >= opt_hdr.ioh_rva_size_cnt)
257                 return(0);
258
259         dir = opt_hdr.ioh_datadir[diridx].idd_vaddr;
260
261         return(pe_translate_addr(imgbase, dir));
262 }
263
264 vm_offset_t
265 pe_translate_addr(imgbase, rva)
266         vm_offset_t             imgbase;
267         uint32_t                rva;
268 {
269         image_optional_header   opt_hdr;
270         image_section_header    *sect_hdr;
271         image_dos_header        *dos_hdr;
272         image_nt_header         *nt_hdr;
273         int                     i = 0, sections, fixedlen;
274
275         if (pe_get_optional_header(imgbase, &opt_hdr))
276                 return(0);
277
278         sections = pe_numsections(imgbase);
279
280         dos_hdr = (image_dos_header *)imgbase;
281         nt_hdr = (image_nt_header *)(imgbase + dos_hdr->idh_lfanew);
282         sect_hdr = (image_section_header *)((vm_offset_t)nt_hdr +
283             sizeof(image_nt_header));
284
285         /*
286          * The test here is to see if the RVA falls somewhere
287          * inside the section, based on the section's start RVA
288          * and its length. However it seems sometimes the
289          * virtual length isn't enough to cover the entire
290          * area of the section. We fudge by taking into account
291          * the section alignment and rounding the section length
292          * up to a page boundary.
293          */
294         while (i++ < sections) {
295                 fixedlen = sect_hdr->ish_misc.ish_vsize;
296                 fixedlen += ((opt_hdr.ioh_sectalign - 1) -
297                     sect_hdr->ish_misc.ish_vsize) &
298                     (opt_hdr.ioh_sectalign - 1);
299                 if (sect_hdr->ish_vaddr <= (u_int32_t)rva &&
300                     (sect_hdr->ish_vaddr + fixedlen) >
301                     (u_int32_t)rva)
302                         break;
303                 sect_hdr++;
304         }
305
306         if (i > sections)
307                 return(0);
308
309         return((vm_offset_t)(imgbase + rva - sect_hdr->ish_vaddr +
310             sect_hdr->ish_rawdataaddr));
311 }
312
313 /*
314  * Get the section header for a particular section. Note that
315  * section names can be anything, but there are some standard
316  * ones (.text, .data, .rdata, .reloc).
317  */
318
319 int
320 pe_get_section(imgbase, hdr, name)
321         vm_offset_t             imgbase;
322         image_section_header    *hdr;
323         const char              *name;
324 {
325         image_dos_header        *dos_hdr;
326         image_nt_header         *nt_hdr;
327         image_section_header    *sect_hdr;
328
329         int                     i, sections;
330
331         if (imgbase == 0 || hdr == NULL)
332                 return(EINVAL);
333
334         if (pe_is_nt_image(imgbase))
335                 return (EINVAL);
336
337         sections = pe_numsections(imgbase);
338
339         dos_hdr = (image_dos_header *)imgbase;
340         nt_hdr = (image_nt_header *)(imgbase + dos_hdr->idh_lfanew);
341         sect_hdr = (image_section_header *)((vm_offset_t)nt_hdr +
342             sizeof(image_nt_header));
343
344         for (i = 0; i < sections; i++) {
345                 if (!strcmp ((char *)&sect_hdr->ish_name, name)) {
346                         bcopy((char *)sect_hdr, (char *)hdr,
347                             sizeof(image_section_header));
348                         return(0);
349                 } else
350                         sect_hdr++;
351         }
352
353         return (ENOEXEC);
354 }
355
356 /*
357  * Apply the base relocations to this image. The relocation table
358  * resides within the .reloc section. Relocations are specified in
359  * blocks which refer to a particular page. We apply the relocations
360  * one page block at a time.
361  */
362
363 int
364 pe_relocate(imgbase)
365         vm_offset_t             imgbase;
366 {
367         image_section_header    sect;
368         image_base_reloc        *relhdr;
369         uint16_t                rel, *sloc;
370         uint32_t                base, delta, *lloc;
371         int                     i, count;
372         vm_offset_t             txt;
373
374         base = pe_imagebase(imgbase);
375         pe_get_section(imgbase, &sect, ".text");
376         txt = pe_translate_addr(imgbase, sect.ish_vaddr);
377         delta = (uint32_t)(txt) - base - sect.ish_vaddr;
378
379         pe_get_section(imgbase, &sect, ".reloc");
380
381         relhdr = (image_base_reloc *)(imgbase + sect.ish_rawdataaddr);
382
383         do {
384                 count = (relhdr->ibr_blocksize -
385                     (sizeof(uint32_t) * 2)) / sizeof(uint16_t);
386                 for (i = 0; i < count; i++) {
387                         rel = relhdr->ibr_rel[i];
388                         switch (IMR_RELTYPE(rel)) {
389                         case IMAGE_REL_BASED_ABSOLUTE:
390                                 break;
391                         case IMAGE_REL_BASED_HIGHLOW:
392                                 lloc = (uint32_t *)pe_translate_addr(imgbase,
393                                     relhdr->ibr_vaddr + IMR_RELOFFSET(rel));
394                                 *lloc = pe_translate_addr(imgbase,
395                                     (*lloc - base));
396                                 break;
397                         case IMAGE_REL_BASED_HIGH:
398                                 sloc = (uint16_t *)pe_translate_addr(imgbase,
399                                     relhdr->ibr_vaddr + IMR_RELOFFSET(rel));
400                                 *sloc += (delta & 0xFFFF0000) >> 16;
401                                 break;
402                         case IMAGE_REL_BASED_LOW:
403                                 sloc = (uint16_t *)pe_translate_addr(imgbase,
404                                     relhdr->ibr_vaddr + IMR_RELOFFSET(rel));
405                                 *sloc += (delta & 0xFFFF);
406                                 break;
407                         default:
408                                 printf ("[%d]reloc type: %d\n",i,
409                                     IMR_RELTYPE(rel));
410                                 break;
411                         }
412                 }
413                 relhdr = (image_base_reloc *)((vm_offset_t)relhdr +
414                     relhdr->ibr_blocksize);
415         } while (relhdr->ibr_blocksize);
416
417         return(0);
418 }
419
420 /*
421  * Return the import descriptor for a particular module. An image
422  * may be linked against several modules, typically HAL.dll, ntoskrnl.exe
423  * and NDIS.SYS. For each module, there is a list of imported function
424  * names and their addresses.
425  */
426
427 int
428 pe_get_import_descriptor(imgbase, desc, module)
429         vm_offset_t             imgbase;
430         image_import_descriptor *desc;
431         char                    *module;
432 {       
433         vm_offset_t             offset;
434         image_import_descriptor *imp_desc;
435         char                    *modname;
436
437         if (imgbase == 0 || module == NULL || desc == NULL)
438                 return(EINVAL);
439
440         offset = pe_directory_offset(imgbase, IMAGE_DIRECTORY_ENTRY_IMPORT);
441         if (offset == 0)
442                 return (ENOENT);
443
444         imp_desc = (void *)offset;
445
446         while (imp_desc->iid_nameaddr) {
447                 modname = (char *)pe_translate_addr(imgbase,
448                     imp_desc->iid_nameaddr);
449                 if (!strncmp(module, modname, strlen(module))) {
450                         bcopy((char *)imp_desc, (char *)desc,
451                             sizeof(image_import_descriptor));
452                         return(0);
453                 }
454                 imp_desc++;
455         }
456
457         return (ENOENT);
458 }
459
460 int
461 pe_get_messagetable(imgbase, md)
462         vm_offset_t             imgbase;
463         message_resource_data   **md;
464 {
465         image_resource_directory        *rdir, *rtype;
466         image_resource_directory_entry  *dent, *dent2;
467         image_resource_data_entry       *rent;
468         vm_offset_t             offset;
469         int                     i;
470
471         if (imgbase == 0)
472                 return(EINVAL);
473
474         offset = pe_directory_offset(imgbase, IMAGE_DIRECTORY_ENTRY_RESOURCE);
475         if (offset == 0)
476                 return (ENOENT);
477
478         rdir = (image_resource_directory *)offset;
479
480         dent = (image_resource_directory_entry *)(offset +
481             sizeof(image_resource_directory));
482
483         for (i = 0; i < rdir->ird_id_entries; i++){
484                 if (dent->irde_name != RT_MESSAGETABLE) {
485                         dent++;
486                         continue;
487                 }
488                 dent2 = dent;
489                 while (dent2->irde_dataoff & RESOURCE_DIR_FLAG) {
490                         rtype = (image_resource_directory *)(offset +
491                             (dent2->irde_dataoff & ~RESOURCE_DIR_FLAG));
492                         dent2 = (image_resource_directory_entry *)
493                             ((uintptr_t)rtype +
494                              sizeof(image_resource_directory));
495                 }
496                 rent = (image_resource_data_entry *)(offset +
497                     dent2->irde_dataoff);
498                 *md = (message_resource_data *)pe_translate_addr(imgbase,
499                     rent->irde_offset);
500                 return(0);
501         }
502
503         return(ENOENT);
504 }
505
506 int
507 pe_get_message(imgbase, id, str, len, flags)
508         vm_offset_t             imgbase;
509         uint32_t                id;
510         char                    **str;
511         int                     *len;
512         uint16_t                *flags;
513 {
514         message_resource_data   *md = NULL;
515         message_resource_block  *mb;
516         message_resource_entry  *me;
517         uint32_t                i;
518
519         pe_get_messagetable(imgbase, &md);
520
521         if (md == NULL)
522                 return(ENOENT);
523
524         mb = (message_resource_block *)((uintptr_t)md +
525             sizeof(message_resource_data));
526
527         for (i = 0; i < md->mrd_numblocks; i++) {
528                 if (id >= mb->mrb_lowid && id <= mb->mrb_highid) {
529                         me = (message_resource_entry *)((uintptr_t)md +
530                             mb->mrb_entryoff);
531                         for (i = id - mb->mrb_lowid; i > 0; i--)
532                                 me = (message_resource_entry *)((uintptr_t)me +
533                                     me->mre_len);
534                         *str = me->mre_text;
535                         *len = me->mre_len;
536                         *flags = me->mre_flags;
537                         return(0);
538                 }
539                 mb++;
540         }
541
542         return(ENOENT);
543 }
544
545 /*
546  * Find the function that matches a particular name. This doesn't
547  * need to be particularly speedy since it's only run when loading
548  * a module for the first time.
549  */
550
551 static vm_offset_t
552 pe_functbl_match(functbl, name)
553         image_patch_table       *functbl;
554         char                    *name;
555 {
556         image_patch_table       *p;
557
558         if (functbl == NULL || name == NULL)
559                 return(0);
560
561         p = functbl;
562
563         while (p->ipt_name != NULL) {
564                 if (!strcmp(p->ipt_name, name))
565                         return((vm_offset_t)p->ipt_func);
566                 p++;
567         }
568         printf ("no match for %s\n", name);
569         return((vm_offset_t)p->ipt_func);
570 }
571
572 /*
573  * Patch the imported function addresses for a given module.
574  * The caller must specify the module name and provide a table
575  * of function pointers that will be patched into the jump table.
576  * Note that there are actually two copies of the jump table: one
577  * copy is left alone. In a .SYS file, the jump tables are usually
578  * merged into the INIT segment.
579  */
580
581 int
582 pe_patch_imports(imgbase, module, functbl)
583         vm_offset_t             imgbase;
584         char                    *module;
585         image_patch_table       *functbl;
586 {
587         image_import_descriptor imp_desc;
588         char                    *fname;
589         vm_offset_t             *nptr, *fptr;
590         vm_offset_t             func;
591
592         if (imgbase == 0 || module == NULL || functbl == NULL)
593                 return(EINVAL);
594
595         if (pe_get_import_descriptor(imgbase, &imp_desc, module))
596                 return(ENOEXEC);
597
598         nptr = (vm_offset_t *)pe_translate_addr(imgbase,
599             imp_desc.iid_import_name_table_addr);
600         fptr = (vm_offset_t *)pe_translate_addr(imgbase,
601             imp_desc.iid_import_address_table_addr);
602
603         while (nptr != NULL && pe_translate_addr(imgbase, *nptr)) {
604                 fname = (char *)pe_translate_addr(imgbase, (*nptr) + 2);
605                 func = pe_functbl_match(functbl, fname);
606                 if (func)
607                         *fptr = func;
608 #ifdef notdef
609                 if (*fptr == 0)
610                         return(ENOENT);
611 #endif
612                 nptr++;
613                 fptr++;
614         }
615
616         return(0);
617 }