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
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, §hdr, sizeof(secthdr)) != sizeof(secthdr))
|
|
goto err_noexec;
|
|
}
|
|
w32dll_update_rva(dll, §hdr);
|
|
w32dll_add_section(dll, fd, §hdr);
|
|
}
|
|
|
|
/* 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:
|
|
*/
|