diff --git a/tools/winebuild/README b/tools/winebuild/README index dfdde4430085ddbd042fbaaee9131bd83312a5e9..3942c4fef65c2b92c432b65b3d79d62acbeb00f7 100644 --- a/tools/winebuild/README +++ b/tools/winebuild/README @@ -7,7 +7,7 @@ type win16|win32 [mode dll|cuiexe|guiexe|cuiexe_unicode|guiexe_unicode] [heap SIZE] [init FUNCTION] -[import DLL] +[import [IMP_FLAGS] DLL] [rsrc RESFILE] [debug_channels ([CHANNEL [CHANNEL...]])] [ignore ([SYMBOL [SYMBOL...]])] @@ -51,6 +51,12 @@ is loaded. This is only valid for Win32 modules. modules at the present). The import declaration can be present several times. +"IMP_FLAGS" is a series of optional flags, preceded by a '-' +character. The supported flags are: +"-delay": the module this module depends upon will be loaded + when the first API will be called (and not while this + module is loaded) + "rsrc" specifies the path of the compiled resource file. "debug_channels" specifies the list of debug channels used by the dll. diff --git a/tools/winebuild/build.h b/tools/winebuild/build.h index 5173ba5951e260de1ebc8af91813d0047b4ddb56..ecddcf6143dc21011b88686666d1db3d5013ff2d 100644 --- a/tools/winebuild/build.h +++ b/tools/winebuild/build.h @@ -133,7 +133,7 @@ extern void fatal_perror( const char *msg, ... ); extern void warning( const char *msg, ... ); extern void dump_bytes( FILE *outfile, const unsigned char *data, int len, const char *label, int constant ); -extern void add_import_dll( const char *name ); +extern void add_import_dll( const char *name, int delay ); extern void add_ignore_symbol( const char *name ); extern int resolve_imports( FILE *outfile ); extern int output_imports( FILE *outfile ); diff --git a/tools/winebuild/import.c b/tools/winebuild/import.c index 57b2f57d63e3031fafc032f144db4be78ba86edd..382fe0c6d646967029a287f12f829b711ca49e76 100644 --- a/tools/winebuild/import.c +++ b/tools/winebuild/import.c @@ -2,6 +2,7 @@ * DLL imports support * * Copyright 2000 Alexandre Julliard + * 2000 Eric Pouech */ #include <fcntl.h> @@ -15,6 +16,7 @@ struct import { char *dll; /* dll name */ + int delay; /* delay or not dll loading ? */ char **exports; /* functions exported from this dll */ int nb_exports; /* number of exported functions */ char **imports; /* functions we want to import from this dll */ @@ -31,9 +33,10 @@ static int nb_ignore_symbols; static int ignore_size; static struct import **dll_imports = NULL; -static int nb_imports = 0; /* number of imported dlls */ -static int total_imports = 0; /* total number of imported functions */ - +static int nb_imports = 0; /* number of imported dlls (delayed or not) */ +static int nb_delayed = 0; /* number of delayed dlls */ +static int total_imports = 0; /* total number of imported functions */ +static int total_delayed = 0; /* total number of imported functions in delayed DLLs */ /* compare function names; helper for resolve_imports */ static int name_cmp( const void *name, const void *entry ) @@ -44,7 +47,6 @@ static int name_cmp( const void *name, const void *entry ) /* locate a symbol in a (sorted) list */ inline static const char *find_symbol( const char *name, char **table, int size ) { - char **res = NULL; if (table) { @@ -138,15 +140,17 @@ static void read_exported_symbols( const char *name, struct import *imp ) } /* add a dll to the list of imports */ -void add_import_dll( const char *name ) +void add_import_dll( const char *name, int delay ) { struct import *imp = xmalloc( sizeof(*imp) ); imp->dll = xstrdup( name ); + imp->delay = delay; imp->imports = NULL; imp->nb_imports = 0; /* GetToken for the file name has swallowed the '\n', hence pointing to next line */ imp->lineno = current_line - 1; + if (delay) nb_delayed++; read_exported_symbols( name, imp ); dll_imports = xrealloc( dll_imports, (nb_imports+1) * sizeof(*dll_imports) ); @@ -170,6 +174,7 @@ static void add_import_func( struct import *imp, const char *name ) imp->imports = xrealloc( imp->imports, (imp->nb_imports+1) * sizeof(*imp->imports) ); imp->imports[imp->nb_imports++] = xstrdup( name ); total_imports++; + if (imp->delay) total_delayed++; } /* add a symbol to the undef list */ @@ -233,6 +238,11 @@ static void add_extra_undef_symbols(void) break; } ADD_SYM( "RtlRaiseException" ); + if (nb_delayed) + { + ADD_SYM( "LoadLibraryA" ); + ADD_SYM( "GetProcAddress" ); + } if (count) { @@ -334,11 +344,12 @@ int resolve_imports( FILE *outfile ) } /* output the import table of a Win32 module */ -int output_imports( FILE *outfile ) +static int output_immediate_imports( FILE *outfile ) { int i, j, pos; + int nb_imm = nb_imports - nb_delayed; - if (!nb_imports) goto done; + if (!nb_imm) goto done; /* main import header */ @@ -349,18 +360,21 @@ int output_imports( FILE *outfile ) fprintf( outfile, " unsigned int ForwarderChain;\n" ); fprintf( outfile, " const char *Name;\n" ); fprintf( outfile, " void *FirstThunk;\n" ); - fprintf( outfile, " } imp[%d];\n", nb_imports+1 ); - fprintf( outfile, " const char *data[%d];\n", total_imports + nb_imports ); + fprintf( outfile, " } imp[%d];\n", nb_imm+1 ); + fprintf( outfile, " const char *data[%d];\n", + total_imports - total_delayed + nb_imm ); fprintf( outfile, "} imports = {\n {\n" ); /* list of dlls */ for (i = j = 0; i < nb_imports; i++) { + if (dll_imports[i]->delay) continue; fprintf( outfile, " { 0, 0, 0, \"%s\", &imports.data[%d] },\n", dll_imports[i]->dll, j ); j += dll_imports[i]->nb_imports + 1; } + fprintf( outfile, " { 0, 0, 0, 0, 0 },\n" ); fprintf( outfile, " },\n {\n" ); @@ -368,6 +382,7 @@ int output_imports( FILE *outfile ) for (i = 0; i < nb_imports; i++) { + if (dll_imports[i]->delay) continue; fprintf( outfile, " /* %s */\n", dll_imports[i]->dll ); for (j = 0; j < dll_imports[i]->nb_imports; j++) fprintf( outfile, " \"\\0\\0%s\",\n", dll_imports[i]->imports[j] ); @@ -378,10 +393,11 @@ int output_imports( FILE *outfile ) /* thunks for imported functions */ fprintf( outfile, "#ifndef __GNUC__\nstatic void __asm__dummy_import(void) {\n#endif\n\n" ); - pos = 20 * (nb_imports + 1); /* offset of imports.data from start of imports */ + pos = 20 * (nb_imm + 1); /* offset of imports.data from start of imports */ fprintf( outfile, "asm(\".align 8\\n\"\n" ); - for (i = 0; i < nb_imports; i++, pos += 4) + for (i = 0; i < nb_imports; i++) { + if (dll_imports[i]->delay) continue; for (j = 0; j < dll_imports[i]->nb_imports; j++, pos += 4) { fprintf( outfile, " \"\\t" __ASM_FUNC("%s") "\\n\"\n", @@ -394,9 +410,174 @@ int output_imports( FILE *outfile ) else fprintf( outfile, "jmp *(imports+%d)\\n\\tmovl %%esi,%%esi\\n\"\n", pos ); } + pos += 4; } fprintf( outfile, ");\n#ifndef __GNUC__\n}\n#endif\n\n" ); done: - return nb_imports; + return nb_imm; +} + +/* output the delayed import table of a Win32 module */ +static int output_delayed_imports( FILE *outfile ) +{ + int i, idx, j, pos; + + if (!nb_delayed) goto done; + + for (i = 0; i < nb_imports; i++) + { + if (!dll_imports[i]->delay) continue; + fprintf( outfile, "static void *__wine_delay_imp_%d_hmod;\n", i); + for (j = 0; j < dll_imports[i]->nb_imports; j++) + { + fprintf( outfile, "void __wine_delay_imp_%d_%s();\n", + i, dll_imports[i]->imports[j] ); + } + } + fprintf( outfile, "\n" ); + fprintf( outfile, "static struct {\n" ); + fprintf( outfile, " struct ImgDelayDescr {\n" ); + fprintf( outfile, " unsigned int grAttrs;\n" ); + fprintf( outfile, " const char *szName;\n" ); + fprintf( outfile, " void **phmod;\n" ); + fprintf( outfile, " void **pIAT;\n" ); + fprintf( outfile, " const char **pINT;\n" ); + fprintf( outfile, " void* pBoundIAT;\n" ); + fprintf( outfile, " void* pUnloadIAT;\n" ); + fprintf( outfile, " unsigned long dwTimeStamp;\n" ); + fprintf( outfile, " } imp[%d];\n", nb_delayed ); + fprintf( outfile, " void *IAT[%d];\n", total_delayed ); + fprintf( outfile, " const char *INT[%d];\n", total_delayed ); + fprintf( outfile, "} delay_imports = {\n" ); + fprintf( outfile, " {\n" ); + for (i = j = 0; i < nb_imports; i++) + { + if (!dll_imports[i]->delay) continue; + fprintf( outfile, " { 0, \"%s\", &__wine_delay_imp_%d_hmod, &delay_imports.IAT[%d], &delay_imports.INT[%d], 0, 0, 0 },\n", + dll_imports[i]->dll, i, j, j ); + j += dll_imports[i]->nb_imports; + } + fprintf( outfile, " },\n {\n" ); + for (i = 0; i < nb_imports; i++) + { + if (!dll_imports[i]->delay) continue; + fprintf( outfile, " /* %s */\n", dll_imports[i]->dll ); + for (j = 0; j < dll_imports[i]->nb_imports; j++) + { + fprintf( outfile, " &__wine_delay_imp_%d_%s,\n", i, dll_imports[i]->imports[j] ); + } + } + fprintf( outfile, " },\n {\n" ); + for (i = 0; i < nb_imports; i++) + { + if (!dll_imports[i]->delay) continue; + fprintf( outfile, " /* %s */\n", dll_imports[i]->dll ); + for (j = 0; j < dll_imports[i]->nb_imports; j++) + { + fprintf( outfile, " \"\\0\\0%s\",\n", dll_imports[i]->imports[j] ); + } + } + fprintf( outfile, " }\n};\n\n" ); + + /* check if there's some stub defined. if so, exception struct + * is already defined, so don't emit it twice + */ + for (i = 0; i < nb_entry_points; i++) if (EntryPoints[i]->type == TYPE_STUB) break; + + if (i == nb_entry_points) { + fprintf( outfile, "struct exc_record {\n" ); + fprintf( outfile, " unsigned int code, flags;\n" ); + fprintf( outfile, " void *rec, *addr;\n" ); + fprintf( outfile, " unsigned int params;\n" ); + fprintf( outfile, " const void *info[15];\n" ); + fprintf( outfile, "};\n\n" ); + fprintf( outfile, "extern void __stdcall RtlRaiseException( struct exc_record * );\n" ); + } + + fprintf( outfile, "extern void * __stdcall LoadLibraryA(const char*);\n"); + fprintf( outfile, "extern void * __stdcall GetProcAddress(void *, const char*);\n"); + fprintf( outfile, "\n" ); + + fprintf( outfile, "void *__stdcall __wine_delay_load(struct ImgDelayDescr* imd, void **pIAT)\n" ); + fprintf( outfile, "{\n" ); + fprintf( outfile, " int idx = pIAT - imd->pIAT;\n" ); + fprintf( outfile, " const char** pINT = imd->pINT + idx;\n" ); + fprintf( outfile, " void *fn;\n\n" ); + + fprintf( outfile, " if (!*imd->phmod) *imd->phmod = LoadLibraryA(imd->szName);\n" ); + fprintf( outfile, " if (*imd->phmod && (fn = GetProcAddress(*imd->phmod, *pINT + 2)))\n"); + fprintf( outfile, " /* patch IAT with final value */\n" ); + fprintf( outfile, " return *pIAT = fn;\n" ); + fprintf( outfile, " else {\n"); + fprintf( outfile, " struct exc_record rec;\n" ); + fprintf( outfile, " rec.code = 0x80000100;\n" ); + fprintf( outfile, " rec.flags = 1;\n" ); + fprintf( outfile, " rec.rec = 0;\n" ); + fprintf( outfile, " rec.params = 2;\n" ); + fprintf( outfile, " rec.info[0] = imd->szName;\n" ); + fprintf( outfile, " rec.info[1] = *pINT + 2;\n" ); + fprintf( outfile, "#ifdef __GNUC__\n" ); + fprintf( outfile, " rec.addr = __builtin_return_address(1);\n" ); + fprintf( outfile, "#else\n" ); + fprintf( outfile, " rec.addr = 0;\n" ); + fprintf( outfile, "#endif\n" ); + fprintf( outfile, " for (;;) RtlRaiseException( &rec );\n" ); + fprintf( outfile, " return 0; /* shouldn't go here */\n" ); + fprintf( outfile, " }\n}\n\n" ); + + fprintf( outfile, "#ifndef __GNUC__\n" ); + fprintf( outfile, "static void __asm__dummy_delay_import(void) {\n" ); + fprintf( outfile, "#endif\n" ); + fprintf( outfile, "asm(\".align 8\\n\"\n" ); + pos = nb_delayed * 32; + for (i = idx = 0; i < nb_imports; i++) + { + char buffer[128]; + + if (!dll_imports[i]->delay) continue; + + sprintf(buffer, "__wine_dl_%d", i); + fprintf( outfile, " \"\\t" __ASM_FUNC("%s") "\\n\"\n", buffer ); + fprintf( outfile, " \"" PREFIX "%s:\\t", buffer ); + fprintf( outfile, "push $delay_imports+%d\\n\"\n", idx * 32 ); + fprintf( outfile, " \"\\tcall __wine_delay_load\\n\"\n" ); + fprintf( outfile, " \"\\tpop %%edx\\n\\tpop %%ecx\\n\\tjmp *%%eax\\n\"\n" ); + + for (j = 0; j < dll_imports[i]->nb_imports; j++, pos += 4) + { + fprintf( outfile, " \"\\t" __ASM_FUNC("%s") "\\n\"\n", + dll_imports[i]->imports[j] ); + fprintf( outfile, " \"\\t.globl " PREFIX "%s\\n\"\n", + dll_imports[i]->imports[j] ); + fprintf( outfile, " \"" PREFIX "%s:\\t", dll_imports[i]->imports[j] ); + fprintf( outfile, "jmp *(delay_imports+%d)\\n\\tmovl %%esi,%%esi\\n\"\n", pos ); + + sprintf( buffer, "__wine_delay_imp_%d_%s", i, dll_imports[i]->imports[j]); + fprintf( outfile, " \"\\t" __ASM_FUNC("%s") "\\n\"\n", buffer ); + fprintf( outfile, " \"" PREFIX "%s:\\t", buffer ); + fprintf( outfile, "push %%ecx\\n\\tpush %%edx\\n\"\n" ); + fprintf( outfile, " \"\\tpush $delay_imports+%d\\n\"\n", pos ); + fprintf( outfile, " \"\\tjmp __wine_dl_%d\\n\"\n", i ); + } + idx++; + } + + fprintf( outfile, ");\n" ); + fprintf( outfile, "#ifndef __GNUC__\n" ); + fprintf( outfile, "}\n" ); + fprintf( outfile, "#endif\n" ); + fprintf( outfile, "\n" ); + + done: + return nb_delayed; +} + +/* output the import and delayed import tables of a Win32 module + * returns number of DLLs exported in 'immediate' mode + */ +int output_imports( FILE *outfile ) +{ + output_delayed_imports( outfile ); + return output_immediate_imports( outfile ); } diff --git a/tools/winebuild/parser.c b/tools/winebuild/parser.c index b49f20fa931e90dacaadce646bb09f6f3d23cc31..033826c168320797b636c0b17c651ab6ca0a9668 100644 --- a/tools/winebuild/parser.c +++ b/tools/winebuild/parser.c @@ -580,9 +580,23 @@ SPEC_TYPE ParseTopLevel( FILE *file ) } else if (strcmp(token, "import") == 0) { + const char* name; + int delay = 0; + if (SpecType != SPEC_WIN32) fatal_error( "Imports not supported for Win16\n" ); - add_import_dll( GetToken(0) ); + name = GetToken(0); + if (*name == '-') + { + name = GetToken(0); + if (!strcmp(name, "delay")) + { + name = GetToken(0); + delay = 1; + } + else fatal_error( "Unknown option '%s' for import directive\n", name ); + } + add_import_dll( name, delay ); } else if (strcmp(token, "rsrc") == 0) {