wine-wine/tools/specmaker/dll.c

214 lines
5.2 KiB
C

/*
* DLL symbol extraction
*
* Copyright 2000 Jon Griffiths
*/
#include "specmaker.h"
/* DOS/PE Header details */
#define DOS_HEADER_LEN 64
#define DOS_MAGIC 0x5a4d
#define DOS_PE_OFFSET 60
#define PE_HEADER_LEN 248
#define PE_MAGIC 0x4550
#define PE_COUNT_OFFSET 6
#define PE_EXPORTS_OFFSET 120
#define PE_EXPORTS_SIZE PE_EXPORTS_OFFSET + 4
#define SECTION_HEADER_LEN 40
#define SECTION_ADDR_OFFSET 12
#define SECTION_ADDR_SIZE SECTION_ADDR_OFFSET + 4
#define SECTION_POS_OFFSET SECTION_ADDR_SIZE + 4
#define EXPORT_COUNT_OFFSET 24
#define EXPORT_NAME_OFFSET EXPORT_COUNT_OFFSET + 8
/* Minimum memory needed to read both headers into a buffer */
#define MIN_HEADER_LEN (PE_HEADER_LEN * sizeof (unsigned char))
/* Normalise a pointer in the exports section */
#define REBASE(x) ((x) - exports)
/* Module globals */
static FILE *dll_file = NULL;
static char **dll_symbols = NULL;
static size_t dll_num_exports = 0;
/* Get a short from a memory block */
static inline size_t get_short (const char *mem)
{
return *((const unsigned char *)mem) +
(*((const unsigned char *)mem + 1) << 8);
}
/* Get an integer from a memory block */
static inline size_t get_int (const char *mem)
{
assert (sizeof (char) == (size_t)1);
return get_short (mem) + (get_short (mem + 2) << 16);
}
static void dll_close (void);
/*******************************************************************
* dll_open
*
* Open a DLL and read in exported symbols
*/
void dll_open (const char *dll_name)
{
size_t code = 0, code_len = 0, exports, exports_len, count, symbol_data;
char *buff = NULL;
dll_file = open_file (dll_name, ".dll", "r");
atexit (dll_close);
/* Read in the required DOS and PE Headers */
if (!(buff = (char *) malloc (MIN_HEADER_LEN)))
fatal ("Out of memory");
if (fread (buff, DOS_HEADER_LEN, 1, dll_file) != 1 ||
get_short (buff) != DOS_MAGIC)
fatal ("Error reading DOS header");
if (fseek (dll_file, get_int (buff + DOS_PE_OFFSET), SEEK_SET) == -1)
fatal ("Error seeking PE header");
if (fread (buff, PE_HEADER_LEN, 1, dll_file) != 1 ||
get_int (buff) != PE_MAGIC)
fatal ("Error reading PE header");
exports = get_int (buff + PE_EXPORTS_OFFSET);
exports_len = get_int (buff + PE_EXPORTS_SIZE);
if (!exports || !exports_len)
fatal ("No exports in DLL");
if (!(count = get_short (buff + PE_COUNT_OFFSET)))
fatal ("No sections in DLL");
if (VERBOSE)
printf ("DLL has %d sections\n", count);
/* Iterate through sections until we find exports */
while (count--)
{
if (fread (buff, SECTION_HEADER_LEN, 1, dll_file) != 1)
fatal ("Section read error");
code = get_int (buff + SECTION_ADDR_OFFSET);
code_len = get_int (buff + SECTION_ADDR_SIZE);
if (code <= exports && code + code_len > exports)
break;
}
if (!count)
fatal ("No export section");
code_len -= (exports - code);
if (code_len < exports_len)
fatal ("Corrupt exports");
/* Load exports section */
if (fseek (dll_file, get_int (buff + SECTION_POS_OFFSET)
+ exports - code, SEEK_SET) == -1)
fatal ("Export section seek error");
if (VERBOSE)
printf ("Export data size = %d bytes\n", code_len);
if (!(buff = (char *) realloc (buff, code_len)))
fatal ("Out of memory");
if (fread (buff, code_len, 1, dll_file) != 1)
fatal ("Read error");
dll_close();
/* Locate symbol names */
symbol_data = REBASE( get_int (buff + EXPORT_NAME_OFFSET));
if (symbol_data > code_len)
fatal ("Corrupt exports section");
if (!(dll_num_exports = get_int (buff + EXPORT_COUNT_OFFSET)))
fatal ("No export count");
if (!(dll_symbols = (char **) malloc (dll_num_exports * sizeof (char *))))
fatal ("Out of memory");
/* Read symbol names into 'dll_symbols' */
count = 0;
while (count < dll_num_exports)
{
const int symbol_offset = get_int (buff + symbol_data + count * 4);
const char *symbol_name_ptr = REBASE (buff + symbol_offset);
assert(symbol_name_ptr);
dll_symbols[count] = strdup (symbol_name_ptr);
assert(dll_symbols[count]);
count++;
}
if (NORMAL)
printf ("%d exported symbols in DLL\n", dll_num_exports);
free (buff);
/* Set DLL output names */
if ((buff = strrchr (globals.input_name, '/')))
globals.input_name = buff + 1; /* Strip path */
OUTPUT_UC_DLL_NAME = str_toupper( strdup (OUTPUT_DLL_NAME));
}
/*******************************************************************
* dll_next_symbol
*
* Get next exported symbol from dll
*/
char* dll_next_symbol ()
{
static unsigned int current_export = 0;
assert (current_export <= dll_num_exports);
if (current_export == dll_num_exports)
return NULL;
assert (dll_symbols);
assert (dll_symbols [current_export]);
return strdup (dll_symbols [current_export++]);
}
/*******************************************************************
* dll_close
*
* Free resources used by DLL
*/
static void dll_close (void)
{
size_t i;
if (dll_file)
{
fclose (dll_file);
dll_file = NULL;
}
if (dll_symbols)
{
for (i = 0; i < dll_num_exports; i++)
if (dll_symbols [i])
free (dll_symbols [i]);
free (dll_symbols);
dll_symbols = NULL;
}
}