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.
32 * $DragonFly: src/sys/emulation/ndis/subr_pe.c,v 1.3 2006/12/23 00:27:02 swildner Exp $
33 * $FreeBSD: src/sys/compat/ndis/subr_pe.c,v 1.7 2004/01/13 22:49:45 obrien Exp $
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
51 #include <sys/param.h>
52 #include <sys/types.h>
53 #include <sys/errno.h>
55 #include <sys/systm.h>
61 #define kprintf printf /* ndiscvt(8) uses this file */
67 static vm_offset_t
pe_functbl_match(image_patch_table
*, char *);
70 * Check for an MS-DOS executable header. All Windows binaries
71 * have a small MS-DOS executable prepended to them to print out
72 * the "This program requires Windows" message. Even .SYS files
73 * have this header, in spite of the fact that you're can't actually
78 pe_get_dos_header(vm_offset_t imgbase
, image_dos_header
*hdr
)
82 if (imgbase
== 0 || hdr
== NULL
)
85 signature
= *(uint16_t *)imgbase
;
86 if (signature
!= IMAGE_DOS_SIGNATURE
)
89 bcopy ((char *)imgbase
, (char *)hdr
, sizeof(image_dos_header
));
95 * Verify that this image has a Windows NT PE signature.
99 pe_is_nt_image(vm_offset_t imgbase
)
102 image_dos_header
*dos_hdr
;
107 signature
= *(uint16_t *)imgbase
;
108 if (signature
== IMAGE_DOS_SIGNATURE
) {
109 dos_hdr
= (image_dos_header
*)imgbase
;
110 signature
= *(uint32_t *)(imgbase
+ dos_hdr
->idh_lfanew
);
111 if (signature
== IMAGE_NT_SIGNATURE
)
119 * Return a copy of the optional header. This contains the
120 * executable entry point and the directory listing which we
121 * need to find the relocations and imports later.
125 pe_get_optional_header(vm_offset_t imgbase
, image_optional_header
*hdr
)
127 image_dos_header
*dos_hdr
;
128 image_nt_header
*nt_hdr
;
130 if (imgbase
== 0 || hdr
== NULL
)
133 if (pe_is_nt_image(imgbase
))
136 dos_hdr
= (image_dos_header
*)(imgbase
);
137 nt_hdr
= (image_nt_header
*)(imgbase
+ dos_hdr
->idh_lfanew
);
139 bcopy ((char *)&nt_hdr
->inh_optionalhdr
, (char *)hdr
,
140 sizeof(image_optional_header
));
146 * Return a copy of the file header. Contains the number of
147 * sections in this image.
151 pe_get_file_header(vm_offset_t imgbase
, image_file_header
*hdr
)
153 image_dos_header
*dos_hdr
;
154 image_nt_header
*nt_hdr
;
156 if (imgbase
== 0 || hdr
== NULL
)
159 if (pe_is_nt_image(imgbase
))
162 dos_hdr
= (image_dos_header
*)imgbase
;
163 nt_hdr
= (image_nt_header
*)(imgbase
+ dos_hdr
->idh_lfanew
);
165 bcopy ((char *)&nt_hdr
->inh_filehdr
, (char *)hdr
,
166 sizeof(image_file_header
));
172 * Return the header of the first section in this image (usually
177 pe_get_section_header(vm_offset_t imgbase
, image_section_header
*hdr
)
179 image_dos_header
*dos_hdr
;
180 image_nt_header
*nt_hdr
;
181 image_section_header
*sect_hdr
;
183 if (imgbase
== 0 || hdr
== NULL
)
186 if (pe_is_nt_image(imgbase
))
189 dos_hdr
= (image_dos_header
*)imgbase
;
190 nt_hdr
= (image_nt_header
*)(imgbase
+ dos_hdr
->idh_lfanew
);
191 sect_hdr
= (image_section_header
*)((vm_offset_t
)nt_hdr
+
192 sizeof(image_nt_header
));
194 bcopy ((char *)sect_hdr
, (char *)hdr
, sizeof(image_section_header
));
200 * Return the number of sections in this executable, or 0 on error.
204 pe_numsections(vm_offset_t imgbase
)
206 image_file_header file_hdr
;
208 if (pe_get_file_header(imgbase
, &file_hdr
))
211 return (file_hdr
.ifh_numsections
);
215 * Return the base address that this image was linked for.
216 * This helps us calculate relocation addresses later.
220 pe_imagebase(vm_offset_t imgbase
)
222 image_optional_header optional_hdr
;
224 if (pe_get_optional_header(imgbase
, &optional_hdr
))
227 return (optional_hdr
.ioh_imagebase
);
231 * Return the offset of a given directory structure within the
232 * image. Directories reside within sections.
236 pe_directory_offset(vm_offset_t imgbase
, uint32_t diridx
)
238 image_optional_header opt_hdr
;
241 if (pe_get_optional_header(imgbase
, &opt_hdr
))
244 if (diridx
>= opt_hdr
.ioh_rva_size_cnt
)
247 dir
= opt_hdr
.ioh_datadir
[diridx
].idd_vaddr
;
249 return(pe_translate_addr(imgbase
, dir
));
253 pe_translate_addr(vm_offset_t imgbase
, uint32_t rva
)
255 image_optional_header opt_hdr
;
256 image_section_header
*sect_hdr
;
257 image_dos_header
*dos_hdr
;
258 image_nt_header
*nt_hdr
;
259 int i
= 0, sections
, fixedlen
;
261 if (pe_get_optional_header(imgbase
, &opt_hdr
))
264 sections
= pe_numsections(imgbase
);
266 dos_hdr
= (image_dos_header
*)imgbase
;
267 nt_hdr
= (image_nt_header
*)(imgbase
+ dos_hdr
->idh_lfanew
);
268 sect_hdr
= (image_section_header
*)((vm_offset_t
)nt_hdr
+
269 sizeof(image_nt_header
));
272 * The test here is to see if the RVA falls somewhere
273 * inside the section, based on the section's start RVA
274 * and its length. However it seems sometimes the
275 * virtual length isn't enough to cover the entire
276 * area of the section. We fudge by taking into account
277 * the section alignment and rounding the section length
278 * up to a page boundary.
280 while (i
++ < sections
) {
281 fixedlen
= sect_hdr
->ish_misc
.ish_vsize
;
282 fixedlen
+= ((opt_hdr
.ioh_sectalign
- 1) -
283 sect_hdr
->ish_misc
.ish_vsize
) &
284 (opt_hdr
.ioh_sectalign
- 1);
285 if (sect_hdr
->ish_vaddr
<= (u_int32_t
)rva
&&
286 (sect_hdr
->ish_vaddr
+ fixedlen
) >
295 return((vm_offset_t
)(imgbase
+ rva
- sect_hdr
->ish_vaddr
+
296 sect_hdr
->ish_rawdataaddr
));
300 * Get the section header for a particular section. Note that
301 * section names can be anything, but there are some standard
302 * ones (.text, .data, .rdata, .reloc).
306 pe_get_section(vm_offset_t imgbase
, image_section_header
*hdr
,
309 image_dos_header
*dos_hdr
;
310 image_nt_header
*nt_hdr
;
311 image_section_header
*sect_hdr
;
315 if (imgbase
== 0 || hdr
== NULL
)
318 if (pe_is_nt_image(imgbase
))
321 sections
= pe_numsections(imgbase
);
323 dos_hdr
= (image_dos_header
*)imgbase
;
324 nt_hdr
= (image_nt_header
*)(imgbase
+ dos_hdr
->idh_lfanew
);
325 sect_hdr
= (image_section_header
*)((vm_offset_t
)nt_hdr
+
326 sizeof(image_nt_header
));
328 for (i
= 0; i
< sections
; i
++) {
329 if (!strcmp ((char *)§_hdr
->ish_name
, name
)) {
330 bcopy((char *)sect_hdr
, (char *)hdr
,
331 sizeof(image_section_header
));
341 * Apply the base relocations to this image. The relocation table
342 * resides within the .reloc section. Relocations are specified in
343 * blocks which refer to a particular page. We apply the relocations
344 * one page block at a time.
348 pe_relocate(vm_offset_t imgbase
)
350 image_section_header sect
;
351 image_base_reloc
*relhdr
;
353 uint32_t base
, delta
, *lloc
;
357 base
= pe_imagebase(imgbase
);
358 pe_get_section(imgbase
, §
, ".text");
359 txt
= pe_translate_addr(imgbase
, sect
.ish_vaddr
);
360 delta
= (uint32_t)(txt
) - base
- sect
.ish_vaddr
;
362 pe_get_section(imgbase
, §
, ".reloc");
364 relhdr
= (image_base_reloc
*)(imgbase
+ sect
.ish_rawdataaddr
);
367 count
= (relhdr
->ibr_blocksize
-
368 (sizeof(uint32_t) * 2)) / sizeof(uint16_t);
369 for (i
= 0; i
< count
; i
++) {
370 rel
= relhdr
->ibr_rel
[i
];
371 switch (IMR_RELTYPE(rel
)) {
372 case IMAGE_REL_BASED_ABSOLUTE
:
374 case IMAGE_REL_BASED_HIGHLOW
:
375 lloc
= (uint32_t *)pe_translate_addr(imgbase
,
376 relhdr
->ibr_vaddr
+ IMR_RELOFFSET(rel
));
377 *lloc
= pe_translate_addr(imgbase
,
380 case IMAGE_REL_BASED_HIGH
:
381 sloc
= (uint16_t *)pe_translate_addr(imgbase
,
382 relhdr
->ibr_vaddr
+ IMR_RELOFFSET(rel
));
383 *sloc
+= (delta
& 0xFFFF0000) >> 16;
385 case IMAGE_REL_BASED_LOW
:
386 sloc
= (uint16_t *)pe_translate_addr(imgbase
,
387 relhdr
->ibr_vaddr
+ IMR_RELOFFSET(rel
));
388 *sloc
+= (delta
& 0xFFFF);
391 kprintf ("[%d]reloc type: %d\n",i
,
396 relhdr
= (image_base_reloc
*)((vm_offset_t
)relhdr
+
397 relhdr
->ibr_blocksize
);
398 } while (relhdr
->ibr_blocksize
);
404 * Return the import descriptor for a particular module. An image
405 * may be linked against several modules, typically HAL.dll, ntoskrnl.exe
406 * and NDIS.SYS. For each module, there is a list of imported function
407 * names and their addresses.
411 pe_get_import_descriptor(vm_offset_t imgbase
, image_import_descriptor
*desc
,
415 image_import_descriptor
*imp_desc
;
418 if (imgbase
== 0 || module
== NULL
|| desc
== NULL
)
421 offset
= pe_directory_offset(imgbase
, IMAGE_DIRECTORY_ENTRY_IMPORT
);
425 imp_desc
= (void *)offset
;
427 while (imp_desc
->iid_nameaddr
) {
428 modname
= (char *)pe_translate_addr(imgbase
,
429 imp_desc
->iid_nameaddr
);
430 if (!strncmp(module
, modname
, strlen(module
))) {
431 bcopy((char *)imp_desc
, (char *)desc
,
432 sizeof(image_import_descriptor
));
442 pe_get_messagetable(vm_offset_t imgbase
, message_resource_data
**md
)
444 image_resource_directory
*rdir
, *rtype
;
445 image_resource_directory_entry
*dent
, *dent2
;
446 image_resource_data_entry
*rent
;
453 offset
= pe_directory_offset(imgbase
, IMAGE_DIRECTORY_ENTRY_RESOURCE
);
457 rdir
= (image_resource_directory
*)offset
;
459 dent
= (image_resource_directory_entry
*)(offset
+
460 sizeof(image_resource_directory
));
462 for (i
= 0; i
< rdir
->ird_id_entries
; i
++){
463 if (dent
->irde_name
!= RT_MESSAGETABLE
) {
468 while (dent2
->irde_dataoff
& RESOURCE_DIR_FLAG
) {
469 rtype
= (image_resource_directory
*)(offset
+
470 (dent2
->irde_dataoff
& ~RESOURCE_DIR_FLAG
));
471 dent2
= (image_resource_directory_entry
*)
473 sizeof(image_resource_directory
));
475 rent
= (image_resource_data_entry
*)(offset
+
476 dent2
->irde_dataoff
);
477 *md
= (message_resource_data
*)pe_translate_addr(imgbase
,
486 pe_get_message(vm_offset_t imgbase
, uint32_t id
, char **str
, int *len
,
489 message_resource_data
*md
= NULL
;
490 message_resource_block
*mb
;
491 message_resource_entry
*me
;
494 pe_get_messagetable(imgbase
, &md
);
499 mb
= (message_resource_block
*)((uintptr_t)md
+
500 sizeof(message_resource_data
));
502 for (i
= 0; i
< md
->mrd_numblocks
; i
++) {
503 if (id
>= mb
->mrb_lowid
&& id
<= mb
->mrb_highid
) {
504 me
= (message_resource_entry
*)((uintptr_t)md
+
506 for (i
= id
- mb
->mrb_lowid
; i
> 0; i
--)
507 me
= (message_resource_entry
*)((uintptr_t)me
+
511 *flags
= me
->mre_flags
;
521 * Find the function that matches a particular name. This doesn't
522 * need to be particularly speedy since it's only run when loading
523 * a module for the first time.
527 pe_functbl_match(image_patch_table
*functbl
, char *name
)
529 image_patch_table
*p
;
531 if (functbl
== NULL
|| name
== NULL
)
536 while (p
->ipt_name
!= NULL
) {
537 if (!strcmp(p
->ipt_name
, name
))
538 return((vm_offset_t
)p
->ipt_func
);
541 kprintf ("no match for %s\n", name
);
542 return((vm_offset_t
)p
->ipt_func
);
546 * Patch the imported function addresses for a given module.
547 * The caller must specify the module name and provide a table
548 * of function pointers that will be patched into the jump table.
549 * Note that there are actually two copies of the jump table: one
550 * copy is left alone. In a .SYS file, the jump tables are usually
551 * merged into the INIT segment.
555 pe_patch_imports(vm_offset_t imgbase
, char *module
,
556 image_patch_table
*functbl
)
558 image_import_descriptor imp_desc
;
560 vm_offset_t
*nptr
, *fptr
;
563 if (imgbase
== 0 || module
== NULL
|| functbl
== NULL
)
566 if (pe_get_import_descriptor(imgbase
, &imp_desc
, module
))
569 nptr
= (vm_offset_t
*)pe_translate_addr(imgbase
,
570 imp_desc
.iid_import_name_table_addr
);
571 fptr
= (vm_offset_t
*)pe_translate_addr(imgbase
,
572 imp_desc
.iid_import_address_table_addr
);
574 while (nptr
!= NULL
&& pe_translate_addr(imgbase
, *nptr
)) {
575 fname
= (char *)pe_translate_addr(imgbase
, (*nptr
) + 2);
576 func
= pe_functbl_match(functbl
, fname
);