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