265 lines
6.9 KiB
C
265 lines
6.9 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 ORDINAL_BASE_OFFSET 16
|
|
#define ORDINAL_COUNT_OFFSET 20
|
|
#define ORDINAL_NAME_OFFSET ORDINAL_COUNT_OFFSET + 16
|
|
#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 */
|
|
typedef struct _dll_symbol {
|
|
size_t ordinal;
|
|
char *symbol;
|
|
} dll_symbol;
|
|
|
|
static FILE *dll_file = NULL;
|
|
static dll_symbol *dll_symbols = NULL;
|
|
static size_t dll_num_exports = 0;
|
|
static size_t dll_num_ordinals = 0;
|
|
static int dll_ordinal_base = 0;
|
|
|
|
static dll_symbol *dll_current_symbol = NULL;
|
|
static unsigned int dll_current_export = 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);
|
|
}
|
|
|
|
/* Compare symbols by ordinal for qsort */
|
|
static int symbol_cmp(const void *left, const void *right)
|
|
{
|
|
return ((dll_symbol *)left)->ordinal > ((dll_symbol *)right)->ordinal;
|
|
}
|
|
|
|
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;
|
|
size_t ordinal_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/ordinals */
|
|
symbol_data = REBASE( get_int (buff + EXPORT_NAME_OFFSET));
|
|
ordinal_data = REBASE( get_int (buff + ORDINAL_NAME_OFFSET));
|
|
|
|
if (symbol_data > code_len)
|
|
fatal ("Corrupt exports section");
|
|
|
|
if (!(dll_num_ordinals = get_int (buff + ORDINAL_COUNT_OFFSET)))
|
|
fatal ("No ordinal count");
|
|
|
|
if (!(dll_num_exports = get_int (buff + EXPORT_COUNT_OFFSET)))
|
|
fatal ("No export count");
|
|
|
|
if (!(dll_symbols = (dll_symbol *) malloc ((dll_num_exports + 1) * sizeof (dll_symbol))))
|
|
fatal ("Out of memory");
|
|
|
|
dll_ordinal_base = get_int (buff + ORDINAL_BASE_OFFSET);
|
|
|
|
if (dll_num_exports != dll_num_ordinals || dll_ordinal_base > 1)
|
|
globals.do_ordinals = 1;
|
|
|
|
/* 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);
|
|
const int ordinal_offset = get_short (buff + ordinal_data + count * 2);
|
|
|
|
assert(symbol_name_ptr);
|
|
dll_symbols[count].symbol = strdup (symbol_name_ptr);
|
|
assert(dll_symbols[count].symbol);
|
|
dll_symbols[count].ordinal = ordinal_offset + dll_ordinal_base;
|
|
|
|
count++;
|
|
}
|
|
|
|
if (NORMAL)
|
|
printf ("%d named symbols in DLL, %d total\n", dll_num_exports, dll_num_ordinals);
|
|
|
|
free (buff);
|
|
|
|
qsort( dll_symbols, dll_num_exports, sizeof(dll_symbol), symbol_cmp );
|
|
|
|
dll_symbols[dll_num_exports].symbol = NULL;
|
|
|
|
dll_current_symbol = dll_symbols;
|
|
dll_current_export = dll_ordinal_base;
|
|
|
|
/* 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
|
|
*/
|
|
int dll_next_symbol (parsed_symbol * sym)
|
|
{
|
|
char ordinal_text[256];
|
|
if (dll_current_export > dll_num_ordinals)
|
|
return 1;
|
|
|
|
assert (dll_symbols);
|
|
|
|
if (!dll_current_symbol->symbol || dll_current_export < dll_current_symbol->ordinal)
|
|
{
|
|
assert(globals.do_ordinals);
|
|
|
|
/* Ordinal only entry */
|
|
snprintf (ordinal_text, sizeof(ordinal_text), "%s_%d",
|
|
globals.forward_dll ? globals.forward_dll : OUTPUT_UC_DLL_NAME,
|
|
dll_current_export);
|
|
str_toupper(ordinal_text);
|
|
sym->symbol = strdup (ordinal_text);
|
|
}
|
|
else
|
|
{
|
|
sym->symbol = strdup (dll_current_symbol->symbol);
|
|
dll_current_symbol++;
|
|
}
|
|
sym->ordinal = dll_current_export;
|
|
dll_current_export++;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
* 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].symbol)
|
|
free (dll_symbols [i].symbol);
|
|
free (dll_symbols);
|
|
dll_symbols = NULL;
|
|
}
|
|
}
|