You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1228 lines
38 KiB

/*
* 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:
*/