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
33#include <sys/cdefs.h>
34#ifdef __FreeBSD__
35__FBSDID("$FreeBSD: src/sys/compat/ndis/subr_pe.c,v 1.7.2.3 2005/03/31 04:24:36 wpaul Exp $");
36#endif
37#ifdef __NetBSD__
38__KERNEL_RCSID(0, "$NetBSD: subr_pe.c,v 1.6 2009/03/18 17:06:48 cegger Exp $");
39#endif
40
41
42/*
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
54 * exporting them.
55 */
56
57#include <sys/param.h>
58#include <sys/types.h>
59#include <sys/errno.h>
60#include <sys/lock.h>
61#ifdef _KERNEL
62#include <sys/systm.h>
63extern int ndis_strncasecmp(const char *, const char *, size_t);
64#define strncasecmp(a, b, c) ndis_strncasecmp(a, b, c)
65#else
66#include <stdio.h>
67#include <stdlib.h>
68#include <unistd.h>
69#include <string.h>
70#endif
71
72#include <compat/ndis/pe_var.h>
73
74static vm_offset_t pe_functbl_match(image_patch_table *, char *);
75
76/*
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
81 * run them directly.
82 */
83
84int
85pe_get_dos_header(vm_offset_t imgbase, image_dos_header *hdr)
86{
87	uint16_t		signature;
88
89	if (imgbase == 0 || hdr == NULL)
90		return (EINVAL);
91
92	signature = *(uint16_t *)imgbase;
93	if (signature != IMAGE_DOS_SIGNATURE)
94		return (ENOEXEC);
95
96	bcopy ((char *)imgbase, (char *)hdr, sizeof(image_dos_header));
97
98	return(0);
99}
100
101/*
102 * Verify that this image has a Windows NT PE signature.
103 */
104
105int
106pe_is_nt_image(vm_offset_t imgbase)
107{
108	uint32_t		signature;
109	image_dos_header	*dos_hdr;
110
111	if (imgbase == 0)
112		return (EINVAL);
113
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)
119			return(0);
120	}
121
122	return(ENOEXEC);
123}
124
125/*
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.
129 */
130
131int
132pe_get_optional_header(vm_offset_t imgbase, image_optional_header *hdr)
133{
134	image_dos_header	*dos_hdr;
135	image_nt_header		*nt_hdr;
136
137	if (imgbase == 0 || hdr == NULL)
138		return(EINVAL);
139
140	if (pe_is_nt_image(imgbase))
141		return (EINVAL);
142
143	dos_hdr = (image_dos_header *)(imgbase);
144	nt_hdr = (image_nt_header *)(imgbase + dos_hdr->idh_lfanew);
145
146	bcopy ((char *)&nt_hdr->inh_optionalhdr, (char *)hdr,
147	    sizeof(image_optional_header));
148
149	return(0);
150}
151
152/*
153 * Return a copy of the file header. Contains the number of
154 * sections in this image.
155 */
156
157int
158pe_get_file_header(vm_offset_t imgbase, image_file_header *hdr)
159{
160	image_dos_header	*dos_hdr;
161	image_nt_header		*nt_hdr;
162
163	if (imgbase == 0 || hdr == NULL)
164		return(EINVAL);
165
166	if (pe_is_nt_image(imgbase))
167		return (EINVAL);
168
169	dos_hdr = (image_dos_header *)imgbase;
170	nt_hdr = (image_nt_header *)(imgbase + dos_hdr->idh_lfanew);
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
183int
184pe_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_section_header *)((vm_offset_t)nt_hdr +
199	    sizeof(image_nt_header));
200
201	bcopy ((char *)sect_hdr, (char *)hdr, sizeof(image_section_header));
202
203	return(0);
204}
205
206/*
207 * Return the number of sections in this executable, or 0 on error.
208 */
209
210int
211pe_numsections(vm_offset_t imgbase)
212{
213	image_file_header	file_hdr;
214
215	if (pe_get_file_header(imgbase, &file_hdr))
216		return(0);
217
218	return (file_hdr.ifh_numsections);
219}
220
221/*
222 * Return the base address that this image was linked for.
223 * This helps us calculate relocation addresses later.
224 */
225
226vm_offset_t
227pe_imagebase(vm_offset_t imgbase)
228{
229	image_optional_header	optional_hdr;
230
231	if (pe_get_optional_header(imgbase, &optional_hdr))
232		return(0);
233
234	return (optional_hdr.ioh_imagebase);
235}
236
237/*
238 * Return the offset of a given directory structure within the
239 * image. Directories reside within sections.
240 */
241
242vm_offset_t
243pe_directory_offset(vm_offset_t imgbase, uint32_t diridx)
244{
245	image_optional_header	opt_hdr;
246	vm_offset_t		dir;
247
248	if (pe_get_optional_header(imgbase, &opt_hdr))
249		return(0);
250
251	if (diridx >= opt_hdr.ioh_rva_size_cnt)
252		return(0);
253
254	dir = opt_hdr.ioh_datadir[diridx].idd_vaddr;
255
256	return(pe_translate_addr(imgbase, dir));
257}
258
259vm_offset_t
260pe_translate_addr(vm_offset_t imgbase, vm_offset_t rva)
261{
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;
267
268	if (pe_get_optional_header(imgbase, &opt_hdr))
269		return(0);
270
271	sections = pe_numsections(imgbase);
272
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));
277
278	/*
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.
286	 */
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) >
294		    (uint32_t)rva)
295			break;
296		sect_hdr++;
297	}
298
299	if (i > sections)
300		return(0);
301
302	return((vm_offset_t)(imgbase + rva - sect_hdr->ish_vaddr +
303	    sect_hdr->ish_rawdataaddr));
304}
305
306/*
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).
310 */
311
312int
313pe_get_section(vm_offset_t imgbase, image_section_header *hdr, const char *name)
314{
315	image_dos_header	*dos_hdr;
316	image_nt_header		*nt_hdr;
317	image_section_header	*sect_hdr;
318
319	int			i, sections;
320
321	if (imgbase == 0 || hdr == NULL)
322		return(EINVAL);
323
324	if (pe_is_nt_image(imgbase))
325		return (EINVAL);
326
327	sections = pe_numsections(imgbase);
328
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));
333
334	for (i = 0; i < sections; i++) {
335		if (!strcmp ((char *)&sect_hdr->ish_name, name)) {
336			memcpy( (char *)hdr, (char *)sect_hdr,
337			    sizeof(image_section_header));
338			return(0);
339		} else
340			sect_hdr++;
341	}
342
343	return (ENOEXEC);
344}
345
346/*
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.
351 */
352
353int
354pe_relocate(vm_offset_t imgbase)
355{
356	image_section_header	sect;
357	image_base_reloc	*relhdr;
358	uint16_t		rel, *sloc;
359	vm_offset_t		base;
360	vm_size_t		delta;
361	uint32_t		*lloc;
362	uint64_t		*qloc;
363	int			i, count;
364	vm_offset_t		txt;
365
366	base = pe_imagebase(imgbase);
367	pe_get_section(imgbase, &sect, ".text");
368	txt = pe_translate_addr(imgbase, sect.ish_vaddr);
369	delta = (uint32_t)(txt) - base - sect.ish_vaddr;
370
371	pe_get_section(imgbase, &sect, ".reloc");
372
373	relhdr = (image_base_reloc *)(imgbase + sect.ish_rawdataaddr);
374
375	do {
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:
382				break;
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,
387				    (*lloc - base));
388				break;
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;
393				break;
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);
398				break;
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,
403				    (*qloc - base));
404                                break;
405
406			default:
407				printf ("[%d]reloc type: %d\n",i,
408				    IMR_RELTYPE(rel));
409				break;
410			}
411		}
412		relhdr = (image_base_reloc *)((vm_offset_t)relhdr +
413		    relhdr->ibr_blocksize);
414	} while (relhdr->ibr_blocksize);
415
416	return(0);
417}
418
419/*
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.
424 *
425 * Note: module names are case insensitive!
426 */
427
428int
429pe_get_import_descriptor(
430	vm_offset_t		imgbase,
431	image_import_descriptor	*desc,
432	const char		*module
433)
434{
435	vm_offset_t		offset;
436	image_import_descriptor	*imp_desc;
437	char			*modname;
438
439	if (imgbase == 0 || module == NULL || desc == NULL)
440		return(EINVAL);
441
442	offset = pe_directory_offset(imgbase, IMAGE_DIRECTORY_ENTRY_IMPORT);
443	if (offset == 0)
444		return (ENOENT);
445
446	imp_desc = (void *)offset;
447
448	while (imp_desc->iid_nameaddr) {
449		modname = (char *)pe_translate_addr(imgbase,
450		    imp_desc->iid_nameaddr);
451		if (!strncasecmp(module, modname, strlen(module))) {
452			memcpy( (char *)desc, (char *)imp_desc,
453			    sizeof(image_import_descriptor));
454			return(0);
455		}
456		imp_desc++;
457	}
458
459	return (ENOENT);
460}
461
462int
463pe_get_messagetable(vm_offset_t imgbase, 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
506int
507pe_get_message(vm_offset_t imgbase, uint32_t id, char **str, int *len, uint16_t *flags)
508{
509	message_resource_data	*md = NULL;
510	message_resource_block	*mb;
511	message_resource_entry	*me;
512	uint32_t		i;
513
514	pe_get_messagetable(imgbase, &md);
515
516	if (md == NULL)
517		return(ENOENT);
518
519	mb = (message_resource_block *)((uintptr_t)md +
520	    sizeof(message_resource_data));
521
522	for (i = 0; i < md->mrd_numblocks; i++) {
523		if (id >= mb->mrb_lowid && id <= mb->mrb_highid) {
524			me = (message_resource_entry *)((uintptr_t)md +
525			    mb->mrb_entryoff);
526			for (i = id - mb->mrb_lowid; i > 0; i--)
527				me = (message_resource_entry *)((uintptr_t)me +
528				    me->mre_len);
529			*str = me->mre_text;
530			*len = me->mre_len;
531			*flags = me->mre_flags;
532			return(0);
533		}
534		mb++;
535	}
536
537	return(ENOENT);
538}
539
540/*
541 * Find the function that matches a particular name. This doesn't
542 * need to be particularly speedy since it's only run when loading
543 * a module for the first time.
544 */
545
546static vm_offset_t
547pe_functbl_match(image_patch_table *functbl, char *name)
548{
549	image_patch_table	*p;
550
551	if (functbl == NULL || name == NULL)
552		return(0);
553
554	p = functbl;
555
556	while (p->ipt_name != NULL) {
557		if (!strcmp(p->ipt_name, name))
558			return((vm_offset_t)p->ipt_wrap);
559		p++;
560	}
561	printf ("no match for %s\n", name);
562
563	/*
564	 * Return the wrapper pointer for this routine.
565	 * For x86, this is the same as the funcptr.
566	 * For amd64, this points to a wrapper routine
567	 * that does calling convention translation and
568	 * then invokes the underlying routine.
569	 */
570	return((vm_offset_t)p->ipt_wrap);
571}
572
573/*
574 * Patch the imported function addresses for a given module.
575 * The caller must specify the module name and provide a table
576 * of function pointers that will be patched into the jump table.
577 * Note that there are actually two copies of the jump table: one
578 * copy is left alone. In a .SYS file, the jump tables are usually
579 * merged into the INIT segment.
580 */
581
582int
583pe_patch_imports(vm_offset_t imgbase, const char *module, image_patch_table *functbl)
584{
585	image_import_descriptor	imp_desc;
586	char			*fname;
587	vm_offset_t		*nptr, *fptr;
588	vm_offset_t		func;
589
590	if (imgbase == 0 || module == NULL || functbl == NULL)
591		return(EINVAL);
592
593	if (pe_get_import_descriptor(imgbase, &imp_desc, module))
594		return(ENOEXEC);
595
596	nptr = (vm_offset_t *)pe_translate_addr(imgbase,
597	    imp_desc.iid_import_name_table_addr);
598	fptr = (vm_offset_t *)pe_translate_addr(imgbase,
599	    imp_desc.iid_import_address_table_addr);
600
601	while (nptr != NULL && pe_translate_addr(imgbase, *nptr)) {
602		fname = (char *)pe_translate_addr(imgbase, (*nptr) + 2);
603		func = pe_functbl_match(functbl, fname);
604		if (func)
605			*fptr = func;
606#ifdef notdef
607		if (*fptr == 0)
608			return(ENOENT);
609#endif
610		nptr++;
611		fptr++;
612	}
613
614	return(0);
615}
616