214 lines
5.2 KiB
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;
|
|
}
|
|
}
|