Solaris fixes.
[wine] / tools / winebuild / import.c
1 /*
2  * DLL imports support
3  *
4  * Copyright 2000 Alexandre Julliard
5  */
6
7 #include <fcntl.h>
8 #include <stdio.h>
9 #include <unistd.h>
10
11 #include "config.h"
12 #include "winnt.h"
13 #include "build.h"
14
15 struct import
16 {
17     char        *dll;         /* dll name */
18     char       **exports;     /* functions exported from this dll */
19     int          nb_exports;  /* number of exported functions */
20     char       **imports;     /* functions we want to import from this dll */
21     int          nb_imports;  /* number of imported functions */
22     int          lineno;      /* line in .spec file where import is defined */
23 };
24
25 static char **undef_symbols;  /* list of undefined symbols */
26 static int nb_undef_symbols = -1;
27 static int undef_size;
28
29 static char **ignore_symbols; /* list of symbols to ignore */
30 static int nb_ignore_symbols;
31 static int ignore_size;
32
33 static struct import **dll_imports = NULL;
34 static int nb_imports = 0;  /* number of imported dlls */
35 static int total_imports = 0;  /* total number of imported functions */
36
37
38 /* compare function names; helper for resolve_imports */
39 static int name_cmp( const void *name, const void *entry )
40 {
41     return strcmp( *(char **)name, *(char **)entry );
42 }
43
44 /* locate a symbol in a (sorted) list */
45 inline static const char *find_symbol( const char *name, char **table, int size )
46 {
47     
48     char **res = NULL;
49
50     if (table) {
51         res = bsearch( &name, table, size, sizeof(*table), name_cmp );
52     }
53
54     return res ? *res : NULL;
55 }
56
57 /* sort a symbol table */
58 inline static void sort_symbols( char **table, int size )
59 {
60     qsort( table, size, sizeof(*table), name_cmp );
61 }
62
63 /* open the .so library for a given dll in a specified path */
64 static char *try_library_path( const char *path, const char *name )
65 {
66     char *buffer, *p;
67     int fd;
68
69     buffer = xmalloc( strlen(path) + strlen(name) + 8 );
70     sprintf( buffer, "%s/lib%s", path, name );
71     p = buffer + strlen(buffer) - 4;
72     if (!strcmp( p, ".dll" )) *p = 0;
73     strcat( buffer, ".so" );
74     /* check if the file exists */
75     if ((fd = open( buffer, O_RDONLY )) == -1) return NULL;
76     close( fd );
77     return buffer;
78 }
79
80 /* open the .so library for a given dll */
81 static char *open_library( const char *name )
82 {
83     char *fullname;
84     int i;
85
86     for (i = 0; i < nb_lib_paths; i++)
87     {
88         if ((fullname = try_library_path( lib_path[i], name ))) return fullname;
89     }
90     if (!(fullname = try_library_path( ".", name )))
91         fatal_error( "could not open .so file for %s\n", name );
92     return fullname;
93 }
94
95 /* read in the list of exported symbols of a .so */
96 static void read_exported_symbols( const char *name, struct import *imp )
97 {
98     FILE *f;
99     char buffer[1024];
100     char *fullname, *cmdline;
101     const char *ext;
102     int size, err;
103
104     imp->exports    = NULL;
105     imp->nb_exports = size = 0;
106
107     if (!(ext = strrchr( name, '.' ))) ext = name + strlen(name);
108
109     if (!(fullname = open_library( name ))) return;
110     cmdline = xmalloc( strlen(fullname) + 4 );
111     sprintf( cmdline, "nm %s", fullname );
112     free( fullname );
113
114     if (!(f = popen( cmdline, "r" )))
115         fatal_error( "Cannot execute '%s'\n", cmdline );
116
117     while (fgets( buffer, sizeof(buffer), f ))
118     {
119         char *p = buffer + strlen(buffer) - 1;
120         if (p < buffer) continue;
121         if (*p == '\n') *p-- = 0;
122         if (!(p = strstr( buffer, "__wine_dllexport_" ))) continue;
123         p += 17;
124         if (strncmp( p, name, ext - name )) continue;
125         p += ext - name;
126         if (*p++ != '_') continue;
127
128         if (imp->nb_exports == size)
129         {
130             size += 128;
131             imp->exports = xrealloc( imp->exports, size * sizeof(*imp->exports) );
132         }
133         imp->exports[imp->nb_exports++] = xstrdup( p );
134     }
135     if ((err = pclose( f ))) fatal_error( "%s error %d\n", cmdline, err );
136     free( cmdline );
137     sort_symbols( imp->exports, imp->nb_exports );
138 }
139
140 /* add a dll to the list of imports */
141 void add_import_dll( const char *name )
142 {
143     struct import *imp = xmalloc( sizeof(*imp) );
144     imp->dll        = xstrdup( name );
145     imp->imports    = NULL;
146     imp->nb_imports = 0;
147     /* GetToken for the file name has swallowed the '\n', hence pointing to next line */
148     imp->lineno = current_line - 1;
149
150     read_exported_symbols( name, imp );
151
152     dll_imports = xrealloc( dll_imports, (nb_imports+1) * sizeof(*dll_imports) );
153     dll_imports[nb_imports++] = imp;
154 }
155
156 /* Add a symbol to the ignored symbol list */
157 void add_ignore_symbol( const char *name )
158 {
159     if (nb_ignore_symbols == ignore_size)
160     {
161         ignore_size += 32;
162         ignore_symbols = xrealloc( ignore_symbols, ignore_size * sizeof(*ignore_symbols) );
163     }
164     ignore_symbols[nb_ignore_symbols++] = xstrdup( name );
165 }
166
167 /* add a function to the list of imports from a given dll */
168 static void add_import_func( struct import *imp, const char *name )
169 {
170     imp->imports = xrealloc( imp->imports, (imp->nb_imports+1) * sizeof(*imp->imports) );
171     imp->imports[imp->nb_imports++] = xstrdup( name );
172     total_imports++;
173 }
174
175 /* add a symbol to the undef list */
176 inline static void add_undef_symbol( const char *name )
177 {
178     if (nb_undef_symbols == undef_size)
179     {
180         undef_size += 128;
181         undef_symbols = xrealloc( undef_symbols, undef_size * sizeof(*undef_symbols) );
182     }
183     undef_symbols[nb_undef_symbols++] = xstrdup( name );
184 }
185
186 /* remove all the holes in the undefined symbol list; return the number of removed symbols */
187 static int remove_symbol_holes(void)
188 {
189     int i, off;
190     for (i = off = 0; i < nb_undef_symbols; i++)
191     {
192         if (!undef_symbols[i]) off++;
193         else undef_symbols[i - off] = undef_symbols[i];
194     }
195     nb_undef_symbols -= off;
196     return off;
197 }
198
199 /* add the extra undefined symbols that will be contained in the generated spec file itself */
200 static void add_extra_undef_symbols(void)
201 {
202     const char *extras[8];
203     int i, count = 0;
204
205 #define ADD_SYM(name) \
206     do { if (!find_symbol( extras[count] = (name), undef_symbols, \
207                            nb_undef_symbols )) count++; } while(0)
208
209     sort_symbols( undef_symbols, nb_undef_symbols );
210
211     /* add symbols that will be contained in the spec file itself */
212     switch (SpecMode)
213     {
214     case SPEC_MODE_DLL:
215         break;
216     case SPEC_MODE_GUIEXE:
217         ADD_SYM( "GetCommandLineA" );
218         ADD_SYM( "GetStartupInfoA" );
219         ADD_SYM( "GetModuleHandleA" );
220         /* fall through */
221     case SPEC_MODE_CUIEXE:
222         ADD_SYM( "__wine_get_main_args" );
223         ADD_SYM( "ExitProcess" );
224         break;
225     case SPEC_MODE_GUIEXE_UNICODE:
226         ADD_SYM( "GetCommandLineA" );
227         ADD_SYM( "GetStartupInfoA" );
228         ADD_SYM( "GetModuleHandleA" );
229         /* fall through */
230     case SPEC_MODE_CUIEXE_UNICODE:
231         ADD_SYM( "__wine_get_wmain_args" );
232         ADD_SYM( "ExitProcess" );
233         break;
234     }
235     ADD_SYM( "RtlRaiseException" );
236
237     if (count)
238     {
239         for (i = 0; i < count; i++) add_undef_symbol( extras[i] );
240         sort_symbols( undef_symbols, nb_undef_symbols );
241     }
242 }
243
244 /* warn if a given dll is not used, but check forwards first */
245 static void warn_unused( const struct import* imp )
246 {
247     int i, curline;
248     size_t len = strlen(imp->dll);
249     const char *p = strchr( imp->dll, '.' );
250     if (p && !strcasecmp( p, ".dll" )) len = p - imp->dll;
251
252     for (i = Base; i <= Limit; i++)
253     {
254         ORDDEF *odp = Ordinals[i];
255         if (!odp || odp->type != TYPE_FORWARD) continue;
256         if (!strncasecmp( odp->link_name, imp->dll, len ) &&
257             odp->link_name[len] == '.')
258             return;  /* found an import, do not warn */
259     }
260     /* switch current_line temporarily to the line of the import declaration */
261     curline = current_line;
262     current_line = imp->lineno;
263     warning( "%s imported but no symbols used\n", imp->dll );
264     current_line = curline;
265 }
266
267 /* read in the list of undefined symbols */
268 void read_undef_symbols( const char *name )
269 {
270     FILE *f;
271     char buffer[1024];
272     int err;
273
274     undef_size = nb_undef_symbols = 0;
275
276     sprintf( buffer, "nm -u %s", name );
277     if (!(f = popen( buffer, "r" )))
278         fatal_error( "Cannot execute '%s'\n", buffer );
279
280     while (fgets( buffer, sizeof(buffer), f ))
281     {
282         char *p = buffer + strlen(buffer) - 1;
283         if (p < buffer) continue;
284         if (*p == '\n') *p-- = 0;
285         add_undef_symbol( buffer );
286     }
287     if ((err = pclose( f ))) fatal_error( "nm -u %s error %d\n", name, err );
288 }
289
290 static void remove_ignored_symbols(void)
291 {
292     int i;
293
294     sort_symbols( ignore_symbols, nb_ignore_symbols );
295     for (i = 0; i < nb_undef_symbols; i++)
296     {
297         if (find_symbol( undef_symbols[i], ignore_symbols, nb_ignore_symbols ))
298         {
299             free( undef_symbols[i] );
300             undef_symbols[i] = NULL;
301         }
302     }
303     remove_symbol_holes();
304 }
305
306 /* resolve the imports for a Win32 module */
307 int resolve_imports( FILE *outfile )
308 {
309     int i, j;
310
311     if (nb_undef_symbols == -1) return 0; /* no symbol file specified */
312
313     add_extra_undef_symbols();
314     remove_ignored_symbols();
315
316     for (i = 0; i < nb_imports; i++)
317     {
318         struct import *imp = dll_imports[i];
319
320         for (j = 0; j < nb_undef_symbols; j++)
321         {
322             const char *res = find_symbol( undef_symbols[j], imp->exports, imp->nb_exports );
323             if (res)
324             {
325                 add_import_func( imp, res );
326                 free( undef_symbols[j] );
327                 undef_symbols[j] = NULL;
328             }
329         }
330         /* remove all the holes in the undef symbols list */
331         if (!remove_symbol_holes()) warn_unused( imp );
332     }
333     return 1;
334 }
335
336 /* output the import table of a Win32 module */
337 int output_imports( FILE *outfile )
338 {
339     int i, j, pos;
340
341     if (!nb_imports) goto done;
342
343     /* main import header */
344
345     fprintf( outfile, "\nstatic struct {\n" );
346     fprintf( outfile, "  struct {\n" );
347     fprintf( outfile, "    void        *OriginalFirstThunk;\n" );
348     fprintf( outfile, "    unsigned int TimeDateStamp;\n" );
349     fprintf( outfile, "    unsigned int ForwarderChain;\n" );
350     fprintf( outfile, "    const char  *Name;\n" );
351     fprintf( outfile, "    void        *FirstThunk;\n" );
352     fprintf( outfile, "  } imp[%d];\n", nb_imports+1 );
353     fprintf( outfile, "  const char *data[%d];\n", total_imports + nb_imports );
354     fprintf( outfile, "} imports = {\n  {\n" );
355
356     /* list of dlls */
357
358     for (i = j = 0; i < nb_imports; i++)
359     {
360         fprintf( outfile, "    { 0, 0, 0, \"%s\", &imports.data[%d] },\n",
361                  dll_imports[i]->dll, j );
362         j += dll_imports[i]->nb_imports + 1;
363     }
364     fprintf( outfile, "    { 0, 0, 0, 0, 0 },\n" );
365     fprintf( outfile, "  },\n  {\n" );
366
367     /* list of imported functions */
368
369     for (i = 0; i < nb_imports; i++)
370     {
371         fprintf( outfile, "    /* %s */\n", dll_imports[i]->dll );
372         for (j = 0; j < dll_imports[i]->nb_imports; j++)
373             fprintf( outfile, "    \"\\0\\0%s\",\n", dll_imports[i]->imports[j] );
374         fprintf( outfile, "    0,\n" );
375     }
376     fprintf( outfile, "  }\n};\n\n" );
377
378     /* thunks for imported functions */
379
380     fprintf( outfile, "#ifndef __GNUC__\nstatic void __asm__dummy_import(void) {\n#endif\n\n" );
381     pos = 20 * (nb_imports + 1);  /* offset of imports.data from start of imports */
382     fprintf( outfile, "asm(\".align 8\\n\"\n" );
383     for (i = 0; i < nb_imports; i++, pos += 4)
384     {
385         for (j = 0; j < dll_imports[i]->nb_imports; j++, pos += 4)
386         {
387             fprintf( outfile, "    \"\\t" __ASM_FUNC("%s") "\\n\"\n",
388                      dll_imports[i]->imports[j] );
389             fprintf( outfile, "    \"\\t.globl " PREFIX "%s\\n\"\n",
390                      dll_imports[i]->imports[j] );
391             fprintf( outfile, "    \"" PREFIX "%s:\\t", dll_imports[i]->imports[j] );
392             if (strstr( dll_imports[i]->imports[j], "__wine_call_from_16" ))
393                 fprintf( outfile, ".byte 0x2e\\n\\tjmp *(imports+%d)\\n\\tnop\\n\"\n", pos );
394             else
395                 fprintf( outfile, "jmp *(imports+%d)\\n\\tmovl %%esi,%%esi\\n\"\n", pos );
396         }
397     }
398     fprintf( outfile, ");\n#ifndef __GNUC__\n}\n#endif\n\n" );
399
400  done:
401     return nb_imports;
402 }