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