/* * 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; } }