1 /*
2  * w32dll.c -- a simplistic interface to Win32 DLLs (no thread support)
3  * Written by Andrew Church <achurch@achurch.org>
4  *
5  * This file is part of transcode, a video stream processing tool.
6  * transcode is free software, distributable under the terms of the GNU
7  * General Public License (version 2 or later).  See the file COPYING
8  * for details.
9  */
10 
11 #ifdef HAVE_CONFIG_H
12 # include "config.h"
13 #endif
14 
15 #include <stdint.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <unistd.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <limits.h>
22 #include <sys/mman.h>
23 
24 #if !defined(HAVE_MMAP)
25 # error Sorry, mmap() support is required.
26 #endif
27 
28 #if defined(HAVE_ENDIAN_H)
29 # include <endian.h>
30 # if __BYTE_ORDER != __LITTLE_ENDIAN
31 #  error Sorry, only little-endian architectures are supported.
32 # endif
33 #endif
34 
35 #if defined(HAVE_SYSCONF_WITH_SC_PAGESIZE)
36 # define GETPAGESIZE() (sysconf(_SC_PAGESIZE))
37 #elif defined(HAVE_GETPAGESIZE)
38 # define GETPAGESIZE() (getpagesize())
39 #elif defined(PAGESIZE)
40 # define GETPAGESIZE() (PAGESIZE)
41 #elif defined(PAGE_SIZE)
42 # define GETPAGESIZE() (PAGE_SIZE)
43 #else
44 # error System page size is not available!
45 #endif
46 
47 #include "w32dll.h"
48 #include "w32dll-local.h"
49 
50 /*************************************************************************/
51 
52 /* Contents of a DLL handle. */
53 
54 struct w32dllhandle_ {
55     /* Signature (to protect against bad pointers and double-free */
56     uint32_t signature;
57 
58     /* Overall file data */
59     struct pe_header header;
60     struct pe_ext_header extheader;
61 
62     /* File position for each RVA entry */
63     off_t rva_filepos[RVA_MAX];
64 
65     /* Data for each loaded section */
66     int nsections;
67     struct section_info {
68         void *base;
69         uint32_t size;
70         int prot;               /* Protection flags for mprotect() */
71         uint32_t origbase;      /* Virtual address given in section header */
72         uint32_t origsize;      /* Likewise, for size */
73     } *sections;
74 
75     /* Data for exported functions */
76     int export_ordinal_base;
77     int export_ordinal_count;
78     void **export_table;
79     int export_name_count;
80     struct export_name {
81         char *name;
82         uint32_t ordinal;
83     } *export_name_table;
84 };
85 
86 #define HANDLE_SIGNATURE    0xD11DA7A5
87 
88 
89 /* Forward declarations for internal routines. */
90 
91 static int w32dll_add_section(W32DLLHandle dll, int fd,
92                               struct pe_section_header *secthdr);
93 static int w32dll_load_section(int fd, struct pe_section_header *secthdr,
94                                struct section_info *sectinfo);
95 static void w32dll_update_rva(W32DLLHandle dll,
96                               struct pe_section_header *secthdr);
97 static int w32dll_read_exports(W32DLLHandle dll, int fd);
98 static int w32dll_process_imports(W32DLLHandle dll,
99                                   struct import_directory *importdir);
100 static void *w32dll_import_by_name(const char *module,
101                                    const struct import_name_entry *name);
102 static void *w32dll_import_by_ordinal(const char *module, uint32_t ordinal);
103 static int w32dll_read_relocs(W32DLLHandle dll, int fd,
104                               uint32_t **relocs_ptr, int *nrelocs_ptr);
105 static void w32dll_relocate(W32DLLHandle dll, uint32_t *relocs, int nrelocs);
106 static void *w32dll_relocate_addr(W32DLLHandle dll, uint32_t addr);
107 static char *w32dll_read_asciiz(int fd);
108 static int w32dll_init_fs(void);
109 
110 /*************************************************************************/
111 /*************************************************************************/
112 
113 /* External interface routines. */
114 
115 /*************************************************************************/
116 
117 /**
118  * w32dll_load:  Load the given DLL file into memory, and return a handle
119  * to it.
120  *
121  * Parameters:
122  *       path: DLL file pathname.
123  *     compat: If nonzero, adds a memory mapping for the entire DLL to
124  *             accommodate misbehaving DLLs that access memory outside the
125  *             registered sections.
126  * Return value:
127  *     DLL handle (nonzero), or zero on error.
128  * Side effects:
129  *     Sets errno to an appropriate value on error, including ENOEXEC if
130  *     the file is not recognized as a Win32 DLL file or is corrupt or
131  *     truncated, or ETXTBSY if the DLL's DllMain() function returns an
132  *     error.  On successful return, errno is undefined.
133  */
134 
w32dll_load(const char * path,int compat)135 W32DLLHandle w32dll_load(const char *path, int compat)
136 {
137     W32DLLHandle dll;
138     struct dos_header doshdr;
139     int fd, i;
140 
141     /* Allocate and initialize the DLL handle. */
142     dll = malloc(sizeof(*dll));
143     if (!dll)
144         return NULL;
145     memset(&dll->header, 0, sizeof(dll->header));
146     memset(&dll->extheader, 0, sizeof(dll->extheader));
147     memset(&dll->rva_filepos, 0, sizeof(dll->rva_filepos));
148     dll->signature            = HANDLE_SIGNATURE;
149     dll->nsections            = 0;
150     dll->sections             = NULL;
151     dll->export_ordinal_base  = 0;
152     dll->export_ordinal_count = 0;
153     dll->export_table         = NULL;
154     dll->export_name_count    = 0;
155     dll->export_name_table    = NULL;
156 
157     /* Open the file, and ensure that it's seekable. */
158     fd = open(path, O_RDONLY);
159     if (fd == -1 || lseek(fd, 0, SEEK_SET) == -1) {
160         int errno_save = errno;
161         free(dll);
162         errno = errno_save;
163         return NULL;
164     }
165 
166     /* Check for a valid (Win32-style) DOS executable header. */
167     if (read(fd, &doshdr, sizeof(doshdr)) != sizeof(doshdr)
168      || doshdr.signature != DOS_EXE_SIGNATURE
169      || doshdr.reloc_offset < 0x40
170     ) {
171         goto err_noexec;
172     }
173 
174     /* Check for a valid PE header (standard and optional both required). */
175     if (lseek(fd, doshdr.winheader, SEEK_SET) == -1
176      || read(fd, &dll->header, sizeof(dll->header)) != sizeof(dll->header)
177      || dll->header.opt_header_size < sizeof(dll->extheader)
178      || read(fd, &dll->extheader, sizeof(dll->extheader))
179                                                   != sizeof(dll->extheader)
180      || dll->header.signature != WIN_PE_SIGNATURE
181      || !(dll->header.flags & WIN_PE_FLAG_DLL)
182 #if defined(ARCH_X86)
183      || (dll->header.arch & ~3) != WIN_PE_ARCH_X86
184      || dll->extheader.magic != WIN_PE_OPT_MAGIC_32
185 #else
186 # error Sorry, this architecture is not supported.
187 #endif
188     ) {
189         goto err_noexec;
190     }
191     /* Skip past any extra header bytes we didn't need. */
192     if (dll->header.opt_header_size > sizeof(dll->extheader)) {
193         if (lseek(fd, dll->header.opt_header_size - sizeof(dll->extheader),
194                   SEEK_CUR) == -1
195         ) {
196             goto err_noexec;
197         }
198     }
199 
200     /* Go through the section table and attempt to load each section.  Also
201      * determine file positions for each RVA entry.  Note that we do not
202      * simply map the entire file because (1) sections may be larger in
203      * memory than in the file and (2) the system's page size may be larger
204      * than that specified in the file. */
205     for (i = 0; i < dll->header.nsections + (compat ? 1 : 0); i++) {
206         struct pe_section_header secthdr;
207 
208         if (i >= dll->header.nsections) {
209             /* Set up compatibility entry */
210             off_t curpos = lseek(fd, 0, SEEK_CUR);
211             off_t filesize = lseek(fd, 0, SEEK_END);
212             if (curpos==-1 || filesize==-1 || lseek(fd,curpos,SEEK_SET)==-1)
213                 goto error;
214             secthdr.virtaddr = 0;
215             secthdr.virtsize = dll->extheader.image_size;
216             secthdr.fileaddr = 0;
217             secthdr.filesize = filesize;
218             secthdr.flags = SECTION_FLAG_DATA | SECTION_FLAG_READ;
219         } else {
220             if (read(fd, &secthdr, sizeof(secthdr)) != sizeof(secthdr))
221                 goto err_noexec;
222         }
223         w32dll_update_rva(dll, &secthdr);
224         w32dll_add_section(dll, fd, &secthdr);
225     }
226 
227     /* Load and process relocations.  Note that once the sections are
228      * loaded, we could theoretically just retrieve these (and the other
229      * data below) from memory, but since we take the approach of only
230      * loading/mapping the sections we need, we do this the hard way and
231      * read the data directly from the file. */
232     if (dll->rva_filepos[RVA_BASE_RELOC]
233      && dll->extheader.rva[RVA_BASE_RELOC].size
234     ) {
235         uint32_t *relocs = NULL;
236         int nrelocs = 0;
237 
238         if (lseek(fd, dll->rva_filepos[RVA_BASE_RELOC], SEEK_SET) == -1)
239             goto error;
240         while (lseek(fd, 0, SEEK_CUR)
241                <= dll->rva_filepos[RVA_BASE_RELOC]
242                   + dll->extheader.rva[RVA_BASE_RELOC].size - 8
243         ) {
244             int res = w32dll_read_relocs(dll, fd, &relocs, &nrelocs);
245             if (res < 0)
246                 goto error;
247             if (res == 0)
248                 break;
249         }
250         w32dll_relocate(dll, relocs, nrelocs);
251     }
252 
253     /* Load export table. */
254     if (dll->rva_filepos[RVA_EXPORT]
255      && dll->extheader.rva[RVA_EXPORT].size >= sizeof(struct export_directory)
256     ) {
257         if (!w32dll_read_exports(dll, fd))
258             goto error;
259     }
260 
261     /* Load and process import table. */
262     if (dll->rva_filepos[RVA_IMPORT]
263      && dll->extheader.rva[RVA_IMPORT].size >= sizeof(struct import_directory)
264     ) {
265         struct import_directory importdir;
266 
267         if (lseek(fd, dll->rva_filepos[RVA_IMPORT], SEEK_SET) == -1)
268             goto error;
269         while (lseek(fd, 0, SEEK_CUR)
270                <= dll->rva_filepos[RVA_IMPORT]
271                   + dll->extheader.rva[RVA_IMPORT].size - sizeof(importdir)
272         ) {
273             if (read(fd, &importdir, sizeof(importdir)) != sizeof(importdir))
274                 goto err_noexec;
275             if (!importdir.module_name)
276                 break;  /* Last entry in table */
277             if (!importdir.import_table || !importdir.import_addr_table)
278                 goto err_noexec;
279             if (!w32dll_process_imports(dll, &importdir))
280                 goto error;
281         }
282     }
283 
284     /* Set section access privileges appropriately. */
285     for (i = 0; i < dll->nsections; i++) {
286         if (mprotect(dll->sections[i].base, dll->sections[i].size,
287                      dll->sections[i].prot) != 0
288         ) {
289             goto error;
290         }
291     }
292 
293     /* Close file descriptor (no longer needed). */
294     close(fd);
295     fd = -1;
296 
297     /* Set up the FS register with a dummy thread information block.
298      * We deliberately don't support libraries that depend on the OS to
299      * put things here; we just provide the space so that accesses to
300      * %fs:... don't segfault. */
301     if (!w32dll_init_fs())
302         goto error;
303 
304     /* Call the DllMain() entry point. */
305     if (dll->extheader.entry_point) {
306         WINAPI int (*DllMain)(uint32_t handle, uint32_t reason, void *resv);
307         DllMain = w32dll_relocate_addr(dll, dll->extheader.entry_point
308                                             + dll->extheader.image_base);
309         if (!DllMain)
310             goto err_noexec;
311         if (!(*DllMain)(HANDLE_DEFAULT, DLL_PROCESS_ATTACH, NULL)) {
312             (*DllMain)(HANDLE_DEFAULT, DLL_PROCESS_DETACH, NULL);
313             errno = ETXTBSY;
314             goto error;
315         }
316     }
317 
318     /* Successful! */
319     return dll;
320 
321     /* Error handling */
322   err_noexec:
323     errno = ENOEXEC;
324   error:
325     {
326         int errno_save = errno;
327         close(fd);
328         w32dll_unload(dll);
329         errno_save = errno;
330         return NULL;
331     }
332 }
333 
334 /*************************************************************************/
335 
336 /**
337  * w32dll_unload:  Unload the given DLL from memory.  Does nothing if the
338  * given handle is zero or invalid.
339  *
340  * Parameters:
341  *     dll: DLL handle.
342  * Return value:
343  *     None.
344  */
345 
w32dll_unload(W32DLLHandle dll)346 void w32dll_unload(W32DLLHandle dll)
347 {
348     int i;
349 
350     if (!dll || dll->signature != HANDLE_SIGNATURE)
351         return;
352 
353     /* Call the DllMain() entry point with DLL_PROCESS_DETACH. */
354     if (dll->extheader.entry_point) {
355         WINAPI int (*DllMain)(uint32_t handle, uint32_t reason, void *resv);
356         DllMain = w32dll_relocate_addr(dll, dll->extheader.entry_point
357                                             + dll->extheader.image_base);
358         if (DllMain)
359             (*DllMain)(HANDLE_DEFAULT, DLL_PROCESS_DETACH, NULL);
360     }
361 
362     /* Free DLL memory. */
363     for (i = 0; i < dll->nsections; i++) {
364         munmap(dll->sections[i].base, dll->sections[i].size);
365         dll->sections[i].base = NULL;
366         dll->sections[i].size = 0;
367     }
368     free(dll->sections);
369     dll->sections = NULL;
370     dll->nsections = 0;
371 
372     /* Free export tables. */
373     free(dll->export_table);
374     dll->export_table = NULL;
375     for (i = 0; i < dll->export_name_count; i++) {
376         free(dll->export_name_table[i].name);
377         dll->export_name_table[i].name = NULL;
378     }
379     free(dll->export_name_table);
380     dll->export_name_table = NULL;
381 
382     /* Free the handle structure itself. */
383     dll->signature = ~HANDLE_SIGNATURE;
384     free(dll);
385 
386     return;
387 }
388 
389 /*************************************************************************/
390 
391 /**
392  * w32dll_lookup_by_name:  Look up the address of an exported function in
393  * the given DLL, using the function's name.
394  *
395  * Parameters:
396  *      dll: DLL handle.
397  *     name: Function name.
398  * Return value:
399  *     Function address, or NULL on error.
400  * Side effects:
401  *     Sets errno to one of the following values on error:
402  *         EINVAL: `dll' or `name' was invalid.
403  *         ENOENT: The requested function does not exist.
404  *     On successful return, errno is undefined.
405  */
406 
w32dll_lookup_by_name(W32DLLHandle dll,const char * name)407 void *w32dll_lookup_by_name(W32DLLHandle dll, const char *name)
408 {
409     int i;
410 
411     if (!dll || dll->signature != HANDLE_SIGNATURE || !name || !*name) {
412         errno = EINVAL;
413         return NULL;
414     }
415     for (i = 0; i < dll->export_name_count; i++) {
416         if (strcmp(name, dll->export_name_table[i].name) == 0) {
417             return w32dll_lookup_by_ordinal(dll,
418                                             dll->export_name_table[i].ordinal);
419         }
420     }
421     errno = ENOENT;
422     return NULL;
423 }
424 
425 /*************************************************************************/
426 
427 /**
428  * w32dll_lookup_by_ordinal:  Look up the address of an exported function
429  * in the given DLL, using the function's ordinal value.
430  *
431  * Parameters:
432  *         dll: DLL handle.
433  *     ordinal: Function ordinal.
434  * Return value:
435  *     Function address, or NULL on error.
436  * Side effects:
437  *     Sets errno to one of the following values on error:
438  *         EINVAL: `dll' was invalid.
439  *         ENOENT: The requested function does not exist.
440  *     On successful return, errno is undefined.
441  */
442 
w32dll_lookup_by_ordinal(W32DLLHandle dll,uint32_t ordinal)443 void *w32dll_lookup_by_ordinal(W32DLLHandle dll, uint32_t ordinal)
444 {
445     if (!dll || dll->signature != HANDLE_SIGNATURE) {
446         errno = EINVAL;
447         return NULL;
448     }
449     if (ordinal < dll->export_ordinal_base) {
450         errno = ENOENT;
451         return NULL;
452     }
453     ordinal -= dll->export_ordinal_base;
454     if (ordinal >= dll->export_ordinal_count || !dll->export_table[ordinal]) {
455         errno = ENOENT;
456         return NULL;
457     }
458     return dll->export_table[ordinal];
459 }
460 
461 /*************************************************************************/
462 /*************************************************************************/
463 
464 /* Internal routines. */
465 
466 /*************************************************************************/
467 
468 /**
469  * w32dll_add_section:  Checks the given section description, and loads it
470  * in from the DLL file, appending information to the dll->sections[]
471  * array, if appropriate.
472  *
473  * Parameters:
474  *         dll: DLL handle.
475  *          fd: File descriptor to read from.
476  *     secthdr: Pointer to section header.
477  * Return value:
478  *     Nonzero on success (including when the section was intentionally
479  *     not loaded), zero on error.
480  * Notes:
481  *     - On success, the file's current offset is not changed.
482  *     - The allocated memory will be marked read/write; after relocation,
483  *           use mprotect() to set the protection to sectinfo->prot.
484  *     - On error, errno is set appropriately.
485  */
486 
w32dll_add_section(W32DLLHandle dll,int fd,struct pe_section_header * secthdr)487 static int w32dll_add_section(W32DLLHandle dll, int fd,
488                               struct pe_section_header *secthdr)
489 {
490     void *new_sections;
491 
492     if (!(secthdr->flags & (SECTION_FLAG_CODE
493                           | SECTION_FLAG_DATA
494                           | SECTION_FLAG_BSS))
495     ) {
496         /* Don't know what kind of section this is, but we don't need it */
497         return 1;
498     }
499 
500     if (!(secthdr->flags & (SECTION_FLAG_READ
501                           | SECTION_FLAG_WRITE
502                           | SECTION_FLAG_EXEC))
503     ) {
504         /* Don't bother loading--it wouldn't be accessible anyway */
505         return 1;
506     }
507 
508     new_sections = realloc(dll->sections,
509                            sizeof(*dll->sections) * (dll->nsections+1));
510     if (!new_sections)
511         return 0;
512     dll->sections = new_sections;
513     dll->sections[dll->nsections].base = NULL;
514     dll->sections[dll->nsections].size = 0;
515     dll->sections[dll->nsections].prot = 0;
516     dll->sections[dll->nsections].origbase = 0;
517     dll->sections[dll->nsections].origsize = 0;
518     dll->nsections++;
519     if (!w32dll_load_section(fd, secthdr, &dll->sections[dll->nsections-1]))
520         return 0;
521     dll->sections[dll->nsections-1].origbase += dll->extheader.image_base;
522 
523     return 1;
524 }
525 
526 /*************************************************************************/
527 
528 /**
529  * w32dll_load_section:  Loads the section described by `secthdr' from the
530  * file descriptor `fd', setting the `sectinfo' structure appropriately.
531  *
532  * Parameters:
533  *           fd: File to load data from.
534  *      secthdr: Section header loaded from the file.
535  *     sectinfo: Structure to store information about the segment in.
536  * Return value:
537  *     Nonzero on success, zero on error.
538  * Notes:
539  *     - On success, the file's current offset is not changed.
540  *     - The allocated memory will be marked read/write; after relocation,
541  *           use mprotect() to set the protection to sectinfo->prot.
542  *     - On error, errno is set appropriately.
543  */
544 
w32dll_load_section(int fd,struct pe_section_header * secthdr,struct section_info * sectinfo)545 static int w32dll_load_section(int fd, struct pe_section_header *secthdr,
546                                struct section_info *sectinfo)
547 {
548     int newfd;
549     void *base;
550     uint32_t size, toread;
551     off_t oldofs;
552 
553     uint32_t pagesize = GETPAGESIZE();
554     if (pagesize < 0) {
555         errno = EINVAL;
556         return 0;
557     }
558 
559 #ifdef MAP_ANONYMOUS
560     newfd = -1;
561 #else
562     newfd = open("/dev/zero", O_RDWR);
563 #endif
564     size = (secthdr->virtsize + pagesize-1) / pagesize * pagesize;
565     base = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE
566 #ifdef MAP_ANONYMOUS
567                                                     | MAP_ANONYMOUS
568 #endif
569                 , newfd, 0);
570 #ifndef MAP_ANONYMOUS
571     if (newfd != -1) {
572         int errno_save = errno;
573         close(newfd);
574         errno = errno_save;
575     }
576 #endif
577     if (base == MAP_FAILED)
578         return 0;
579 
580     oldofs = lseek(fd, 0, SEEK_CUR);
581     if (oldofs == -1) {
582         munmap(base, size);
583         return 0;
584     }
585     if (secthdr->filesize < secthdr->virtsize)
586         toread = secthdr->filesize;
587     else
588         toread = secthdr->virtsize;
589     if (lseek(fd, secthdr->fileaddr, SEEK_SET) == -1
590      || read(fd, base, toread) != toread
591      || lseek(fd, oldofs, SEEK_SET) == -1
592     ) {
593         munmap(base, size);
594         errno = ENOEXEC;
595         return 0;
596     }
597 
598     sectinfo->base = base;
599     sectinfo->size = size;
600     sectinfo->prot = 0;
601     if (secthdr->flags & SECTION_FLAG_READ)
602         sectinfo->prot |= PROT_READ;
603     if (secthdr->flags & SECTION_FLAG_WRITE)
604         sectinfo->prot |= PROT_WRITE;
605     if (secthdr->flags & SECTION_FLAG_EXEC)
606         sectinfo->prot |= PROT_EXEC;
607     sectinfo->origbase = secthdr->virtaddr;
608     sectinfo->origsize = secthdr->virtsize;
609 
610     return 1;
611 }
612 
613 /*************************************************************************/
614 
615 /**
616  * w32dll_update_rva:  Update the rva_filepos[] table in the DLL handle
617  * for any RVAs within the given segment.
618  *
619  * Parameters:
620  *         dll: DLL handle.
621  *     secthdr: Pointer to section header.
622  * Return value:
623  *     None.
624  */
625 
w32dll_update_rva(W32DLLHandle dll,struct pe_section_header * secthdr)626 static void w32dll_update_rva(W32DLLHandle dll,
627                               struct pe_section_header *secthdr)
628 {
629     int i;
630 
631     for (i = 0; i < RVA_MAX; i++) {
632         if (!dll->rva_filepos[i]
633          && dll->extheader.rva[i].address >= secthdr->virtaddr
634          && dll->extheader.rva[i].address < secthdr->virtaddr+secthdr->virtsize
635         ) {
636             dll->rva_filepos[i] =
637                 dll->extheader.rva[i].address - secthdr->virtaddr
638                                               + secthdr->fileaddr;
639         }
640     }
641 }
642 
643 /*************************************************************************/
644 
645 /**
646  * w32dll_read_exports:  Read in the DLL's export table, and fill in the
647  * export data in the DLL handle.
648  *
649  * Parameters:
650  *     dll: DLL handle.
651  *      fd: File descriptor to read from.
652  * Return value:
653  *     Nonzero on success, zero on failure.
654  * Notes:
655  *     On error, errno is set appropriately.
656  */
657 
w32dll_read_exports(W32DLLHandle dll,int fd)658 static int w32dll_read_exports(W32DLLHandle dll, int fd)
659 {
660     struct export_directory exportdir;
661     off_t secofs = (off_t)dll->rva_filepos[RVA_EXPORT]
662                    - (off_t)dll->extheader.rva[RVA_EXPORT].address;
663 
664     /* Read in the export table. */
665     if (lseek(fd, dll->rva_filepos[RVA_EXPORT], SEEK_SET) == -1)
666         goto error;
667     if (read(fd, &exportdir, sizeof(exportdir)) != sizeof(exportdir))
668         goto err_noexec;
669     dll->export_ordinal_base = exportdir.ordinal_base;
670 
671     /* Read in each exported function address, relocate it, and store the
672      * relocated address in the DLL handle structure. */
673     if (exportdir.nfuncs) {
674         dll->export_table =
675             malloc(sizeof(*dll->export_table) * exportdir.nfuncs);
676         if (!dll->export_table)
677             goto error;
678         if (lseek(fd, exportdir.func_table + secofs, SEEK_SET) == -1)
679             goto error;
680         /* Use the entry count field itself as a loop variable, to ensure
681          * that the correct number of entries are cleaned up on error
682          * (doesn't matter here, but avoids a memory leak for the name
683          * array handling below) */
684         for (dll->export_ordinal_count = 0;
685              dll->export_ordinal_count < exportdir.nfuncs;
686              dll->export_ordinal_count++
687         ) {
688             uint32_t address;
689             if (read(fd, &address, 4) != 4)
690                 goto err_noexec;
691             address += dll->extheader.image_base;
692             dll->export_table[dll->export_ordinal_count] =
693                 w32dll_relocate_addr(dll, address);
694         }
695     }
696 
697     /* Read in each exported function name, and store the name and its
698      * associated ordinal in the DLL handle structure. */
699     if (exportdir.nnames) {
700         int i;
701         dll->export_name_table =
702             malloc(sizeof(*dll->export_name_table) * exportdir.nnames);
703         if (!dll->export_name_table)
704             goto error;
705         if (lseek(fd, exportdir.name_ordinal_table + secofs, SEEK_SET) == -1)
706             goto error;
707         for (i = 0; i < exportdir.nnames; i++) {
708             uint16_t ordinal;
709             if (read(fd, &ordinal, 2) != 2)
710                 goto err_noexec;
711             dll->export_name_table[i].ordinal =
712                 dll->export_ordinal_base + ordinal;
713         }
714         for (dll->export_name_count = 0;
715              dll->export_name_count < exportdir.nnames;
716              dll->export_name_count++
717         ) {
718             uint32_t name_address;
719             char *s;
720             if (lseek(fd, exportdir.name_table+secofs+dll->export_name_count*4,
721                       SEEK_SET) == -1
722             ) {
723                 goto error;
724             }
725             if (read(fd, &name_address, 4) != 4)
726                 goto err_noexec;
727             if (lseek(fd, name_address + secofs, SEEK_SET) == -1)
728                 goto error;
729             s = w32dll_read_asciiz(fd);
730             if (!s)
731                 goto error;
732             dll->export_name_table[dll->export_name_count].name = s;
733         }
734     }
735 
736     /* Success! */
737     return 1;
738 
739   err_noexec:
740     errno = ENOEXEC;
741   error:
742     return 0;
743 }
744 
745 /*************************************************************************/
746 
747 /**
748  * w32dll_process_imports:  Reads the list of imports described by
749  * `importdir' and sets the pointers to appropriate values (emulation
750  * functions or a placeholder function).
751  *
752  * Parameters:
753  *           dll: DLL handle.
754  *     importdir: Import directory structure.
755  * Return value:
756  *     Nonzero on success, zero on error.
757  * Notes:
758  *     - On error, errno is set appropriately.
759  *     - This routine assumes that all import data is located in the same
760  *       section.  Since the import address table has to be in a loaded
761  *       section (usually a data section), this implies that the rest of
762  *       the import data is also in a loaded section; therefore, we take
763  *       the easy approach and access the data directly in memory.
764  */
765 
w32dll_process_imports(W32DLLHandle dll,struct import_directory * importdir)766 static int w32dll_process_imports(W32DLLHandle dll,
767                                   struct import_directory *importdir)
768 {
769     const uint32_t imgbase = dll->extheader.image_base;  // shorthand
770     const char *module;
771     const struct import_name_entry **names;
772     void **addrs;
773     int i;
774 
775     /* Relocate import directory addresses. */
776     module = w32dll_relocate_addr(dll, importdir->module_name + imgbase);
777     names = w32dll_relocate_addr(dll, importdir->import_table + imgbase);
778     addrs = w32dll_relocate_addr(dll, importdir->import_addr_table + imgbase);
779     if (!module || !*module || !names || !addrs)
780         goto err_noexec;
781 
782     /* Process the imports. */
783     for (i = 0; names[i]; i++) {
784         const struct import_name_entry *name;
785         uint32_t ordinal;
786         if ((uint32_t)names[i] & 0x80000000UL) {
787             name = NULL;
788             ordinal = (uint32_t)names[i] & 0x7FFFFFFFUL;
789         } else {
790             name = w32dll_relocate_addr(dll, (uint32_t)names[i] + imgbase);
791             if (!name)
792                 goto err_noexec;
793         }
794         if (name)
795             addrs[i] = w32dll_import_by_name(module, name);
796         else
797             addrs[i] = w32dll_import_by_ordinal(module, ordinal);
798     }
799 
800     /* All done. */
801     return 1;
802 
803   err_noexec:
804     errno = ENOEXEC;
805     return 0;
806 }
807 
808 /*************************************************************************/
809 
810 /**
811  * w32dll_import_by_name, w32dll_import_by_ordinal:  Return the address
812  * corresponding to the given import, selected by either name or ordinal.
813  *
814  * Parameters:
815  *      module: Name of the module from which to import.
816  *        name: Import name descriptor (w32dll_import_by_name() only).
817  *     ordinal: Import ordinal (w32dll_import_by_ordinal() only).
818  * Return value:
819  *     The address corresponding to the import, or NULL if the import
820  *     failed.
821  * Notes:
822  *     - A NULL return is *not* considered an error, and thus errno is
823  *       undefined after returning from these functions.
824  *     - Currently, these functions just ask the Win32 emulation layer for
825  *       an appropriate function, and do not handle linking between
826  *       multiple loaded DLLs.
827  */
828 
w32dll_import_by_name(const char * module,const struct import_name_entry * name)829 static void *w32dll_import_by_name(const char *module,
830                                    const struct import_name_entry *name)
831 {
832     return w32dll_emu_import_by_name(module, name);
833 }
834 
835 /************************************/
836 
w32dll_import_by_ordinal(const char * module,uint32_t ordinal)837 static void *w32dll_import_by_ordinal(const char *module, uint32_t ordinal)
838 {
839     return w32dll_emu_import_by_ordinal(module, ordinal);
840 }
841 
842 /*************************************************************************/
843 
844 /**
845  * w32dll_read_relocs:  Read a set of relocation offsets for the DLL from
846  * the given file descriptor.
847  *
848  * Parameters:
849  *             dll: DLL handle.
850  *              fd: File descriptor to read from.
851  *      relocs_ptr: Pointer to relocation entry array (dynamically allocated).
852  *     nrelocs_ptr: Pointer to relocation entry count.
853  * Return value:
854  *     Positive if relocations were read successfully.
855  *     Zero if the end of the relocation table was reached.
856  *     Negative if an error cocurred.
857  * Notes:
858  *     - *relocs_ptr and *nrelocs_ptr must be initialized to NULL and 0,
859  *       respectively, before the first call; they will be updated with
860  *       each call.
861  *     - On error, errno is set appropriately.
862  */
863 
w32dll_read_relocs(W32DLLHandle dll,int fd,uint32_t ** relocs_ptr,int * nrelocs_ptr)864 static int w32dll_read_relocs(W32DLLHandle dll, int fd,
865                               uint32_t **relocs_ptr, int *nrelocs_ptr)
866 {
867     uint32_t base, size, *new_relocs;
868     int index;
869 
870     if (read(fd, &base, 4) != 4
871      || read(fd, &size, 4) != 4
872      || (size > 0 && size < 8)
873     ) {
874         free(*relocs_ptr);
875         *relocs_ptr = NULL;
876         *nrelocs_ptr = 0;
877         errno = ENOEXEC;
878         return -1;
879     }
880     if (!size)
881         return 0;
882     if (size <= 8)  // Technically == works too, but play it safe
883         return 1;
884     size = (size-8) / 2;  // Number of entries in this group
885     index = *nrelocs_ptr;
886     new_relocs = realloc(*relocs_ptr,
887                          sizeof(**relocs_ptr) * (*nrelocs_ptr + size));
888     if (!new_relocs)
889         goto err_noexec;
890     *relocs_ptr = new_relocs;
891     *nrelocs_ptr += size;
892     while (size > 0) {
893         uint16_t buf[1000];
894         int toread, i;
895         toread = size;
896         if (toread > sizeof(buf)/2)
897             toread = sizeof(buf)/2;
898         if (read(fd, buf, toread*2) != toread*2)
899             goto err_noexec;
900         for (i = 0; i < toread; i++) {
901             if (buf[i]>>12 == 3) {
902                 (*relocs_ptr)[index++] = dll->extheader.image_base
903                                        + base + (buf[i] & 0xFFF);
904             } else {
905                 (*nrelocs_ptr)--;
906             }
907         }
908         size -= toread;
909     }
910     return 1;
911 
912   err_noexec:
913     {
914         int errno_save = errno;
915         free(*relocs_ptr);
916         *relocs_ptr = NULL;
917         *nrelocs_ptr = 0;
918         errno = errno_save;
919         return -1;
920     }
921 }
922 
923 /*************************************************************************/
924 
925 /**
926  * w32dll_relocate:  Perform relocations on the loaded DLL.
927  *
928  * Parameters:
929  *         dll: DLL handle with all sections loaded.
930  *      relocs: Array of virtual addresses to be relocated.
931  *     nrelocs: Number of relocations.
932  * Return value:
933  *     None.
934  */
935 
936 #include <stdio.h>
w32dll_relocate(W32DLLHandle dll,uint32_t * relocs,int nrelocs)937 static void w32dll_relocate(W32DLLHandle dll, uint32_t *relocs, int nrelocs)
938 {
939     int i;
940 
941     for (i = 0; i < nrelocs; i++) {
942         uint32_t *addr = w32dll_relocate_addr(dll, relocs[i]);
943         if (addr)
944             *addr = (uint32_t)w32dll_relocate_addr(dll, *addr);
945     }
946 }
947 
948 /*************************************************************************/
949 
950 /**
951  * w32dll_relocate_addr:  Relocate a single address.
952  *
953  * Parameters:
954  *      dll: DLL handle.
955  *     addr: Address to relocate.
956  * Return value:
957  *     The relocated address, or NULL if the address is not in a loaded
958  *     section.
959  */
960 
w32dll_relocate_addr(W32DLLHandle dll,uint32_t addr)961 static void *w32dll_relocate_addr(W32DLLHandle dll, uint32_t addr)
962 {
963     int i;
964 
965     for (i = 0; i < dll->nsections; i++) {
966         if (addr >= dll->sections[i].origbase
967          && addr <  dll->sections[i].origbase + dll->sections[i].origsize
968         ) {
969             return (uint8_t *)dll->sections[i].base
970                    + (addr - dll->sections[i].origbase);
971         }
972     }
973     return NULL;
974 }
975 
976 /*************************************************************************/
977 
978 /**
979  * w32dll_read_asciiz:  Read a null-terminated string from the given file
980  * descriptor.
981  *
982  * Parameters:
983  *     fd: File descriptor to read from.
984  * Return value:
985  *     String read in (allocated with malloc()), or NULL on error.
986  * Notes:
987  *     On error, errno is set appropriately.
988  */
989 
w32dll_read_asciiz(int fd)990 static char *w32dll_read_asciiz(int fd)
991 {
992     char *str = NULL;
993     int size = 0, len = 0;
994 
995     do {
996         if (len >= size) {
997             size = len+100;
998             char *newstr = realloc(str, size);
999             if (!newstr) {
1000                 int errno_save = errno;
1001                 free(str);
1002                 errno = errno_save;
1003                 return NULL;
1004             }
1005             str = newstr;
1006         }
1007         if (read(fd, str+len, 1) != 1) {
1008             free(str);
1009             errno = ENOEXEC;
1010             return NULL;
1011         }
1012         len++;
1013     } while (str[len-1] != 0);
1014     return str;
1015 }
1016 
1017 /*************************************************************************/
1018 
1019 /**
1020  * w32dll_init_fs:  Set up the FS segment register to point to a page of
1021  * data (empty except for the linear address pointer at 0x18).
1022  *
1023  * Parameters:
1024  *     None.
1025  * Return value:
1026  *     Nonzero on success, zero on failure.
1027  * Notes:
1028  *     On error, errno is set appropriately.
1029  */
1030 
1031 #if defined(OS_LINUX)
1032 # include <asm/unistd.h>
1033 # include <asm/ldt.h>
1034 // This doesn't work, because of PIC:
1035 //static _syscall3(int, modify_ldt, int, func, void *, ptr, unsigned long, bytecount);
modify_ldt(int func,void * ptr,unsigned long bytecount)1036 static int modify_ldt(int func, void *ptr, unsigned long bytecount) {
1037     long __res;
1038     __asm__ volatile ("push %%ebx; mov %%esi, %%ebx; int $0x80; pop %%ebx"
1039                       : "=a" (__res)
1040                       : "0" (__NR_modify_ldt), "S" ((long)(func)),
1041                         "c" ((long)(ptr)), "d" ((long)(bytecount))
1042                       : "memory");
1043     /* Errors are from -1 to -128, according to <asm/unistd.h> */
1044     if ((__res & 0xFFFFFF80UL) == 0xFFFFFF80UL) {
1045         errno = -__res & 0xFF;
1046         __res = (unsigned long)-1;
1047     }
1048     return (int)__res;
1049 }
1050 #else
1051 # error OS not supported in w32dll_init_fs()
1052 #endif
1053 
w32dll_init_fs(void)1054 static int w32dll_init_fs(void)
1055 {
1056     int fd;
1057     void *base;
1058     int segment;
1059 #if defined(OS_LINUX)
1060     struct user_desc ldt;
1061 #endif
1062 
1063     fd = open("/dev/zero", O_RDWR);
1064     if (fd < 0)
1065         return 0;
1066     base = mmap(NULL, GETPAGESIZE(), PROT_READ | PROT_WRITE,
1067                 MAP_PRIVATE, fd, 0);
1068     if (base == MAP_FAILED) {
1069         int errno_save = errno;
1070         close(fd);
1071         errno = errno_save;
1072         return 0;
1073     }
1074     close(fd);
1075     *(void **)((uint8_t *)base + 0x18) = base;
1076 
1077 #if defined(OS_LINUX)
1078     memset(&ldt, 0, sizeof(ldt));
1079     /* Pick a random number that's hopefully unused.  How does one
1080      * determine which segment numbers are in use? */
1081     ldt.entry_number = 172;
1082     ldt.base_addr = (long)base;
1083     ldt.limit = GETPAGESIZE();
1084     ldt.seg_32bit = 1;
1085     ldt.read_exec_only = 0;
1086     ldt.seg_not_present = 0;
1087     ldt.contents = MODIFY_LDT_CONTENTS_DATA;
1088     ldt.limit_in_pages = 0;
1089     ldt.seg_not_present = 0;
1090     ldt.useable = 1;
1091     if (modify_ldt(17, &ldt, sizeof(ldt)) != 0) {
1092         int errno_save = errno;
1093         munmap(base, GETPAGESIZE());
1094         errno = errno_save;
1095         return 0;
1096     }
1097     segment = ldt.entry_number;
1098 #endif
1099 
1100     /* Bit 2: 1 == use LDT; bits 1-0: 3 == privilege level 3 */
1101     asm("movw %%ax,%%fs" : : "a" (segment<<3 | 1<<2 | 3));
1102     return 1;
1103 }
1104 
1105 /*************************************************************************/
1106 /*************************************************************************/
1107 
1108 #ifdef TEST
1109 
1110 #include <stdio.h>
1111 
main(int ac,char ** av)1112 int main(int ac, char **av)
1113 {
1114     W32DLLHandle dll;
1115 
1116     if (ac < 2 || strcmp(av[1], "-h") == 0 || strcmp(av[1], "--help") == 0) {
1117         fprintf(stderr, "Usage: %s file.dll [procname | =ordinal]\n", av[0]);
1118         return -1;
1119     }
1120     dll = w32dll_load(av[1], 1);
1121     if (!dll) {
1122         perror(av[1]);
1123         return 1;
1124     }
1125     if (ac >= 3) {
1126         int i;
1127         void *(*func)(void);
1128         void ***codec, **functable, ***vid, **vidtable, ***aud, **audtable;
1129         WINAPI void *(*init)(int, int);
1130         WINAPI void *(*fini)(void);
1131         WINAPI void *(*getvid)(void);
1132         WINAPI void *(*getaud)(void);
1133         static char buf[0x14000], buf2[720*480*4], buf2a[0x800], buf3[0x2000];
1134         struct { int dataset; void *workbuf; struct {uint8_t w8, h8, f02, f03; int ofs;} *inparam; void *inbuf; struct {int stride; void *outbuf;} *outparam;} vidparam;
1135         void *bufptr = buf;
1136         struct { int flag; char *buffer; } audparam;
1137         int fd = open("/scratch/pv3/060428-192352-720x480i.dv", O_RDONLY);
1138         read(fd, buf, 0x14000);
1139         close(fd);
1140         if (av[2][0] == '=')
1141             func = w32dll_lookup_by_ordinal(dll, strtoul(av[2]+1,NULL,0));
1142         else
1143             func = w32dll_lookup_by_name(dll, av[2]);
1144         if (!func) {
1145             perror(av[2]);
1146             w32dll_unload(dll);
1147             return 2;
1148         }
1149         printf("%s: %p\n", av[2], func);
1150         codec = func();
1151         functable = *codec;
1152         printf("--> %p [%p %p %p %p...]\n", codec,
1153                functable[0], functable[1], functable[2], functable[3]);
1154         init = functable[0];
1155         fini = functable[1];
1156         getvid = functable[2];
1157         getaud = functable[3];
1158         printf("calling init...\n");
1159         //(*init)(4, 2);
1160         asm("push $2; push $4; call *%0" : : "r" (init), "c" (codec));
1161         printf("...done!\n");
1162         printf("calling getvid...\n");
1163         asm("call *%1" : "=a" (vid) : "r" (getvid), "c" (codec));
1164         vidtable = *vid;
1165         printf("...done! (%p -> %p %p ... %p ...)\n", aud,
1166                vidtable[0], vidtable[1], vidtable[5]);
1167         memset(&vidparam, 0, sizeof(vidparam));
1168         vidparam.dataset = 0;
1169         vidparam.workbuf = buf2a;
1170         vidparam.inparam = calloc(sizeof(*vidparam.inparam), 1);
1171         vidparam.inparam->w8 = buf[4];
1172         vidparam.inparam->h8 = buf[5];
1173         vidparam.inparam->ofs = 0;
1174         vidparam.inbuf = &bufptr;
1175         vidparam.outparam = malloc(sizeof(*vidparam.outparam));
1176         vidparam.outparam->stride = (buf[4]*8)*2;
1177         vidparam.outparam->outbuf = buf2;
1178         printf("calling video_decode...\n");  // 10013830
1179         asm("push %2; call *%1"
1180             : "=a" (i)
1181             : "r" (vidtable[5]), "r" (&vidparam), "c" (vid));
1182         printf("...done! (%d)\n", i);
1183         printf("and again...\n");
1184         vidparam.dataset = 1;
1185         asm("push %2; call *%1"
1186             : "=a" (i)
1187             : "r" (vidtable[5]), "r" (&vidparam), "c" (vid));
1188         printf("...done! (%d)\n", i);
1189         printf("calling getaud...\n");
1190         asm("call *%1" : "=a" (aud) : "r" (getaud), "c" (codec));
1191         audtable = *aud;
1192         printf("...done! (%p -> %p %p %p ...)\n", aud,
1193                audtable[0], audtable[1], audtable[2]);
1194         audparam.flag = 0;
1195         audparam.buffer = buf;
1196         *(void **)(buf3+24) = buf3+32;
1197         printf("calling audio_decode...\n");  // 10013830
1198         asm("push %3; push %2; call *%1"
1199             : "=a" (i)
1200             : "r" (audtable[1]), "r" (&audparam), "r" (buf3), "c" (aud));
1201         printf("...done! (%d)\n", i);
1202         fd = open("/scratch/pv3/test.raw", O_WRONLY | O_CREAT | O_TRUNC, 0666);
1203         write(fd, buf3, sizeof(buf3));
1204         write(fd, buf2, sizeof(buf2));
1205         close(fd);
1206         printf("calling fini...\n");
1207         asm("call *%0" : : "r" (fini), "c" (codec));
1208         //(*fini)();
1209         printf("...done!\n");
1210     }
1211     w32dll_unload(dll);
1212     return 0;
1213 }
1214 
1215 #endif
1216 
1217 /*************************************************************************/
1218 
1219 /*
1220  * Local variables:
1221  *   c-file-style: "stroustrup"
1222  *   c-file-offsets: ((case-label . *) (statement-case-intro . *))
1223  *   indent-tabs-mode: nil
1224  * End:
1225  *
1226  * vim: expandtab shiftwidth=4:
1227  */
1228