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