3 * Bill Paul <wpaul@windriver.com>. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
33 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD: src/sys/compat/ndis/subr_pe.c,v 1.7.2.3 2005/03/31 04:24:36 wpaul Exp $");
38 __KERNEL_RCSID(0, "$NetBSD: subr_pe.c,v 1.5 2009/03/14 15:36:16 dsl Exp $");
43 * This file contains routines for relocating and dynamically linking
44 * executable object code files in the Windows(r) PE (Portable Executable)
45 * format. In Windows, anything with a .EXE, .DLL or .SYS extention is
46 * considered an executable, and all such files have some structures in
47 * common. The PE format was apparently based largely on COFF but has
48 * mutated significantly over time. We are mainly concerned with .SYS files,
49 * so this module implements only enough routines to be able to parse the
50 * headers and sections of a .SYS object file and perform the necessary
51 * relocations and jump table patching to allow us to call into it
52 * (and to have it call back to us). Note that while this module
53 * can handle fixups for imported symbols, it knows nothing about
57 #include <sys/param.h>
58 #include <sys/types.h>
59 #include <sys/errno.h>
62 #include <sys/systm.h>
63 extern int ndis_strncasecmp(const char *, const char *, size_t);
64 #define strncasecmp(a, b, c) ndis_strncasecmp(a, b, c)
72 #include <compat/ndis/pe_var.h>
74 static vm_offset_t
pe_functbl_match(image_patch_table
*, char *);
77 * Check for an MS-DOS executable header. All Windows binaries
78 * have a small MS-DOS executable prepended to them to print out
79 * the "This program requires Windows" message. Even .SYS files
80 * have this header, in spite of the fact that you're can't actually
85 pe_get_dos_header(vm_offset_t imgbase
, image_dos_header
*hdr
)
89 if (imgbase
== 0 || hdr
== NULL
)
92 signature
= *(uint16_t *)imgbase
;
93 if (signature
!= IMAGE_DOS_SIGNATURE
)
96 bcopy ((char *)imgbase
, (char *)hdr
, sizeof(image_dos_header
));
102 * Verify that this image has a Windows NT PE signature.
106 pe_is_nt_image(vm_offset_t imgbase
)
109 image_dos_header
*dos_hdr
;
114 signature
= *(uint16_t *)imgbase
;
115 if (signature
== IMAGE_DOS_SIGNATURE
) {
116 dos_hdr
= (image_dos_header
*)imgbase
;
117 signature
= *(uint32_t *)(imgbase
+ dos_hdr
->idh_lfanew
);
118 if (signature
== IMAGE_NT_SIGNATURE
)
126 * Return a copy of the optional header. This contains the
127 * executable entry point and the directory listing which we
128 * need to find the relocations and imports later.
132 pe_get_optional_header(vm_offset_t imgbase
, image_optional_header
*hdr
)
134 image_dos_header
*dos_hdr
;
135 image_nt_header
*nt_hdr
;
137 if (imgbase
== 0 || hdr
== NULL
)
140 if (pe_is_nt_image(imgbase
))
143 dos_hdr
= (image_dos_header
*)(imgbase
);
144 nt_hdr
= (image_nt_header
*)(imgbase
+ dos_hdr
->idh_lfanew
);
146 bcopy ((char *)&nt_hdr
->inh_optionalhdr
, (char *)hdr
,
147 sizeof(image_optional_header
));
153 * Return a copy of the file header. Contains the number of
154 * sections in this image.
158 pe_get_file_header(vm_offset_t imgbase
, image_file_header
*hdr
)
160 image_dos_header
*dos_hdr
;
161 image_nt_header
*nt_hdr
;
163 if (imgbase
== 0 || hdr
== NULL
)
166 if (pe_is_nt_image(imgbase
))
169 dos_hdr
= (image_dos_header
*)imgbase
;
170 nt_hdr
= (image_nt_header
*)(imgbase
+ dos_hdr
->idh_lfanew
);
172 bcopy ((char *)&nt_hdr
->inh_filehdr
, (char *)hdr
,
173 sizeof(image_file_header
));
179 * Return the header of the first section in this image (usually
184 pe_get_section_header(vm_offset_t imgbase
, image_section_header
*hdr
)
186 image_dos_header
*dos_hdr
;
187 image_nt_header
*nt_hdr
;
188 image_section_header
*sect_hdr
;
190 if (imgbase
== 0 || hdr
== NULL
)
193 if (pe_is_nt_image(imgbase
))
196 dos_hdr
= (image_dos_header
*)imgbase
;
197 nt_hdr
= (image_nt_header
*)(imgbase
+ dos_hdr
->idh_lfanew
);
198 sect_hdr
= (image_section_header
*)((vm_offset_t
)nt_hdr
+
199 sizeof(image_nt_header
));
201 bcopy ((char *)sect_hdr
, (char *)hdr
, sizeof(image_section_header
));
207 * Return the number of sections in this executable, or 0 on error.
211 pe_numsections(vm_offset_t imgbase
)
213 image_file_header file_hdr
;
215 if (pe_get_file_header(imgbase
, &file_hdr
))
218 return (file_hdr
.ifh_numsections
);
222 * Return the base address that this image was linked for.
223 * This helps us calculate relocation addresses later.
227 pe_imagebase(vm_offset_t imgbase
)
229 image_optional_header optional_hdr
;
231 if (pe_get_optional_header(imgbase
, &optional_hdr
))
234 return (optional_hdr
.ioh_imagebase
);
238 * Return the offset of a given directory structure within the
239 * image. Directories reside within sections.
243 pe_directory_offset(vm_offset_t imgbase
, uint32_t diridx
)
245 image_optional_header opt_hdr
;
248 if (pe_get_optional_header(imgbase
, &opt_hdr
))
251 if (diridx
>= opt_hdr
.ioh_rva_size_cnt
)
254 dir
= opt_hdr
.ioh_datadir
[diridx
].idd_vaddr
;
256 return(pe_translate_addr(imgbase
, dir
));
260 pe_translate_addr(vm_offset_t imgbase
, vm_offset_t rva
)
262 image_optional_header opt_hdr
;
263 image_section_header
*sect_hdr
;
264 image_dos_header
*dos_hdr
;
265 image_nt_header
*nt_hdr
;
266 int i
= 0, sections
, fixedlen
;
268 if (pe_get_optional_header(imgbase
, &opt_hdr
))
271 sections
= pe_numsections(imgbase
);
273 dos_hdr
= (image_dos_header
*)imgbase
;
274 nt_hdr
= (image_nt_header
*)(imgbase
+ dos_hdr
->idh_lfanew
);
275 sect_hdr
= (image_section_header
*)((vm_offset_t
)nt_hdr
+
276 sizeof(image_nt_header
));
279 * The test here is to see if the RVA falls somewhere
280 * inside the section, based on the section's start RVA
281 * and its length. However it seems sometimes the
282 * virtual length isn't enough to cover the entire
283 * area of the section. We fudge by taking into account
284 * the section alignment and rounding the section length
285 * up to a page boundary.
287 while (i
++ < sections
) {
288 fixedlen
= sect_hdr
->ish_misc
.ish_vsize
;
289 fixedlen
+= ((opt_hdr
.ioh_sectalign
- 1) -
290 sect_hdr
->ish_misc
.ish_vsize
) &
291 (opt_hdr
.ioh_sectalign
- 1);
292 if (sect_hdr
->ish_vaddr
<= (uint32_t)rva
&&
293 (sect_hdr
->ish_vaddr
+ fixedlen
) >
302 return((vm_offset_t
)(imgbase
+ rva
- sect_hdr
->ish_vaddr
+
303 sect_hdr
->ish_rawdataaddr
));
307 * Get the section header for a particular section. Note that
308 * section names can be anything, but there are some standard
309 * ones (.text, .data, .rdata, .reloc).
313 pe_get_section(vm_offset_t imgbase
, image_section_header
*hdr
, const char *name
)
315 image_dos_header
*dos_hdr
;
316 image_nt_header
*nt_hdr
;
317 image_section_header
*sect_hdr
;
321 if (imgbase
== 0 || hdr
== NULL
)
324 if (pe_is_nt_image(imgbase
))
327 sections
= pe_numsections(imgbase
);
329 dos_hdr
= (image_dos_header
*)imgbase
;
330 nt_hdr
= (image_nt_header
*)(imgbase
+ dos_hdr
->idh_lfanew
);
331 sect_hdr
= (image_section_header
*)((vm_offset_t
)nt_hdr
+
332 sizeof(image_nt_header
));
334 for (i
= 0; i
< sections
; i
++) {
335 if (!strcmp ((char *)§_hdr
->ish_name
, name
)) {
336 memcpy( (char *)hdr
, (char *)sect_hdr
,
337 sizeof(image_section_header
));
347 * Apply the base relocations to this image. The relocation table
348 * resides within the .reloc section. Relocations are specified in
349 * blocks which refer to a particular page. We apply the relocations
350 * one page block at a time.
354 pe_relocate(vm_offset_t imgbase
)
356 image_section_header sect
;
357 image_base_reloc
*relhdr
;
366 base
= pe_imagebase(imgbase
);
367 pe_get_section(imgbase
, §
, ".text");
368 txt
= pe_translate_addr(imgbase
, sect
.ish_vaddr
);
369 delta
= (uint32_t)(txt
) - base
- sect
.ish_vaddr
;
371 pe_get_section(imgbase
, §
, ".reloc");
373 relhdr
= (image_base_reloc
*)(imgbase
+ sect
.ish_rawdataaddr
);
376 count
= (relhdr
->ibr_blocksize
-
377 (sizeof(uint32_t) * 2)) / sizeof(uint16_t);
378 for (i
= 0; i
< count
; i
++) {
379 rel
= relhdr
->ibr_rel
[i
];
380 switch (IMR_RELTYPE(rel
)) {
381 case IMAGE_REL_BASED_ABSOLUTE
:
383 case IMAGE_REL_BASED_HIGHLOW
:
384 lloc
= (uint32_t *)pe_translate_addr(imgbase
,
385 relhdr
->ibr_vaddr
+ IMR_RELOFFSET(rel
));
386 *lloc
= pe_translate_addr(imgbase
,
389 case IMAGE_REL_BASED_HIGH
:
390 sloc
= (uint16_t *)pe_translate_addr(imgbase
,
391 relhdr
->ibr_vaddr
+ IMR_RELOFFSET(rel
));
392 *sloc
+= (delta
& 0xFFFF0000) >> 16;
394 case IMAGE_REL_BASED_LOW
:
395 sloc
= (uint16_t *)pe_translate_addr(imgbase
,
396 relhdr
->ibr_vaddr
+ IMR_RELOFFSET(rel
));
397 *sloc
+= (delta
& 0xFFFF);
399 case IMAGE_REL_BASED_DIR64
:
400 qloc
= (uint64_t *)pe_translate_addr(imgbase
,
401 relhdr
->ibr_vaddr
+ IMR_RELOFFSET(rel
));
402 *qloc
= pe_translate_addr(imgbase
,
407 printf ("[%d]reloc type: %d\n",i
,
412 relhdr
= (image_base_reloc
*)((vm_offset_t
)relhdr
+
413 relhdr
->ibr_blocksize
);
414 } while (relhdr
->ibr_blocksize
);
420 * Return the import descriptor for a particular module. An image
421 * may be linked against several modules, typically HAL.dll, ntoskrnl.exe
422 * and NDIS.SYS. For each module, there is a list of imported function
423 * names and their addresses.
425 * Note: module names are case insensitive!
429 pe_get_import_descriptor(imgbase
, desc
, module
)
431 image_import_descriptor
*desc
;
435 image_import_descriptor
*imp_desc
;
438 if (imgbase
== 0 || module
== NULL
|| desc
== NULL
)
441 offset
= pe_directory_offset(imgbase
, IMAGE_DIRECTORY_ENTRY_IMPORT
);
445 imp_desc
= (void *)offset
;
447 while (imp_desc
->iid_nameaddr
) {
448 modname
= (char *)pe_translate_addr(imgbase
,
449 imp_desc
->iid_nameaddr
);
450 if (!strncasecmp(module
, modname
, strlen(module
))) {
451 memcpy( (char *)desc
, (char *)imp_desc
,
452 sizeof(image_import_descriptor
));
462 pe_get_messagetable(vm_offset_t imgbase
, message_resource_data
**md
)
464 image_resource_directory
*rdir
, *rtype
;
465 image_resource_directory_entry
*dent
, *dent2
;
466 image_resource_data_entry
*rent
;
473 offset
= pe_directory_offset(imgbase
, IMAGE_DIRECTORY_ENTRY_RESOURCE
);
477 rdir
= (image_resource_directory
*)offset
;
479 dent
= (image_resource_directory_entry
*)(offset
+
480 sizeof(image_resource_directory
));
482 for (i
= 0; i
< rdir
->ird_id_entries
; i
++){
483 if (dent
->irde_name
!= RT_MESSAGETABLE
) {
488 while (dent2
->irde_dataoff
& RESOURCE_DIR_FLAG
) {
489 rtype
= (image_resource_directory
*)(offset
+
490 (dent2
->irde_dataoff
& ~RESOURCE_DIR_FLAG
));
491 dent2
= (image_resource_directory_entry
*)
493 sizeof(image_resource_directory
));
495 rent
= (image_resource_data_entry
*)(offset
+
496 dent2
->irde_dataoff
);
497 *md
= (message_resource_data
*)pe_translate_addr(imgbase
,
506 pe_get_message(vm_offset_t imgbase
, uint32_t id
, char **str
, int *len
, uint16_t *flags
)
508 message_resource_data
*md
= NULL
;
509 message_resource_block
*mb
;
510 message_resource_entry
*me
;
513 pe_get_messagetable(imgbase
, &md
);
518 mb
= (message_resource_block
*)((uintptr_t)md
+
519 sizeof(message_resource_data
));
521 for (i
= 0; i
< md
->mrd_numblocks
; i
++) {
522 if (id
>= mb
->mrb_lowid
&& id
<= mb
->mrb_highid
) {
523 me
= (message_resource_entry
*)((uintptr_t)md
+
525 for (i
= id
- mb
->mrb_lowid
; i
> 0; i
--)
526 me
= (message_resource_entry
*)((uintptr_t)me
+
530 *flags
= me
->mre_flags
;
540 * Find the function that matches a particular name. This doesn't
541 * need to be particularly speedy since it's only run when loading
542 * a module for the first time.
546 pe_functbl_match(image_patch_table
*functbl
, char *name
)
548 image_patch_table
*p
;
550 if (functbl
== NULL
|| name
== NULL
)
555 while (p
->ipt_name
!= NULL
) {
556 if (!strcmp(p
->ipt_name
, name
))
557 return((vm_offset_t
)p
->ipt_wrap
);
560 printf ("no match for %s\n", name
);
563 * Return the wrapper pointer for this routine.
564 * For x86, this is the same as the funcptr.
565 * For amd64, this points to a wrapper routine
566 * that does calling convention translation and
567 * then invokes the underlying routine.
569 return((vm_offset_t
)p
->ipt_wrap
);
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.
582 pe_patch_imports(vm_offset_t imgbase
, const char *module
, image_patch_table
*functbl
)
584 image_import_descriptor imp_desc
;
586 vm_offset_t
*nptr
, *fptr
;
589 if (imgbase
== 0 || module
== NULL
|| functbl
== NULL
)
592 if (pe_get_import_descriptor(imgbase
, &imp_desc
, module
))
595 nptr
= (vm_offset_t
*)pe_translate_addr(imgbase
,
596 imp_desc
.iid_import_name_table_addr
);
597 fptr
= (vm_offset_t
*)pe_translate_addr(imgbase
,
598 imp_desc
.iid_import_address_table_addr
);
600 while (nptr
!= NULL
&& pe_translate_addr(imgbase
, *nptr
)) {
601 fname
= (char *)pe_translate_addr(imgbase
, (*nptr
) + 2);
602 func
= pe_functbl_match(functbl
, fname
);