Added an unknown VxD error code.
[wine] / tools / specmaker / dll.c
1 /*
2  *  DLL symbol extraction
3  *
4  *  Copyright 2000 Jon Griffiths
5  */
6 #include "specmaker.h"
7
8 /* DOS/PE Header details */
9 #define DOS_HEADER_LEN      64
10 #define DOS_MAGIC           0x5a4d
11 #define DOS_PE_OFFSET       60
12 #define PE_HEADER_LEN       248
13 #define PE_MAGIC            0x4550
14 #define PE_COUNT_OFFSET     6
15 #define PE_EXPORTS_OFFSET   120
16 #define PE_EXPORTS_SIZE     PE_EXPORTS_OFFSET + 4
17 #define SECTION_HEADER_LEN  40
18 #define SECTION_ADDR_OFFSET 12
19 #define SECTION_ADDR_SIZE   SECTION_ADDR_OFFSET + 4
20 #define SECTION_POS_OFFSET  SECTION_ADDR_SIZE + 4
21 #define ORDINAL_BASE_OFFSET 16
22 #define ORDINAL_COUNT_OFFSET 20
23 #define ORDINAL_NAME_OFFSET  ORDINAL_COUNT_OFFSET + 16
24 #define EXPORT_COUNT_OFFSET 24
25 #define EXPORT_NAME_OFFSET  EXPORT_COUNT_OFFSET + 8
26
27 /* Minimum memory needed to read both headers into a buffer */
28 #define MIN_HEADER_LEN (PE_HEADER_LEN * sizeof (unsigned char))
29
30 /* Normalise a pointer in the exports section */
31 #define REBASE(x) ((x) - exports)
32
33 /* Module globals */
34 typedef struct _dll_symbol {
35   size_t ordinal;
36   char *symbol;
37 } dll_symbol;
38
39 static FILE  *dll_file = NULL;
40 static dll_symbol *dll_symbols = NULL;
41 static size_t dll_num_exports = 0;
42 static size_t dll_num_ordinals = 0;
43 static int dll_ordinal_base = 0;
44
45 static dll_symbol *dll_current_symbol = NULL;
46 static unsigned int dll_current_export = 0;
47
48 /* Get a short from a memory block */
49 static inline size_t get_short (const char *mem)
50 {
51   return *((const unsigned char *)mem) +
52          (*((const unsigned char *)mem + 1) << 8);
53 }
54
55 /* Get an integer from a memory block */
56 static inline size_t get_int (const char *mem)
57 {
58   assert (sizeof (char) == (size_t)1);
59   return get_short (mem) + (get_short (mem + 2) << 16);
60 }
61
62 /* Compare symbols by ordinal for qsort */
63 static int symbol_cmp(const void *left, const void *right)
64 {
65   return ((dll_symbol *)left)->ordinal > ((dll_symbol *)right)->ordinal;
66 }
67
68 static void dll_close (void);
69
70
71 /*******************************************************************
72  *         dll_open
73  *
74  * Open a DLL and read in exported symbols
75  */
76 void  dll_open (const char *dll_name)
77 {
78   size_t code = 0, code_len = 0, exports, exports_len, count, symbol_data;
79   size_t ordinal_data;
80   char *buff = NULL;
81   dll_file = open_file (dll_name, ".dll", "r");
82
83   atexit (dll_close);
84
85   /* Read in the required DOS and PE Headers */
86   if (!(buff = (char *) malloc (MIN_HEADER_LEN)))
87     fatal ("Out of memory");
88
89   if (fread (buff, DOS_HEADER_LEN, 1, dll_file) != 1 ||
90       get_short (buff) != DOS_MAGIC)
91     fatal ("Error reading DOS header");
92
93   if (fseek (dll_file, get_int (buff + DOS_PE_OFFSET), SEEK_SET) == -1)
94     fatal ("Error seeking PE header");
95
96   if (fread (buff, PE_HEADER_LEN, 1, dll_file) != 1 ||
97       get_int (buff) != PE_MAGIC)
98     fatal ("Error reading PE header");
99
100   exports = get_int (buff + PE_EXPORTS_OFFSET);
101   exports_len = get_int (buff + PE_EXPORTS_SIZE);
102
103   if (!exports || !exports_len)
104     fatal ("No exports in DLL");
105
106   if (!(count = get_short (buff + PE_COUNT_OFFSET)))
107     fatal ("No sections in DLL");
108
109   if (VERBOSE)
110     printf ("DLL has %d sections\n", count);
111
112   /* Iterate through sections until we find exports */
113   while (count--)
114   {
115     if (fread (buff, SECTION_HEADER_LEN, 1, dll_file) != 1)
116       fatal ("Section read error");
117
118     code = get_int (buff + SECTION_ADDR_OFFSET);
119     code_len = get_int (buff + SECTION_ADDR_SIZE);
120
121     if (code <= exports && code + code_len > exports)
122       break;
123   }
124
125   if (!count)
126     fatal ("No export section");
127
128   code_len -= (exports - code);
129
130   if (code_len < exports_len)
131     fatal ("Corrupt exports");
132
133   /* Load exports section */
134   if (fseek (dll_file, get_int (buff + SECTION_POS_OFFSET)
135          + exports - code, SEEK_SET) == -1)
136     fatal ("Export section seek error");
137
138   if (VERBOSE)
139     printf ("Export data size = %d bytes\n", code_len);
140
141   if (!(buff = (char *) realloc (buff, code_len)))
142     fatal ("Out of memory");
143
144   if (fread (buff, code_len, 1, dll_file) != 1)
145     fatal ("Read error");
146
147   dll_close();
148
149   /* Locate symbol names/ordinals */
150   symbol_data = REBASE( get_int (buff + EXPORT_NAME_OFFSET));
151   ordinal_data = REBASE( get_int (buff + ORDINAL_NAME_OFFSET));
152
153   if (symbol_data > code_len)
154     fatal ("Corrupt exports section");
155
156   if (!(dll_num_ordinals = get_int (buff + ORDINAL_COUNT_OFFSET)))
157     fatal ("No ordinal count");
158
159   if (!(dll_num_exports = get_int (buff + EXPORT_COUNT_OFFSET)))
160     fatal ("No export count");
161
162   if (!(dll_symbols = (dll_symbol *) malloc ((dll_num_exports + 1) * sizeof (dll_symbol))))
163     fatal ("Out of memory");
164
165   dll_ordinal_base = get_int (buff + ORDINAL_BASE_OFFSET);
166
167   if (dll_num_exports != dll_num_ordinals || dll_ordinal_base > 1)
168     globals.do_ordinals = 1;
169
170   /* Read symbol names into 'dll_symbols' */
171   count = 0;
172   while (count <  dll_num_exports)
173   {
174     const int   symbol_offset = get_int (buff + symbol_data + count * 4);
175     const char *symbol_name_ptr = REBASE (buff + symbol_offset);
176     const int   ordinal_offset = get_short (buff + ordinal_data + count * 2);
177
178     assert(symbol_name_ptr);
179     dll_symbols[count].symbol = strdup (symbol_name_ptr);
180     assert(dll_symbols[count].symbol);
181     dll_symbols[count].ordinal = ordinal_offset + dll_ordinal_base;
182
183     count++;
184   }
185
186   if (NORMAL)
187     printf ("%d named symbols in DLL, %d total\n", dll_num_exports, dll_num_ordinals);
188
189   free (buff);
190
191   qsort( dll_symbols, dll_num_exports, sizeof(dll_symbol), symbol_cmp );
192
193   dll_symbols[dll_num_exports].symbol = NULL;
194
195   dll_current_symbol = dll_symbols;
196   dll_current_export = dll_ordinal_base;
197
198   /* Set DLL output names */
199   if ((buff = strrchr (globals.input_name, '/')))
200     globals.input_name = buff + 1; /* Strip path */
201
202   OUTPUT_UC_DLL_NAME = str_toupper( strdup (OUTPUT_DLL_NAME));
203 }
204
205
206 /*******************************************************************
207  *         dll_next_symbol
208  *
209  * Get next exported symbol from dll
210  */
211 int dll_next_symbol (parsed_symbol * sym)
212 {
213   char ordinal_text[256];
214   if (dll_current_export > dll_num_ordinals)
215     return 1;
216
217   assert (dll_symbols);
218
219   if (!dll_current_symbol->symbol || dll_current_export < dll_current_symbol->ordinal)
220   {
221     assert(globals.do_ordinals);
222
223     /* Ordinal only entry */
224     snprintf (ordinal_text, sizeof(ordinal_text), "%s_%d",
225               globals.forward_dll ? globals.forward_dll : OUTPUT_UC_DLL_NAME,
226               dll_current_export);
227     str_toupper(ordinal_text);
228     sym->symbol = strdup (ordinal_text);
229   }
230   else
231   {
232     sym->symbol = strdup (dll_current_symbol->symbol);
233     dll_current_symbol++;
234   }
235   sym->ordinal = dll_current_export;
236   dll_current_export++;
237   return 0;
238 }
239
240
241 /*******************************************************************
242  *         dll_close
243  *
244  * Free resources used by DLL
245  */
246 static void dll_close (void)
247 {
248   size_t i;
249
250   if (dll_file)
251   {
252     fclose (dll_file);
253     dll_file = NULL;
254   }
255
256   if (dll_symbols)
257   {
258     for (i = 0; i < dll_num_exports; i++)
259       if (dll_symbols [i].symbol)
260         free (dll_symbols [i].symbol);
261     free (dll_symbols);
262     dll_symbols = NULL;
263   }
264 }