summaryrefslogtreecommitdiffstats
path: root/debian/transcode/transcode-1.1.7/import/w32dll.c
diff options
context:
space:
mode:
Diffstat (limited to 'debian/transcode/transcode-1.1.7/import/w32dll.c')
-rw-r--r--debian/transcode/transcode-1.1.7/import/w32dll.c1227
1 files changed, 1227 insertions, 0 deletions
diff --git a/debian/transcode/transcode-1.1.7/import/w32dll.c b/debian/transcode/transcode-1.1.7/import/w32dll.c
new file mode 100644
index 00000000..3923d0cf
--- /dev/null
+++ b/debian/transcode/transcode-1.1.7/import/w32dll.c
@@ -0,0 +1,1227 @@
+/*
+ * w32dll.c -- a simplistic interface to Win32 DLLs (no thread support)
+ * Written by Andrew Church <achurch@achurch.org>
+ *
+ * This file is part of transcode, a video stream processing tool.
+ * transcode is free software, distributable under the terms of the GNU
+ * General Public License (version 2 or later). See the file COPYING
+ * for details.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <sys/mman.h>
+
+#if !defined(HAVE_MMAP)
+# error Sorry, mmap() support is required.
+#endif
+
+#if defined(HAVE_ENDIAN_H)
+# include <endian.h>
+# if __BYTE_ORDER != __LITTLE_ENDIAN
+# error Sorry, only little-endian architectures are supported.
+# endif
+#endif
+
+#if defined(HAVE_SYSCONF_WITH_SC_PAGESIZE)
+# define GETPAGESIZE() (sysconf(_SC_PAGESIZE))
+#elif defined(HAVE_GETPAGESIZE)
+# define GETPAGESIZE() (getpagesize())
+#elif defined(PAGESIZE)
+# define GETPAGESIZE() (PAGESIZE)
+#elif defined(PAGE_SIZE)
+# define GETPAGESIZE() (PAGE_SIZE)
+#else
+# error System page size is not available!
+#endif
+
+#include "w32dll.h"
+#include "w32dll-local.h"
+
+/*************************************************************************/
+
+/* Contents of a DLL handle. */
+
+struct w32dllhandle_ {
+ /* Signature (to protect against bad pointers and double-free */
+ uint32_t signature;
+
+ /* Overall file data */
+ struct pe_header header;
+ struct pe_ext_header extheader;
+
+ /* File position for each RVA entry */
+ off_t rva_filepos[RVA_MAX];
+
+ /* Data for each loaded section */
+ int nsections;
+ struct section_info {
+ void *base;
+ uint32_t size;
+ int prot; /* Protection flags for mprotect() */
+ uint32_t origbase; /* Virtual address given in section header */
+ uint32_t origsize; /* Likewise, for size */
+ } *sections;
+
+ /* Data for exported functions */
+ int export_ordinal_base;
+ int export_ordinal_count;
+ void **export_table;
+ int export_name_count;
+ struct export_name {
+ char *name;
+ uint32_t ordinal;
+ } *export_name_table;
+};
+
+#define HANDLE_SIGNATURE 0xD11DA7A5
+
+
+/* Forward declarations for internal routines. */
+
+static int w32dll_add_section(W32DLLHandle dll, int fd,
+ struct pe_section_header *secthdr);
+static int w32dll_load_section(int fd, struct pe_section_header *secthdr,
+ struct section_info *sectinfo);
+static void w32dll_update_rva(W32DLLHandle dll,
+ struct pe_section_header *secthdr);
+static int w32dll_read_exports(W32DLLHandle dll, int fd);
+static int w32dll_process_imports(W32DLLHandle dll,
+ struct import_directory *importdir);
+static void *w32dll_import_by_name(const char *module,
+ const struct import_name_entry *name);
+static void *w32dll_import_by_ordinal(const char *module, uint32_t ordinal);
+static int w32dll_read_relocs(W32DLLHandle dll, int fd,
+ uint32_t **relocs_ptr, int *nrelocs_ptr);
+static void w32dll_relocate(W32DLLHandle dll, uint32_t *relocs, int nrelocs);
+static void *w32dll_relocate_addr(W32DLLHandle dll, uint32_t addr);
+static char *w32dll_read_asciiz(int fd);
+static int w32dll_init_fs(void);
+
+/*************************************************************************/
+/*************************************************************************/
+
+/* External interface routines. */
+
+/*************************************************************************/
+
+/**
+ * w32dll_load: Load the given DLL file into memory, and return a handle
+ * to it.
+ *
+ * Parameters:
+ * path: DLL file pathname.
+ * compat: If nonzero, adds a memory mapping for the entire DLL to
+ * accommodate misbehaving DLLs that access memory outside the
+ * registered sections.
+ * Return value:
+ * DLL handle (nonzero), or zero on error.
+ * Side effects:
+ * Sets errno to an appropriate value on error, including ENOEXEC if
+ * the file is not recognized as a Win32 DLL file or is corrupt or
+ * truncated, or ETXTBSY if the DLL's DllMain() function returns an
+ * error. On successful return, errno is undefined.
+ */
+
+W32DLLHandle w32dll_load(const char *path, int compat)
+{
+ W32DLLHandle dll;
+ struct dos_header doshdr;
+ int fd, i;
+
+ /* Allocate and initialize the DLL handle. */
+ dll = malloc(sizeof(*dll));
+ if (!dll)
+ return NULL;
+ memset(&dll->header, 0, sizeof(dll->header));
+ memset(&dll->extheader, 0, sizeof(dll->extheader));
+ memset(&dll->rva_filepos, 0, sizeof(dll->rva_filepos));
+ dll->signature = HANDLE_SIGNATURE;
+ dll->nsections = 0;
+ dll->sections = NULL;
+ dll->export_ordinal_base = 0;
+ dll->export_ordinal_count = 0;
+ dll->export_table = NULL;
+ dll->export_name_count = 0;
+ dll->export_name_table = NULL;
+
+ /* Open the file, and ensure that it's seekable. */
+ fd = open(path, O_RDONLY);
+ if (fd == -1 || lseek(fd, 0, SEEK_SET) == -1) {
+ int errno_save = errno;
+ free(dll);
+ errno = errno_save;
+ return NULL;
+ }
+
+ /* Check for a valid (Win32-style) DOS executable header. */
+ if (read(fd, &doshdr, sizeof(doshdr)) != sizeof(doshdr)
+ || doshdr.signature != DOS_EXE_SIGNATURE
+ || doshdr.reloc_offset < 0x40
+ ) {
+ goto err_noexec;
+ }
+
+ /* Check for a valid PE header (standard and optional both required). */
+ if (lseek(fd, doshdr.winheader, SEEK_SET) == -1
+ || read(fd, &dll->header, sizeof(dll->header)) != sizeof(dll->header)
+ || dll->header.opt_header_size < sizeof(dll->extheader)
+ || read(fd, &dll->extheader, sizeof(dll->extheader))
+ != sizeof(dll->extheader)
+ || dll->header.signature != WIN_PE_SIGNATURE
+ || !(dll->header.flags & WIN_PE_FLAG_DLL)
+#if defined(ARCH_X86)
+ || (dll->header.arch & ~3) != WIN_PE_ARCH_X86
+ || dll->extheader.magic != WIN_PE_OPT_MAGIC_32
+#else
+# error Sorry, this architecture is not supported.
+#endif
+ ) {
+ goto err_noexec;
+ }
+ /* Skip past any extra header bytes we didn't need. */
+ if (dll->header.opt_header_size > sizeof(dll->extheader)) {
+ if (lseek(fd, dll->header.opt_header_size - sizeof(dll->extheader),
+ SEEK_CUR) == -1
+ ) {
+ goto err_noexec;
+ }
+ }
+
+ /* Go through the section table and attempt to load each section. Also
+ * determine file positions for each RVA entry. Note that we do not
+ * simply map the entire file because (1) sections may be larger in
+ * memory than in the file and (2) the system's page size may be larger
+ * than that specified in the file. */
+ for (i = 0; i < dll->header.nsections + (compat ? 1 : 0); i++) {
+ struct pe_section_header secthdr;
+
+ if (i >= dll->header.nsections) {
+ /* Set up compatibility entry */
+ off_t curpos = lseek(fd, 0, SEEK_CUR);
+ off_t filesize = lseek(fd, 0, SEEK_END);
+ if (curpos==-1 || filesize==-1 || lseek(fd,curpos,SEEK_SET)==-1)
+ goto error;
+ secthdr.virtaddr = 0;
+ secthdr.virtsize = dll->extheader.image_size;
+ secthdr.fileaddr = 0;
+ secthdr.filesize = filesize;
+ secthdr.flags = SECTION_FLAG_DATA | SECTION_FLAG_READ;
+ } else {
+ if (read(fd, &secthdr, sizeof(secthdr)) != sizeof(secthdr))
+ goto err_noexec;
+ }
+ w32dll_update_rva(dll, &secthdr);
+ w32dll_add_section(dll, fd, &secthdr);
+ }
+
+ /* Load and process relocations. Note that once the sections are
+ * loaded, we could theoretically just retrieve these (and the other
+ * data below) from memory, but since we take the approach of only
+ * loading/mapping the sections we need, we do this the hard way and
+ * read the data directly from the file. */
+ if (dll->rva_filepos[RVA_BASE_RELOC]
+ && dll->extheader.rva[RVA_BASE_RELOC].size
+ ) {
+ uint32_t *relocs = NULL;
+ int nrelocs = 0;
+
+ if (lseek(fd, dll->rva_filepos[RVA_BASE_RELOC], SEEK_SET) == -1)
+ goto error;
+ while (lseek(fd, 0, SEEK_CUR)
+ <= dll->rva_filepos[RVA_BASE_RELOC]
+ + dll->extheader.rva[RVA_BASE_RELOC].size - 8
+ ) {
+ int res = w32dll_read_relocs(dll, fd, &relocs, &nrelocs);
+ if (res < 0)
+ goto error;
+ if (res == 0)
+ break;
+ }
+ w32dll_relocate(dll, relocs, nrelocs);
+ }
+
+ /* Load export table. */
+ if (dll->rva_filepos[RVA_EXPORT]
+ && dll->extheader.rva[RVA_EXPORT].size >= sizeof(struct export_directory)
+ ) {
+ if (!w32dll_read_exports(dll, fd))
+ goto error;
+ }
+
+ /* Load and process import table. */
+ if (dll->rva_filepos[RVA_IMPORT]
+ && dll->extheader.rva[RVA_IMPORT].size >= sizeof(struct import_directory)
+ ) {
+ struct import_directory importdir;
+
+ if (lseek(fd, dll->rva_filepos[RVA_IMPORT], SEEK_SET) == -1)
+ goto error;
+ while (lseek(fd, 0, SEEK_CUR)
+ <= dll->rva_filepos[RVA_IMPORT]
+ + dll->extheader.rva[RVA_IMPORT].size - sizeof(importdir)
+ ) {
+ if (read(fd, &importdir, sizeof(importdir)) != sizeof(importdir))
+ goto err_noexec;
+ if (!importdir.module_name)
+ break; /* Last entry in table */
+ if (!importdir.import_table || !importdir.import_addr_table)
+ goto err_noexec;
+ if (!w32dll_process_imports(dll, &importdir))
+ goto error;
+ }
+ }
+
+ /* Set section access privileges appropriately. */
+ for (i = 0; i < dll->nsections; i++) {
+ if (mprotect(dll->sections[i].base, dll->sections[i].size,
+ dll->sections[i].prot) != 0
+ ) {
+ goto error;
+ }
+ }
+
+ /* Close file descriptor (no longer needed). */
+ close(fd);
+ fd = -1;
+
+ /* Set up the FS register with a dummy thread information block.
+ * We deliberately don't support libraries that depend on the OS to
+ * put things here; we just provide the space so that accesses to
+ * %fs:... don't segfault. */
+ if (!w32dll_init_fs())
+ goto error;
+
+ /* Call the DllMain() entry point. */
+ if (dll->extheader.entry_point) {
+ WINAPI int (*DllMain)(uint32_t handle, uint32_t reason, void *resv);
+ DllMain = w32dll_relocate_addr(dll, dll->extheader.entry_point
+ + dll->extheader.image_base);
+ if (!DllMain)
+ goto err_noexec;
+ if (!(*DllMain)(HANDLE_DEFAULT, DLL_PROCESS_ATTACH, NULL)) {
+ (*DllMain)(HANDLE_DEFAULT, DLL_PROCESS_DETACH, NULL);
+ errno = ETXTBSY;
+ goto error;
+ }
+ }
+
+ /* Successful! */
+ return dll;
+
+ /* Error handling */
+ err_noexec:
+ errno = ENOEXEC;
+ error:
+ {
+ int errno_save = errno;
+ close(fd);
+ w32dll_unload(dll);
+ errno_save = errno;
+ return NULL;
+ }
+}
+
+/*************************************************************************/
+
+/**
+ * w32dll_unload: Unload the given DLL from memory. Does nothing if the
+ * given handle is zero or invalid.
+ *
+ * Parameters:
+ * dll: DLL handle.
+ * Return value:
+ * None.
+ */
+
+void w32dll_unload(W32DLLHandle dll)
+{
+ int i;
+
+ if (!dll || dll->signature != HANDLE_SIGNATURE)
+ return;
+
+ /* Call the DllMain() entry point with DLL_PROCESS_DETACH. */
+ if (dll->extheader.entry_point) {
+ WINAPI int (*DllMain)(uint32_t handle, uint32_t reason, void *resv);
+ DllMain = w32dll_relocate_addr(dll, dll->extheader.entry_point
+ + dll->extheader.image_base);
+ if (DllMain)
+ (*DllMain)(HANDLE_DEFAULT, DLL_PROCESS_DETACH, NULL);
+ }
+
+ /* Free DLL memory. */
+ for (i = 0; i < dll->nsections; i++) {
+ munmap(dll->sections[i].base, dll->sections[i].size);
+ dll->sections[i].base = NULL;
+ dll->sections[i].size = 0;
+ }
+ free(dll->sections);
+ dll->sections = NULL;
+ dll->nsections = 0;
+
+ /* Free export tables. */
+ free(dll->export_table);
+ dll->export_table = NULL;
+ for (i = 0; i < dll->export_name_count; i++) {
+ free(dll->export_name_table[i].name);
+ dll->export_name_table[i].name = NULL;
+ }
+ free(dll->export_name_table);
+ dll->export_name_table = NULL;
+
+ /* Free the handle structure itself. */
+ dll->signature = ~HANDLE_SIGNATURE;
+ free(dll);
+
+ return;
+}
+
+/*************************************************************************/
+
+/**
+ * w32dll_lookup_by_name: Look up the address of an exported function in
+ * the given DLL, using the function's name.
+ *
+ * Parameters:
+ * dll: DLL handle.
+ * name: Function name.
+ * Return value:
+ * Function address, or NULL on error.
+ * Side effects:
+ * Sets errno to one of the following values on error:
+ * EINVAL: `dll' or `name' was invalid.
+ * ENOENT: The requested function does not exist.
+ * On successful return, errno is undefined.
+ */
+
+void *w32dll_lookup_by_name(W32DLLHandle dll, const char *name)
+{
+ int i;
+
+ if (!dll || dll->signature != HANDLE_SIGNATURE || !name || !*name) {
+ errno = EINVAL;
+ return NULL;
+ }
+ for (i = 0; i < dll->export_name_count; i++) {
+ if (strcmp(name, dll->export_name_table[i].name) == 0) {
+ return w32dll_lookup_by_ordinal(dll,
+ dll->export_name_table[i].ordinal);
+ }
+ }
+ errno = ENOENT;
+ return NULL;
+}
+
+/*************************************************************************/
+
+/**
+ * w32dll_lookup_by_ordinal: Look up the address of an exported function
+ * in the given DLL, using the function's ordinal value.
+ *
+ * Parameters:
+ * dll: DLL handle.
+ * ordinal: Function ordinal.
+ * Return value:
+ * Function address, or NULL on error.
+ * Side effects:
+ * Sets errno to one of the following values on error:
+ * EINVAL: `dll' was invalid.
+ * ENOENT: The requested function does not exist.
+ * On successful return, errno is undefined.
+ */
+
+void *w32dll_lookup_by_ordinal(W32DLLHandle dll, uint32_t ordinal)
+{
+ if (!dll || dll->signature != HANDLE_SIGNATURE) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if (ordinal < dll->export_ordinal_base) {
+ errno = ENOENT;
+ return NULL;
+ }
+ ordinal -= dll->export_ordinal_base;
+ if (ordinal >= dll->export_ordinal_count || !dll->export_table[ordinal]) {
+ errno = ENOENT;
+ return NULL;
+ }
+ return dll->export_table[ordinal];
+}
+
+/*************************************************************************/
+/*************************************************************************/
+
+/* Internal routines. */
+
+/*************************************************************************/
+
+/**
+ * w32dll_add_section: Checks the given section description, and loads it
+ * in from the DLL file, appending information to the dll->sections[]
+ * array, if appropriate.
+ *
+ * Parameters:
+ * dll: DLL handle.
+ * fd: File descriptor to read from.
+ * secthdr: Pointer to section header.
+ * Return value:
+ * Nonzero on success (including when the section was intentionally
+ * not loaded), zero on error.
+ * Notes:
+ * - On success, the file's current offset is not changed.
+ * - The allocated memory will be marked read/write; after relocation,
+ * use mprotect() to set the protection to sectinfo->prot.
+ * - On error, errno is set appropriately.
+ */
+
+static int w32dll_add_section(W32DLLHandle dll, int fd,
+ struct pe_section_header *secthdr)
+{
+ void *new_sections;
+
+ if (!(secthdr->flags & (SECTION_FLAG_CODE
+ | SECTION_FLAG_DATA
+ | SECTION_FLAG_BSS))
+ ) {
+ /* Don't know what kind of section this is, but we don't need it */
+ return 1;
+ }
+
+ if (!(secthdr->flags & (SECTION_FLAG_READ
+ | SECTION_FLAG_WRITE
+ | SECTION_FLAG_EXEC))
+ ) {
+ /* Don't bother loading--it wouldn't be accessible anyway */
+ return 1;
+ }
+
+ new_sections = realloc(dll->sections,
+ sizeof(*dll->sections) * (dll->nsections+1));
+ if (!new_sections)
+ return 0;
+ dll->sections = new_sections;
+ dll->sections[dll->nsections].base = NULL;
+ dll->sections[dll->nsections].size = 0;
+ dll->sections[dll->nsections].prot = 0;
+ dll->sections[dll->nsections].origbase = 0;
+ dll->sections[dll->nsections].origsize = 0;
+ dll->nsections++;
+ if (!w32dll_load_section(fd, secthdr, &dll->sections[dll->nsections-1]))
+ return 0;
+ dll->sections[dll->nsections-1].origbase += dll->extheader.image_base;
+
+ return 1;
+}
+
+/*************************************************************************/
+
+/**
+ * w32dll_load_section: Loads the section described by `secthdr' from the
+ * file descriptor `fd', setting the `sectinfo' structure appropriately.
+ *
+ * Parameters:
+ * fd: File to load data from.
+ * secthdr: Section header loaded from the file.
+ * sectinfo: Structure to store information about the segment in.
+ * Return value:
+ * Nonzero on success, zero on error.
+ * Notes:
+ * - On success, the file's current offset is not changed.
+ * - The allocated memory will be marked read/write; after relocation,
+ * use mprotect() to set the protection to sectinfo->prot.
+ * - On error, errno is set appropriately.
+ */
+
+static int w32dll_load_section(int fd, struct pe_section_header *secthdr,
+ struct section_info *sectinfo)
+{
+ int newfd;
+ void *base;
+ uint32_t size, toread;
+ off_t oldofs;
+
+ uint32_t pagesize = GETPAGESIZE();
+ if (pagesize < 0) {
+ errno = EINVAL;
+ return 0;
+ }
+
+#ifdef MAP_ANONYMOUS
+ newfd = -1;
+#else
+ newfd = open("/dev/zero", O_RDWR);
+#endif
+ size = (secthdr->virtsize + pagesize-1) / pagesize * pagesize;
+ base = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE
+#ifdef MAP_ANONYMOUS
+ | MAP_ANONYMOUS
+#endif
+ , newfd, 0);
+#ifndef MAP_ANONYMOUS
+ if (newfd != -1) {
+ int errno_save = errno;
+ close(newfd);
+ errno = errno_save;
+ }
+#endif
+ if (base == MAP_FAILED)
+ return 0;
+
+ oldofs = lseek(fd, 0, SEEK_CUR);
+ if (oldofs == -1) {
+ munmap(base, size);
+ return 0;
+ }
+ if (secthdr->filesize < secthdr->virtsize)
+ toread = secthdr->filesize;
+ else
+ toread = secthdr->virtsize;
+ if (lseek(fd, secthdr->fileaddr, SEEK_SET) == -1
+ || read(fd, base, toread) != toread
+ || lseek(fd, oldofs, SEEK_SET) == -1
+ ) {
+ munmap(base, size);
+ errno = ENOEXEC;
+ return 0;
+ }
+
+ sectinfo->base = base;
+ sectinfo->size = size;
+ sectinfo->prot = 0;
+ if (secthdr->flags & SECTION_FLAG_READ)
+ sectinfo->prot |= PROT_READ;
+ if (secthdr->flags & SECTION_FLAG_WRITE)
+ sectinfo->prot |= PROT_WRITE;
+ if (secthdr->flags & SECTION_FLAG_EXEC)
+ sectinfo->prot |= PROT_EXEC;
+ sectinfo->origbase = secthdr->virtaddr;
+ sectinfo->origsize = secthdr->virtsize;
+
+ return 1;
+}
+
+/*************************************************************************/
+
+/**
+ * w32dll_update_rva: Update the rva_filepos[] table in the DLL handle
+ * for any RVAs within the given segment.
+ *
+ * Parameters:
+ * dll: DLL handle.
+ * secthdr: Pointer to section header.
+ * Return value:
+ * None.
+ */
+
+static void w32dll_update_rva(W32DLLHandle dll,
+ struct pe_section_header *secthdr)
+{
+ int i;
+
+ for (i = 0; i < RVA_MAX; i++) {
+ if (!dll->rva_filepos[i]
+ && dll->extheader.rva[i].address >= secthdr->virtaddr
+ && dll->extheader.rva[i].address < secthdr->virtaddr+secthdr->virtsize
+ ) {
+ dll->rva_filepos[i] =
+ dll->extheader.rva[i].address - secthdr->virtaddr
+ + secthdr->fileaddr;
+ }
+ }
+}
+
+/*************************************************************************/
+
+/**
+ * w32dll_read_exports: Read in the DLL's export table, and fill in the
+ * export data in the DLL handle.
+ *
+ * Parameters:
+ * dll: DLL handle.
+ * fd: File descriptor to read from.
+ * Return value:
+ * Nonzero on success, zero on failure.
+ * Notes:
+ * On error, errno is set appropriately.
+ */
+
+static int w32dll_read_exports(W32DLLHandle dll, int fd)
+{
+ struct export_directory exportdir;
+ off_t secofs = (off_t)dll->rva_filepos[RVA_EXPORT]
+ - (off_t)dll->extheader.rva[RVA_EXPORT].address;
+
+ /* Read in the export table. */
+ if (lseek(fd, dll->rva_filepos[RVA_EXPORT], SEEK_SET) == -1)
+ goto error;
+ if (read(fd, &exportdir, sizeof(exportdir)) != sizeof(exportdir))
+ goto err_noexec;
+ dll->export_ordinal_base = exportdir.ordinal_base;
+
+ /* Read in each exported function address, relocate it, and store the
+ * relocated address in the DLL handle structure. */
+ if (exportdir.nfuncs) {
+ dll->export_table =
+ malloc(sizeof(*dll->export_table) * exportdir.nfuncs);
+ if (!dll->export_table)
+ goto error;
+ if (lseek(fd, exportdir.func_table + secofs, SEEK_SET) == -1)
+ goto error;
+ /* Use the entry count field itself as a loop variable, to ensure
+ * that the correct number of entries are cleaned up on error
+ * (doesn't matter here, but avoids a memory leak for the name
+ * array handling below) */
+ for (dll->export_ordinal_count = 0;
+ dll->export_ordinal_count < exportdir.nfuncs;
+ dll->export_ordinal_count++
+ ) {
+ uint32_t address;
+ if (read(fd, &address, 4) != 4)
+ goto err_noexec;
+ address += dll->extheader.image_base;
+ dll->export_table[dll->export_ordinal_count] =
+ w32dll_relocate_addr(dll, address);
+ }
+ }
+
+ /* Read in each exported function name, and store the name and its
+ * associated ordinal in the DLL handle structure. */
+ if (exportdir.nnames) {
+ int i;
+ dll->export_name_table =
+ malloc(sizeof(*dll->export_name_table) * exportdir.nnames);
+ if (!dll->export_name_table)
+ goto error;
+ if (lseek(fd, exportdir.name_ordinal_table + secofs, SEEK_SET) == -1)
+ goto error;
+ for (i = 0; i < exportdir.nnames; i++) {
+ uint16_t ordinal;
+ if (read(fd, &ordinal, 2) != 2)
+ goto err_noexec;
+ dll->export_name_table[i].ordinal =
+ dll->export_ordinal_base + ordinal;
+ }
+ for (dll->export_name_count = 0;
+ dll->export_name_count < exportdir.nnames;
+ dll->export_name_count++
+ ) {
+ uint32_t name_address;
+ char *s;
+ if (lseek(fd, exportdir.name_table+secofs+dll->export_name_count*4,
+ SEEK_SET) == -1
+ ) {
+ goto error;
+ }
+ if (read(fd, &name_address, 4) != 4)
+ goto err_noexec;
+ if (lseek(fd, name_address + secofs, SEEK_SET) == -1)
+ goto error;
+ s = w32dll_read_asciiz(fd);
+ if (!s)
+ goto error;
+ dll->export_name_table[dll->export_name_count].name = s;
+ }
+ }
+
+ /* Success! */
+ return 1;
+
+ err_noexec:
+ errno = ENOEXEC;
+ error:
+ return 0;
+}
+
+/*************************************************************************/
+
+/**
+ * w32dll_process_imports: Reads the list of imports described by
+ * `importdir' and sets the pointers to appropriate values (emulation
+ * functions or a placeholder function).
+ *
+ * Parameters:
+ * dll: DLL handle.
+ * importdir: Import directory structure.
+ * Return value:
+ * Nonzero on success, zero on error.
+ * Notes:
+ * - On error, errno is set appropriately.
+ * - This routine assumes that all import data is located in the same
+ * section. Since the import address table has to be in a loaded
+ * section (usually a data section), this implies that the rest of
+ * the import data is also in a loaded section; therefore, we take
+ * the easy approach and access the data directly in memory.
+ */
+
+static int w32dll_process_imports(W32DLLHandle dll,
+ struct import_directory *importdir)
+{
+ const uint32_t imgbase = dll->extheader.image_base; // shorthand
+ const char *module;
+ const struct import_name_entry **names;
+ void **addrs;
+ int i;
+
+ /* Relocate import directory addresses. */
+ module = w32dll_relocate_addr(dll, importdir->module_name + imgbase);
+ names = w32dll_relocate_addr(dll, importdir->import_table + imgbase);
+ addrs = w32dll_relocate_addr(dll, importdir->import_addr_table + imgbase);
+ if (!module || !*module || !names || !addrs)
+ goto err_noexec;
+
+ /* Process the imports. */
+ for (i = 0; names[i]; i++) {
+ const struct import_name_entry *name;
+ uint32_t ordinal;
+ if ((uint32_t)names[i] & 0x80000000UL) {
+ name = NULL;
+ ordinal = (uint32_t)names[i] & 0x7FFFFFFFUL;
+ } else {
+ name = w32dll_relocate_addr(dll, (uint32_t)names[i] + imgbase);
+ if (!name)
+ goto err_noexec;
+ }
+ if (name)
+ addrs[i] = w32dll_import_by_name(module, name);
+ else
+ addrs[i] = w32dll_import_by_ordinal(module, ordinal);
+ }
+
+ /* All done. */
+ return 1;
+
+ err_noexec:
+ errno = ENOEXEC;
+ return 0;
+}
+
+/*************************************************************************/
+
+/**
+ * w32dll_import_by_name, w32dll_import_by_ordinal: Return the address
+ * corresponding to the given import, selected by either name or ordinal.
+ *
+ * Parameters:
+ * module: Name of the module from which to import.
+ * name: Import name descriptor (w32dll_import_by_name() only).
+ * ordinal: Import ordinal (w32dll_import_by_ordinal() only).
+ * Return value:
+ * The address corresponding to the import, or NULL if the import
+ * failed.
+ * Notes:
+ * - A NULL return is *not* considered an error, and thus errno is
+ * undefined after returning from these functions.
+ * - Currently, these functions just ask the Win32 emulation layer for
+ * an appropriate function, and do not handle linking between
+ * multiple loaded DLLs.
+ */
+
+static void *w32dll_import_by_name(const char *module,
+ const struct import_name_entry *name)
+{
+ return w32dll_emu_import_by_name(module, name);
+}
+
+/************************************/
+
+static void *w32dll_import_by_ordinal(const char *module, uint32_t ordinal)
+{
+ return w32dll_emu_import_by_ordinal(module, ordinal);
+}
+
+/*************************************************************************/
+
+/**
+ * w32dll_read_relocs: Read a set of relocation offsets for the DLL from
+ * the given file descriptor.
+ *
+ * Parameters:
+ * dll: DLL handle.
+ * fd: File descriptor to read from.
+ * relocs_ptr: Pointer to relocation entry array (dynamically allocated).
+ * nrelocs_ptr: Pointer to relocation entry count.
+ * Return value:
+ * Positive if relocations were read successfully.
+ * Zero if the end of the relocation table was reached.
+ * Negative if an error cocurred.
+ * Notes:
+ * - *relocs_ptr and *nrelocs_ptr must be initialized to NULL and 0,
+ * respectively, before the first call; they will be updated with
+ * each call.
+ * - On error, errno is set appropriately.
+ */
+
+static int w32dll_read_relocs(W32DLLHandle dll, int fd,
+ uint32_t **relocs_ptr, int *nrelocs_ptr)
+{
+ uint32_t base, size, *new_relocs;
+ int index;
+
+ if (read(fd, &base, 4) != 4
+ || read(fd, &size, 4) != 4
+ || (size > 0 && size < 8)
+ ) {
+ free(*relocs_ptr);
+ *relocs_ptr = NULL;
+ *nrelocs_ptr = 0;
+ errno = ENOEXEC;
+ return -1;
+ }
+ if (!size)
+ return 0;
+ if (size <= 8) // Technically == works too, but play it safe
+ return 1;
+ size = (size-8) / 2; // Number of entries in this group
+ index = *nrelocs_ptr;
+ new_relocs = realloc(*relocs_ptr,
+ sizeof(**relocs_ptr) * (*nrelocs_ptr + size));
+ if (!new_relocs)
+ goto err_noexec;
+ *relocs_ptr = new_relocs;
+ *nrelocs_ptr += size;
+ while (size > 0) {
+ uint16_t buf[1000];
+ int toread, i;
+ toread = size;
+ if (toread > sizeof(buf)/2)
+ toread = sizeof(buf)/2;
+ if (read(fd, buf, toread*2) != toread*2)
+ goto err_noexec;
+ for (i = 0; i < toread; i++) {
+ if (buf[i]>>12 == 3) {
+ (*relocs_ptr)[index++] = dll->extheader.image_base
+ + base + (buf[i] & 0xFFF);
+ } else {
+ (*nrelocs_ptr)--;
+ }
+ }
+ size -= toread;
+ }
+ return 1;
+
+ err_noexec:
+ {
+ int errno_save = errno;
+ free(*relocs_ptr);
+ *relocs_ptr = NULL;
+ *nrelocs_ptr = 0;
+ errno = errno_save;
+ return -1;
+ }
+}
+
+/*************************************************************************/
+
+/**
+ * w32dll_relocate: Perform relocations on the loaded DLL.
+ *
+ * Parameters:
+ * dll: DLL handle with all sections loaded.
+ * relocs: Array of virtual addresses to be relocated.
+ * nrelocs: Number of relocations.
+ * Return value:
+ * None.
+ */
+
+#include <stdio.h>
+static void w32dll_relocate(W32DLLHandle dll, uint32_t *relocs, int nrelocs)
+{
+ int i;
+
+ for (i = 0; i < nrelocs; i++) {
+ uint32_t *addr = w32dll_relocate_addr(dll, relocs[i]);
+ if (addr)
+ *addr = (uint32_t)w32dll_relocate_addr(dll, *addr);
+ }
+}
+
+/*************************************************************************/
+
+/**
+ * w32dll_relocate_addr: Relocate a single address.
+ *
+ * Parameters:
+ * dll: DLL handle.
+ * addr: Address to relocate.
+ * Return value:
+ * The relocated address, or NULL if the address is not in a loaded
+ * section.
+ */
+
+static void *w32dll_relocate_addr(W32DLLHandle dll, uint32_t addr)
+{
+ int i;
+
+ for (i = 0; i < dll->nsections; i++) {
+ if (addr >= dll->sections[i].origbase
+ && addr < dll->sections[i].origbase + dll->sections[i].origsize
+ ) {
+ return (uint8_t *)dll->sections[i].base
+ + (addr - dll->sections[i].origbase);
+ }
+ }
+ return NULL;
+}
+
+/*************************************************************************/
+
+/**
+ * w32dll_read_asciiz: Read a null-terminated string from the given file
+ * descriptor.
+ *
+ * Parameters:
+ * fd: File descriptor to read from.
+ * Return value:
+ * String read in (allocated with malloc()), or NULL on error.
+ * Notes:
+ * On error, errno is set appropriately.
+ */
+
+static char *w32dll_read_asciiz(int fd)
+{
+ char *str = NULL;
+ int size = 0, len = 0;
+
+ do {
+ if (len >= size) {
+ size = len+100;
+ char *newstr = realloc(str, size);
+ if (!newstr) {
+ int errno_save = errno;
+ free(str);
+ errno = errno_save;
+ return NULL;
+ }
+ str = newstr;
+ }
+ if (read(fd, str+len, 1) != 1) {
+ free(str);
+ errno = ENOEXEC;
+ return NULL;
+ }
+ len++;
+ } while (str[len-1] != 0);
+ return str;
+}
+
+/*************************************************************************/
+
+/**
+ * w32dll_init_fs: Set up the FS segment register to point to a page of
+ * data (empty except for the linear address pointer at 0x18).
+ *
+ * Parameters:
+ * None.
+ * Return value:
+ * Nonzero on success, zero on failure.
+ * Notes:
+ * On error, errno is set appropriately.
+ */
+
+#if defined(OS_LINUX)
+# include <asm/unistd.h>
+# include <asm/ldt.h>
+// This doesn't work, because of PIC:
+//static _syscall3(int, modify_ldt, int, func, void *, ptr, unsigned long, bytecount);
+static int modify_ldt(int func, void *ptr, unsigned long bytecount) {
+ long __res;
+ __asm__ volatile ("push %%ebx; mov %%esi, %%ebx; int $0x80; pop %%ebx"
+ : "=a" (__res)
+ : "0" (__NR_modify_ldt), "S" ((long)(func)),
+ "c" ((long)(ptr)), "d" ((long)(bytecount))
+ : "memory");
+ /* Errors are from -1 to -128, according to <asm/unistd.h> */
+ if ((__res & 0xFFFFFF80UL) == 0xFFFFFF80UL) {
+ errno = -__res & 0xFF;
+ __res = (unsigned long)-1;
+ }
+ return (int)__res;
+}
+#else
+# error OS not supported in w32dll_init_fs()
+#endif
+
+static int w32dll_init_fs(void)
+{
+ int fd;
+ void *base;
+ int segment;
+#if defined(OS_LINUX)
+ struct user_desc ldt;
+#endif
+
+ fd = open("/dev/zero", O_RDWR);
+ if (fd < 0)
+ return 0;
+ base = mmap(NULL, GETPAGESIZE(), PROT_READ | PROT_WRITE,
+ MAP_PRIVATE, fd, 0);
+ if (base == MAP_FAILED) {
+ int errno_save = errno;
+ close(fd);
+ errno = errno_save;
+ return 0;
+ }
+ close(fd);
+ *(void **)((uint8_t *)base + 0x18) = base;
+
+#if defined(OS_LINUX)
+ memset(&ldt, 0, sizeof(ldt));
+ /* Pick a random number that's hopefully unused. How does one
+ * determine which segment numbers are in use? */
+ ldt.entry_number = 172;
+ ldt.base_addr = (long)base;
+ ldt.limit = GETPAGESIZE();
+ ldt.seg_32bit = 1;
+ ldt.read_exec_only = 0;
+ ldt.seg_not_present = 0;
+ ldt.contents = MODIFY_LDT_CONTENTS_DATA;
+ ldt.limit_in_pages = 0;
+ ldt.seg_not_present = 0;
+ ldt.useable = 1;
+ if (modify_ldt(17, &ldt, sizeof(ldt)) != 0) {
+ int errno_save = errno;
+ munmap(base, GETPAGESIZE());
+ errno = errno_save;
+ return 0;
+ }
+ segment = ldt.entry_number;
+#endif
+
+ /* Bit 2: 1 == use LDT; bits 1-0: 3 == privilege level 3 */
+ asm("movw %%ax,%%fs" : : "a" (segment<<3 | 1<<2 | 3));
+ return 1;
+}
+
+/*************************************************************************/
+/*************************************************************************/
+
+#ifdef TEST
+
+#include <stdio.h>
+
+int main(int ac, char **av)
+{
+ W32DLLHandle dll;
+
+ if (ac < 2 || strcmp(av[1], "-h") == 0 || strcmp(av[1], "--help") == 0) {
+ fprintf(stderr, "Usage: %s file.dll [procname | =ordinal]\n", av[0]);
+ return -1;
+ }
+ dll = w32dll_load(av[1], 1);
+ if (!dll) {
+ perror(av[1]);
+ return 1;
+ }
+ if (ac >= 3) {
+ int i;
+ void *(*func)(void);
+ void ***codec, **functable, ***vid, **vidtable, ***aud, **audtable;
+ WINAPI void *(*init)(int, int);
+ WINAPI void *(*fini)(void);
+ WINAPI void *(*getvid)(void);
+ WINAPI void *(*getaud)(void);
+ static char buf[0x14000], buf2[720*480*4], buf2a[0x800], buf3[0x2000];
+ struct { int dataset; void *workbuf; struct {uint8_t w8, h8, f02, f03; int ofs;} *inparam; void *inbuf; struct {int stride; void *outbuf;} *outparam;} vidparam;
+ void *bufptr = buf;
+ struct { int flag; char *buffer; } audparam;
+ int fd = open("/scratch/pv3/060428-192352-720x480i.dv", O_RDONLY);
+ read(fd, buf, 0x14000);
+ close(fd);
+ if (av[2][0] == '=')
+ func = w32dll_lookup_by_ordinal(dll, strtoul(av[2]+1,NULL,0));
+ else
+ func = w32dll_lookup_by_name(dll, av[2]);
+ if (!func) {
+ perror(av[2]);
+ w32dll_unload(dll);
+ return 2;
+ }
+ printf("%s: %p\n", av[2], func);
+ codec = func();
+ functable = *codec;
+ printf("--> %p [%p %p %p %p...]\n", codec,
+ functable[0], functable[1], functable[2], functable[3]);
+ init = functable[0];
+ fini = functable[1];
+ getvid = functable[2];
+ getaud = functable[3];
+ printf("calling init...\n");
+ //(*init)(4, 2);
+ asm("push $2; push $4; call *%0" : : "r" (init), "c" (codec));
+ printf("...done!\n");
+ printf("calling getvid...\n");
+ asm("call *%1" : "=a" (vid) : "r" (getvid), "c" (codec));
+ vidtable = *vid;
+ printf("...done! (%p -> %p %p ... %p ...)\n", aud,
+ vidtable[0], vidtable[1], vidtable[5]);
+ memset(&vidparam, 0, sizeof(vidparam));
+ vidparam.dataset = 0;
+ vidparam.workbuf = buf2a;
+ vidparam.inparam = calloc(sizeof(*vidparam.inparam), 1);
+ vidparam.inparam->w8 = buf[4];
+ vidparam.inparam->h8 = buf[5];
+ vidparam.inparam->ofs = 0;
+ vidparam.inbuf = &bufptr;
+ vidparam.outparam = malloc(sizeof(*vidparam.outparam));
+ vidparam.outparam->stride = (buf[4]*8)*2;
+ vidparam.outparam->outbuf = buf2;
+ printf("calling video_decode...\n"); // 10013830
+ asm("push %2; call *%1"
+ : "=a" (i)
+ : "r" (vidtable[5]), "r" (&vidparam), "c" (vid));
+ printf("...done! (%d)\n", i);
+ printf("and again...\n");
+ vidparam.dataset = 1;
+ asm("push %2; call *%1"
+ : "=a" (i)
+ : "r" (vidtable[5]), "r" (&vidparam), "c" (vid));
+ printf("...done! (%d)\n", i);
+ printf("calling getaud...\n");
+ asm("call *%1" : "=a" (aud) : "r" (getaud), "c" (codec));
+ audtable = *aud;
+ printf("...done! (%p -> %p %p %p ...)\n", aud,
+ audtable[0], audtable[1], audtable[2]);
+ audparam.flag = 0;
+ audparam.buffer = buf;
+ *(void **)(buf3+24) = buf3+32;
+ printf("calling audio_decode...\n"); // 10013830
+ asm("push %3; push %2; call *%1"
+ : "=a" (i)
+ : "r" (audtable[1]), "r" (&audparam), "r" (buf3), "c" (aud));
+ printf("...done! (%d)\n", i);
+ fd = open("/scratch/pv3/test.raw", O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ write(fd, buf3, sizeof(buf3));
+ write(fd, buf2, sizeof(buf2));
+ close(fd);
+ printf("calling fini...\n");
+ asm("call *%0" : : "r" (fini), "c" (codec));
+ //(*fini)();
+ printf("...done!\n");
+ }
+ w32dll_unload(dll);
+ return 0;
+}
+
+#endif
+
+/*************************************************************************/
+
+/*
+ * Local variables:
+ * c-file-style: "stroustrup"
+ * c-file-offsets: ((case-label . *) (statement-case-intro . *))
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vim: expandtab shiftwidth=4:
+ */