diff --git a/Make.rules.in b/Make.rules.in index d312a830298..8b7af95a896 100644 --- a/Make.rules.in +++ b/Make.rules.in @@ -157,14 +157,11 @@ DLLS = \ # Implicit rules -.SUFFIXES: .mc .rc .mc.rc .res .spec .spec.c .spec.o .glue.c +.SUFFIXES: .mc .rc .mc.rc .res .spec .spec.c .glue.c .c.o: $(CC) -c $(ALLCFLAGS) -o $*.o $< -.spec.c.spec.o: - $(CC) -c $(ALLCFLAGS) @GCC_NO_BUILTIN@ -o $*.spec.o $< - .s.o: $(AS) -o $*.o $< @@ -178,7 +175,7 @@ DLLS = \ $(LDPATH) $(WRC) $(WRCFLAGS) $(DIVINCL) -o $@ -r $< .spec.spec.c: - $(LDPATH) $(WINEBUILD) @DLLFLAGS@ -o $@ -spec $< + $(LDPATH) $(WINEBUILD) @DLLFLAGS@ -L $(DLLDIR) -o $@ -spec $< .c.glue.c: $(LDPATH) $(WINEBUILD) @DLLFLAGS@ -o $@ -glue $< @@ -192,6 +189,16 @@ DLLS = \ all: Makefile +# Rule for main module intermediate object + +$(MODULE).tmp.o: $(OBJS) Makefile.in + $(LDCOMBINE) $(OBJS) -o $@ + +# Rule for main module spec file + +$(MODULE).spec.c: $(MODULE).spec $(RC_SRCS:.rc=.res) $(SYMBOLFILE) $(WINEBUILD) + $(LDPATH) $(WINEBUILD) @DLLFLAGS@ -L $(DLLDIR) $(SYMBOLFILE:%=-sym %) -o $@ -spec $(MODULE).spec + # Rule to rebuild the resource compiler $(WRC): @@ -212,10 +219,6 @@ $(MAKEDEP): $(WINEBUILD): cd $(TOPOBJDIR)/tools/winebuild && $(MAKE) winebuild -# Rule for main module spec file - -$(MODULE).spec.c: $(RC_SRCS:.rc=.res) - # Rules for makefile Makefile: Makefile.in $(TOPSRCDIR)/configure diff --git a/Makefile.in b/Makefile.in index d671706fe80..e483b92433d 100644 --- a/Makefile.in +++ b/Makefile.in @@ -99,7 +99,9 @@ libwine_unicode.$(LIBEXT): unicode/libwine_unicode.$(LIBEXT) # Dependencies between directories -$(EMUOBJS) $(DLLOBJS) $(PROGRAMS): tools +$(DLLOBJS) $(PROGRAMS): tools + +$(EMUOBJS): tools dlls $(LIBPROGRAMS): tools dlls libwine.$(LIBEXT) libwine_unicode.$(LIBEXT) diff --git a/tools/winebuild/build.h b/tools/winebuild/build.h index 62227e505ad..2f9fe33e1ee 100644 --- a/tools/winebuild/build.h +++ b/tools/winebuild/build.h @@ -142,7 +142,7 @@ 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 resolve_imports( FILE *outfile ); +extern int resolve_imports( FILE *outfile ); extern int output_imports( FILE *outfile ); extern void load_res32_file( const char *name ); extern int output_resources( FILE *outfile ); @@ -153,7 +153,7 @@ extern int output_res16_directory( unsigned char *buffer ); extern void BuildGlue( FILE *outfile, FILE *infile ); extern void BuildRelays( FILE *outfile ); extern void BuildSpec16File( FILE *outfile ); -extern void BuildSpec32File( FILE *outfile ); +extern void BuildSpec32File( FILE *outfile, int output_main ); extern SPEC_TYPE ParseTopLevel( FILE *file ); /* global variables */ @@ -167,6 +167,7 @@ extern int DLLHeapSize; extern int UsePIC; extern int debugging; extern int nb_debug_channels; +extern int nb_lib_paths; extern char DLLName[80]; extern char DLLFileName[80]; @@ -175,6 +176,7 @@ extern char owner_name[80]; extern const char *input_file_name; extern const char *output_file_name; extern char **debug_channels; +extern char **lib_path; extern ORDDEF EntryPoints[MAX_ORDINALS]; extern ORDDEF *Ordinals[MAX_ORDINALS]; diff --git a/tools/winebuild/import.c b/tools/winebuild/import.c index e27fe47dd9e..60d2045f85e 100644 --- a/tools/winebuild/import.c +++ b/tools/winebuild/import.c @@ -14,16 +14,116 @@ struct import { char *dll; /* dll name */ + char **exports; /* functions exported from this dll */ + int nb_exports; /* number of exported functions */ char **imports; /* functions we want to import from this dll */ int nb_imports; /* number of imported functions */ }; +static char **undef_symbols; /* list of undefined symbols */ +static int nb_undef_symbols, undef_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 */ +/* compare function names; helper for resolve_imports */ +static int name_cmp( const void *name, const void *entry ) +{ + return strcmp( *(char **)name, *(char **)entry ); +} + +/* locate a symbol in a (sorted) list */ +inline static const char *find_symbol( const char *name, char **table, int size ) +{ + char **res = bsearch( &name, table, size, sizeof(*table), name_cmp ); + return res ? *res : NULL; +} + +/* sort a symbol table */ +inline static void sort_symbols( char **table, int size ) +{ + qsort( table, size, sizeof(*table), name_cmp ); +} + +/* open the .so library for a given dll in a specified path */ +static char *try_library_path( const char *path, const char *name ) +{ + char *buffer, *p; + int fd; + + buffer = xmalloc( strlen(path) + strlen(name) + 8 ); + sprintf( buffer, "%s/lib%s", path, name ); + p = buffer + strlen(buffer) - 4; + if (!strcmp( p, ".dll" )) *p = 0; + strcat( buffer, ".so" ); + /* check if the file exists */ + if ((fd = open( buffer, O_RDONLY )) == -1) return NULL; + close( fd ); + return buffer; +} + +/* open the .so library for a given dll */ +static char *open_library( const char *name ) +{ + char *fullname; + int i; + + for (i = 0; i < nb_lib_paths; i++) + { + if ((fullname = try_library_path( lib_path[i], name ))) return fullname; + } + if (!(fullname = try_library_path( ".", name ))) + fatal_error( "could not open .so file for %s\n", name ); + return fullname; +} + +/* read in the list of exported symbols of a .so */ +static void read_exported_symbols( const char *name, struct import *imp ) +{ + FILE *f; + char buffer[1024]; + char *fullname, *cmdline; + const char *ext; + int size, err; + + imp->exports = NULL; + imp->nb_exports = size = 0; + + if (!(ext = strrchr( name, '.' ))) ext = name + strlen(name); + + if (!(fullname = open_library( name ))) return; + cmdline = xmalloc( strlen(fullname) + 4 ); + sprintf( cmdline, "nm %s", fullname ); + free( fullname ); + + if (!(f = popen( cmdline, "r" ))) + fatal_error( "Cannot execute '%s'\n", cmdline ); + + while (fgets( buffer, sizeof(buffer), f )) + { + char *p = buffer + strlen(buffer) - 1; + if (p < buffer) continue; + if (*p == '\n') *p-- = 0; + if (!(p = strstr( buffer, "__wine_dllexport_" ))) continue; + p += 17; + if (strncmp( p, name, ext - name )) continue; + p += ext - name; + if (*p++ != '_') continue; + + if (imp->nb_exports == size) + { + size += 128; + imp->exports = xrealloc( imp->exports, size * sizeof(*imp->exports) ); + } + imp->exports[imp->nb_exports++] = xstrdup( p ); + } + if ((err = pclose( f ))) fatal_error( "%s error %d\n", cmdline, err ); + free( cmdline ); + sort_symbols( imp->exports, imp->nb_exports ); +} + /* add a dll to the list of imports */ void add_import_dll( const char *name ) { @@ -32,11 +132,12 @@ void add_import_dll( const char *name ) imp->imports = NULL; imp->nb_imports = 0; + read_exported_symbols( name, imp ); + dll_imports = xrealloc( dll_imports, (nb_imports+1) * sizeof(*dll_imports) ); dll_imports[nb_imports++] = imp; } -#ifdef notyet /* add a function to the list of imports from a given dll */ static void add_import_func( struct import *imp, const char *name ) { @@ -44,7 +145,113 @@ static void add_import_func( struct import *imp, const char *name ) imp->imports[imp->nb_imports++] = xstrdup( name ); total_imports++; } -#endif + +/* add a symbol to the undef list */ +inline static void add_undef_symbol( const char *name ) +{ + if (nb_undef_symbols == undef_size) + { + undef_size += 128; + undef_symbols = xrealloc( undef_symbols, undef_size * sizeof(*undef_symbols) ); + } + undef_symbols[nb_undef_symbols++] = xstrdup( name ); +} + +/* add the extra undefined symbols that will be contained in the generated spec file itself */ +static void add_extra_undef_symbols(void) +{ + const char *extras[8]; + int i, count = 0; + +#define ADD_SYM(name) \ + do { if (!find_symbol( extras[count] = (name), undef_symbols, \ + nb_undef_symbols )) count++; } while(0) + + sort_symbols( undef_symbols, nb_undef_symbols ); + + /* add symbols that will be contained in the spec file itself */ + switch (SpecMode) + { + case SPEC_MODE_GUIEXE: + ADD_SYM( "GetCommandLineA" ); + ADD_SYM( "GetStartupInfoA" ); + ADD_SYM( "GetModuleHandleA" ); + /* fall through */ + case SPEC_MODE_CUIEXE: + ADD_SYM( "__wine_get_main_args" ); + ADD_SYM( "ExitProcess" ); + /* fall through */ + case SPEC_MODE_DLL: + case SPEC_MODE_GUIEXE_NO_MAIN: + case SPEC_MODE_CUIEXE_NO_MAIN: + ADD_SYM( "RtlRaiseException" ); + break; + } + if (count) + { + for (i = 0; i < count; i++) add_undef_symbol( extras[i] ); + sort_symbols( undef_symbols, nb_undef_symbols ); + } +} + +/* read in the list of undefined symbols */ +void read_undef_symbols( const char *name ) +{ + FILE *f; + char buffer[1024]; + int err; + + undef_size = nb_undef_symbols = 0; + + sprintf( buffer, "nm -u %s", name ); + if (!(f = popen( buffer, "r" ))) + fatal_error( "Cannot execute '%s'\n", buffer ); + + while (fgets( buffer, sizeof(buffer), f )) + { + char *p = buffer + strlen(buffer) - 1; + if (p < buffer) continue; + if (*p == '\n') *p-- = 0; + add_undef_symbol( buffer ); + } + if ((err = pclose( f ))) fatal_error( "nm -u %s error %d\n", name, err ); +} + +/* resolve the imports for a Win32 module */ +int resolve_imports( FILE *outfile ) +{ + int i, j, off; + char **p; + + if (!nb_undef_symbols) return 0; /* no symbol file specified */ + + add_extra_undef_symbols(); + + for (i = 0; i < nb_imports; i++) + { + struct import *imp = dll_imports[i]; + + for (j = 0; j < nb_undef_symbols; j++) + { + const char *res = find_symbol( undef_symbols[j], imp->exports, imp->nb_exports ); + if (res) + { + add_import_func( imp, res ); + undef_symbols[j] = NULL; + } + } + /* remove all the holes in the undef symbols list */ + p = undef_symbols; + for (j = off = 0; j < nb_undef_symbols; j++) + { + if (!undef_symbols[j]) off++; + else undef_symbols[j - off] = undef_symbols[j]; + } + nb_undef_symbols -= off; + if (!off) warning( "%s imported but no symbols used\n", imp->dll ); + } + return 1; +} /* output the import table of a Win32 module */ int output_imports( FILE *outfile ) @@ -93,21 +300,21 @@ int output_imports( FILE *outfile ) 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 */ - fprintf( outfile, "asm(\".align 4\");\n" ); + fprintf( outfile, "asm(\".align 4\\n\"\n" ); for (i = 0; i < nb_imports; i++, pos += 4) { for (j = 0; j < dll_imports[i]->nb_imports; j++, pos += 4) { - fprintf( outfile, - "asm(\".type " PREFIX "%s,@function\\n\\t" - ".globl " PREFIX "%s\\n" - PREFIX "%s:\\tjmp *(imports+%d)\\n\\t" - "movl %%esi,%%esi\");\n", - dll_imports[i]->imports[j], dll_imports[i]->imports[j], + fprintf( outfile, " \"\\t.type " PREFIX "%s,@function\\n\"\n", + dll_imports[i]->imports[j] ); + fprintf( outfile, " \"\\t.globl " PREFIX "%s\\n\"\n", + dll_imports[i]->imports[j] ); + fprintf( outfile, " \"" PREFIX "%s:\\tjmp *(imports+%d)\\n\"\n", dll_imports[i]->imports[j], pos ); + fprintf( outfile, " \"\\tmovl %%esi,%%esi\\n\"\n" ); } } - fprintf( outfile, "#ifndef __GNUC__\n}\n#endif\n\n" ); + fprintf( outfile, ");\n#ifndef __GNUC__\n}\n#endif\n\n" ); done: return nb_imports; diff --git a/tools/winebuild/main.c b/tools/winebuild/main.c index fbe6936d7b8..941abf6dc75 100644 --- a/tools/winebuild/main.c +++ b/tools/winebuild/main.c @@ -30,12 +30,14 @@ int nb_entry_points = 0; int nb_names = 0; int debugging = 1; int nb_debug_channels = 0; +int nb_lib_paths = 0; char DLLName[80]; char DLLFileName[80]; char DLLInitFunc[80]; char owner_name[80]; -char **debug_channels; +char **debug_channels = NULL; +char **lib_path = NULL; const char *input_file_name; const char *output_file_name; @@ -82,12 +84,16 @@ static void do_usage(void); static void do_spec( const char *arg ); static void do_glue( const char *arg ); static void do_relay(void); +static void do_sym( const char *arg ); +static void do_lib( const char *arg ); static const struct option option_table[] = { { "-fPIC", 0, do_pic, "-fPIC Generate PIC code" }, { "-h", 0, do_usage, "-h Display this help message" }, + { "-L", 1, do_lib, "-L directory Look for imports libraries in 'directory'" }, { "-o", 1, do_output, "-o name Set the output file name (default: stdout)" }, + { "-sym", 1, do_sym, "-sym file.o Read the list of undefined symbols from 'file.o'" }, { "-spec", 1, do_spec, "-spec file.spec Build a .c file from a spec file" }, { "-glue", 1, do_glue, "-glue file.c Build the 16-bit glue for a .c file" }, { "-relay", 0, do_relay, "-relay Build the relay assembly routines" }, @@ -145,6 +151,17 @@ static void do_relay(void) exec_mode = MODE_RELAY; } +static void do_sym( const char *arg ) +{ + extern void read_undef_symbols( const char *name ); + read_undef_symbols( arg ); +} + +static void do_lib( const char *arg ) +{ + lib_path = xrealloc( lib_path, (nb_lib_paths+1) * sizeof(*lib_path) ); + lib_path[nb_lib_paths++] = xstrdup( arg ); +} /* parse options from the argv array and remove all the recognized ones */ static void parse_options( char *argv[] ) @@ -186,7 +203,7 @@ int main(int argc, char **argv) BuildSpec16File( output_file ); break; case SPEC_WIN32: - BuildSpec32File( output_file ); + BuildSpec32File( output_file, !resolve_imports( output_file ) ); break; default: assert(0); } diff --git a/tools/winebuild/spec32.c b/tools/winebuild/spec32.c index b0a78c2e10b..1d8c7ca062a 100644 --- a/tools/winebuild/spec32.c +++ b/tools/winebuild/spec32.c @@ -9,6 +9,7 @@ */ #include +#include #include #include "winbase.h" @@ -300,6 +301,19 @@ static void output_exports( FILE *outfile, int nr_exports, int nr_names, int fwd #endif /* __i386__ */ fprintf( outfile, " }\n};\n\n" ); + + /* output __wine_dllexport symbols */ + + for (i = 0; i < nb_names; i++) + { + char *p; + /* 'extern' definitions are not available for implicit import */ + if (Names[i]->type == TYPE_EXTERN) continue; + /* check for invalid characters in the name */ + for (p = Names[i]->name; *p; p++) if (!isalnum(*p) && *p != '_') break; + if (!*p) fprintf( outfile, "const char __wine_dllexport_%s_%s = 0;\n", + DLLName, Names[i]->name ); + } } @@ -308,7 +322,7 @@ static void output_exports( FILE *outfile, int nr_exports, int nr_names, int fwd * * Build a Win32 C file from a spec file. */ -void BuildSpec32File( FILE *outfile ) +void BuildSpec32File( FILE *outfile, int output_main ) { ORDDEF *odp; int i, fwd_size = 0, have_regs = FALSE; @@ -468,46 +482,54 @@ void BuildSpec32File( FILE *outfile ) if (!init_func) init_func = "WinMain"; fprintf( outfile, "\n#include \n" - "static void exe_main(void)\n" + "int _ARGC;\n" + "char **_ARGV;\n" + "static void __wine_exe_main(void)\n" "{\n" " extern int PASCAL %s(HINSTANCE,HINSTANCE,LPSTR,INT);\n" + " extern int __wine_get_main_args( char ***argv );\n" " STARTUPINFOA info;\n" " LPSTR cmdline = GetCommandLineA();\n" " while (*cmdline && *cmdline != ' ') cmdline++;\n" " if (*cmdline) cmdline++;\n" " GetStartupInfoA( &info );\n" " if (!(info.dwFlags & STARTF_USESHOWWINDOW)) info.wShowWindow = 1;\n" + " _ARGC = __wine_get_main_args( &_ARGV );\n" " ExitProcess( %s( GetModuleHandleA(0), 0, cmdline, info.wShowWindow ) );\n" "}\n\n", init_func, init_func ); - fprintf( outfile, - "int main( int argc, char *argv[] )\n" - "{\n" - " extern void PROCESS_InitWinelib( int, char ** );\n" - " PROCESS_InitWinelib( argc, argv );\n" - " return 1;\n" - "}\n\n" ); - init_func = "exe_main"; + if (output_main) + fprintf( outfile, + "int main( int argc, char *argv[] )\n" + "{\n" + " extern void PROCESS_InitWinelib( int, char ** );\n" + " PROCESS_InitWinelib( argc, argv );\n" + " return 1;\n" + "}\n\n" ); + init_func = "__wine_exe_main"; subsystem = IMAGE_SUBSYSTEM_WINDOWS_GUI; break; case SPEC_MODE_CUIEXE: - if (!init_func) init_func = "wine_main"; + if (!init_func) init_func = output_main ? "wine_main" : "main"; fprintf( outfile, "\n#include \n" - "static void exe_main(void)\n" + "int _ARGC;\n" + "char **_ARGV;\n" + "static void __wine_exe_main(void)\n" "{\n" " extern int %s( int argc, char *argv[] );\n" - " extern int _ARGC;\n" - " extern char **_ARGV;\n" + " extern int __wine_get_main_args( char ***argv );\n" + " _ARGC = __wine_get_main_args( &_ARGV );\n" " ExitProcess( %s( _ARGC, _ARGV ) );\n" "}\n\n", init_func, init_func ); - fprintf( outfile, - "int main( int argc, char *argv[] )\n" - "{\n" - " extern void PROCESS_InitWinelib( int, char ** );\n" - " PROCESS_InitWinelib( argc, argv );\n" - " return 1;\n" - "}\n\n" ); - init_func = "exe_main"; + if (output_main) + fprintf( outfile, + "int main( int argc, char *argv[] )\n" + "{\n" + " extern void PROCESS_InitWinelib( int, char ** );\n" + " PROCESS_InitWinelib( argc, argv );\n" + " return 1;\n" + "}\n\n" ); + init_func = "__wine_exe_main"; subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI; break; case SPEC_MODE_GUIEXE_NO_MAIN: