From 36e7b406b5425598a1e10b04f1e9582707c4acde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9=20van=20Geldorp?= Date: Mon, 14 Feb 2005 18:39:15 +0000 Subject: [PATCH] Sync to Wine-20050211 svn path=/trunk/; revision=13573 --- reactos/tools/winebuild/Makefile | 43 + reactos/tools/winebuild/Makefile.in | 42 + reactos/tools/winebuild/build.h | 213 ++++ reactos/tools/winebuild/import.c | 1105 ++++++++++++++++++++ reactos/tools/winebuild/main.c | 495 +++++++++ reactos/tools/winebuild/mkstemps.c | 138 +++ reactos/tools/winebuild/parser.c | 1001 ++++++++++++++++++ reactos/tools/winebuild/relay.c | 1208 ++++++++++++++++++++++ reactos/tools/winebuild/res16.c | 360 +++++++ reactos/tools/winebuild/res32.c | 489 +++++++++ reactos/tools/winebuild/spec16.c | 832 +++++++++++++++ reactos/tools/winebuild/spec32.c | 1057 +++++++++++++++++++ reactos/tools/winebuild/utils.c | 415 ++++++++ reactos/tools/winebuild/winebuild.man.in | 434 ++++++++ reactos/tools/winebuild/winglue.h | 31 + 15 files changed, 7863 insertions(+) create mode 100644 reactos/tools/winebuild/Makefile create mode 100644 reactos/tools/winebuild/Makefile.in create mode 100644 reactos/tools/winebuild/build.h create mode 100644 reactos/tools/winebuild/import.c create mode 100644 reactos/tools/winebuild/main.c create mode 100644 reactos/tools/winebuild/mkstemps.c create mode 100644 reactos/tools/winebuild/parser.c create mode 100644 reactos/tools/winebuild/relay.c create mode 100644 reactos/tools/winebuild/res16.c create mode 100644 reactos/tools/winebuild/res32.c create mode 100644 reactos/tools/winebuild/spec16.c create mode 100644 reactos/tools/winebuild/spec32.c create mode 100644 reactos/tools/winebuild/utils.c create mode 100644 reactos/tools/winebuild/winebuild.man.in create mode 100644 reactos/tools/winebuild/winglue.h diff --git a/reactos/tools/winebuild/Makefile b/reactos/tools/winebuild/Makefile new file mode 100644 index 00000000000..925892a46bc --- /dev/null +++ b/reactos/tools/winebuild/Makefile @@ -0,0 +1,43 @@ +# +# winebuild +# +PATH_TO_TOP = ../.. + +include $(PATH_TO_TOP)/rules.mak + +TARGET = winebuild$(EXE_POSTFIX) + +all: $(TARGET) + +# relay.o spec16.o +OBJECTS = \ + import.o \ + main.o \ + parser.o \ + res16.o \ + res32.o \ + spec32.o \ + utils.o \ + mkstemps.o + +CLEAN_FILES = *.o $(TARGET) + +HOST_CFLAGS = -D__USE_W32API -I$(PATH_TO_TOP)/include/wine + +%.o: %.c + $(HOST_CC) $(HOST_CFLAGS) -c $< -o $@ + +$(TARGET): $(OBJECTS) + $(HOST_CC) $(OBJECTS) -o $(TARGET) + +ifeq ($(HOST),mingw32-linux) +clean: + rm -f $(CLEAN_FILES) +endif +ifneq ($(HOST),mingw32-linux) +clean: + del $(CLEAN_FILES) +endif + +.PHONY: clean + diff --git a/reactos/tools/winebuild/Makefile.in b/reactos/tools/winebuild/Makefile.in new file mode 100644 index 00000000000..39dd8c840a0 --- /dev/null +++ b/reactos/tools/winebuild/Makefile.in @@ -0,0 +1,42 @@ +TOPSRCDIR = @top_srcdir@ +TOPOBJDIR = ../.. +SRCDIR = @srcdir@ +VPATH = @srcdir@ +EXEEXT = @EXEEXT@ + +PROGRAMS = winebuild$(EXEEXT) +MODULE = none + +C_SRCS = \ + import.c \ + main.c \ + parser.c \ + relay.c \ + res16.c \ + res32.c \ + spec16.c \ + spec32.c \ + utils.c + +all: $(PROGRAMS) winebuild.man + +@MAKE_RULES@ + +winebuild$(EXEEXT): $(OBJS) + $(CC) $(CFLAGS) -o $@ $(OBJS) $(LIBPORT) $(LDFLAGS) + +winebuild.man: winebuild.man.in + sed -e 's,@PACKAGE_STRING\@,@PACKAGE_STRING@,g' $(SRCDIR)/winebuild.man.in >$@ || ($(RM) $@ && false) + +install:: $(PROGRAMS) winebuild.man + $(MKINSTALLDIRS) $(bindir) $(mandir)/man$(prog_manext) + $(INSTALL_PROGRAM) winebuild$(EXEEXT) $(bindir)/winebuild$(EXEEXT) + $(INSTALL_DATA) winebuild.man $(mandir)/man$(prog_manext)/winebuild.$(prog_manext) + +uninstall:: + $(RM) $(bindir)/winebuild$(EXEEXT) $(mandir)/man$(prog_manext)/winebuild.$(prog_manext) + +clean:: + $(RM) winebuild.man + +### Dependencies: diff --git a/reactos/tools/winebuild/build.h b/reactos/tools/winebuild/build.h new file mode 100644 index 00000000000..c17c7208202 --- /dev/null +++ b/reactos/tools/winebuild/build.h @@ -0,0 +1,213 @@ +/* + * Copyright 1993 Robert J. Amstadt + * Copyright 1995 Martin von Loewis + * Copyright 1995, 1996, 1997 Alexandre Julliard + * Copyright 1997 Eric Youngdale + * Copyright 1999 Ulrich Weigand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __WINE_BUILD_H +#define __WINE_BUILD_H + +#ifndef __WINE_CONFIG_H +# error You must include config.h to use this header +#endif + +#include +#include +#include + +typedef enum +{ + TYPE_VARIABLE, /* variable */ + TYPE_PASCAL, /* pascal function (Win16) */ + TYPE_ABS, /* absolute value (Win16) */ + TYPE_STUB, /* unimplemented stub */ + TYPE_STDCALL, /* stdcall function (Win32) */ + TYPE_CDECL, /* cdecl function (Win32) */ + TYPE_VARARGS, /* varargs function (Win32) */ + TYPE_EXTERN, /* external symbol (Win32) */ + TYPE_NBTYPES +} ORD_TYPE; + +typedef enum +{ + SPEC_WIN16, + SPEC_WIN32 +} SPEC_TYPE; + +typedef struct +{ + int n_values; + int *values; +} ORD_VARIABLE; + +typedef struct +{ + char arg_types[21]; +} ORD_FUNCTION; + +typedef struct +{ + int value; +} ORD_ABS; + +typedef struct +{ + ORD_TYPE type; + int ordinal; + int offset; + int lineno; + int flags; + char *name; /* public name of this function */ + char *link_name; /* name of the C symbol to link to */ + char *export_name; /* name exported under for noname exports */ + union + { + ORD_VARIABLE var; + ORD_FUNCTION func; + ORD_ABS abs; + } u; +} ORDDEF; + +typedef struct +{ + char *file_name; /* file name of the dll */ + char *dll_name; /* internal name of the dll */ + char *owner_name; /* name of the 32-bit dll owning this one */ + char *init_func; /* initialization routine */ + SPEC_TYPE type; /* type of dll (Win16/Win32) */ + int base; /* ordinal base */ + int limit; /* ordinal limit */ + int stack_size; /* exe stack size */ + int heap_size; /* exe heap size */ + int nb_entry_points; /* number of used entry points */ + int alloc_entry_points; /* number of allocated entry points */ + int nb_names; /* number of entry points with names */ + int nb_resources; /* number of resources */ + int characteristics; /* characteristics for the PE header */ + int subsystem; /* subsystem id */ + int subsystem_major; /* subsystem version major number */ + int subsystem_minor; /* subsystem version minor number */ + ORDDEF *entry_points; /* dll entry points */ + ORDDEF **names; /* array of entry point names (points into entry_points) */ + ORDDEF **ordinals; /* array of dll ordinals (points into entry_points) */ + struct resource *resources; /* array of dll resources (format differs between Win16/Win32) */ +} DLLSPEC; + +/* entry point flags */ +#define FLAG_NORELAY 0x01 /* don't use relay debugging for this function */ +#define FLAG_NONAME 0x02 /* don't import function by name */ +#define FLAG_RET16 0x04 /* function returns a 16-bit value */ +#define FLAG_RET64 0x08 /* function returns a 64-bit value */ +#define FLAG_I386 0x10 /* function is i386 only */ +#define FLAG_REGISTER 0x20 /* use register calling convention */ +#define FLAG_PRIVATE 0x40 /* function is private (cannot be imported) */ + +#define FLAG_FORWARD 0x80 /* function is a forwarded name */ + + /* Offset of a structure field relative to the start of the struct */ +#define STRUCTOFFSET(type,field) ((int)&((type *)0)->field) + + /* Offset of register relative to the start of the CONTEXT struct */ +#define CONTEXTOFFSET(reg) STRUCTOFFSET(CONTEXT86,reg) + + /* Offset of register relative to the start of the STACK16FRAME struct */ +#define STACK16OFFSET(reg) STRUCTOFFSET(STACK16FRAME,reg) + + /* Offset of register relative to the start of the STACK32FRAME struct */ +#define STACK32OFFSET(reg) STRUCTOFFSET(STACK32FRAME,reg) + + /* Offset of the stack pointer relative to %fs:(0) */ +#define STACKOFFSET (STRUCTOFFSET(TEB,cur_stack)) + + +#define MAX_ORDINALS 65535 + +/* global functions */ + +#ifndef __GNUC__ +#define __attribute__(X) +#endif + +extern void *xmalloc (size_t size); +extern void *xrealloc (void *ptr, size_t size); +extern char *xstrdup( const char *str ); +extern char *strupper(char *s); +extern void fatal_error( const char *msg, ... ) + __attribute__ ((__format__ (__printf__, 1, 2))); +extern void fatal_perror( const char *msg, ... ) + __attribute__ ((__format__ (__printf__, 1, 2))); +extern void error( const char *msg, ... ) + __attribute__ ((__format__ (__printf__, 1, 2))); +extern void warning( const char *msg, ... ) + __attribute__ ((__format__ (__printf__, 1, 2))); +extern void output_standard_file_header( FILE *outfile ); +extern FILE *open_input_file( const char *srcdir, const char *name ); +extern void close_input_file( FILE *file ); +extern void dump_bytes( FILE *outfile, const unsigned char *data, int len, + const char *label, int constant ); +extern int remove_stdcall_decoration( char *name ); +extern DLLSPEC *alloc_dll_spec(void); +extern void free_dll_spec( DLLSPEC *spec ); +extern const char *make_c_identifier( const char *str ); +extern int get_alignment(int alignBoundary); + +extern void add_import_dll( const char *name, int delay ); +extern void add_ignore_symbol( const char *name ); +extern void read_undef_symbols( char **argv ); +extern int resolve_imports( DLLSPEC *spec ); +extern int output_imports( FILE *outfile, DLLSPEC *spec ); +extern int load_res32_file( const char *name, DLLSPEC *spec ); +extern void output_resources( FILE *outfile, DLLSPEC *spec ); +extern void load_res16_file( const char *name, DLLSPEC *spec ); +extern int output_res16_data( FILE *outfile, DLLSPEC *spec ); +extern int output_res16_directory( unsigned char *buffer, DLLSPEC *spec ); +extern void output_dll_init( FILE *outfile, const char *constructor, const char *destructor ); + +extern void BuildRelays16( FILE *outfile ); +extern void BuildRelays32( FILE *outfile ); +extern void BuildSpec16File( FILE *outfile, DLLSPEC *spec ); +extern void BuildSpec32File( FILE *outfile, DLLSPEC *spec ); +extern void BuildDef32File( FILE *outfile, DLLSPEC *spec ); +extern void BuildDebugFile( FILE *outfile, const char *srcdir, char **argv ); +extern void BuildPedllFile( FILE *outfile, DLLSPEC *spec ); + +extern int parse_spec_file( FILE *file, DLLSPEC *spec ); +extern int parse_def_file( FILE *file, DLLSPEC *spec ); +extern int parse_debug_channels( const char *srcdir, const char *filename ); + +/* global variables */ + +extern int current_line; +extern int UsePIC; +extern int debugging; +extern int nb_debug_channels; +extern int nb_lib_paths; +extern int nb_errors; +extern int display_warnings; +extern int kill_at; + +extern char *input_file_name; +extern const char *output_file_name; +extern char **debug_channels; +extern char **lib_path; + +extern char *ld_command; +extern char *nm_command; + +#endif /* __WINE_BUILD_H */ diff --git a/reactos/tools/winebuild/import.c b/reactos/tools/winebuild/import.c new file mode 100644 index 00000000000..aa655375e95 --- /dev/null +++ b/reactos/tools/winebuild/import.c @@ -0,0 +1,1105 @@ +/* + * DLL imports support + * + * Copyright 2000, 2004 Alexandre Julliard + * Copyright 2000 Eric Pouech + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +# include +#endif + +#include "winglue.h" +#include "build.h" + +struct import +{ + DLLSPEC *spec; /* description of the imported dll */ + int delay; /* delay or not dll loading ? */ + ORDDEF **exports; /* functions exported from this dll */ + int nb_exports; /* number of exported functions */ + ORDDEF **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 = -1; +static int undef_size; + +static char **ignore_symbols; /* list of symbols to ignore */ +static int nb_ignore_symbols; +static int ignore_size; + +static char *ld_tmp_file; /* ld temp file name */ + +static struct import **dll_imports = NULL; +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 */ + +/* list of symbols that are ignored by default */ +static const char * const default_ignored_symbols[] = +{ + "abs", + "acos", + "asin", + "atan", + "atan2", + "atof", + "atoi", + "atol", + "bsearch", + "ceil", + "cos", + "cosh", + "exp", + "fabs", + "floor", + "fmod", + "frexp", + "labs", + "log", + "log10", + "memchr", + "memcmp", + "memcpy", + "memmove", + "memset", + "modf", + "pow", + "qsort", + "sin", + "sinh", + "sqrt", + "strcat", + "strchr", + "strcmp", + "strcpy", + "strcspn", + "strlen", + "strncat", + "strncmp", + "strncpy", + "strpbrk", + "strrchr", + "strspn", + "strstr", + "tan", + "tanh" +}; + +#ifdef __powerpc__ +# ifdef __APPLE__ +# define ppc_high(mem) "ha16(" mem ")" +# define ppc_low(mem) "lo16(" mem ")" +static const char * const ppc_reg[32] = { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10","r11","r12","r13","r14","r15", + "r16","r17","r18","r19","r20","r21","r22","r23", + "r24","r25","r26","r27","r28","r29","r30","r31" }; +# else /* __APPLE__ */ +# define ppc_high(mem) "(" mem ")@hi" +# define ppc_low(mem) "(" mem ")@l" +static const char * const ppc_reg[32] = { "0", "1", "2", "3", "4", "5", "6", "7", + "8", "9", "10","11","12","13","14","15", + "16","17","18","19","20","21","22","23", + "24","25","26","27","28","29","30","31" }; +# endif /* __APPLE__ */ +#endif /* __powerpc__ */ + +/* compare function names; helper for resolve_imports */ +static int name_cmp( const void *name, const void *entry ) +{ + return strcmp( *(const char* const *)name, *(const char* const *)entry ); +} + +/* compare function names; helper for resolve_imports */ +static int func_cmp( const void *func1, const void *func2 ) +{ + const ORDDEF *odp1 = *(const ORDDEF * const *)func1; + const ORDDEF *odp2 = *(const ORDDEF * const *)func2; + return strcmp( odp1->name ? odp1->name : odp1->export_name, + odp2->name ? odp2->name : odp2->export_name ); +} + +/* 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) { + res = bsearch( &name, table, size, sizeof(*table), name_cmp ); + } + + return res ? *res : NULL; +} + +/* locate an export in a (sorted) export list */ +inline static ORDDEF *find_export( const char *name, ORDDEF **table, int size ) +{ + ORDDEF func, *odp, **res = NULL; + + func.name = (char *)name; + func.ordinal = -1; + odp = &func; + if (table) res = bsearch( &odp, table, size, sizeof(*table), func_cmp ); + return res ? *res : NULL; +} + +/* sort a symbol table */ +inline static void sort_symbols( char **table, int size ) +{ + if (table ) + qsort( table, size, sizeof(*table), name_cmp ); +} + +inline static void output_function_size( FILE *outfile, const char *name ) +{ +#ifdef HAVE_ASM_DOT_SIZE + fprintf( outfile, " \"\\t.size " __ASM_NAME("%s") ", . - " __ASM_NAME("%s") "\\n\"\n", name, name); +#endif +} + +/* free an import structure */ +static void free_imports( struct import *imp ) +{ + free( imp->exports ); + free( imp->imports ); + free_dll_spec( imp->spec ); + free( imp ); +} + +/* remove the temp file at exit */ +static void remove_ld_tmp_file(void) +{ + if (ld_tmp_file) unlink( ld_tmp_file ); +} + +/* check whether a given dll has already been imported */ +static int is_already_imported( const char *name ) +{ + int i; + + for (i = 0; i < nb_imports; i++) + { + if (!strcmp( dll_imports[i]->spec->file_name, name )) return 1; + } + return 0; +} + +/* 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; + int fd; + + buffer = xmalloc( strlen(path) + strlen(name) + 9 ); + sprintf( buffer, "%s/lib%s.def", path, name ); + + /* check if the file exists */ + if ((fd = open( buffer, O_RDONLY )) != -1) + { + close( fd ); + return buffer; + } + free( buffer ); + return NULL; +} + +/* 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; + } + fatal_error( "could not open .def file for %s\n", name ); + return NULL; +} + +/* read in the list of exported symbols of an import library */ +static int read_import_lib( const char *name, struct import *imp ) +{ + FILE *f; + char *fullname; + int i, ret; + DLLSPEC *spec = imp->spec; + + imp->exports = NULL; + imp->nb_exports = 0; + + fullname = open_library( name ); + f = open_input_file( NULL, fullname ); + free( fullname ); + + ret = parse_def_file( f, spec ); + close_input_file( f ); + if (!ret) return 0; + if (is_already_imported( spec->file_name )) return 0; + + imp->exports = xmalloc( spec->nb_entry_points * sizeof(*imp->exports) ); + + for (i = 0; i < spec->nb_entry_points; i++) + { + ORDDEF *odp = &spec->entry_points[i]; + + if (odp->type != TYPE_STDCALL && odp->type != TYPE_CDECL) continue; + if (odp->flags & FLAG_PRIVATE) continue; + imp->exports[imp->nb_exports++] = odp; + } + imp->exports = xrealloc( imp->exports, imp->nb_exports * sizeof(*imp->exports) ); + if (imp->nb_exports) + qsort( imp->exports, imp->nb_exports, sizeof(*imp->exports), func_cmp ); + return 1; +} + +/* add a dll to the list of imports */ +void add_import_dll( const char *name, int delay ) +{ + struct import *imp; + char *fullname; + + fullname = xmalloc( strlen(name) + 5 ); + strcpy( fullname, name ); + if (!strchr( fullname, '.' )) strcat( fullname, ".dll" ); + + /* check if we already imported it */ + if (is_already_imported( fullname )) + { + free( fullname ); + return; + } + + imp = xmalloc( sizeof(*imp) ); + imp->spec = alloc_dll_spec(); + imp->spec->file_name = fullname; + imp->delay = delay; + imp->imports = NULL; + imp->nb_imports = 0; + if (delay) nb_delayed++; + + if (read_import_lib( name, imp )) + { + dll_imports = xrealloc( dll_imports, (nb_imports+1) * sizeof(*dll_imports) ); + dll_imports[nb_imports++] = imp; + } + else + { + free_imports( imp ); + if (nb_errors) exit(1); + } +} + +/* remove an imported dll, based on its index in the dll_imports array */ +static void remove_import_dll( int index ) +{ + struct import *imp = dll_imports[index]; + + memmove( &dll_imports[index], &dll_imports[index+1], sizeof(imp) * (nb_imports - index - 1) ); + nb_imports--; + if (imp->delay) nb_delayed--; + free_imports( imp ); +} + +/* initialize the list of ignored symbols */ +static void init_ignored_symbols(void) +{ + int i; + + nb_ignore_symbols = sizeof(default_ignored_symbols)/sizeof(default_ignored_symbols[0]); + ignore_size = nb_ignore_symbols + 32; + ignore_symbols = xmalloc( ignore_size * sizeof(*ignore_symbols) ); + for (i = 0; i < nb_ignore_symbols; i++) + ignore_symbols[i] = xstrdup( default_ignored_symbols[i] ); +} + +/* add a symbol to the ignored symbol list */ +/* if the name starts with '-' the symbol is removed instead */ +void add_ignore_symbol( const char *name ) +{ + int i; + + if (!ignore_symbols) init_ignored_symbols(); /* first time around, fill list with defaults */ + + if (name[0] == '-') /* remove it */ + { + if (!name[1]) /* remove everything */ + { + for (i = 0; i < nb_ignore_symbols; i++) free( ignore_symbols[i] ); + nb_ignore_symbols = 0; + } + else + { + for (i = 0; i < nb_ignore_symbols; i++) + { + if (!strcmp( ignore_symbols[i], name+1 )) + { + free( ignore_symbols[i] ); + memmove( &ignore_symbols[i], &ignore_symbols[i+1], nb_ignore_symbols - i - 1 ); + nb_ignore_symbols--; + } + } + } + } + else + { + if (nb_ignore_symbols == ignore_size) + { + ignore_size += 32; + ignore_symbols = xrealloc( ignore_symbols, ignore_size * sizeof(*ignore_symbols) ); + } + ignore_symbols[nb_ignore_symbols++] = xstrdup( name ); + } +} + +/* add a function to the list of imports from a given dll */ +static void add_import_func( struct import *imp, ORDDEF *func ) +{ + imp->imports = xrealloc( imp->imports, (imp->nb_imports+1) * sizeof(*imp->imports) ); + imp->imports[imp->nb_imports++] = func; + total_imports++; + if (imp->delay) total_delayed++; +} + +/* 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 ); +} + +/* remove all the holes in the undefined symbol list; return the number of removed symbols */ +static int remove_symbol_holes(void) +{ + int i, off; + for (i = off = 0; i < nb_undef_symbols; i++) + { + if (!undef_symbols[i]) off++; + else undef_symbols[i - off] = undef_symbols[i]; + } + nb_undef_symbols -= off; + return off; +} + +/* add a symbol to the extra list, but only if needed */ +static int add_extra_symbol( const char **extras, int *count, const char *name, const DLLSPEC *spec ) +{ + int i; + + if (!find_symbol( name, undef_symbols, nb_undef_symbols )) + { + /* check if the symbol is being exported by this dll */ + for (i = 0; i < spec->nb_entry_points; i++) + { + ORDDEF *odp = &spec->entry_points[i]; + if (odp->type == TYPE_STDCALL || + odp->type == TYPE_CDECL || + odp->type == TYPE_VARARGS || + odp->type == TYPE_EXTERN) + { + if (odp->name && !strcmp( odp->name, name )) return 0; + } + } + extras[*count] = name; + (*count)++; + } + return 1; +} + +/* add the extra undefined symbols that will be contained in the generated spec file itself */ +static void add_extra_undef_symbols( const DLLSPEC *spec ) +{ + const char *extras[10]; + int i, count = 0, nb_stubs = 0, nb_regs = 0; + int kernel_imports = 0, ntdll_imports = 0; + + sort_symbols( undef_symbols, nb_undef_symbols ); + + for (i = 0; i < spec->nb_entry_points; i++) + { + ORDDEF *odp = &spec->entry_points[i]; + if (odp->type == TYPE_STUB) nb_stubs++; + if (odp->flags & FLAG_REGISTER) nb_regs++; + } + + /* add symbols that will be contained in the spec file itself */ + if (!(spec->characteristics & IMAGE_FILE_DLL)) + { + switch (spec->subsystem) + { + case IMAGE_SUBSYSTEM_WINDOWS_GUI: + case IMAGE_SUBSYSTEM_WINDOWS_CUI: + kernel_imports += add_extra_symbol( extras, &count, "GetCommandLineA", spec ); + kernel_imports += add_extra_symbol( extras, &count, "GetStartupInfoA", spec ); + kernel_imports += add_extra_symbol( extras, &count, "GetModuleHandleA", spec ); + kernel_imports += add_extra_symbol( extras, &count, "ExitProcess", spec ); + break; + } + } + if (nb_delayed) + { + kernel_imports += add_extra_symbol( extras, &count, "LoadLibraryA", spec ); + kernel_imports += add_extra_symbol( extras, &count, "GetProcAddress", spec ); + } + if (nb_regs) + ntdll_imports += add_extra_symbol( extras, &count, "__wine_call_from_32_regs", spec ); + if (nb_delayed || nb_stubs) + ntdll_imports += add_extra_symbol( extras, &count, "RtlRaiseException", spec ); + + /* make sure we import the dlls that contain these functions */ + if (kernel_imports) add_import_dll( "kernel32", 0 ); + if (ntdll_imports) add_import_dll( "ntdll", 0 ); + + if (count) + { + for (i = 0; i < count; i++) add_undef_symbol( extras[i] ); + sort_symbols( undef_symbols, nb_undef_symbols ); + } +} + +/* check if a given imported dll is not needed, taking forwards into account */ +static int check_unused( const struct import* imp, const DLLSPEC *spec ) +{ + int i; + const char *file_name = imp->spec->file_name; + size_t len = strlen( file_name ); + const char *p = strchr( file_name, '.' ); + if (p && !strcasecmp( p, ".dll" )) len = p - file_name; + + for (i = spec->base; i <= spec->limit; i++) + { + ORDDEF *odp = spec->ordinals[i]; + if (!odp || !(odp->flags & FLAG_FORWARD)) continue; + if (!strncasecmp( odp->link_name, file_name, len ) && + odp->link_name[len] == '.') + return 0; /* found a forward, it is used */ + } + return 1; +} + +/* combine a list of object files with ld into a single object file */ +/* returns the name of the combined file */ +static const char *ldcombine_files( char **argv ) +{ + int i, len = 0; + char *cmd; + int fd, err; + + if (output_file_name && output_file_name[0]) + { + ld_tmp_file = xmalloc( strlen(output_file_name) + 10 ); + strcpy( ld_tmp_file, output_file_name ); + strcat( ld_tmp_file, ".XXXXXX.o" ); + } + else ld_tmp_file = xstrdup( "/tmp/winebuild.tmp.XXXXXX.o" ); + + if ((fd = mkstemps( ld_tmp_file, 2 ) == -1)) fatal_error( "could not generate a temp file\n" ); + close( fd ); + atexit( remove_ld_tmp_file ); + + for (i = 0; argv[i]; i++) len += strlen(argv[i]) + 1; + cmd = xmalloc( len + strlen(ld_tmp_file) + 8 + strlen(ld_command) ); + sprintf( cmd, "%s -r -o %s", ld_command, ld_tmp_file ); + for (i = 0; argv[i]; i++) sprintf( cmd + strlen(cmd), " %s", argv[i] ); + err = system( cmd ); + if (err) fatal_error( "%s -r failed with status %d\n", ld_command, err ); + free( cmd ); + return ld_tmp_file; +} + +/* read in the list of undefined symbols */ +void read_undef_symbols( char **argv ) +{ + static const char name_prefix[] = __ASM_NAME(""); + static const int prefix_len = sizeof(name_prefix) - 1; + FILE *f; + char *cmd, buffer[1024]; + int err; + const char *name; + + if (!argv[0]) return; + + undef_size = nb_undef_symbols = 0; + + /* if we have multiple object files, link them together */ + if (argv[1]) name = ldcombine_files( argv ); + else name = argv[0]; + + cmd = xmalloc( strlen(nm_command) + strlen(name) + 5 ); + sprintf( cmd, "%s -u %s", nm_command, name ); + if (!(f = popen( cmd, "r" ))) + fatal_error( "Cannot execute '%s'\n", cmd ); + + while (fgets( buffer, sizeof(buffer), f )) + { + char *p = buffer + strlen(buffer) - 1; + if (p < buffer) continue; + if (*p == '\n') *p-- = 0; + p = buffer; + while (*p == ' ') p++; + if (p[0] == 'U' && p[1] == ' ' && p[2]) p += 2; + if (prefix_len && !strncmp( p, name_prefix, prefix_len )) p += prefix_len; + add_undef_symbol( p ); + } + if ((err = pclose( f ))) warning( "%s failed with status %d\n", cmd, err ); + free( cmd ); +} + +static void remove_ignored_symbols(void) +{ + int i; + + if (!ignore_symbols) init_ignored_symbols(); + sort_symbols( ignore_symbols, nb_ignore_symbols ); + for (i = 0; i < nb_undef_symbols; i++) + { + if (find_symbol( undef_symbols[i], ignore_symbols, nb_ignore_symbols )) + { + free( undef_symbols[i] ); + undef_symbols[i] = NULL; + } + } + remove_symbol_holes(); +} + +/* resolve the imports for a Win32 module */ +int resolve_imports( DLLSPEC *spec ) +{ + int i, j; + + if (nb_undef_symbols == -1) return 0; /* no symbol file specified */ + + add_extra_undef_symbols( spec ); + remove_ignored_symbols(); + + for (i = 0; i < nb_imports; i++) + { + struct import *imp = dll_imports[i]; + + for (j = 0; j < nb_undef_symbols; j++) + { + ORDDEF *odp = find_export( undef_symbols[j], imp->exports, imp->nb_exports ); + if (odp) + { + add_import_func( imp, odp ); + free( undef_symbols[j] ); + undef_symbols[j] = NULL; + } + } + /* remove all the holes in the undef symbols list */ + if (!remove_symbol_holes() && check_unused( imp, spec )) + { + /* the dll is not used, get rid of it */ + warning( "%s imported but no symbols used\n", imp->spec->file_name ); + remove_import_dll( i ); + i--; + } + } + return 1; +} + +/* output the import table of a Win32 module */ +static int output_immediate_imports( FILE *outfile ) +{ + int i, j, pos; + int nb_imm = nb_imports - nb_delayed; + static const char import_thunks[] = "__wine_spec_import_thunks"; + + if (!nb_imm) goto done; + + /* main import header */ + + fprintf( outfile, "\nstatic struct {\n" ); + fprintf( outfile, " struct {\n" ); + fprintf( outfile, " void *OriginalFirstThunk;\n" ); + fprintf( outfile, " unsigned int TimeDateStamp;\n" ); + fprintf( outfile, " unsigned int ForwarderChain;\n" ); + fprintf( outfile, " const char *Name;\n" ); + fprintf( outfile, " void *FirstThunk;\n" ); + 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]->spec->file_name, j ); + j += dll_imports[i]->nb_imports + 1; + } + + fprintf( outfile, " { 0, 0, 0, 0, 0 },\n" ); + fprintf( outfile, " },\n {\n" ); + + /* list of imported functions */ + + for (i = 0; i < nb_imports; i++) + { + if (dll_imports[i]->delay) continue; + fprintf( outfile, " /* %s */\n", dll_imports[i]->spec->file_name ); + for (j = 0; j < dll_imports[i]->nb_imports; j++) + { + ORDDEF *odp = dll_imports[i]->imports[j]; + if (!(odp->flags & FLAG_NONAME)) + { + unsigned short ord = odp->ordinal; + fprintf( outfile, " \"\\%03o\\%03o%s\",\n", + *(unsigned char *)&ord, *((unsigned char *)&ord + 1), odp->name ); + } + else + fprintf( outfile, " (char *)%d,\n", odp->ordinal ); + } + fprintf( outfile, " 0,\n" ); + } + fprintf( outfile, " }\n};\n\n" ); + + /* thunks for imported functions */ + + fprintf( outfile, "#ifndef __GNUC__\nstatic void __asm__dummy_import(void) {\n#endif\n\n" ); + pos = (sizeof(void *) + 2*sizeof(unsigned int) + sizeof(const char *) + sizeof(void *)) * + (nb_imm + 1); /* offset of imports.data from start of imports */ + fprintf( outfile, "asm(\".text\\n\\t.align %d\\n\"\n", get_alignment(8) ); + fprintf( outfile, " \"" __ASM_NAME("%s") ":\\n\"\n", import_thunks); + + for (i = 0; i < nb_imports; i++) + { + if (dll_imports[i]->delay) continue; + for (j = 0; j < dll_imports[i]->nb_imports; j++, pos += sizeof(const char *)) + { + ORDDEF *odp = dll_imports[i]->imports[j]; + const char *name = odp->name ? odp->name : odp->export_name; + fprintf( outfile, " \"\\t" __ASM_FUNC("%s") "\\n\"\n", name ); + fprintf( outfile, " \"\\t.globl " __ASM_NAME("%s") "\\n\"\n", name ); + fprintf( outfile, " \"" __ASM_NAME("%s") ":\\n\\t", name); + +#if defined(__i386__) + if (strstr( name, "__wine_call_from_16" )) + fprintf( outfile, ".byte 0x2e\\n\\tjmp *(imports+%d)\\n\\tnop\\n", pos ); + else + fprintf( outfile, "jmp *(imports+%d)\\n\\tmovl %%esi,%%esi\\n", pos ); +#elif defined(__sparc__) + if ( !UsePIC ) + { + fprintf( outfile, "sethi %%hi(imports+%d), %%g1\\n\\t", pos ); + fprintf( outfile, "ld [%%g1+%%lo(imports+%d)], %%g1\\n\\t", pos ); + fprintf( outfile, "jmp %%g1\\n\\tnop\\n" ); + } + else + { + /* Hmpf. Stupid sparc assembler always interprets global variable + names as GOT offsets, so we have to do it the long way ... */ + fprintf( outfile, "save %%sp, -96, %%sp\\n" ); + fprintf( outfile, "0:\\tcall 1f\\n\\tnop\\n" ); + fprintf( outfile, "1:\\tsethi %%hi(imports+%d-0b), %%g1\\n\\t", pos ); + fprintf( outfile, "or %%g1, %%lo(imports+%d-0b), %%g1\\n\\t", pos ); + fprintf( outfile, "ld [%%g1+%%o7], %%g1\\n\\t" ); + fprintf( outfile, "jmp %%g1\\n\\trestore\\n" ); + } + +#elif defined(__powerpc__) + fprintf(outfile, "\taddi %s, %s, -0x4\\n\"\n", ppc_reg[1], ppc_reg[1]); + fprintf(outfile, "\t\"\\tstw %s, 0(%s)\\n\"\n", ppc_reg[9], ppc_reg[1]); + fprintf(outfile, "\t\"\\taddi %s, %s, -0x4\\n\"\n", ppc_reg[1], ppc_reg[1]); + fprintf(outfile, "\t\"\\tstw %s, 0(%s)\\n\"\n", ppc_reg[8], ppc_reg[1]); + fprintf(outfile, "\t\"\\taddi %s, %s, -0x4\\n\"\n", ppc_reg[1], ppc_reg[1]); + fprintf(outfile, "\t\"\\tstw %s, 0(%s)\\n\"\n", ppc_reg[7], ppc_reg[1]); + + fprintf(outfile, "\t\"\\tlis %s, " ppc_high(__ASM_NAME("imports") "+ %d") "\\n\"\n", ppc_reg[9], pos); + fprintf(outfile, "\t\"\\tla %s, " ppc_low (__ASM_NAME("imports") "+ %d") "(%s)\\n\"\n", ppc_reg[8], pos, ppc_reg[9]); + fprintf(outfile, "\t\"\\tlwz %s, 0(%s)\\n\"\n", ppc_reg[7], ppc_reg[8]); + fprintf(outfile, "\t\"\\tmtctr %s\\n\"\n", ppc_reg[7]); + + fprintf(outfile, "\t\"\\tlwz %s, 0(%s)\\n\"\n", ppc_reg[7], ppc_reg[1]); + fprintf(outfile, "\t\"\\taddi %s, %s, 0x4\\n\"\n", ppc_reg[1], ppc_reg[1]); + fprintf(outfile, "\t\"\\tlwz %s, 0(%s)\\n\"\n", ppc_reg[8], ppc_reg[1]); + fprintf(outfile, "\t\"\\taddi %s, %s, 0x4\\n\"\n", ppc_reg[1], ppc_reg[1]); + fprintf(outfile, "\t\"\\tlwz %s, 0(%s)\\n\"\n", ppc_reg[9], ppc_reg[1]); + fprintf(outfile, "\t\"\\taddi %s, %s, 0x4\\n\"\n", ppc_reg[1], ppc_reg[1]); + fprintf(outfile, "\t\"\\tbctr\\n"); +#elif defined(__ALPHA__) + fprintf( outfile, "\tlda $0,imports\\n\"\n" ); + fprintf( outfile, "\t\"\\tlda $0,%d($0)\\n\"\n", pos); + fprintf( outfile, "\t\"\\tjmp $31,($0)\\n" ); +#else +#error You need to define import thunks for your architecture! +#endif + fprintf( outfile, "\"\n" ); + output_function_size( outfile, name ); + } + pos += 4; + } + output_function_size( outfile, import_thunks ); + fprintf( outfile, " \".text\");\n#ifndef __GNUC__\n}\n#endif\n\n" ); + + done: + return nb_imm; +} + +/* output the delayed import table of a Win32 module */ +static int output_delayed_imports( FILE *outfile, const DLLSPEC *spec ) +{ + int i, idx, j, pos; + static const char delayed_import_loaders[] = "__wine_spec_delayed_import_loaders"; + static const char delayed_import_thunks[] = "__wine_spec_delayed_import_thunks"; + + 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++) + { + ORDDEF *odp = dll_imports[i]->imports[j]; + const char *name = odp->name ? odp->name : odp->export_name; + fprintf( outfile, "void __wine_delay_imp_%d_%s();\n", i, name ); + } + } + 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]->spec->file_name, 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]->spec->file_name ); + for (j = 0; j < dll_imports[i]->nb_imports; j++) + { + ORDDEF *odp = dll_imports[i]->imports[j]; + const char *name = odp->name ? odp->name : odp->export_name; + fprintf( outfile, " &__wine_delay_imp_%d_%s,\n", i, name ); + } + } + fprintf( outfile, " },\n {\n" ); + for (i = 0; i < nb_imports; i++) + { + if (!dll_imports[i]->delay) continue; + fprintf( outfile, " /* %s */\n", dll_imports[i]->spec->file_name ); + for (j = 0; j < dll_imports[i]->nb_imports; j++) + { + ORDDEF *odp = dll_imports[i]->imports[j]; + if (!odp->name) + fprintf( outfile, " (char *)%d,\n", odp->ordinal ); + else + fprintf( outfile, " \"%s\",\n", odp->name ); + } + } + 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 < spec->nb_entry_points; i++) if (spec->entry_points[i].type == TYPE_STUB) break; + + if (i == spec->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( int idx_nr )\n" ); + fprintf( outfile, "{\n" ); + fprintf( outfile, " int idx = idx_nr >> 16, nr = idx_nr & 0xffff;\n" ); + fprintf( outfile, " struct ImgDelayDescr *imd = delay_imports.imp + idx;\n" ); + fprintf( outfile, " void **pIAT = imd->pIAT + nr;\n" ); + fprintf( outfile, " const char** pINT = imd->pINT + nr;\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)))\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;\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 %d\\n\"\n", get_alignment(8) ); + fprintf( outfile, " \"" __ASM_NAME("%s") ":\\n\"\n", delayed_import_loaders); + fprintf( outfile, " \"\\t" __ASM_FUNC("__wine_delay_load_asm") "\\n\"\n" ); + fprintf( outfile, " \"" __ASM_NAME("__wine_delay_load_asm") ":\\n\"\n" ); +#if defined(__i386__) + fprintf( outfile, " \"\\tpushl %%ecx\\n\\tpushl %%edx\\n\\tpushl %%eax\\n\"\n" ); + fprintf( outfile, " \"\\tcall __wine_delay_load\\n\"\n" ); + fprintf( outfile, " \"\\tpopl %%edx\\n\\tpopl %%ecx\\n\\tjmp *%%eax\\n\"\n" ); +#elif defined(__sparc__) + fprintf( outfile, " \"\\tsave %%sp, -96, %%sp\\n\"\n" ); + fprintf( outfile, " \"\\tcall __wine_delay_load\\n\"\n" ); + fprintf( outfile, " \"\\tmov %%g1, %%o0\\n\"\n" ); + fprintf( outfile, " \"\\tjmp %%o0\\n\\trestore\\n\"\n" ); +#elif defined(__powerpc__) +# if defined(__APPLE__) +/* On darwin an extra 56 bytes must be allowed for the linkage area+param area */ +# define extra_stack_storage 56 +# else +# define extra_stack_storage 0 +# endif + /* Save all callee saved registers into a stackframe. */ + fprintf( outfile, " \"\\tstwu %s, -%d(%s)\\n\"\n",ppc_reg[1], 48+extra_stack_storage, ppc_reg[1]); + fprintf( outfile, " \"\\tstw %s, %d(%s)\\n\"\n", ppc_reg[3], 4+extra_stack_storage, ppc_reg[1]); + fprintf( outfile, " \"\\tstw %s, %d(%s)\\n\"\n", ppc_reg[4], 8+extra_stack_storage, ppc_reg[1]); + fprintf( outfile, " \"\\tstw %s, %d(%s)\\n\"\n", ppc_reg[5], 12+extra_stack_storage, ppc_reg[1]); + fprintf( outfile, " \"\\tstw %s, %d(%s)\\n\"\n", ppc_reg[6], 16+extra_stack_storage, ppc_reg[1]); + fprintf( outfile, " \"\\tstw %s, %d(%s)\\n\"\n", ppc_reg[7], 20+extra_stack_storage, ppc_reg[1]); + fprintf( outfile, " \"\\tstw %s, %d(%s)\\n\"\n", ppc_reg[8], 24+extra_stack_storage, ppc_reg[1]); + fprintf( outfile, " \"\\tstw %s, %d(%s)\\n\"\n", ppc_reg[9], 28+extra_stack_storage, ppc_reg[1]); + fprintf( outfile, " \"\\tstw %s, %d(%s)\\n\"\n", ppc_reg[10],32+extra_stack_storage, ppc_reg[1]); + fprintf( outfile, " \"\\tstw %s, %d(%s)\\n\"\n", ppc_reg[11],36+extra_stack_storage, ppc_reg[1]); + fprintf( outfile, " \"\\tstw %s, %d(%s)\\n\"\n", ppc_reg[12],40+extra_stack_storage, ppc_reg[1]); + + /* r0 -> r3 (arg1) */ + fprintf( outfile, " \"\\tmr %s, %s\\n\"\n", ppc_reg[3], ppc_reg[0]); + + /* save return address */ + fprintf( outfile, " \"\\tmflr %s\\n\"\n", ppc_reg[0]); + fprintf( outfile, " \"\\tstw %s, %d(%s)\\n\"\n", ppc_reg[0], 44+extra_stack_storage, ppc_reg[1]); + + /* Call the __wine_delay_load function, arg1 is arg1. */ + fprintf( outfile, " \"\\tbl " __ASM_NAME("__wine_delay_load") "\\n\"\n"); + + /* Load return value from call into ctr register */ + fprintf( outfile, " \"\\tmtctr %s\\n\"\n", ppc_reg[3]); + + /* restore all saved registers and drop stackframe. */ + fprintf( outfile, " \"\\tlwz %s, %d(%s)\\n\"\n", ppc_reg[3], 4+extra_stack_storage, ppc_reg[1]); + fprintf( outfile, " \"\\tlwz %s, %d(%s)\\n\"\n", ppc_reg[4], 8+extra_stack_storage, ppc_reg[1]); + fprintf( outfile, " \"\\tlwz %s, %d(%s)\\n\"\n", ppc_reg[5], 12+extra_stack_storage, ppc_reg[1]); + fprintf( outfile, " \"\\tlwz %s, %d(%s)\\n\"\n", ppc_reg[6], 16+extra_stack_storage, ppc_reg[1]); + fprintf( outfile, " \"\\tlwz %s, %d(%s)\\n\"\n", ppc_reg[7], 20+extra_stack_storage, ppc_reg[1]); + fprintf( outfile, " \"\\tlwz %s, %d(%s)\\n\"\n", ppc_reg[8], 24+extra_stack_storage, ppc_reg[1]); + fprintf( outfile, " \"\\tlwz %s, %d(%s)\\n\"\n", ppc_reg[9], 28+extra_stack_storage, ppc_reg[1]); + fprintf( outfile, " \"\\tlwz %s, %d(%s)\\n\"\n", ppc_reg[10],32+extra_stack_storage, ppc_reg[1]); + fprintf( outfile, " \"\\tlwz %s, %d(%s)\\n\"\n", ppc_reg[11],36+extra_stack_storage, ppc_reg[1]); + fprintf( outfile, " \"\\tlwz %s, %d(%s)\\n\"\n", ppc_reg[12],40+extra_stack_storage, ppc_reg[1]); + + /* Load return value from call into return register */ + fprintf( outfile, " \"\\tlwz %s, %d(%s)\\n\"\n", ppc_reg[0], 44+extra_stack_storage, ppc_reg[1]); + fprintf( outfile, " \"\\tmtlr %s\\n\"\n", ppc_reg[0]); + fprintf( outfile, " \"\\taddi %s, %s, %d\\n\"\n", ppc_reg[1], ppc_reg[1], 48+extra_stack_storage); + + /* branch to ctr register. */ + fprintf( outfile, " \"bctr\\n\"\n"); +#elif defined(__ALPHA__) + fprintf( outfile, " \"\\tjsr $26,__wine_delay_load\\n\"\n" ); + fprintf( outfile, " \"\\tjmp $31,($0)\\n\"\n" ); +#else +#error You need to defined delayed import thunks for your architecture! +#endif + output_function_size( outfile, "__wine_delay_load_asm" ); + + for (i = idx = 0; i < nb_imports; i++) + { + if (!dll_imports[i]->delay) continue; + for (j = 0; j < dll_imports[i]->nb_imports; j++) + { + char buffer[128]; + ORDDEF *odp = dll_imports[i]->imports[j]; + const char *name = odp->name ? odp->name : odp->export_name; + + sprintf( buffer, "__wine_delay_imp_%d_%s", i, name ); + fprintf( outfile, " \"\\t" __ASM_FUNC("%s") "\\n\"\n", buffer ); + fprintf( outfile, " \"" __ASM_NAME("%s") ":\\n\"\n", buffer ); +#if defined(__i386__) + fprintf( outfile, " \"\\tmovl $%d, %%eax\\n\"\n", (idx << 16) | j ); + fprintf( outfile, " \"\\tjmp __wine_delay_load_asm\\n\"\n" ); +#elif defined(__sparc__) + fprintf( outfile, " \"\\tset %d, %%g1\\n\"\n", (idx << 16) | j ); + fprintf( outfile, " \"\\tb,a __wine_delay_load_asm\\n\"\n" ); +#elif defined(__powerpc__) +#ifdef __APPLE__ + /* On Darwin we can use r0 and r2 */ + /* Upper part in r2 */ + fprintf( outfile, " \"\\tlis %s, %d\\n\"\n", ppc_reg[2], idx); + /* Lower part + r2 -> r0, Note we can't use r0 directly */ + fprintf( outfile, " \"\\taddi %s, %s, %d\\n\"\n", ppc_reg[0], ppc_reg[2], j); + fprintf( outfile, " \"\\tb " __ASM_NAME("__wine_delay_load_asm") "\\n\"\n"); +#else /* __APPLE__ */ + /* On linux we can't use r2 since r2 is not a scratch register (hold the TOC) */ + /* Save r13 on the stack */ + fprintf( outfile, " \"\\taddi %s, %s, -0x4\\n\"\n", ppc_reg[1], ppc_reg[1]); + fprintf( outfile, " \"\\tstw %s, 0(%s)\\n\"\n", ppc_reg[13], ppc_reg[1]); + /* Upper part in r13 */ + fprintf( outfile, " \"\\tlis %s, %d\\n\"\n", ppc_reg[13], idx); + /* Lower part + r13 -> r0, Note we can't use r0 directly */ + fprintf( outfile, " \"\\taddi %s, %s, %d\\n\"\n", ppc_reg[0], ppc_reg[13], j); + /* Restore r13 */ + fprintf( outfile, " \"\\tstw %s, 0(%s)\\n\"\n", ppc_reg[13], ppc_reg[1]); + fprintf( outfile, " \"\\taddic %s, %s, 0x4\\n\"\n", ppc_reg[1], ppc_reg[1]); + fprintf( outfile, " \"\\tb " __ASM_NAME("__wine_delay_load_asm") "\\n\"\n"); +#endif /* __APPLE__ */ +#elif defined(__ALPHA__) + fprintf( outfile, " \"\\tlda $0,%d($31)\\n\"\n", j); + fprintf( outfile, " \"\\tldah $0,%d($0)\\n\"\n", idx); + fprintf( outfile, " \"\\tjmp $31,__wine_delay_load_asm\\n\"\n" ); +#else +#error You need to defined delayed import thunks for your architecture! +#endif + output_function_size( outfile, name ); + } + idx++; + } + output_function_size( outfile, delayed_import_loaders ); + + fprintf( outfile, "\n \".align %d\\n\"\n", get_alignment(8) ); + fprintf( outfile, " \"" __ASM_NAME("%s") ":\\n\"\n", delayed_import_thunks); + pos = nb_delayed * 32; + 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) + { + ORDDEF *odp = dll_imports[i]->imports[j]; + const char *name = odp->name ? odp->name : odp->export_name; + + fprintf( outfile, " \"\\t" __ASM_FUNC("%s") "\\n\"\n", name ); + fprintf( outfile, " \"\\t.globl " __ASM_NAME("%s") "\\n\"\n", name ); + fprintf( outfile, " \"" __ASM_NAME("%s") ":\\n\\t\"", name ); +#if defined(__i386__) + if (strstr( name, "__wine_call_from_16" )) + fprintf( outfile, "\".byte 0x2e\\n\\tjmp *(delay_imports+%d)\\n\\tnop\\n\"", pos ); + else + fprintf( outfile, "\"jmp *(delay_imports+%d)\\n\\tmovl %%esi,%%esi\\n\"", pos ); +#elif defined(__sparc__) + if ( !UsePIC ) + { + fprintf( outfile, "\"sethi %%hi(delay_imports+%d), %%g1\\n\\t\"", pos ); + fprintf( outfile, "\"ld [%%g1+%%lo(delay_imports+%d)], %%g1\\n\\t\"", pos ); + fprintf( outfile, "\"jmp %%g1\\n\\tnop\\n\"" ); + } + else + { + /* Hmpf. Stupid sparc assembler always interprets global variable + names as GOT offsets, so we have to do it the long way ... */ + fprintf( outfile, "\"save %%sp, -96, %%sp\\n\"" ); + fprintf( outfile, "\"0:\\tcall 1f\\n\\tnop\\n\"" ); + fprintf( outfile, "\"1:\\tsethi %%hi(delay_imports+%d-0b), %%g1\\n\\t\"", pos ); + fprintf( outfile, "\"or %%g1, %%lo(delay_imports+%d-0b), %%g1\\n\\t\"", pos ); + fprintf( outfile, "\"ld [%%g1+%%o7], %%g1\\n\\t\"" ); + fprintf( outfile, "\"jmp %%g1\\n\\trestore\\n\"" ); + } + +#elif defined(__powerpc__) + fprintf( outfile, "\t\"addi %s, %s, -0x4\\n\"\n", ppc_reg[1], ppc_reg[1]); + fprintf( outfile, "\t\"\\tstw %s, 0(%s)\\n\"\n", ppc_reg[9], ppc_reg[1]); + fprintf( outfile, "\t\"\\taddi %s, %s, -0x4\\n\"\n", ppc_reg[1], ppc_reg[1]); + fprintf( outfile, "\t\"\\tstw %s, 0(%s)\\n\"\n", ppc_reg[8], ppc_reg[1]); + fprintf( outfile, "\t\"\\taddi %s, %s, -0x4\\n\"\n", ppc_reg[1], ppc_reg[1]); + fprintf( outfile, "\t\"\\tstw %s, 0(%s)\\n\"\n", ppc_reg[7], ppc_reg[1]); + + fprintf( outfile, "\t\"\\tlis %s, " ppc_high(__ASM_NAME("delay_imports") "+ %d") "\\n\"\n", ppc_reg[9], pos); + fprintf( outfile, "\t\"\\tla %s, " ppc_low (__ASM_NAME("delay_imports") "+ %d") "(%s)\\n\"\n", ppc_reg[8], pos, ppc_reg[9]); + fprintf( outfile, "\t\"\\tlwz %s, 0(%s)\\n\"\n", ppc_reg[7], ppc_reg[8]); + fprintf( outfile, "\t\"\\tmtctr %s\\n\"\n", ppc_reg[7]); + + fprintf( outfile, "\t\"\\tlwz %s, 0(%s)\\n\"\n", ppc_reg[7], ppc_reg[1]); + fprintf( outfile, "\t\"\\taddi %s, %s, 0x4\\n\"\n", ppc_reg[1], ppc_reg[1]); + fprintf( outfile, "\t\"\\tlwz %s, 0(%s)\\n\"\n", ppc_reg[8], ppc_reg[1]); + fprintf( outfile, "\t\"\\taddi %s, %s, 0x4\\n\"\n", ppc_reg[1], ppc_reg[1]); + fprintf( outfile, "\t\"\\tlwz %s, 0(%s)\\n\"\n", ppc_reg[9], ppc_reg[1]); + fprintf( outfile, "\t\"\\taddi %s, %s, 0x4\\n\"\n", ppc_reg[1], ppc_reg[1]); + fprintf( outfile, "\t\"\\tbctr\\n\""); +#elif defined(__ALPHA__) + fprintf( outfile, "\t\"lda $0,delay_imports\\n\"\n" ); + fprintf( outfile, "\t\"\\tlda $0,%d($0)\\n\"\n", pos); + fprintf( outfile, "\t\"\\tjmp $31,($0)\\n\"" ); +#else +#error You need to define delayed import thunks for your architecture! +#endif + fprintf( outfile, "\n" ); + output_function_size( outfile, name ); + } + } + output_function_size( outfile, delayed_import_thunks ); + 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, DLLSPEC *spec ) +{ + output_delayed_imports( outfile, spec ); + return output_immediate_imports( outfile ); +} diff --git a/reactos/tools/winebuild/main.c b/reactos/tools/winebuild/main.c new file mode 100644 index 00000000000..2071777f741 --- /dev/null +++ b/reactos/tools/winebuild/main.c @@ -0,0 +1,495 @@ +/* + * Main function + * + * Copyright 1993 Robert J. Amstadt + * Copyright 1995 Martin von Loewis + * Copyright 1995, 1996, 1997 Alexandre Julliard + * Copyright 1997 Eric Youngdale + * Copyright 1999 Ulrich Weigand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_GETOPT_H +# include +#endif + +#include "winglue.h" +#include "build.h" + +int UsePIC = 0; +int nb_debug_channels = 0; +int nb_lib_paths = 0; +int nb_errors = 0; +int display_warnings = 0; +int kill_at = 0; + +/* we only support relay debugging on i386 */ +#ifdef __i386__ +int debugging = 1; +#else +int debugging = 0; +#endif + +char **debug_channels = NULL; +char **lib_path = NULL; + +char *input_file_name = NULL; +const char *output_file_name = NULL; + +char *ld_command = "ld"; +char *nm_command = "nm"; + +static FILE *output_file; +static const char *current_src_dir; +static int nb_res_files; +static char **res_files; +static char *spec_file_name; + +/* execution mode */ +enum exec_mode_values +{ + MODE_NONE, + MODE_DLL, + MODE_EXE, + MODE_DEF, + MODE_DEBUG, + MODE_RELAY16, + MODE_RELAY32, + MODE_PEDLL +}; + +static enum exec_mode_values exec_mode = MODE_NONE; + +/* set the dll file name from the input file name */ +static void set_dll_file_name( const char *name, DLLSPEC *spec ) +{ + char *p; + + if (spec->file_name) return; + + if ((p = strrchr( name, '\\' ))) name = p + 1; + if ((p = strrchr( name, '/' ))) name = p + 1; + spec->file_name = xmalloc( strlen(name) + 5 ); + strcpy( spec->file_name, name ); + if ((p = strrchr( spec->file_name, '.' ))) + { + if (!strcmp( p, ".spec" ) || !strcmp( p, ".def" )) *p = 0; + } + if (!strchr( spec->file_name, '.' )) strcat( spec->file_name, ".dll" ); +} + +/* set the dll subsystem */ +static void set_subsystem( const char *subsystem, DLLSPEC *spec ) +{ + char *major, *minor, *str = xstrdup( subsystem ); + + if ((major = strchr( str, ':' ))) *major++ = 0; + if (!strcmp( str, "native" )) spec->subsystem = IMAGE_SUBSYSTEM_NATIVE; + else if (!strcmp( str, "windows" )) spec->subsystem = IMAGE_SUBSYSTEM_WINDOWS_GUI; + else if (!strcmp( str, "console" )) spec->subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI; + else fatal_error( "Invalid subsystem name '%s'\n", subsystem ); + if (major) + { + if ((minor = strchr( major, '.' ))) + { + *minor++ = 0; + spec->subsystem_minor = atoi( minor ); + } + spec->subsystem_major = atoi( major ); + } + free( str ); +} + +/* cleanup on program exit */ +static void cleanup(void) +{ + if (output_file_name) unlink( output_file_name ); +} + +/* clean things up when aborting on a signal */ +static void exit_on_signal( int sig ) +{ + exit(1); /* this will call atexit functions */ +} + +/******************************************************************* + * command-line option handling + */ +static const char usage_str[] = +"Usage: winebuild [OPTIONS] [FILES]\n\n" +"Options:\n" +" -C --source-dir=DIR Look for source files in DIR\n" +" -d --delay-lib=LIB Import the specified library in delayed mode\n" +" -D SYM Ignored for C flags compatibility\n" +" -e --entry=FUNC Set the DLL entry point function (default: DllMain)\n" +" -f FLAGS Compiler flags (only -fPIC is supported)\n" +" -F --filename=DLLFILE Set the DLL filename (default: from input file name)\n" +" -h --help Display this help message\n" +" -H --heap=SIZE Set the heap size for a Win16 dll\n" +" -i --ignore=SYM[,SYM] Ignore specified symbols when resolving imports\n" +" -I DIR Ignored for C flags compatibility\n" +" -k --kill-at Kill stdcall decorations in generated .def files\n" +" -K FLAGS Compiler flags (only -KPIC is supported)\n" +" --ld-cmd=LD Command to use for linking (default: ld)\n" +" -l --library=LIB Import the specified library\n" +" -L --library-path=DIR Look for imports libraries in DIR\n" +" -M --main-module=MODULE Set the name of the main module for a Win16 dll\n" +" --nm-cmd=NM Command to use to get undefined symbols (default: nm)\n" +" -N --dll-name=DLLNAME Set the DLL name (default: from input file name)\n" +" -o --output=NAME Set the output file name (default: stdout)\n" +" -r --res=RSRC.RES Load resources from RSRC.RES\n" +" --subsystem=SUBSYS Set the subsystem (one of native, windows, console)\n" +" --version Print the version and exit\n" +" -w --warnings Turn on warnings\n" +"\nMode options:\n" +" --dll=FILE Build a .c file from a .spec or .def file\n" +" --def=FILE.SPEC Build a .def file from a spec file\n" +" --exe=NAME Build a .c file for the named executable\n" +" --debug [FILES] Build a .c file with the debug channels declarations\n" +" --relay16 Build the 16-bit relay assembly routines\n" +" --relay32 Build the 32-bit relay assembly routines\n" +" --pedll Build a .c file for PE dll\n\n" +"The mode options are mutually exclusive; you must specify one and only one.\n\n"; + +enum long_options_values +{ + LONG_OPT_DLL = 1, + LONG_OPT_DEF, + LONG_OPT_EXE, + LONG_OPT_DEBUG, + LONG_OPT_LDCMD, + LONG_OPT_NMCMD, + LONG_OPT_RELAY16, + LONG_OPT_RELAY32, + LONG_OPT_SUBSYSTEM, + LONG_OPT_VERSION, + LONG_OPT_PEDLL +}; + +static const char short_options[] = "C:D:F:H:I:K:L:M:N:d:e:f:hi:kl:m:o:r:w"; + +static const struct option long_options[] = +{ + { "dll", 1, 0, LONG_OPT_DLL }, + { "def", 1, 0, LONG_OPT_DEF }, + { "exe", 1, 0, LONG_OPT_EXE }, + { "debug", 0, 0, LONG_OPT_DEBUG }, + { "ld-cmd", 1, 0, LONG_OPT_LDCMD }, + { "nm-cmd", 1, 0, LONG_OPT_NMCMD }, + { "relay16", 0, 0, LONG_OPT_RELAY16 }, + { "relay32", 0, 0, LONG_OPT_RELAY32 }, + { "subsystem",1, 0, LONG_OPT_SUBSYSTEM }, + { "version", 0, 0, LONG_OPT_VERSION }, + { "pedll", 1, 0, LONG_OPT_PEDLL }, + /* aliases for short options */ + { "source-dir", 1, 0, 'C' }, + { "delay-lib", 1, 0, 'd' }, + { "entry", 1, 0, 'e' }, + { "filename", 1, 0, 'F' }, + { "help", 0, 0, 'h' }, + { "heap", 1, 0, 'H' }, + { "ignore", 1, 0, 'i' }, + { "kill-at", 0, 0, 'k' }, + { "library", 1, 0, 'l' }, + { "library-path", 1, 0, 'L' }, + { "main-module", 1, 0, 'M' }, + { "dll-name", 1, 0, 'N' }, + { "output", 1, 0, 'o' }, + { "res", 1, 0, 'r' }, + { "warnings", 0, 0, 'w' }, + { NULL, 0, 0, 0 } +}; + +static void usage( int exit_code ) +{ + fprintf( stderr, "%s", usage_str ); + exit( exit_code ); +} + +static void set_exec_mode( enum exec_mode_values mode ) +{ + if (exec_mode != MODE_NONE) usage(1); + exec_mode = mode; +} + +/* parse options from the argv array and remove all the recognized ones */ +static char **parse_options( int argc, char **argv, DLLSPEC *spec ) +{ + char *p; + int optc; + + while ((optc = getopt_long( argc, argv, short_options, long_options, NULL )) != -1) + { + switch(optc) + { + case 'C': + current_src_dir = optarg; + break; + case 'D': + /* ignored */ + break; + case 'F': + spec->file_name = xstrdup( optarg ); + break; + case 'H': + if (!isdigit(optarg[0])) + fatal_error( "Expected number argument with -H option instead of '%s'\n", optarg ); + spec->heap_size = atoi(optarg); + if (spec->heap_size > 65535) + fatal_error( "Invalid heap size %d, maximum is 65535\n", spec->heap_size ); + break; + case 'I': + /* ignored */ + break; + case 'K': + /* ignored, because cc generates correct code. */ + break; + case 'L': + lib_path = xrealloc( lib_path, (nb_lib_paths+1) * sizeof(*lib_path) ); + lib_path[nb_lib_paths++] = xstrdup( optarg ); + break; + case 'M': + spec->owner_name = xstrdup( optarg ); + spec->type = SPEC_WIN16; + break; + case 'N': + spec->dll_name = xstrdup( optarg ); + break; + case 'd': + add_import_dll( optarg, 1 ); + break; + case 'e': + spec->init_func = xstrdup( optarg ); + if ((p = strchr( spec->init_func, '@' ))) *p = 0; /* kill stdcall decoration */ + break; + case 'f': + if (!strcmp( optarg, "PIC") || !strcmp( optarg, "pic")) UsePIC = 1; + /* ignore all other flags */ + break; + case 'h': + usage(0); + break; + case 'i': + { + char *str = xstrdup( optarg ); + char *token = strtok( str, "," ); + while (token) + { + add_ignore_symbol( token ); + token = strtok( NULL, "," ); + } + free( str ); + } + break; + case 'k': + kill_at = 1; + break; + case 'l': + add_import_dll( optarg, 0 ); + break; + case 'o': + if (unlink( optarg ) == -1 && errno != ENOENT) + fatal_error( "Unable to create output file '%s'\n", optarg ); + if (!(output_file = fopen( optarg, "w" ))) + fatal_error( "Unable to create output file '%s'\n", optarg ); + output_file_name = xstrdup(optarg); + atexit( cleanup ); /* make sure we remove the output file on exit */ + break; + case 'r': + res_files = xrealloc( res_files, (nb_res_files+1) * sizeof(*res_files) ); + res_files[nb_res_files++] = xstrdup( optarg ); + break; + case 'w': + display_warnings = 1; + break; + case LONG_OPT_DLL: + set_exec_mode( MODE_DLL ); + spec_file_name = xstrdup( optarg ); + set_dll_file_name( optarg, spec ); + break; + case LONG_OPT_DEF: + set_exec_mode( MODE_DEF ); + spec_file_name = xstrdup( optarg ); + set_dll_file_name( optarg, spec ); + break; + case LONG_OPT_EXE: + set_exec_mode( MODE_EXE ); + if ((p = strrchr( optarg, '/' ))) p++; + else p = optarg; + spec->file_name = xmalloc( strlen(p) + 5 ); + strcpy( spec->file_name, p ); + if (!strchr( spec->file_name, '.' )) strcat( spec->file_name, ".exe" ); + if (!spec->subsystem) spec->subsystem = IMAGE_SUBSYSTEM_WINDOWS_GUI; + break; + case LONG_OPT_DEBUG: + set_exec_mode( MODE_DEBUG ); + break; + case LONG_OPT_LDCMD: + ld_command = xstrdup( optarg ); + break; + case LONG_OPT_NMCMD: + nm_command = xstrdup( optarg ); + break; + case LONG_OPT_RELAY16: + set_exec_mode( MODE_RELAY16 ); + break; + case LONG_OPT_RELAY32: + set_exec_mode( MODE_RELAY32 ); + break; + case LONG_OPT_SUBSYSTEM: + set_subsystem( optarg, spec ); + break; + case LONG_OPT_VERSION: + printf( "winebuild version " PACKAGE_VERSION "\n" ); + exit(0); + case LONG_OPT_PEDLL: + set_exec_mode( MODE_PEDLL ); + spec_file_name = xstrdup( optarg ); + set_dll_file_name( optarg, spec ); + break; + case '?': + usage(1); + break; + } + } + return &argv[optind]; +} + + +/* load all specified resource files */ +static void load_resources( char *argv[], DLLSPEC *spec ) +{ + int i; + char **ptr, **last; + + switch (spec->type) + { + case SPEC_WIN16: + for (i = 0; i < nb_res_files; i++) load_res16_file( res_files[i], spec ); + break; + + case SPEC_WIN32: + for (i = 0; i < nb_res_files; i++) + { + if (!load_res32_file( res_files[i], spec )) + fatal_error( "%s is not a valid Win32 resource file\n", res_files[i] ); + } + + /* load any resource file found in the remaining arguments */ + for (ptr = last = argv; *ptr; ptr++) + { + if (!load_res32_file( *ptr, spec )) + *last++ = *ptr; /* not a resource file, keep it in the list */ + } + *last = NULL; + break; + } +} + +static int parse_input_file( DLLSPEC *spec ) +{ + FILE *input_file = open_input_file( NULL, spec_file_name ); + char *extension = strrchr( spec_file_name, '.' ); + + if (extension && !strcmp( extension, ".def" )) + return parse_def_file( input_file, spec ); + else + return parse_spec_file( input_file, spec ); + close_input_file( input_file ); +} + + +/******************************************************************* + * main + */ +int main(int argc, char **argv) +{ + DLLSPEC *spec = alloc_dll_spec(); + +#ifdef SIGHUP + signal( SIGHUP, exit_on_signal ); +#endif + signal( SIGTERM, exit_on_signal ); + signal( SIGINT, exit_on_signal ); + + output_file = stdout; + argv = parse_options( argc, argv, spec ); + + switch(exec_mode) + { + case MODE_DLL: + spec->characteristics |= IMAGE_FILE_DLL; + load_resources( argv, spec ); + if (!parse_input_file( spec )) break; + switch (spec->type) + { + case SPEC_WIN16: + fatal_error( "Win16 specs are not supported in ReactOS version of winebuild\n" ); + break; + case SPEC_WIN32: + read_undef_symbols( argv ); + BuildSpec32File( output_file, spec ); + break; + default: assert(0); + } + break; + case MODE_EXE: + if (spec->type == SPEC_WIN16) fatal_error( "Cannot build 16-bit exe files\n" ); + load_resources( argv, spec ); + read_undef_symbols( argv ); + BuildSpec32File( output_file, spec ); + break; + case MODE_DEF: + if (argv[0]) fatal_error( "file argument '%s' not allowed in this mode\n", argv[0] ); + if (spec->type == SPEC_WIN16) fatal_error( "Cannot yet build .def file for 16-bit dlls\n" ); + if (!parse_input_file( spec )) break; + BuildDef32File( output_file, spec ); + break; + case MODE_DEBUG: + BuildDebugFile( output_file, current_src_dir, argv ); + break; + case MODE_RELAY16: + fatal_error( "Win16 relays are not supported in ReactOS version of winebuild\n" ); + break; + case MODE_RELAY32: + fatal_error( "Win32 relays are not supported in ReactOS version of winebuild\n" ); + break; + case MODE_PEDLL: + if (argv[0]) fatal_error( "file argument '%s' not allowed in this mode\n", argv[0] ); + if (!parse_input_file( spec )) break; + BuildPedllFile( output_file, spec ); + break; + default: + usage(1); + break; + } + if (nb_errors) exit(1); + if (output_file_name) + { + fclose( output_file ); + output_file_name = NULL; + } + return 0; +} diff --git a/reactos/tools/winebuild/mkstemps.c b/reactos/tools/winebuild/mkstemps.c new file mode 100644 index 00000000000..9919eac949f --- /dev/null +++ b/reactos/tools/winebuild/mkstemps.c @@ -0,0 +1,138 @@ +/* Copyright (C) 1991, 1992, 1996, 1998 Free Software Foundation, Inc. + This file is derived from mkstemp.c from the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYS_TIME_H +#include +#endif +#ifdef HAVE_PROCESS_H +#include +#endif + +/* We need to provide a type for gcc_uint64_t. */ +#ifdef __GNUC__ +__extension__ typedef unsigned long long gcc_uint64_t; +#else +typedef unsigned long gcc_uint64_t; +#endif + +#ifndef TMP_MAX +#define TMP_MAX 16384 +#endif + +/* + +@deftypefn Replacement int mkstemps (char *@var{template}, int @var{suffix_len}) + +Generate a unique temporary file name from @var{template}. +@var{template} has the form: + +@example + @var{path}/ccXXXXXX@var{suffix} +@end example + +@var{suffix_len} tells us how long @var{suffix} is (it can be zero +length). The last six characters of @var{template} before @var{suffix} +must be @samp{XXXXXX}; they are replaced with a string that makes the +filename unique. Returns a file descriptor open on the file for +reading and writing. + +@end deftypefn + +*/ + +int +mkstemps ( + char *template, + int suffix_len) +{ + static const char letters[] + = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + static gcc_uint64_t value; +#ifdef HAVE_GETTIMEOFDAY + struct timeval tv; +#endif + char *XXXXXX; + size_t len; + int count; + + len = strlen (template); + + if ((int) len < 6 + suffix_len + || strncmp (&template[len - 6 - suffix_len], "XXXXXX", 6)) + { + return -1; + } + + XXXXXX = &template[len - 6 - suffix_len]; + +#ifdef HAVE_GETTIMEOFDAY + /* Get some more or less random data. */ + gettimeofday (&tv, NULL); + value += ((gcc_uint64_t) tv.tv_usec << 16) ^ tv.tv_sec ^ getpid (); +#else + value += getpid (); +#endif + + for (count = 0; count < TMP_MAX; ++count) + { + gcc_uint64_t v = value; + int fd; + + /* Fill in the random bits. */ + XXXXXX[0] = letters[v % 62]; + v /= 62; + XXXXXX[1] = letters[v % 62]; + v /= 62; + XXXXXX[2] = letters[v % 62]; + v /= 62; + XXXXXX[3] = letters[v % 62]; + v /= 62; + XXXXXX[4] = letters[v % 62]; + v /= 62; + XXXXXX[5] = letters[v % 62]; + +#ifdef VMS + fd = open (template, O_RDWR|O_CREAT|O_EXCL, 0600, "fop=tmd"); +#else + fd = open (template, O_RDWR|O_CREAT|O_EXCL, 0600); +#endif + if (fd >= 0) + /* The file does not exist. */ + return fd; + + /* This is a random value. It is only necessary that the next + TMP_MAX values generated by adding 7777 to VALUE are different + with (module 2^32). */ + value += 7777; + } + + /* We return the null string if we can't find a unique file name. */ + template[0] = '\0'; + return -1; +} diff --git a/reactos/tools/winebuild/parser.c b/reactos/tools/winebuild/parser.c new file mode 100644 index 00000000000..a998777d873 --- /dev/null +++ b/reactos/tools/winebuild/parser.c @@ -0,0 +1,1001 @@ +/* + * Spec file parser + * + * Copyright 1993 Robert J. Amstadt + * Copyright 1995 Martin von Loewis + * Copyright 1995, 1996, 1997, 2004 Alexandre Julliard + * Copyright 1997 Eric Youngdale + * Copyright 1999 Ulrich Weigand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include "winglue.h" +#include "build.h" + +int current_line = 0; + +static char ParseBuffer[512]; +static char TokenBuffer[512]; +static char *ParseNext = ParseBuffer; +static FILE *input_file; + +static const char *separator_chars; +static const char *comment_chars; + +static const char * const TypeNames[TYPE_NBTYPES] = +{ + "variable", /* TYPE_VARIABLE */ + "pascal", /* TYPE_PASCAL */ + "equate", /* TYPE_ABS */ + "stub", /* TYPE_STUB */ + "stdcall", /* TYPE_STDCALL */ + "cdecl", /* TYPE_CDECL */ + "varargs", /* TYPE_VARARGS */ + "extern" /* TYPE_EXTERN */ +}; + +static const char * const FlagNames[] = +{ + "norelay", /* FLAG_NORELAY */ + "noname", /* FLAG_NONAME */ + "ret16", /* FLAG_RET16 */ + "ret64", /* FLAG_RET64 */ + "i386", /* FLAG_I386 */ + "register", /* FLAG_REGISTER */ + "private", /* FLAG_PRIVATE */ + NULL +}; + +static int IsNumberString(const char *s) +{ + while (*s) if (!isdigit(*s++)) return 0; + return 1; +} + +inline static int is_token_separator( char ch ) +{ + return strchr( separator_chars, ch ) != NULL; +} + +inline static int is_token_comment( char ch ) +{ + return strchr( comment_chars, ch ) != NULL; +} + +/* get the next line from the input file, or return 0 if at eof */ +static int get_next_line(void) +{ + ParseNext = ParseBuffer; + current_line++; + return (fgets(ParseBuffer, sizeof(ParseBuffer), input_file) != NULL); +} + +static const char * GetToken( int allow_eol ) +{ + char *p = ParseNext; + char *token = TokenBuffer; + + for (;;) + { + /* remove initial white space */ + p = ParseNext; + while (isspace(*p)) p++; + + if (*p == '\\' && p[1] == '\n') /* line continuation */ + { + if (!get_next_line()) + { + if (!allow_eol) error( "Unexpected end of file\n" ); + return NULL; + } + } + else break; + } + + if ((*p == '\0') || is_token_comment(*p)) + { + if (!allow_eol) error( "Declaration not terminated properly\n" ); + return NULL; + } + + /* + * Find end of token. + */ + if (is_token_separator(*p)) + { + /* a separator is always a complete token */ + *token++ = *p++; + } + else while (*p != '\0' && !is_token_separator(*p) && !isspace(*p)) + { + if (*p == '\\') p++; + if (*p) *token++ = *p++; + } + *token = '\0'; + ParseNext = p; + return TokenBuffer; +} + + +static ORDDEF *add_entry_point( DLLSPEC *spec ) +{ + if (spec->nb_entry_points == spec->alloc_entry_points) + { + spec->alloc_entry_points += 128; + spec->entry_points = xrealloc( spec->entry_points, + spec->alloc_entry_points * sizeof(*spec->entry_points) ); + } + return &spec->entry_points[spec->nb_entry_points++]; +} + +/******************************************************************* + * parse_spec_variable + * + * Parse a variable definition in a .spec file. + */ +static int parse_spec_variable( ORDDEF *odp, DLLSPEC *spec ) +{ + char *endptr; + int *value_array; + int n_values; + int value_array_size; + const char *token; + + if (spec->type == SPEC_WIN32) + { + error( "'variable' not supported in Win32, use 'extern' instead\n" ); + return 0; + } + + if (!(token = GetToken(0))) return 0; + if (*token != '(') + { + error( "Expected '(' got '%s'\n", token ); + return 0; + } + + n_values = 0; + value_array_size = 25; + value_array = xmalloc(sizeof(*value_array) * value_array_size); + + for (;;) + { + if (!(token = GetToken(0))) + { + free( value_array ); + return 0; + } + if (*token == ')') + break; + + value_array[n_values++] = strtol(token, &endptr, 0); + if (n_values == value_array_size) + { + value_array_size += 25; + value_array = xrealloc(value_array, + sizeof(*value_array) * value_array_size); + } + + if (endptr == NULL || *endptr != '\0') + { + error( "Expected number value, got '%s'\n", token ); + free( value_array ); + return 0; + } + } + + odp->u.var.n_values = n_values; + odp->u.var.values = xrealloc(value_array, sizeof(*value_array) * n_values); + return 1; +} + + +/******************************************************************* + * parse_spec_export + * + * Parse an exported function definition in a .spec file. + */ +static int parse_spec_export( ORDDEF *odp, DLLSPEC *spec ) +{ + const char *token; + unsigned int i; + + switch(spec->type) + { + case SPEC_WIN16: + if (odp->type == TYPE_STDCALL) + { + error( "'stdcall' not supported for Win16\n" ); + return 0; + } + break; + case SPEC_WIN32: + if (odp->type == TYPE_PASCAL) + { + error( "'pascal' not supported for Win32\n" ); + return 0; + } + break; + default: + break; + } + + if (!(token = GetToken(0))) return 0; + if (*token != '(') + { + error( "Expected '(' got '%s'\n", token ); + return 0; + } + + for (i = 0; i < sizeof(odp->u.func.arg_types); i++) + { + if (!(token = GetToken(0))) return 0; + if (*token == ')') + break; + + if (!strcmp(token, "word")) + odp->u.func.arg_types[i] = 'w'; + else if (!strcmp(token, "s_word")) + odp->u.func.arg_types[i] = 's'; + else if (!strcmp(token, "long") || !strcmp(token, "segptr")) + odp->u.func.arg_types[i] = 'l'; + else if (!strcmp(token, "ptr")) + odp->u.func.arg_types[i] = 'p'; + else if (!strcmp(token, "str")) + odp->u.func.arg_types[i] = 't'; + else if (!strcmp(token, "wstr")) + odp->u.func.arg_types[i] = 'W'; + else if (!strcmp(token, "segstr")) + odp->u.func.arg_types[i] = 'T'; + else if (!strcmp(token, "double")) + { + odp->u.func.arg_types[i++] = 'l'; + if (i < sizeof(odp->u.func.arg_types)) odp->u.func.arg_types[i] = 'l'; + } + else + { + error( "Unknown argument type '%s'\n", token ); + return 0; + } + + if (spec->type == SPEC_WIN32) + { + if (strcmp(token, "long") && + strcmp(token, "ptr") && + strcmp(token, "str") && + strcmp(token, "wstr") && + strcmp(token, "double")) + { + error( "Type '%s' not supported for Win32\n", token ); + return 0; + } + } + } + if ((*token != ')') || (i >= sizeof(odp->u.func.arg_types))) + { + error( "Too many arguments\n" ); + return 0; + } + + odp->u.func.arg_types[i] = '\0'; + if (odp->type == TYPE_VARARGS) + odp->flags |= FLAG_NORELAY; /* no relay debug possible for varags entry point */ + + if (!(token = GetToken(1))) + { + if (!strcmp( odp->name, "@" )) + { + error( "Missing handler name for anonymous function\n" ); + return 0; + } + odp->link_name = xstrdup( odp->name ); + } + else + { + odp->link_name = xstrdup( token ); + if (strchr( odp->link_name, '.' )) + { + if (spec->type == SPEC_WIN16) + { + error( "Forwarded functions not supported for Win16\n" ); + return 0; + } + odp->flags |= FLAG_FORWARD; + } + } + return 1; +} + + +/******************************************************************* + * parse_spec_equate + * + * Parse an 'equate' definition in a .spec file. + */ +static int parse_spec_equate( ORDDEF *odp, DLLSPEC *spec ) +{ + char *endptr; + int value; + const char *token; + + if (spec->type == SPEC_WIN32) + { + error( "'equate' not supported for Win32\n" ); + return 0; + } + if (!(token = GetToken(0))) return 0; + value = strtol(token, &endptr, 0); + if (endptr == NULL || *endptr != '\0') + { + error( "Expected number value, got '%s'\n", token ); + return 0; + } + odp->u.abs.value = value; + return 1; +} + + +/******************************************************************* + * parse_spec_stub + * + * Parse a 'stub' definition in a .spec file + */ +static int parse_spec_stub( ORDDEF *odp, DLLSPEC *spec ) +{ + odp->u.func.arg_types[0] = '\0'; + odp->link_name = xstrdup(""); + return 1; +} + + +/******************************************************************* + * parse_spec_extern + * + * Parse an 'extern' definition in a .spec file. + */ +static int parse_spec_extern( ORDDEF *odp, DLLSPEC *spec ) +{ + const char *token; + + if (spec->type == SPEC_WIN16) + { + error( "'extern' not supported for Win16, use 'variable' instead\n" ); + return 0; + } + if (!(token = GetToken(1))) + { + if (!strcmp( odp->name, "@" )) + { + error( "Missing handler name for anonymous extern\n" ); + return 0; + } + odp->link_name = xstrdup( odp->name ); + } + else + { + odp->link_name = xstrdup( token ); + if (strchr( odp->link_name, '.' )) odp->flags |= FLAG_FORWARD; + } + return 1; +} + + +/******************************************************************* + * parse_spec_flags + * + * Parse the optional flags for an entry point in a .spec file. + */ +static const char *parse_spec_flags( ORDDEF *odp ) +{ + unsigned int i; + const char *token; + + do + { + if (!(token = GetToken(0))) break; + for (i = 0; FlagNames[i]; i++) + if (!strcmp( FlagNames[i], token )) break; + if (!FlagNames[i]) + { + error( "Unknown flag '%s'\n", token ); + return NULL; + } + odp->flags |= 1 << i; + token = GetToken(0); + } while (token && *token == '-'); + + return token; +} + + +/******************************************************************* + * parse_spec_ordinal + * + * Parse an ordinal definition in a .spec file. + */ +static int parse_spec_ordinal( int ordinal, DLLSPEC *spec ) +{ + const char *token; + + ORDDEF *odp = add_entry_point( spec ); + memset( odp, 0, sizeof(*odp) ); + + if (!(token = GetToken(0))) goto error; + + for (odp->type = 0; odp->type < TYPE_NBTYPES; odp->type++) + if (TypeNames[odp->type] && !strcmp( token, TypeNames[odp->type] )) + break; + + if (odp->type >= TYPE_NBTYPES) + { + error( "Expected type after ordinal, found '%s' instead\n", token ); + goto error; + } + + if (!(token = GetToken(0))) goto error; + if (*token == '-' && !(token = parse_spec_flags( odp ))) goto error; + + odp->name = xstrdup( token ); + odp->lineno = current_line; + odp->ordinal = ordinal; + + switch(odp->type) + { + case TYPE_VARIABLE: + if (!parse_spec_variable( odp, spec )) goto error; + break; + case TYPE_PASCAL: + case TYPE_STDCALL: + case TYPE_VARARGS: + case TYPE_CDECL: + if (!parse_spec_export( odp, spec )) goto error; + break; + case TYPE_ABS: + if (!parse_spec_equate( odp, spec )) goto error; + break; + case TYPE_STUB: + if (!parse_spec_stub( odp, spec )) goto error; + break; + case TYPE_EXTERN: + if (!parse_spec_extern( odp, spec )) goto error; + break; + default: + assert( 0 ); + } + +#ifndef __i386__ + if (odp->flags & FLAG_I386) + { + /* ignore this entry point on non-Intel archs */ + spec->nb_entry_points--; + return 1; + } +#endif + + if (ordinal != -1) + { + if (!ordinal) + { + error( "Ordinal 0 is not valid\n" ); + goto error; + } + if (ordinal >= MAX_ORDINALS) + { + error( "Ordinal number %d too large\n", ordinal ); + goto error; + } + if (ordinal > spec->limit) spec->limit = ordinal; + if (ordinal < spec->base) spec->base = ordinal; + odp->ordinal = ordinal; + } + + if (odp->type == TYPE_STDCALL && !(odp->flags & FLAG_PRIVATE)) + { + if (!strcmp( odp->name, "DllRegisterServer" ) || + !strcmp( odp->name, "DllUnregisterServer" ) || + !strcmp( odp->name, "DllGetClassObject" ) || + !strcmp( odp->name, "DllCanUnloadNow" )) + { + warning( "Function %s should be marked private\n", odp->name ); + } + } + + if (!strcmp( odp->name, "@" ) || odp->flags & FLAG_NONAME) + { + if (ordinal == -1) + { + error( "Nameless function needs an explicit ordinal number\n" ); + goto error; + } + if (spec->type != SPEC_WIN32) + { + error( "Nameless functions not supported for Win16\n" ); + goto error; + } + if (!strcmp( odp->name, "@" )) free( odp->name ); + else odp->export_name = odp->name; + odp->name = NULL; + } + return 1; + +error: + spec->nb_entry_points--; + free( odp->name ); + return 0; +} + + +static int name_compare( const void *name1, const void *name2 ) +{ + const ORDDEF *odp1 = *(const ORDDEF * const *)name1; + const ORDDEF *odp2 = *(const ORDDEF * const *)name2; + return strcmp( odp1->name, odp2->name ); +} + +/******************************************************************* + * assign_names + * + * Build the name array and catch duplicates. + */ +static void assign_names( DLLSPEC *spec ) +{ + int i, j; + + spec->nb_names = 0; + for (i = 0; i < spec->nb_entry_points; i++) + if (spec->entry_points[i].name) spec->nb_names++; + if (!spec->nb_names) return; + + spec->names = xmalloc( spec->nb_names * sizeof(spec->names[0]) ); + for (i = j = 0; i < spec->nb_entry_points; i++) + if (spec->entry_points[i].name) spec->names[j++] = &spec->entry_points[i]; + + /* sort the list of names */ + qsort( spec->names, spec->nb_names, sizeof(spec->names[0]), name_compare ); + + /* check for duplicate names */ + for (i = 0; i < spec->nb_names - 1; i++) + { + if (!strcmp( spec->names[i]->name, spec->names[i+1]->name )) + { + current_line = max( spec->names[i]->lineno, spec->names[i+1]->lineno ); + error( "'%s' redefined\n%s:%d: First defined here\n", + spec->names[i]->name, input_file_name, + min( spec->names[i]->lineno, spec->names[i+1]->lineno ) ); + } + } +} + +/******************************************************************* + * assign_ordinals + * + * Build the ordinal array. + */ +static void assign_ordinals( DLLSPEC *spec ) +{ + int i, count, ordinal; + + /* start assigning from base, or from 1 if no ordinal defined yet */ + if (spec->base == MAX_ORDINALS) spec->base = 1; + if (spec->limit < spec->base) spec->limit = spec->base; + + count = max( spec->limit + 1, spec->base + spec->nb_entry_points ); + spec->ordinals = xmalloc( count * sizeof(spec->ordinals[0]) ); + memset( spec->ordinals, 0, count * sizeof(spec->ordinals[0]) ); + + /* fill in all explicitly specified ordinals */ + for (i = 0; i < spec->nb_entry_points; i++) + { + ordinal = spec->entry_points[i].ordinal; + if (ordinal == -1) continue; + if (spec->ordinals[ordinal]) + { + current_line = max( spec->entry_points[i].lineno, spec->ordinals[ordinal]->lineno ); + error( "ordinal %d redefined\n%s:%d: First defined here\n", + ordinal, input_file_name, + min( spec->entry_points[i].lineno, spec->ordinals[ordinal]->lineno ) ); + } + else spec->ordinals[ordinal] = &spec->entry_points[i]; + } + + /* now assign ordinals to the rest */ + for (i = 0, ordinal = spec->base; i < spec->nb_names; i++) + { + if (spec->names[i]->ordinal != -1) continue; /* already has an ordinal */ + while (spec->ordinals[ordinal]) ordinal++; + if (ordinal >= MAX_ORDINALS) + { + current_line = spec->names[i]->lineno; + fatal_error( "Too many functions defined (max %d)\n", MAX_ORDINALS ); + } + spec->names[i]->ordinal = ordinal; + spec->ordinals[ordinal] = spec->names[i]; + } + if (ordinal > spec->limit) spec->limit = ordinal; +} + + +/******************************************************************* + * parse_spec_file + * + * Parse a .spec file. + */ +int parse_spec_file( FILE *file, DLLSPEC *spec ) +{ + const char *token; + + input_file = file; + current_line = 0; + + comment_chars = "#;"; + separator_chars = "()-"; + + while (get_next_line()) + { + if (!(token = GetToken(1))) continue; + if (strcmp(token, "@") == 0) + { + if (spec->type != SPEC_WIN32) + { + error( "'@' ordinals not supported for Win16\n" ); + continue; + } + if (!parse_spec_ordinal( -1, spec )) continue; + } + else if (IsNumberString(token)) + { + if (!parse_spec_ordinal( atoi(token), spec )) continue; + } + else + { + error( "Expected ordinal declaration, got '%s'\n", token ); + continue; + } + if ((token = GetToken(1))) error( "Syntax error near '%s'\n", token ); + } + + current_line = 0; /* no longer parsing the input file */ + assign_names( spec ); + assign_ordinals( spec ); + return !nb_errors; +} + + +/******************************************************************* + * parse_def_library + * + * Parse a LIBRARY declaration in a .def file. + */ +static int parse_def_library( DLLSPEC *spec ) +{ + const char *token = GetToken(1); + + if (!token) return 1; + if (strcmp( token, "BASE" )) + { + free( spec->file_name ); + spec->file_name = xstrdup( token ); + if (!(token = GetToken(1))) return 1; + } + if (strcmp( token, "BASE" )) + { + error( "Expected library name or BASE= declaration, got '%s'\n", token ); + return 0; + } + if (!(token = GetToken(0))) return 0; + if (strcmp( token, "=" )) + { + error( "Expected '=' after BASE, got '%s'\n", token ); + return 0; + } + if (!(token = GetToken(0))) return 0; + /* FIXME: do something with base address */ + + return 1; +} + + +/******************************************************************* + * parse_def_stack_heap_size + * + * Parse a STACKSIZE or HEAPSIZE declaration in a .def file. + */ +static int parse_def_stack_heap_size( int is_stack, DLLSPEC *spec ) +{ + const char *token = GetToken(0); + char *end; + unsigned long size; + + if (!token) return 0; + size = strtoul( token, &end, 0 ); + if (*end) + { + error( "Invalid number '%s'\n", token ); + return 0; + } + if (is_stack) spec->stack_size = size / 1024; + else spec->heap_size = size / 1024; + if (!(token = GetToken(1))) return 1; + if (strcmp( token, "," )) + { + error( "Expected ',' after size, got '%s'\n", token ); + return 0; + } + if (!(token = GetToken(0))) return 0; + /* FIXME: do something with reserve size */ + return 1; +} + + +/******************************************************************* + * parse_def_export + * + * Parse an export declaration in a .def file. + */ +static int parse_def_export( char *name, DLLSPEC *spec ) +{ + int i, args; + const char *token = GetToken(1); + + ORDDEF *odp = add_entry_point( spec ); + memset( odp, 0, sizeof(*odp) ); + + odp->lineno = current_line; + odp->ordinal = -1; + odp->name = name; + args = remove_stdcall_decoration( odp->name ); + if (args == -1) odp->type = TYPE_CDECL; + else + { + odp->type = TYPE_STDCALL; + args /= sizeof(int); + if (args >= sizeof(odp->u.func.arg_types)) + { + error( "Too many arguments in stdcall function '%s'\n", odp->name ); + return 0; + } + for (i = 0; i < args; i++) odp->u.func.arg_types[i] = 'l'; + } + + /* check for optional internal name */ + + if (token && !strcmp( token, "=" )) + { + if (!(token = GetToken(0))) goto error; + odp->link_name = xstrdup( token ); + remove_stdcall_decoration( odp->link_name ); + token = GetToken(1); + } + else + { + odp->link_name = xstrdup( name ); + } + + /* check for optional ordinal */ + + if (token && token[0] == '@') + { + int ordinal; + + if (!IsNumberString( token+1 )) + { + error( "Expected number after '@', got '%s'\n", token+1 ); + goto error; + } + ordinal = atoi( token+1 ); + if (!ordinal) + { + error( "Ordinal 0 is not valid\n" ); + goto error; + } + if (ordinal >= MAX_ORDINALS) + { + error( "Ordinal number %d too large\n", ordinal ); + goto error; + } + if (ordinal > spec->limit) spec->limit = ordinal; + if (ordinal < spec->base) spec->base = ordinal; + odp->ordinal = ordinal; + token = GetToken(1); + } + + /* check for other optional keywords */ + + if (token && !strcmp( token, "NONAME" )) + { + if (odp->ordinal == -1) + { + error( "NONAME requires an ordinal\n" ); + goto error; + } + odp->export_name = odp->name; + odp->name = NULL; + odp->flags |= FLAG_NONAME; + token = GetToken(1); + } + if (token && !strcmp( token, "PRIVATE" )) + { + odp->flags |= FLAG_PRIVATE; + token = GetToken(1); + } + if (token && !strcmp( token, "DATA" )) + { + odp->type = TYPE_EXTERN; + token = GetToken(1); + } + if (token) + { + error( "Garbage text '%s' found at end of export declaration\n", token ); + goto error; + } + return 1; + +error: + spec->nb_entry_points--; + free( odp->name ); + return 0; +} + + +/******************************************************************* + * parse_def_file + * + * Parse a .def file. + */ +int parse_def_file( FILE *file, DLLSPEC *spec ) +{ + const char *token; + int in_exports = 0; + + input_file = file; + current_line = 0; + + comment_chars = ";"; + separator_chars = ",="; + + while (get_next_line()) + { + if (!(token = GetToken(1))) continue; + + if (!strcmp( token, "LIBRARY" ) || !strcmp( token, "NAME" )) + { + if (!parse_def_library( spec )) continue; + goto end_of_line; + } + else if (!strcmp( token, "STACKSIZE" )) + { + if (!parse_def_stack_heap_size( 1, spec )) continue; + goto end_of_line; + } + else if (!strcmp( token, "HEAPSIZE" )) + { + if (!parse_def_stack_heap_size( 0, spec )) continue; + goto end_of_line; + } + else if (!strcmp( token, "EXPORTS" )) + { + in_exports = 1; + if (!(token = GetToken(1))) continue; + } + else if (!strcmp( token, "IMPORTS" )) + { + in_exports = 0; + if (!(token = GetToken(1))) continue; + } + else if (!strcmp( token, "SECTIONS" )) + { + in_exports = 0; + if (!(token = GetToken(1))) continue; + } + + if (!in_exports) continue; /* ignore this line */ + if (!parse_def_export( xstrdup(token), spec )) continue; + + end_of_line: + if ((token = GetToken(1))) error( "Syntax error near '%s'\n", token ); + } + + current_line = 0; /* no longer parsing the input file */ + assign_names( spec ); + assign_ordinals( spec ); + return !nb_errors; +} + + +/******************************************************************* + * add_debug_channel + */ +static void add_debug_channel( const char *name ) +{ + int i; + + for (i = 0; i < nb_debug_channels; i++) + if (!strcmp( debug_channels[i], name )) return; + + debug_channels = xrealloc( debug_channels, (nb_debug_channels + 1) * sizeof(*debug_channels)); + debug_channels[nb_debug_channels++] = xstrdup(name); +} + + +/******************************************************************* + * parse_debug_channels + * + * Parse a source file and extract the debug channel definitions. + */ +int parse_debug_channels( const char *srcdir, const char *filename ) +{ + FILE *file; + int eol_seen = 1; + + file = open_input_file( srcdir, filename ); + while (fgets( ParseBuffer, sizeof(ParseBuffer), file )) + { + char *channel, *end, *p = ParseBuffer; + + p = ParseBuffer + strlen(ParseBuffer) - 1; + if (!eol_seen) /* continuation line */ + { + eol_seen = (*p == '\n'); + continue; + } + if ((eol_seen = (*p == '\n'))) *p = 0; + + p = ParseBuffer; + while (isspace(*p)) p++; + if (!memcmp( p, "WINE_DECLARE_DEBUG_CHANNEL", 26 ) || + !memcmp( p, "WINE_DEFAULT_DEBUG_CHANNEL", 26 )) + { + p += 26; + while (isspace(*p)) p++; + if (*p != '(') + { + error( "invalid debug channel specification '%s'\n", ParseBuffer ); + goto next; + } + p++; + while (isspace(*p)) p++; + if (!isalpha(*p)) + { + error( "invalid debug channel specification '%s'\n", ParseBuffer ); + goto next; + } + channel = p; + while (isalnum(*p) || *p == '_') p++; + end = p; + while (isspace(*p)) p++; + if (*p != ')') + { + error( "invalid debug channel specification '%s'\n", ParseBuffer ); + goto next; + } + *end = 0; + add_debug_channel( channel ); + } + next: + current_line++; + } + close_input_file( file ); + return !nb_errors; +} diff --git a/reactos/tools/winebuild/relay.c b/reactos/tools/winebuild/relay.c new file mode 100644 index 00000000000..b43f5a09633 --- /dev/null +++ b/reactos/tools/winebuild/relay.c @@ -0,0 +1,1208 @@ +/* + * Relay calls helper routines + * + * Copyright 1993 Robert J. Amstadt + * Copyright 1995 Martin von Loewis + * Copyright 1995, 1996, 1997 Alexandre Julliard + * Copyright 1997 Eric Youngdale + * Copyright 1999 Ulrich Weigand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#include + +#include "thread.h" +#include "stackframe.h" + +#include "build.h" + +#ifdef __i386__ + +static void function_header( FILE *outfile, const char *name ) +{ + fprintf( outfile, "\n\t.align %d\n", get_alignment(4) ); + fprintf( outfile, "\t" __ASM_FUNC("%s") "\n", name ); + fprintf( outfile, "\t.globl " __ASM_NAME("%s") "\n", name ); + fprintf( outfile, __ASM_NAME("%s") ":\n", name ); +} + + +static void function_footer( FILE *outfile, const char *name ) +{ + fprintf( outfile, ".size " __ASM_NAME("%s") ", . - " __ASM_NAME("%s") "\n", name, name ); +} + +/******************************************************************* + * BuildCallFrom16Core + * + * This routine builds the core routines used in 16->32 thunks: + * CallFrom16Word, CallFrom16Long, CallFrom16Register, and CallFrom16Thunk. + * + * These routines are intended to be called via a far call (with 32-bit + * operand size) from 16-bit code. The 16-bit code stub must push %bp, + * the 32-bit entry point to be called, and the argument conversion + * routine to be used (see stack layout below). + * + * The core routine completes the STACK16FRAME on the 16-bit stack and + * switches to the 32-bit stack. Then, the argument conversion routine + * is called; it gets passed the 32-bit entry point and a pointer to the + * 16-bit arguments (on the 16-bit stack) as parameters. (You can either + * use conversion routines automatically generated by BuildCallFrom16, + * or write your own for special purposes.) + * + * The conversion routine must call the 32-bit entry point, passing it + * the converted arguments, and return its return value to the core. + * After the conversion routine has returned, the core switches back + * to the 16-bit stack, converts the return value to the DX:AX format + * (CallFrom16Long), and returns to the 16-bit call stub. All parameters, + * including %bp, are popped off the stack. + * + * The 16-bit call stub now returns to the caller, popping the 16-bit + * arguments if necessary (pascal calling convention). + * + * In the case of a 'register' function, CallFrom16Register fills a + * CONTEXT86 structure with the values all registers had at the point + * the first instruction of the 16-bit call stub was about to be + * executed. A pointer to this CONTEXT86 is passed as third parameter + * to the argument conversion routine, which typically passes it on + * to the called 32-bit entry point. + * + * CallFrom16Thunk is a special variant used by the implementation of + * the Win95 16->32 thunk functions C16ThkSL and C16ThkSL01 and is + * implemented as follows: + * On entry, the EBX register is set up to contain a flat pointer to the + * 16-bit stack such that EBX+22 points to the first argument. + * Then, the entry point is called, while EBP is set up to point + * to the return address (on the 32-bit stack). + * The called function returns with CX set to the number of bytes + * to be popped of the caller's stack. + * + * Stack layout upon entry to the core routine (STACK16FRAME): + * ... ... + * (sp+24) word first 16-bit arg + * (sp+22) word cs + * (sp+20) word ip + * (sp+18) word bp + * (sp+14) long 32-bit entry point (reused for Win16 mutex recursion count) + * (sp+12) word ip of actual entry point (necessary for relay debugging) + * (sp+8) long relay (argument conversion) function entry point + * (sp+4) long cs of 16-bit entry point + * (sp) long ip of 16-bit entry point + * + * Added on the stack: + * (sp-2) word saved gs + * (sp-4) word saved fs + * (sp-6) word saved es + * (sp-8) word saved ds + * (sp-12) long saved ebp + * (sp-16) long saved ecx + * (sp-20) long saved edx + * (sp-24) long saved previous stack + */ +static void BuildCallFrom16Core( FILE *outfile, int reg_func, int thunk, int short_ret ) +{ + const char *name = thunk? "thunk" : reg_func? "regs" : short_ret? "word" : "long"; + + /* Function header */ + if (thunk) function_header( outfile, "__wine_call_from_16_thunk" ); + else if (reg_func) function_header( outfile, "__wine_call_from_16_regs" ); + else if (short_ret) function_header( outfile, "__wine_call_from_16_word" ); + else function_header( outfile, "__wine_call_from_16_long" ); + + /* Create STACK16FRAME (except STACK32FRAME link) */ + fprintf( outfile, "\tpushw %%gs\n" ); + fprintf( outfile, "\tpushw %%fs\n" ); + fprintf( outfile, "\tpushw %%es\n" ); + fprintf( outfile, "\tpushw %%ds\n" ); + fprintf( outfile, "\tpushl %%ebp\n" ); + fprintf( outfile, "\tpushl %%ecx\n" ); + fprintf( outfile, "\tpushl %%edx\n" ); + + /* Save original EFlags register */ + fprintf( outfile, "\tpushfl\n" ); + + if ( UsePIC ) + { + /* Get Global Offset Table into %ecx */ + fprintf( outfile, "\tcall .L__wine_call_from_16_%s.getgot1\n", name ); + fprintf( outfile, ".L__wine_call_from_16_%s.getgot1:\n", name ); + fprintf( outfile, "\tpopl %%ecx\n" ); + fprintf( outfile, "\taddl $_GLOBAL_OFFSET_TABLE_+[.-.L__wine_call_from_16_%s.getgot1], %%ecx\n", name ); + } + + if (UsePIC) + { + fprintf( outfile, "\t.byte 0x2e\n\tmovl " __ASM_NAME("CallTo16_DataSelector@GOT") "(%%ecx), %%edx\n" ); + fprintf( outfile, "\t.byte 0x2e\n\tmovl (%%edx), %%edx\n" ); + } + else + fprintf( outfile, "\t.byte 0x2e\n\tmovl " __ASM_NAME("CallTo16_DataSelector") ",%%edx\n" ); + + /* Load 32-bit segment registers */ +#ifdef __svr4__ + fprintf( outfile, "\tdata16\n"); +#endif + fprintf( outfile, "\tmovw %%dx, %%ds\n" ); +#ifdef __svr4__ + fprintf( outfile, "\tdata16\n"); +#endif + fprintf( outfile, "\tmovw %%dx, %%es\n" ); + + if ( UsePIC ) + { + fprintf( outfile, "\tmovl " __ASM_NAME("CallTo16_TebSelector@GOT") "(%%ecx), %%edx\n" ); + fprintf( outfile, "\tmovw (%%edx), %%fs\n" ); + } + else + fprintf( outfile, "\tmovw " __ASM_NAME("CallTo16_TebSelector") ", %%fs\n" ); + + fprintf( outfile, "\t.byte 0x64\n\tmovl (%d),%%gs\n", STRUCTOFFSET(TEB,gs_sel) ); + + /* Get address of wine_ldt_copy array into %ecx */ + if ( UsePIC ) + fprintf( outfile, "\tmovl " __ASM_NAME("wine_ldt_copy@GOT") "(%%ecx), %%ecx\n" ); + else + fprintf( outfile, "\tmovl $" __ASM_NAME("wine_ldt_copy") ", %%ecx\n" ); + + /* Translate STACK16FRAME base to flat offset in %edx */ + fprintf( outfile, "\tmovw %%ss, %%dx\n" ); + fprintf( outfile, "\tandl $0xfff8, %%edx\n" ); + fprintf( outfile, "\tshrl $1, %%edx\n" ); + fprintf( outfile, "\tmovl (%%ecx,%%edx), %%edx\n" ); + fprintf( outfile, "\tmovzwl %%sp, %%ebp\n" ); + fprintf( outfile, "\tleal (%%ebp,%%edx), %%edx\n" ); + + /* Get saved flags into %ecx */ + fprintf( outfile, "\tpopl %%ecx\n" ); + + /* Get the 32-bit stack pointer from the TEB and complete STACK16FRAME */ + fprintf( outfile, "\t.byte 0x64\n\tmovl (%d), %%ebp\n", STACKOFFSET ); + fprintf( outfile, "\tpushl %%ebp\n" ); + + /* Switch stacks */ +#ifdef __svr4__ + fprintf( outfile,"\tdata16\n"); +#endif + fprintf( outfile, "\t.byte 0x64\n\tmovw %%ss, (%d)\n", STACKOFFSET + 2 ); + fprintf( outfile, "\t.byte 0x64\n\tmovw %%sp, (%d)\n", STACKOFFSET ); + fprintf( outfile, "\tpushl %%ds\n" ); + fprintf( outfile, "\tpopl %%ss\n" ); + fprintf( outfile, "\tmovl %%ebp, %%esp\n" ); + fprintf( outfile, "\taddl $%d, %%ebp\n", STRUCTOFFSET(STACK32FRAME, ebp) ); + + + /* At this point: + STACK16FRAME is completely set up + DS, ES, SS: flat data segment + FS: current TEB + ESP: points to last STACK32FRAME + EBP: points to ebp member of last STACK32FRAME + EDX: points to current STACK16FRAME + ECX: contains saved flags + all other registers: unchanged */ + + /* Special case: C16ThkSL stub */ + if ( thunk ) + { + /* Set up registers as expected and call thunk */ + fprintf( outfile, "\tleal %d(%%edx), %%ebx\n", sizeof(STACK16FRAME)-22 ); + fprintf( outfile, "\tleal -4(%%esp), %%ebp\n" ); + + fprintf( outfile, "\tcall *%d(%%edx)\n", STACK16OFFSET(entry_point) ); + + /* Switch stack back */ + fprintf( outfile, "\t.byte 0x64\n\tmovw (%d), %%ss\n", STACKOFFSET+2 ); + fprintf( outfile, "\t.byte 0x64\n\tmovzwl (%d), %%esp\n", STACKOFFSET ); + fprintf( outfile, "\t.byte 0x64\n\tpopl (%d)\n", STACKOFFSET ); + + /* Restore registers and return directly to caller */ + fprintf( outfile, "\taddl $8, %%esp\n" ); + fprintf( outfile, "\tpopl %%ebp\n" ); + fprintf( outfile, "\tpopw %%ds\n" ); + fprintf( outfile, "\tpopw %%es\n" ); + fprintf( outfile, "\tpopw %%fs\n" ); + fprintf( outfile, "\tpopw %%gs\n" ); + fprintf( outfile, "\taddl $20, %%esp\n" ); + + fprintf( outfile, "\txorb %%ch, %%ch\n" ); + fprintf( outfile, "\tpopl %%ebx\n" ); + fprintf( outfile, "\taddw %%cx, %%sp\n" ); + fprintf( outfile, "\tpush %%ebx\n" ); + + fprintf( outfile, "\t.byte 0x66\n" ); + fprintf( outfile, "\tlret\n" ); + + return; + } + + + /* Build register CONTEXT */ + if ( reg_func ) + { + fprintf( outfile, "\tsubl $%d, %%esp\n", sizeof(CONTEXT86) ); + + fprintf( outfile, "\tmovl %%ecx, %d(%%esp)\n", CONTEXTOFFSET(EFlags) ); + + fprintf( outfile, "\tmovl %%eax, %d(%%esp)\n", CONTEXTOFFSET(Eax) ); + fprintf( outfile, "\tmovl %%ebx, %d(%%esp)\n", CONTEXTOFFSET(Ebx) ); + fprintf( outfile, "\tmovl %%esi, %d(%%esp)\n", CONTEXTOFFSET(Esi) ); + fprintf( outfile, "\tmovl %%edi, %d(%%esp)\n", CONTEXTOFFSET(Edi) ); + + fprintf( outfile, "\tmovl %d(%%edx), %%eax\n", STACK16OFFSET(ebp) ); + fprintf( outfile, "\tmovl %%eax, %d(%%esp)\n", CONTEXTOFFSET(Ebp) ); + fprintf( outfile, "\tmovl %d(%%edx), %%eax\n", STACK16OFFSET(ecx) ); + fprintf( outfile, "\tmovl %%eax, %d(%%esp)\n", CONTEXTOFFSET(Ecx) ); + fprintf( outfile, "\tmovl %d(%%edx), %%eax\n", STACK16OFFSET(edx) ); + fprintf( outfile, "\tmovl %%eax, %d(%%esp)\n", CONTEXTOFFSET(Edx) ); + + fprintf( outfile, "\tmovzwl %d(%%edx), %%eax\n", STACK16OFFSET(ds) ); + fprintf( outfile, "\tmovl %%eax, %d(%%esp)\n", CONTEXTOFFSET(SegDs) ); + fprintf( outfile, "\tmovzwl %d(%%edx), %%eax\n", STACK16OFFSET(es) ); + fprintf( outfile, "\tmovl %%eax, %d(%%esp)\n", CONTEXTOFFSET(SegEs) ); + fprintf( outfile, "\tmovzwl %d(%%edx), %%eax\n", STACK16OFFSET(fs) ); + fprintf( outfile, "\tmovl %%eax, %d(%%esp)\n", CONTEXTOFFSET(SegFs) ); + fprintf( outfile, "\tmovzwl %d(%%edx), %%eax\n", STACK16OFFSET(gs) ); + fprintf( outfile, "\tmovl %%eax, %d(%%esp)\n", CONTEXTOFFSET(SegGs) ); + + fprintf( outfile, "\tmovzwl %d(%%edx), %%eax\n", STACK16OFFSET(cs) ); + fprintf( outfile, "\tmovl %%eax, %d(%%esp)\n", CONTEXTOFFSET(SegCs) ); + fprintf( outfile, "\tmovzwl %d(%%edx), %%eax\n", STACK16OFFSET(ip) ); + fprintf( outfile, "\tmovl %%eax, %d(%%esp)\n", CONTEXTOFFSET(Eip) ); + + fprintf( outfile, "\t.byte 0x64\n\tmovzwl (%d), %%eax\n", STACKOFFSET+2 ); + fprintf( outfile, "\tmovl %%eax, %d(%%esp)\n", CONTEXTOFFSET(SegSs) ); + fprintf( outfile, "\t.byte 0x64\n\tmovzwl (%d), %%eax\n", STACKOFFSET ); + fprintf( outfile, "\taddl $%d, %%eax\n", STACK16OFFSET(ip) ); + fprintf( outfile, "\tmovl %%eax, %d(%%esp)\n", CONTEXTOFFSET(Esp) ); +#if 0 + fprintf( outfile, "\tfsave %d(%%esp)\n", CONTEXTOFFSET(FloatSave) ); +#endif + + /* Push address of CONTEXT86 structure -- popped by the relay routine */ + fprintf( outfile, "\tpushl %%esp\n" ); + } + + + /* Print debug info before call */ + if ( debugging ) + { + if ( UsePIC ) + { + fprintf( outfile, "\tpushl %%ebx\n" ); + + /* Get Global Offset Table into %ebx (for PLT call) */ + fprintf( outfile, "\tcall .L__wine_call_from_16_%s.getgot2\n", name ); + fprintf( outfile, ".L__wine_call_from_16_%s.getgot2:\n", name ); + fprintf( outfile, "\tpopl %%ebx\n" ); + fprintf( outfile, "\taddl $_GLOBAL_OFFSET_TABLE_+[.-.L__wine_call_from_16_%s.getgot2], %%ebx\n", name ); + } + + fprintf( outfile, "\tpushl %%edx\n" ); + if ( reg_func ) + fprintf( outfile, "\tleal -%d(%%ebp), %%eax\n\tpushl %%eax\n", + sizeof(CONTEXT) + STRUCTOFFSET(STACK32FRAME, ebp) ); + else + fprintf( outfile, "\tpushl $0\n" ); + + if ( UsePIC ) + fprintf( outfile, "\tcall " __ASM_NAME("RELAY_DebugCallFrom16@PLT") "\n "); + else + fprintf( outfile, "\tcall " __ASM_NAME("RELAY_DebugCallFrom16") "\n "); + + fprintf( outfile, "\tpopl %%edx\n" ); + fprintf( outfile, "\tpopl %%edx\n" ); + + if ( UsePIC ) + fprintf( outfile, "\tpopl %%ebx\n" ); + } + + /* Call relay routine (which will call the API entry point) */ + fprintf( outfile, "\tleal %d(%%edx), %%eax\n", sizeof(STACK16FRAME) ); + fprintf( outfile, "\tpushl %%eax\n" ); + fprintf( outfile, "\tpushl %d(%%edx)\n", STACK16OFFSET(entry_point) ); + fprintf( outfile, "\tcall *%d(%%edx)\n", STACK16OFFSET(relay) ); + + /* Print debug info after call */ + if ( debugging ) + { + if ( UsePIC ) + { + fprintf( outfile, "\tpushl %%ebx\n" ); + + /* Get Global Offset Table into %ebx (for PLT call) */ + fprintf( outfile, "\tcall .L__wine_call_from_16_%s.getgot3\n", name ); + fprintf( outfile, ".L__wine_call_from_16_%s.getgot3:\n", name ); + fprintf( outfile, "\tpopl %%ebx\n" ); + fprintf( outfile, "\taddl $_GLOBAL_OFFSET_TABLE_+[.-.L__wine_call_from_16_%s.getgot3], %%ebx\n", name ); + } + + fprintf( outfile, "\tpushl %%eax\n" ); + if ( reg_func ) + fprintf( outfile, "\tleal -%d(%%ebp), %%eax\n\tpushl %%eax\n", + sizeof(CONTEXT) + STRUCTOFFSET(STACK32FRAME, ebp) ); + else + fprintf( outfile, "\tpushl $0\n" ); + + if ( UsePIC ) + fprintf( outfile, "\tcall " __ASM_NAME("RELAY_DebugCallFrom16Ret@PLT") "\n "); + else + fprintf( outfile, "\tcall " __ASM_NAME("RELAY_DebugCallFrom16Ret") "\n "); + + fprintf( outfile, "\tpopl %%eax\n" ); + fprintf( outfile, "\tpopl %%eax\n" ); + + if ( UsePIC ) + fprintf( outfile, "\tpopl %%ebx\n" ); + } + + + if ( reg_func ) + { + fprintf( outfile, "\tmovl %%esp, %%ebx\n" ); + + /* Switch stack back */ + fprintf( outfile, "\t.byte 0x64\n\tmovw (%d), %%ss\n", STACKOFFSET+2 ); + fprintf( outfile, "\t.byte 0x64\n\tmovzwl (%d), %%esp\n", STACKOFFSET ); + fprintf( outfile, "\t.byte 0x64\n\tpopl (%d)\n", STACKOFFSET ); + + /* Get return address to CallFrom16 stub */ + fprintf( outfile, "\taddw $%d, %%sp\n", STACK16OFFSET(callfrom_ip)-4 ); + fprintf( outfile, "\tpopl %%eax\n" ); + fprintf( outfile, "\tpopl %%edx\n" ); + + /* Restore all registers from CONTEXT */ + fprintf( outfile, "\tmovw %d(%%ebx), %%ss\n", CONTEXTOFFSET(SegSs) ); + fprintf( outfile, "\tmovl %d(%%ebx), %%esp\n", CONTEXTOFFSET(Esp) ); + fprintf( outfile, "\taddl $4, %%esp\n" ); /* room for final return address */ + + fprintf( outfile, "\tpushw %d(%%ebx)\n", CONTEXTOFFSET(SegCs) ); + fprintf( outfile, "\tpushw %d(%%ebx)\n", CONTEXTOFFSET(Eip) ); + fprintf( outfile, "\tpushl %%edx\n" ); + fprintf( outfile, "\tpushl %%eax\n" ); + fprintf( outfile, "\tpushl %d(%%ebx)\n", CONTEXTOFFSET(EFlags) ); + fprintf( outfile, "\tpushl %d(%%ebx)\n", CONTEXTOFFSET(SegDs) ); + + fprintf( outfile, "\tpushl %d(%%ebx)\n", CONTEXTOFFSET(SegEs) ); + fprintf( outfile, "\tpopl %%es\n" ); + fprintf( outfile, "\tpushl %d(%%ebx)\n", CONTEXTOFFSET(SegFs) ); + fprintf( outfile, "\tpopl %%fs\n" ); + fprintf( outfile, "\tpushl %d(%%ebx)\n", CONTEXTOFFSET(SegGs) ); + fprintf( outfile, "\tpopl %%gs\n" ); + + fprintf( outfile, "\tmovl %d(%%ebx), %%ebp\n", CONTEXTOFFSET(Ebp) ); + fprintf( outfile, "\tmovl %d(%%ebx), %%esi\n", CONTEXTOFFSET(Esi) ); + fprintf( outfile, "\tmovl %d(%%ebx), %%edi\n", CONTEXTOFFSET(Edi) ); + fprintf( outfile, "\tmovl %d(%%ebx), %%eax\n", CONTEXTOFFSET(Eax) ); + fprintf( outfile, "\tmovl %d(%%ebx), %%edx\n", CONTEXTOFFSET(Edx) ); + fprintf( outfile, "\tmovl %d(%%ebx), %%ecx\n", CONTEXTOFFSET(Ecx) ); + fprintf( outfile, "\tmovl %d(%%ebx), %%ebx\n", CONTEXTOFFSET(Ebx) ); + + fprintf( outfile, "\tpopl %%ds\n" ); + fprintf( outfile, "\tpopfl\n" ); + fprintf( outfile, "\tlret\n" ); + } + else + { + /* Switch stack back */ + fprintf( outfile, "\t.byte 0x64\n\tmovw (%d), %%ss\n", STACKOFFSET+2 ); + fprintf( outfile, "\t.byte 0x64\n\tmovzwl (%d), %%esp\n", STACKOFFSET ); + fprintf( outfile, "\t.byte 0x64\n\tpopl (%d)\n", STACKOFFSET ); + + /* Restore registers */ + fprintf( outfile, "\tpopl %%edx\n" ); + fprintf( outfile, "\tpopl %%ecx\n" ); + fprintf( outfile, "\tpopl %%ebp\n" ); + fprintf( outfile, "\tpopw %%ds\n" ); + fprintf( outfile, "\tpopw %%es\n" ); + fprintf( outfile, "\tpopw %%fs\n" ); + fprintf( outfile, "\tpopw %%gs\n" ); + + /* Prepare return value and set flags accordingly */ + if ( !short_ret ) + fprintf( outfile, "\tshldl $16, %%eax, %%edx\n" ); + fprintf( outfile, "\torl %%eax, %%eax\n" ); + + /* Return to return stub which will return to caller */ + fprintf( outfile, "\tlret $12\n" ); + } + if (thunk) function_footer( outfile, "__wine_call_from_16_thunk" ); + else if (reg_func) function_footer( outfile, "__wine_call_from_16_regs" ); + else if (short_ret) function_footer( outfile, "__wine_call_from_16_word" ); + else function_footer( outfile, "__wine_call_from_16_long" ); +} + + +/******************************************************************* + * BuildCallTo16Core + * + * This routine builds the core routines used in 32->16 thunks: + * + * extern DWORD WINAPI wine_call_to_16( FARPROC16 target, DWORD cbArgs, PEXCEPTION_HANDLER handler ); + * extern void WINAPI wine_call_to_16_regs( CONTEXT86 *context, DWORD cbArgs, PEXCEPTION_HANDLER handler ); + * + * These routines can be called directly from 32-bit code. + * + * All routines expect that the 16-bit stack contents (arguments) and the + * return address (segptr to CallTo16_Ret) were already set up by the + * caller; nb_args must contain the number of bytes to be conserved. The + * 16-bit SS:SP will be set accordinly. + * + * All other registers are either taken from the CONTEXT86 structure + * or else set to default values. The target routine address is either + * given directly or taken from the CONTEXT86. + */ +static void BuildCallTo16Core( FILE *outfile, int reg_func ) +{ + const char *name = reg_func ? "wine_call_to_16_regs" : "wine_call_to_16"; + + /* Function header */ + function_header( outfile, name ); + + /* Function entry sequence */ + fprintf( outfile, "\tpushl %%ebp\n" ); + fprintf( outfile, "\tmovl %%esp, %%ebp\n" ); + + /* Save the 32-bit registers */ + fprintf( outfile, "\tpushl %%ebx\n" ); + fprintf( outfile, "\tpushl %%esi\n" ); + fprintf( outfile, "\tpushl %%edi\n" ); + fprintf( outfile, "\t.byte 0x64\n\tmovl %%gs,(%d)\n", STRUCTOFFSET(TEB,gs_sel) ); + + /* Setup exception frame */ + fprintf( outfile, "\t.byte 0x64\n\tpushl (%d)\n", STACKOFFSET ); + fprintf( outfile, "\tpushl 16(%%ebp)\n" ); /* handler */ + fprintf( outfile, "\t.byte 0x64\n\tpushl (%d)\n", STRUCTOFFSET(TEB,Tib.ExceptionList) ); + fprintf( outfile, "\t.byte 0x64\n\tmovl %%esp,(%d)\n", STRUCTOFFSET(TEB,Tib.ExceptionList) ); + + /* Call the actual CallTo16 routine (simulate a lcall) */ + fprintf( outfile, "\tpushl %%cs\n" ); + fprintf( outfile, "\tcall .L%s\n", name ); + + /* Remove exception frame */ + fprintf( outfile, "\t.byte 0x64\n\tpopl (%d)\n", STRUCTOFFSET(TEB,Tib.ExceptionList) ); + fprintf( outfile, "\taddl $4, %%esp\n" ); + fprintf( outfile, "\t.byte 0x64\n\tpopl (%d)\n", STACKOFFSET ); + + if ( !reg_func ) + { + /* Convert return value */ + fprintf( outfile, "\tandl $0xffff,%%eax\n" ); + fprintf( outfile, "\tshll $16,%%edx\n" ); + fprintf( outfile, "\torl %%edx,%%eax\n" ); + } + else + { + /* + * Modify CONTEXT86 structure to contain new values + * + * NOTE: We restore only EAX, EBX, EDX, EDX, EBP, and ESP. + * The segment registers as well as ESI and EDI should + * not be modified by a well-behaved 16-bit routine in + * any case. [If necessary, we could restore them as well, + * at the cost of a somewhat less efficient return path.] + */ + + fprintf( outfile, "\tmovl %d(%%esp), %%edi\n", STACK32OFFSET(target) - STACK32OFFSET(edi)); + /* everything above edi has been popped already */ + + fprintf( outfile, "\tmovl %%eax, %d(%%edi)\n", CONTEXTOFFSET(Eax) ); + fprintf( outfile, "\tmovl %%ebx, %d(%%edi)\n", CONTEXTOFFSET(Ebx) ); + fprintf( outfile, "\tmovl %%ecx, %d(%%edi)\n", CONTEXTOFFSET(Ecx) ); + fprintf( outfile, "\tmovl %%edx, %d(%%edi)\n", CONTEXTOFFSET(Edx) ); + fprintf( outfile, "\tmovl %%ebp, %d(%%edi)\n", CONTEXTOFFSET(Ebp) ); + fprintf( outfile, "\tmovl %%esi, %d(%%edi)\n", CONTEXTOFFSET(Esp) ); + /* The return glue code saved %esp into %esi */ + } + + /* Restore the 32-bit registers */ + fprintf( outfile, "\tpopl %%edi\n" ); + fprintf( outfile, "\tpopl %%esi\n" ); + fprintf( outfile, "\tpopl %%ebx\n" ); + + /* Function exit sequence */ + fprintf( outfile, "\tpopl %%ebp\n" ); + fprintf( outfile, "\tret $12\n" ); + + + /* Start of the actual CallTo16 routine */ + + fprintf( outfile, ".L%s:\n", name ); + + /* Switch to the 16-bit stack */ + fprintf( outfile, "\tmovl %%esp,%%edx\n" ); +#ifdef __svr4__ + fprintf( outfile,"\tdata16\n"); +#endif + fprintf( outfile, "\t.byte 0x64\n\tmovw (%d),%%ss\n", STACKOFFSET + 2); + fprintf( outfile, "\t.byte 0x64\n\tmovw (%d),%%sp\n", STACKOFFSET ); + fprintf( outfile, "\t.byte 0x64\n\tmovl %%edx,(%d)\n", STACKOFFSET ); + + /* Make %bp point to the previous stackframe (built by CallFrom16) */ + fprintf( outfile, "\tmovzwl %%sp,%%ebp\n" ); + fprintf( outfile, "\tleal %d(%%ebp),%%ebp\n", STACK16OFFSET(bp) ); + + /* Add the specified offset to the new sp */ + fprintf( outfile, "\tsubw %d(%%edx), %%sp\n", STACK32OFFSET(nb_args) ); + + if (reg_func) + { + /* Push the called routine address */ + fprintf( outfile, "\tmovl %d(%%edx),%%edx\n", STACK32OFFSET(target) ); + fprintf( outfile, "\tpushw %d(%%edx)\n", CONTEXTOFFSET(SegCs) ); + fprintf( outfile, "\tpushw %d(%%edx)\n", CONTEXTOFFSET(Eip) ); + + /* Get the registers */ + fprintf( outfile, "\tpushw %d(%%edx)\n", CONTEXTOFFSET(SegDs) ); + fprintf( outfile, "\tpushl %d(%%edx)\n", CONTEXTOFFSET(SegEs) ); + fprintf( outfile, "\tpopl %%es\n" ); + fprintf( outfile, "\tpushl %d(%%edx)\n", CONTEXTOFFSET(SegFs) ); + fprintf( outfile, "\tpopl %%fs\n" ); + fprintf( outfile, "\tpushl %d(%%edx)\n", CONTEXTOFFSET(SegGs) ); + fprintf( outfile, "\tpopl %%gs\n" ); + fprintf( outfile, "\tmovl %d(%%edx),%%ebp\n", CONTEXTOFFSET(Ebp) ); + fprintf( outfile, "\tmovl %d(%%edx),%%esi\n", CONTEXTOFFSET(Esi) ); + fprintf( outfile, "\tmovl %d(%%edx),%%edi\n", CONTEXTOFFSET(Edi) ); + fprintf( outfile, "\tmovl %d(%%edx),%%eax\n", CONTEXTOFFSET(Eax) ); + fprintf( outfile, "\tmovl %d(%%edx),%%ebx\n", CONTEXTOFFSET(Ebx) ); + fprintf( outfile, "\tmovl %d(%%edx),%%ecx\n", CONTEXTOFFSET(Ecx) ); + fprintf( outfile, "\tmovl %d(%%edx),%%edx\n", CONTEXTOFFSET(Edx) ); + + /* Get the 16-bit ds */ + fprintf( outfile, "\tpopw %%ds\n" ); + } + else /* not a register function */ + { + /* Push the called routine address */ + fprintf( outfile, "\tpushl %d(%%edx)\n", STACK32OFFSET(target) ); + + /* Set %fs and %gs to the value saved by the last CallFrom16 */ + fprintf( outfile, "\tpushw %d(%%ebp)\n", STACK16OFFSET(fs)-STACK16OFFSET(bp) ); + fprintf( outfile, "\tpopw %%fs\n" ); + fprintf( outfile, "\tpushw %d(%%ebp)\n", STACK16OFFSET(gs)-STACK16OFFSET(bp) ); + fprintf( outfile, "\tpopw %%gs\n" ); + + /* Set %ds and %es (and %ax just in case) equal to %ss */ + fprintf( outfile, "\tmovw %%ss,%%ax\n" ); + fprintf( outfile, "\tmovw %%ax,%%ds\n" ); + fprintf( outfile, "\tmovw %%ax,%%es\n" ); + } + + /* Jump to the called routine */ + fprintf( outfile, "\t.byte 0x66\n" ); + fprintf( outfile, "\tlret\n" ); + + /* Function footer */ + function_footer( outfile, name ); +} + + +/******************************************************************* + * BuildRet16Func + * + * Build the return code for 16-bit callbacks + */ +static void BuildRet16Func( FILE *outfile ) +{ + function_header( outfile, "CallTo16_Ret" ); + + /* Save %esp into %esi */ + fprintf( outfile, "\tmovl %%esp,%%esi\n" ); + + /* Restore 32-bit segment registers */ + + fprintf( outfile, "\t.byte 0x2e\n\tmovl " __ASM_NAME("CallTo16_DataSelector") "-" __ASM_NAME("Call16_Ret_Start") ",%%edi\n" ); +#ifdef __svr4__ + fprintf( outfile, "\tdata16\n"); +#endif + fprintf( outfile, "\tmovw %%di,%%ds\n" ); +#ifdef __svr4__ + fprintf( outfile, "\tdata16\n"); +#endif + fprintf( outfile, "\tmovw %%di,%%es\n" ); + + fprintf( outfile, "\t.byte 0x2e\n\tmovl " __ASM_NAME("CallTo16_TebSelector") "-" __ASM_NAME("Call16_Ret_Start") ",%%fs\n" ); + + fprintf( outfile, "\t.byte 0x64\n\tmovl (%d),%%gs\n", STRUCTOFFSET(TEB,gs_sel) ); + + /* Restore the 32-bit stack */ + +#ifdef __svr4__ + fprintf( outfile, "\tdata16\n"); +#endif + fprintf( outfile, "\tmovw %%di,%%ss\n" ); + fprintf( outfile, "\t.byte 0x64\n\tmovl (%d),%%esp\n", STACKOFFSET ); + + /* Return to caller */ + + fprintf( outfile, "\tlret\n" ); + + /* Function footer */ + function_footer( outfile, "CallTo16_Ret" ); + + /* Declare the return address and data selector variables */ + + fprintf( outfile, "\n\t.align %d\n", get_alignment(4) ); + fprintf( outfile, "\t.globl " __ASM_NAME("CallTo16_DataSelector") "\n" ); + fprintf( outfile, __ASM_NAME("CallTo16_DataSelector") ":\t.long 0\n" ); + fprintf( outfile, "\t.globl " __ASM_NAME("CallTo16_TebSelector") "\n" ); + fprintf( outfile, __ASM_NAME("CallTo16_TebSelector") ":\t.long 0\n" ); +} + + +/******************************************************************* + * BuildCallTo32CBClient + * + * Call a CBClient relay stub from 32-bit code (KERNEL.620). + * + * Since the relay stub is itself 32-bit, this should not be a problem; + * unfortunately, the relay stubs are expected to switch back to a + * 16-bit stack (and 16-bit code) after completion :-( + * + * This would conflict with our 16- vs. 32-bit stack handling, so + * we simply switch *back* to our 32-bit stack before returning to + * the caller ... + * + * The CBClient relay stub expects to be called with the following + * 16-bit stack layout, and with ebp and ebx pointing into the 16-bit + * stack at the designated places: + * + * ... + * (ebp+14) original arguments to the callback routine + * (ebp+10) far return address to original caller + * (ebp+6) Thunklet target address + * (ebp+2) Thunklet relay ID code + * (ebp) BP (saved by CBClientGlueSL) + * (ebp-2) SI (saved by CBClientGlueSL) + * (ebp-4) DI (saved by CBClientGlueSL) + * (ebp-6) DS (saved by CBClientGlueSL) + * + * ... buffer space used by the 16-bit side glue for temp copies + * + * (ebx+4) far return address to 16-bit side glue code + * (ebx) saved 16-bit ss:sp (pointing to ebx+4) + * + * The 32-bit side glue code accesses both the original arguments (via ebp) + * and the temporary copies prepared by the 16-bit side glue (via ebx). + * After completion, the stub will load ss:sp from the buffer at ebx + * and perform a far return to 16-bit code. + * + * To trick the relay stub into returning to us, we replace the 16-bit + * return address to the glue code by a cs:ip pair pointing to our + * return entry point (the original return address is saved first). + * Our return stub thus called will then reload the 32-bit ss:esp and + * return to 32-bit code (by using and ss:esp value that we have also + * pushed onto the 16-bit stack before and a cs:eip values found at + * that position on the 32-bit stack). The ss:esp to be restored is + * found relative to the 16-bit stack pointer at: + * + * (ebx-4) ss (flat) + * (ebx-8) sp (32-bit stack pointer) + * + * The second variant of this routine, CALL32_CBClientEx, which is used + * to implement KERNEL.621, has to cope with yet another problem: Here, + * the 32-bit side directly returns to the caller of the CBClient thunklet, + * restoring registers saved by CBClientGlueSL and cleaning up the stack. + * As we have to return to our 32-bit code first, we have to adapt the + * layout of our temporary area so as to include values for the registers + * that are to be restored, and later (in the implementation of KERNEL.621) + * we *really* restore them. The return stub restores DS, DI, SI, and BP + * from the stack, skips the next 8 bytes (CBClient relay code / target), + * and then performs a lret NN, where NN is the number of arguments to be + * removed. Thus, we prepare our temporary area as follows: + * + * (ebx+22) 16-bit cs (this segment) + * (ebx+20) 16-bit ip ('16-bit' return entry point) + * (ebx+16) 32-bit ss (flat) + * (ebx+12) 32-bit sp (32-bit stack pointer) + * (ebx+10) 16-bit bp (points to ebx+24) + * (ebx+8) 16-bit si (ignored) + * (ebx+6) 16-bit di (ignored) + * (ebx+4) 16-bit ds (we actually use the flat DS here) + * (ebx+2) 16-bit ss (16-bit stack segment) + * (ebx+0) 16-bit sp (points to ebx+4) + * + * Note that we ensure that DS is not changed and remains the flat segment, + * and the 32-bit stack pointer our own return stub needs fits just + * perfectly into the 8 bytes that are skipped by the Windows stub. + * One problem is that we have to determine the number of removed arguments, + * as these have to be really removed in KERNEL.621. Thus, the BP value + * that we place in the temporary area to be restored, contains the value + * that SP would have if no arguments were removed. By comparing the actual + * value of SP with this value in our return stub we can compute the number + * of removed arguments. This is then returned to KERNEL.621. + * + * The stack layout of this function: + * (ebp+20) nArgs pointer to variable receiving nr. of args (Ex only) + * (ebp+16) esi pointer to caller's esi value + * (ebp+12) arg ebp value to be set for relay stub + * (ebp+8) func CBClient relay stub address + * (ebp+4) ret addr + * (ebp) ebp + */ +static void BuildCallTo32CBClient( FILE *outfile, BOOL isEx ) +{ + const char *name = isEx? "CBClientEx" : "CBClient"; + int size = isEx? 24 : 12; + + /* Function header */ + + fprintf( outfile, "\n\t.align %d\n", get_alignment(4) ); + fprintf( outfile, "\t.globl " __ASM_NAME("CALL32_%s") "\n", name ); + fprintf( outfile, __ASM_NAME("CALL32_%s") ":\n", name ); + + /* Entry code */ + + fprintf( outfile, "\tpushl %%ebp\n" ); + fprintf( outfile, "\tmovl %%esp,%%ebp\n" ); + fprintf( outfile, "\tpushl %%edi\n" ); + fprintf( outfile, "\tpushl %%esi\n" ); + fprintf( outfile, "\tpushl %%ebx\n" ); + + /* Get the 16-bit stack */ + + fprintf( outfile, "\t.byte 0x64\n\tmovl (%d),%%ebx\n", STACKOFFSET); + + /* Convert it to a flat address */ + + fprintf( outfile, "\tshldl $16,%%ebx,%%eax\n" ); + fprintf( outfile, "\tandl $0xfff8,%%eax\n" ); + fprintf( outfile, "\tshrl $1,%%eax\n" ); + fprintf( outfile, "\tmovl " __ASM_NAME("wine_ldt_copy") "(%%eax),%%esi\n" ); + fprintf( outfile, "\tmovw %%bx,%%ax\n" ); + fprintf( outfile, "\taddl %%eax,%%esi\n" ); + + /* Allocate temporary area (simulate STACK16_PUSH) */ + + fprintf( outfile, "\tpushf\n" ); + fprintf( outfile, "\tcld\n" ); + fprintf( outfile, "\tleal -%d(%%esi), %%edi\n", size ); + fprintf( outfile, "\tmovl $%d, %%ecx\n", sizeof(STACK16FRAME) ); + fprintf( outfile, "\trep\n\tmovsb\n" ); + fprintf( outfile, "\tpopf\n" ); + + fprintf( outfile, "\t.byte 0x64\n\tsubw $%d,(%d)\n", size, STACKOFFSET ); + + fprintf( outfile, "\tpushl %%edi\n" ); /* remember address */ + + /* Set up temporary area */ + + if ( !isEx ) + { + fprintf( outfile, "\tleal 4(%%edi), %%edi\n" ); + + fprintf( outfile, "\tleal -8(%%esp), %%eax\n" ); + fprintf( outfile, "\tmovl %%eax, -8(%%edi)\n" ); /* 32-bit sp */ + + fprintf( outfile, "\tmovw %%ss, %%ax\n" ); + fprintf( outfile, "\tandl $0x0000ffff, %%eax\n" ); + fprintf( outfile, "\tmovl %%eax, -4(%%edi)\n" ); /* 32-bit ss */ + + fprintf( outfile, "\taddl $%d, %%ebx\n", sizeof(STACK16FRAME)-size+4 + 4 ); + fprintf( outfile, "\tmovl %%ebx, 0(%%edi)\n" ); /* 16-bit ss:sp */ + + fprintf( outfile, "\tmovl " __ASM_NAME("CALL32_%s_RetAddr") ", %%eax\n", name ); + fprintf( outfile, "\tmovl %%eax, 4(%%edi)\n" ); /* overwrite return address */ + } + else + { + fprintf( outfile, "\taddl $%d, %%ebx\n", sizeof(STACK16FRAME)-size+4 ); + fprintf( outfile, "\tmovl %%ebx, 0(%%edi)\n" ); + + fprintf( outfile, "\tmovw %%ds, %%ax\n" ); + fprintf( outfile, "\tmovw %%ax, 4(%%edi)\n" ); + + fprintf( outfile, "\taddl $20, %%ebx\n" ); + fprintf( outfile, "\tmovw %%bx, 10(%%edi)\n" ); + + fprintf( outfile, "\tleal -8(%%esp), %%eax\n" ); + fprintf( outfile, "\tmovl %%eax, 12(%%edi)\n" ); + + fprintf( outfile, "\tmovw %%ss, %%ax\n" ); + fprintf( outfile, "\tandl $0x0000ffff, %%eax\n" ); + fprintf( outfile, "\tmovl %%eax, 16(%%edi)\n" ); + + fprintf( outfile, "\tmovl " __ASM_NAME("CALL32_%s_RetAddr") ", %%eax\n", name ); + fprintf( outfile, "\tmovl %%eax, 20(%%edi)\n" ); + } + + /* Set up registers and call CBClient relay stub (simulating a far call) */ + + fprintf( outfile, "\tmovl 16(%%ebp), %%esi\n" ); + fprintf( outfile, "\tmovl (%%esi), %%esi\n" ); + + fprintf( outfile, "\tmovl %%edi, %%ebx\n" ); + fprintf( outfile, "\tmovl 8(%%ebp), %%eax\n" ); + fprintf( outfile, "\tmovl 12(%%ebp), %%ebp\n" ); + + fprintf( outfile, "\tpushl %%cs\n" ); + fprintf( outfile, "\tcall *%%eax\n" ); + + /* Return new esi value to caller */ + + fprintf( outfile, "\tmovl 32(%%esp), %%edi\n" ); + fprintf( outfile, "\tmovl %%esi, (%%edi)\n" ); + + /* Cleanup temporary area (simulate STACK16_POP) */ + + fprintf( outfile, "\tpop %%esi\n" ); + + fprintf( outfile, "\tpushf\n" ); + fprintf( outfile, "\tstd\n" ); + fprintf( outfile, "\tdec %%esi\n" ); + fprintf( outfile, "\tleal %d(%%esi), %%edi\n", size ); + fprintf( outfile, "\tmovl $%d, %%ecx\n", sizeof(STACK16FRAME) ); + fprintf( outfile, "\trep\n\tmovsb\n" ); + fprintf( outfile, "\tpopf\n" ); + + fprintf( outfile, "\t.byte 0x64\n\taddw $%d,(%d)\n", size, STACKOFFSET ); + + /* Return argument size to caller */ + if ( isEx ) + { + fprintf( outfile, "\tmovl 32(%%esp), %%ebx\n" ); + fprintf( outfile, "\tmovl %%ebp, (%%ebx)\n" ); + } + + /* Restore registers and return */ + + fprintf( outfile, "\tpopl %%ebx\n" ); + fprintf( outfile, "\tpopl %%esi\n" ); + fprintf( outfile, "\tpopl %%edi\n" ); + fprintf( outfile, "\tpopl %%ebp\n" ); + fprintf( outfile, "\tret\n" ); + fprintf( outfile, ".size " __ASM_NAME("CALL32_%s") ", . - " __ASM_NAME("CALL32_%s") "\n", name, name ); +} + +static void BuildCallTo32CBClientRet( FILE *outfile, BOOL isEx ) +{ + const char *name = isEx? "CBClientEx" : "CBClient"; + + /* '16-bit' return stub */ + + fprintf( outfile, "\n\t.globl " __ASM_NAME("CALL32_%s_Ret") "\n", name ); + fprintf( outfile, __ASM_NAME("CALL32_%s_Ret") ":\n", name ); + + if ( !isEx ) + { + fprintf( outfile, "\tmovzwl %%sp, %%ebx\n" ); + fprintf( outfile, "\tlssl %%ss:-16(%%ebx), %%esp\n" ); + } + else + { + fprintf( outfile, "\tmovzwl %%bp, %%ebx\n" ); + fprintf( outfile, "\tsubw %%bp, %%sp\n" ); + fprintf( outfile, "\tmovzwl %%sp, %%ebp\n" ); + fprintf( outfile, "\tlssl %%ss:-12(%%ebx), %%esp\n" ); + } + fprintf( outfile, "\tlret\n" ); + + fprintf( outfile, ".size " __ASM_NAME("CALL32_%s_Ret") ", . - " __ASM_NAME("CALL32_%s_Ret") "\n", name, name ); + + /* Declare the return address variable */ + + fprintf( outfile, "\n\t.globl " __ASM_NAME("CALL32_%s_RetAddr") "\n", name ); + fprintf( outfile, __ASM_NAME("CALL32_%s_RetAddr") ":\t.long 0\n", name ); +} + + +/******************************************************************* + * BuildCallFrom32Regs + * + * Build a 32-bit-to-Wine call-back function for a 'register' function. + * 'args' is the number of dword arguments. + * + * Stack layout: + * ... + * (ebp+12) first arg + * (ebp+8) ret addr to user code + * (ebp+4) ret addr to relay code + * (ebp+0) saved ebp + * (ebp-128) buffer area to allow stack frame manipulation + * (ebp-332) CONTEXT86 struct + * (ebp-336) CONTEXT86 *argument + * .... other arguments copied from (ebp+12) + * + * The entry point routine is called with a CONTEXT* extra argument, + * following the normal args. In this context structure, EIP_reg + * contains the return address to user code, and ESP_reg the stack + * pointer on return (with the return address and arguments already + * removed). + */ +static void BuildCallFrom32Regs( FILE *outfile ) +{ + static const int STACK_SPACE = 128 + sizeof(CONTEXT86); + + /* Function header */ + + function_header( outfile, "__wine_call_from_32_regs" ); + + /* Allocate some buffer space on the stack */ + + fprintf( outfile, "\tpushl %%ebp\n" ); + fprintf( outfile, "\tmovl %%esp,%%ebp\n "); + fprintf( outfile, "\tleal -%d(%%esp), %%esp\n", STACK_SPACE ); + + /* Build the context structure */ + + fprintf( outfile, "\tmovl %%eax,%d(%%ebp)\n", CONTEXTOFFSET(Eax) - STACK_SPACE ); + fprintf( outfile, "\tpushfl\n" ); + fprintf( outfile, "\tpopl %%eax\n" ); + fprintf( outfile, "\tmovl %%eax,%d(%%ebp)\n", CONTEXTOFFSET(EFlags) - STACK_SPACE ); + fprintf( outfile, "\tmovl 0(%%ebp),%%eax\n" ); + fprintf( outfile, "\tmovl %%eax,%d(%%ebp)\n", CONTEXTOFFSET(Ebp) - STACK_SPACE ); + fprintf( outfile, "\tmovl %%ebx,%d(%%ebp)\n", CONTEXTOFFSET(Ebx) - STACK_SPACE ); + fprintf( outfile, "\tmovl %%ecx,%d(%%ebp)\n", CONTEXTOFFSET(Ecx) - STACK_SPACE ); + fprintf( outfile, "\tmovl %%edx,%d(%%ebp)\n", CONTEXTOFFSET(Edx) - STACK_SPACE ); + fprintf( outfile, "\tmovl %%esi,%d(%%ebp)\n", CONTEXTOFFSET(Esi) - STACK_SPACE ); + fprintf( outfile, "\tmovl %%edi,%d(%%ebp)\n", CONTEXTOFFSET(Edi) - STACK_SPACE ); + + fprintf( outfile, "\txorl %%eax,%%eax\n" ); + fprintf( outfile, "\tmovw %%cs,%%ax\n" ); + fprintf( outfile, "\tmovl %%eax,%d(%%ebp)\n", CONTEXTOFFSET(SegCs) - STACK_SPACE ); + fprintf( outfile, "\tmovw %%es,%%ax\n" ); + fprintf( outfile, "\tmovl %%eax,%d(%%ebp)\n", CONTEXTOFFSET(SegEs) - STACK_SPACE ); + fprintf( outfile, "\tmovw %%fs,%%ax\n" ); + fprintf( outfile, "\tmovl %%eax,%d(%%ebp)\n", CONTEXTOFFSET(SegFs) - STACK_SPACE ); + fprintf( outfile, "\tmovw %%gs,%%ax\n" ); + fprintf( outfile, "\tmovl %%eax,%d(%%ebp)\n", CONTEXTOFFSET(SegGs) - STACK_SPACE ); + fprintf( outfile, "\tmovw %%ss,%%ax\n" ); + fprintf( outfile, "\tmovl %%eax,%d(%%ebp)\n", CONTEXTOFFSET(SegSs) - STACK_SPACE ); + fprintf( outfile, "\tmovw %%ds,%%ax\n" ); + fprintf( outfile, "\tmovl %%eax,%d(%%ebp)\n", CONTEXTOFFSET(SegDs) - STACK_SPACE ); + fprintf( outfile, "\tmovw %%ax,%%es\n" ); /* set %es equal to %ds just in case */ + + fprintf( outfile, "\tmovl $0x%x,%%eax\n", CONTEXT86_FULL ); + fprintf( outfile, "\tmovl %%eax,%d(%%ebp)\n", CONTEXTOFFSET(ContextFlags) - STACK_SPACE ); + + fprintf( outfile, "\tmovl 8(%%ebp),%%eax\n" ); /* Get %eip at time of call */ + fprintf( outfile, "\tmovl %%eax,%d(%%ebp)\n", CONTEXTOFFSET(Eip) - STACK_SPACE ); + + /* Transfer the arguments */ + + fprintf( outfile, "\tmovl 4(%%ebp),%%ebx\n" ); /* get relay code addr */ + fprintf( outfile, "\tpushl %%esp\n" ); /* push ptr to context struct */ + fprintf( outfile, "\tmovzbl 4(%%ebx),%%ecx\n" ); /* fetch number of args to copy */ + fprintf( outfile, "\tjecxz 1f\n" ); + fprintf( outfile, "\tsubl %%ecx,%%esp\n" ); + fprintf( outfile, "\tleal 12(%%ebp),%%esi\n" ); /* get %esp at time of call */ + fprintf( outfile, "\tmovl %%esp,%%edi\n" ); + fprintf( outfile, "\tshrl $2,%%ecx\n" ); + fprintf( outfile, "\tcld\n" ); + fprintf( outfile, "\trep\n\tmovsl\n" ); /* copy args */ + + fprintf( outfile, "1:\tmovzbl 5(%%ebx),%%eax\n" ); /* fetch number of args to remove */ + fprintf( outfile, "\tleal 12(%%ebp,%%eax),%%eax\n" ); + fprintf( outfile, "\tmovl %%eax,%d(%%ebp)\n", CONTEXTOFFSET(Esp) - STACK_SPACE ); + + /* Call the entry point */ + + fprintf( outfile, "\tcall *0(%%ebx)\n" ); + fprintf( outfile, "\tleal -%d(%%ebp),%%ecx\n", STACK_SPACE ); + + /* Restore the context structure */ + + fprintf( outfile, "2:\tpushl %d(%%ecx)\n", CONTEXTOFFSET(SegEs) ); + fprintf( outfile, "\tpopl %%es\n" ); + fprintf( outfile, "\tpushl %d(%%ecx)\n", CONTEXTOFFSET(SegFs) ); + fprintf( outfile, "\tpopl %%fs\n" ); + fprintf( outfile, "\tpushl %d(%%ecx)\n", CONTEXTOFFSET(SegGs) ); + fprintf( outfile, "\tpopl %%gs\n" ); + + fprintf( outfile, "\tmovl %d(%%ecx),%%edi\n", CONTEXTOFFSET(Edi) ); + fprintf( outfile, "\tmovl %d(%%ecx),%%esi\n", CONTEXTOFFSET(Esi) ); + fprintf( outfile, "\tmovl %d(%%ecx),%%edx\n", CONTEXTOFFSET(Edx) ); + fprintf( outfile, "\tmovl %d(%%ecx),%%ebx\n", CONTEXTOFFSET(Ebx) ); + fprintf( outfile, "\tmovl %d(%%ecx),%%eax\n", CONTEXTOFFSET(Eax) ); + fprintf( outfile, "\tmovl %d(%%ecx),%%ebp\n", CONTEXTOFFSET(Ebp) ); + + fprintf( outfile, "\tpushl %d(%%ecx)\n", CONTEXTOFFSET(SegSs) ); + fprintf( outfile, "\tpopl %%ss\n" ); + fprintf( outfile, "\tmovl %d(%%ecx),%%esp\n", CONTEXTOFFSET(Esp) ); + + fprintf( outfile, "\tpushl %d(%%ecx)\n", CONTEXTOFFSET(EFlags) ); + fprintf( outfile, "\tpushl %d(%%ecx)\n", CONTEXTOFFSET(SegCs) ); + fprintf( outfile, "\tpushl %d(%%ecx)\n", CONTEXTOFFSET(Eip) ); + fprintf( outfile, "\tpushl %d(%%ecx)\n", CONTEXTOFFSET(SegDs) ); + fprintf( outfile, "\tmovl %d(%%ecx),%%ecx\n", CONTEXTOFFSET(Ecx) ); + + fprintf( outfile, "\tpopl %%ds\n" ); + fprintf( outfile, "\tiret\n" ); + function_footer( outfile, "__wine_call_from_32_regs" ); + + function_header( outfile, "__wine_call_from_32_restore_regs" ); + fprintf( outfile, "\tleal 4(%%esp),%%ecx\n" ); + fprintf( outfile, "\tjmp 2b\n" ); + function_footer( outfile, "__wine_call_from_32_restore_regs" ); +} + + +/******************************************************************* + * BuildPendingEventCheck + * + * Build a function that checks whether there are any + * pending DPMI events. + * + * Stack layout: + * + * (sp+12) long eflags + * (sp+6) long cs + * (sp+2) long ip + * (sp) word fs + * + * On entry to function, fs register points to a valid TEB. + * On exit from function, stack will be popped. + */ +static void BuildPendingEventCheck( FILE *outfile ) +{ + /* Function header */ + + function_header( outfile, "DPMI_PendingEventCheck" ); + + /* Check for pending events. */ + + fprintf( outfile, "\t.byte 0x64\n\ttestl $0xffffffff,(%d)\n", + STRUCTOFFSET(TEB,vm86_pending) ); + fprintf( outfile, "\tje " __ASM_NAME("DPMI_PendingEventCheck_Cleanup") "\n" ); + + fprintf( outfile, "\t.byte 0x64\n\ttestl $0xffffffff,(%d)\n", + STRUCTOFFSET(TEB,dpmi_vif) ); + + fprintf( outfile, "\tje " __ASM_NAME("DPMI_PendingEventCheck_Cleanup") "\n" ); + + /* Process pending events. */ + + fprintf( outfile, "\tsti\n" ); + + /* Start cleanup. Restore fs register. */ + + fprintf( outfile, ".globl " __ASM_NAME("DPMI_PendingEventCheck_Cleanup") "\n" ); + fprintf( outfile, __ASM_NAME("DPMI_PendingEventCheck_Cleanup") ":\n" ); + fprintf( outfile, "\tpopw %%fs\n" ); + + /* Return from function. */ + + fprintf( outfile, ".globl " __ASM_NAME("DPMI_PendingEventCheck_Return") "\n" ); + fprintf( outfile, __ASM_NAME("DPMI_PendingEventCheck_Return") ":\n" ); + fprintf( outfile, "\tiret\n" ); + + function_footer( outfile, "DPMI_PendingEventCheck" ); +} + + +/******************************************************************* + * BuildRelays16 + * + * Build all the 16-bit relay callbacks + */ +void BuildRelays16( FILE *outfile ) +{ + /* File header */ + + fprintf( outfile, "/* File generated automatically. Do not edit! */\n\n" ); + fprintf( outfile, "\t.text\n" ); + + fprintf( outfile, __ASM_NAME("__wine_spec_thunk_text_16") ":\n\n" ); + + fprintf( outfile, __ASM_NAME("Call16_Start") ":\n" ); + fprintf( outfile, "\t.globl " __ASM_NAME("Call16_Start") "\n" ); + fprintf( outfile, "\t.byte 0\n\n" ); + + /* Standard CallFrom16 routine (WORD return) */ + BuildCallFrom16Core( outfile, FALSE, FALSE, TRUE ); + + /* Standard CallFrom16 routine (DWORD return) */ + BuildCallFrom16Core( outfile, FALSE, FALSE, FALSE ); + + /* Register CallFrom16 routine */ + BuildCallFrom16Core( outfile, TRUE, FALSE, FALSE ); + + /* C16ThkSL CallFrom16 routine */ + BuildCallFrom16Core( outfile, FALSE, TRUE, FALSE ); + + /* Standard CallTo16 routine */ + BuildCallTo16Core( outfile, 0 ); + + /* Register CallTo16 routine */ + BuildCallTo16Core( outfile, 1 ); + + /* CBClientThunkSL routine */ + BuildCallTo32CBClient( outfile, FALSE ); + + /* CBClientThunkSLEx routine */ + BuildCallTo32CBClient( outfile, TRUE ); + + fprintf( outfile, __ASM_NAME("Call16_End") ":\n" ); + fprintf( outfile, "\t.globl " __ASM_NAME("Call16_End") "\n" ); + fprintf( outfile, "\t.size " __ASM_NAME("__wine_spec_thunk_text_16") ",. - " __ASM_NAME("__wine_spec_thunk_text_16") "\n" ); + + /* The whole Call16_Ret segment must lie within the .data section */ + fprintf( outfile, "\n\t.data\n" ); + fprintf( outfile, __ASM_NAME("__wine_spec_thunk_data_16") ":\n\n" ); + fprintf( outfile, "\t.globl " __ASM_NAME("Call16_Ret_Start") "\n" ); + fprintf( outfile, __ASM_NAME("Call16_Ret_Start") ":\n" ); + + /* Standard CallTo16 return stub */ + BuildRet16Func( outfile ); + + /* CBClientThunkSL return stub */ + BuildCallTo32CBClientRet( outfile, FALSE ); + + /* CBClientThunkSLEx return stub */ + BuildCallTo32CBClientRet( outfile, TRUE ); + + /* Pending DPMI events check stub */ + BuildPendingEventCheck( outfile ); + + /* End of Call16_Ret segment */ + fprintf( outfile, "\n\t.globl " __ASM_NAME("Call16_Ret_End") "\n" ); + fprintf( outfile, __ASM_NAME("Call16_Ret_End") ":\n" ); + fprintf( outfile, "\t.size " __ASM_NAME("__wine_spec_thunk_data_16") ",. - " __ASM_NAME("__wine_spec_thunk_data_16") "\n" ); +} + +/******************************************************************* + * BuildRelays32 + * + * Build all the 32-bit relay callbacks + */ +void BuildRelays32( FILE *outfile ) +{ + /* File header */ + + fprintf( outfile, "/* File generated automatically. Do not edit! */\n\n" ); + fprintf( outfile, "\t.text\n" ); + fprintf( outfile, __ASM_NAME("__wine_spec_thunk_text_32") ":\n\n" ); + + /* 32-bit register entry point */ + BuildCallFrom32Regs( outfile ); + + fprintf( outfile, "\t.size " __ASM_NAME("__wine_spec_thunk_text_32") ",. - " __ASM_NAME("__wine_spec_thunk_text_32") "\n" ); +} + +#else /* __i386__ */ + +void BuildRelays16( FILE *outfile ) +{ + fprintf( outfile, "/* File not used with this architecture. Do not edit! */\n\n" ); +} + +void BuildRelays32( FILE *outfile ) +{ + fprintf( outfile, "/* File not used with this architecture. Do not edit! */\n\n" ); +} + +#endif /* __i386__ */ diff --git a/reactos/tools/winebuild/res16.c b/reactos/tools/winebuild/res16.c new file mode 100644 index 00000000000..285e890859f --- /dev/null +++ b/reactos/tools/winebuild/res16.c @@ -0,0 +1,360 @@ +/* + * Builtin dlls resource support + * + * Copyright 2000 Alexandre Julliard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#include +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef HAVE_SYS_MMAN_H +#include +#endif + +#include "winglue.h" +#include "build.h" + +#define ALIGNMENT 2 /* alignment for resource data */ +#define ALIGN_MASK ((1 << ALIGNMENT) - 1) + +/* Unicode string or integer id */ +struct string_id +{ + char *str; /* ptr to string */ + WORD id; /* integer id if str is NULL */ +}; + +/* descriptor for a resource */ +struct resource +{ + struct string_id type; + struct string_id name; + const void *data; + unsigned int data_size; + WORD memopt; +}; + +/* type level of the resource tree */ +struct res_type +{ + const struct string_id *type; /* type name */ + const struct resource *res; /* first resource of this type */ + unsigned int nb_names; /* total number of names */ +}; + +/* top level of the resource tree */ +struct res_tree +{ + struct res_type *types; /* types array */ + unsigned int nb_types; /* total number of types */ +}; + +static const unsigned char *file_pos; /* current position in resource file */ +static const unsigned char *file_end; /* end of resource file */ +static const char *file_name; /* current resource file name */ + + +inline static struct resource *add_resource( DLLSPEC *spec ) +{ + spec->resources = xrealloc( spec->resources, (spec->nb_resources + 1) * sizeof(*spec->resources) ); + return &spec->resources[spec->nb_resources++]; +} + +static struct res_type *add_type( struct res_tree *tree, const struct resource *res ) +{ + struct res_type *type; + tree->types = xrealloc( tree->types, (tree->nb_types + 1) * sizeof(*tree->types) ); + type = &tree->types[tree->nb_types++]; + type->type = &res->type; + type->res = res; + type->nb_names = 0; + return type; +} + +/* get the next byte from the current resource file */ +static unsigned char get_byte(void) +{ + unsigned char ret = *file_pos++; + if (file_pos > file_end) fatal_error( "%s is a truncated/corrupted file\n", file_name ); + return ret; +} + +/* get the next word from the current resource file */ +static WORD get_word(void) +{ + /* might not be aligned */ +#ifdef WORDS_BIGENDIAN + unsigned char high = get_byte(); + unsigned char low = get_byte(); +#else + unsigned char low = get_byte(); + unsigned char high = get_byte(); +#endif + return low | (high << 8); +} + +/* get the next dword from the current resource file */ +static DWORD get_dword(void) +{ +#ifdef WORDS_BIGENDIAN + WORD high = get_word(); + WORD low = get_word(); +#else + WORD low = get_word(); + WORD high = get_word(); +#endif + return low | (high << 16); +} + +/* get a string from the current resource file */ +static void get_string( struct string_id *str ) +{ + if (*file_pos == 0xff) + { + get_byte(); /* skip the 0xff */ + str->str = NULL; + str->id = get_word(); + } + else + { + char *p = xmalloc( (strlen(file_pos) + 1) ); + str->str = p; + str->id = 0; + while ((*p++ = get_byte())); + } +} + +/* load the next resource from the current file */ +static void load_next_resource( DLLSPEC *spec ) +{ + struct resource *res = add_resource( spec ); + + get_string( &res->type ); + get_string( &res->name ); + res->memopt = get_word(); + res->data_size = get_dword(); + res->data = file_pos; + file_pos += res->data_size; + if (file_pos > file_end) fatal_error( "%s is a truncated/corrupted file\n", file_name ); +} + +/* load a Win16 .res file */ +void load_res16_file( const char *name, DLLSPEC *spec ) +{ + int fd; + void *base; + struct stat st; + + if ((fd = open( name, O_RDONLY )) == -1) fatal_perror( "Cannot open %s", name ); + if ((fstat( fd, &st ) == -1)) fatal_perror( "Cannot stat %s", name ); + if (!st.st_size) fatal_error( "%s is an empty file\n", name ); +#ifdef HAVE_MMAP + if ((base = mmap( NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0 )) == (void*)-1) +#endif /* HAVE_MMAP */ + { + base = xmalloc( st.st_size ); + if (read( fd, base, st.st_size ) != st.st_size) + fatal_error( "Cannot read %s\n", name ); + } + + file_name = name; + file_pos = base; + file_end = file_pos + st.st_size; + while (file_pos < file_end) load_next_resource( spec ); +} + +/* compare two strings/ids */ +static int cmp_string( const struct string_id *str1, const struct string_id *str2 ) +{ + if (!str1->str) + { + if (!str2->str) return str1->id - str2->id; + return 1; /* an id compares larger than a string */ + } + if (!str2->str) return -1; + return strcasecmp( str1->str, str2->str ); +} + +/* compare two resources for sorting the resource directory */ +/* resources are stored first by type, then by name */ +static int cmp_res( const void *ptr1, const void *ptr2 ) +{ + const struct resource *res1 = ptr1; + const struct resource *res2 = ptr2; + int ret; + + if ((ret = cmp_string( &res1->type, &res2->type ))) return ret; + return cmp_string( &res1->name, &res2->name ); +} + +/* build the 2-level (type,name) resource tree */ +static struct res_tree *build_resource_tree( DLLSPEC *spec ) +{ + int i; + struct res_tree *tree; + struct res_type *type = NULL; + + qsort( spec->resources, spec->nb_resources, sizeof(*spec->resources), cmp_res ); + + tree = xmalloc( sizeof(*tree) ); + tree->types = NULL; + tree->nb_types = 0; + + for (i = 0; i < spec->nb_resources; i++) + { + if (!i || cmp_string( &spec->resources[i].type, &spec->resources[i-1].type )) /* new type */ + type = add_type( tree, &spec->resources[i] ); + type->nb_names++; + } + return tree; +} + +/* free the resource tree */ +static void free_resource_tree( struct res_tree *tree ) +{ + free( tree->types ); + free( tree ); +} + +inline static void put_byte( unsigned char **buffer, unsigned char val ) +{ + *(*buffer)++ = val; +} + +inline static void put_word( unsigned char **buffer, WORD val ) +{ +#ifdef WORDS_BIGENDIAN + put_byte( buffer, HIBYTE(val) ); + put_byte( buffer, LOBYTE(val) ); +#else + put_byte( buffer, LOBYTE(val) ); + put_byte( buffer, HIBYTE(val) ); +#endif +} + +/* output a string preceded by its length */ +static void output_string( unsigned char **buffer, const char *str ) +{ + int len = strlen(str); + put_byte( buffer, len ); + while (len--) put_byte( buffer, *str++ ); +} + +/* output the resource data */ +int output_res16_data( FILE *outfile, DLLSPEC *spec ) +{ + const struct resource *res; + unsigned char *buffer, *p; + int i, total; + + if (!spec->nb_resources) return 0; + + for (i = total = 0, res = spec->resources; i < spec->nb_resources; i++, res++) + total += (res->data_size + ALIGN_MASK) & ~ALIGN_MASK; + + buffer = p = xmalloc( total ); + for (i = 0, res = spec->resources; i < spec->nb_resources; i++, res++) + { + memcpy( p, res->data, res->data_size ); + p += res->data_size; + while ((int)p & ALIGN_MASK) *p++ = 0; + } + dump_bytes( outfile, buffer, total, "resource_data", 1 ); + free( buffer ); + return total; +} + +/* output the resource definitions */ +int output_res16_directory( unsigned char *buffer, DLLSPEC *spec ) +{ + int i, offset, res_offset = 0; + unsigned int j; + struct res_tree *tree; + const struct res_type *type; + const struct resource *res; + unsigned char *start = buffer; + + tree = build_resource_tree( spec ); + + offset = 4; /* alignment + terminator */ + offset += tree->nb_types * 8; /* typeinfo structures */ + offset += spec->nb_resources * 12; /* nameinfo structures */ + + put_word( &buffer, ALIGNMENT ); + + /* type and name structures */ + + for (i = 0, type = tree->types; i < tree->nb_types; i++, type++) + { + if (type->type->str) + { + put_word( &buffer, offset ); + offset += strlen(type->type->str) + 1; + } + else + put_word( &buffer, type->type->id | 0x8000 ); + + put_word( &buffer, type->nb_names ); + put_word( &buffer, 0 ); + put_word( &buffer, 0 ); + + for (j = 0, res = type->res; j < type->nb_names; j++, res++) + { + put_word( &buffer, res_offset >> ALIGNMENT ); + put_word( &buffer, (res->data_size + ALIGN_MASK) >> ALIGNMENT ); + put_word( &buffer, res->memopt ); + if (res->name.str) + { + put_word( &buffer, offset ); + offset += strlen(res->name.str) + 1; + } + else + put_word( &buffer, res->name.id | 0x8000 ); + put_word( &buffer, 0 ); + put_word( &buffer, 0 ); + res_offset += (res->data_size + ALIGN_MASK) & ~ALIGN_MASK; + } + } + put_word( &buffer, 0 ); /* terminator */ + + /* name strings */ + + for (i = 0, type = tree->types; i < tree->nb_types; i++, type++) + { + if (type->type->str) output_string( &buffer, type->type->str ); + for (j = 0, res = type->res; j < type->nb_names; j++, res++) + { + if (res->name.str) output_string( &buffer, res->name.str ); + } + } + put_byte( &buffer, 0 ); /* names terminator */ + if ((buffer - start) & 1) put_byte( &buffer, 0 ); /* align on word boundary */ + + free_resource_tree( tree ); + return buffer - start; +} diff --git a/reactos/tools/winebuild/res32.c b/reactos/tools/winebuild/res32.c new file mode 100644 index 00000000000..1e4d28bde84 --- /dev/null +++ b/reactos/tools/winebuild/res32.c @@ -0,0 +1,489 @@ +/* + * Builtin dlls resource support + * + * Copyright 2000 Alexandre Julliard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#include +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef HAVE_SYS_MMAN_H +#include +#endif + +#include "winglue.h" +#include "build.h" + +/* Unicode string or integer id */ +struct string_id +{ + WCHAR *str; /* ptr to Unicode string */ + WORD id; /* integer id if str is NULL */ +}; + +/* descriptor for a resource */ +struct resource +{ + struct string_id type; + struct string_id name; + const void *data; + unsigned int data_size; + WORD lang; +}; + +/* name level of the resource tree */ +struct res_name +{ + const struct string_id *name; /* name */ + const struct resource *res; /* resource */ + int nb_languages; /* number of languages */ + unsigned int name_offset; /* offset of name in resource dir */ +}; + +/* type level of the resource tree */ +struct res_type +{ + const struct string_id *type; /* type name */ + struct res_name *names; /* names array */ + unsigned int nb_names; /* total number of names */ + unsigned int nb_id_names; /* number of names that have a numeric id */ + unsigned int name_offset; /* offset of type name in resource dir */ +}; + +/* top level of the resource tree */ +struct res_tree +{ + struct res_type *types; /* types array */ + unsigned int nb_types; /* total number of types */ +}; + +static const unsigned char *file_pos; /* current position in resource file */ +static const unsigned char *file_end; /* end of resource file */ +static const char *file_name; /* current resource file name */ + +/* size of a resource directory with n entries */ +#define RESDIR_SIZE(n) ((4 + 2 * (n)) * sizeof(int)) + + +inline static struct resource *add_resource( DLLSPEC *spec ) +{ + spec->resources = xrealloc( spec->resources, (spec->nb_resources + 1) * sizeof(spec->resources[0]) ); + return &spec->resources[spec->nb_resources++]; +} + +static inline unsigned int strlenW( const WCHAR *str ) +{ + const WCHAR *s = str; + while (*s) s++; + return s - str; +} + +static inline int strcmpW( const WCHAR *str1, const WCHAR *str2 ) +{ + while (*str1 && (*str1 == *str2)) { str1++; str2++; } + return *str1 - *str2; +} + +static struct res_name *add_name( struct res_type *type, const struct resource *res ) +{ + struct res_name *name; + type->names = xrealloc( type->names, (type->nb_names + 1) * sizeof(*type->names) ); + name = &type->names[type->nb_names++]; + name->name = &res->name; + name->res = res; + name->nb_languages = 1; + if (!name->name->str) type->nb_id_names++; + return name; +} + +static struct res_type *add_type( struct res_tree *tree, const struct resource *res ) +{ + struct res_type *type; + tree->types = xrealloc( tree->types, (tree->nb_types + 1) * sizeof(*tree->types) ); + type = &tree->types[tree->nb_types++]; + type->type = &res->type; + type->names = NULL; + type->nb_names = 0; + type->nb_id_names = 0; + return type; +} + +/* get the next word from the current resource file */ +static WORD get_word(void) +{ + WORD ret = *(const WORD *)file_pos; + file_pos += sizeof(WORD); + if (file_pos > file_end) fatal_error( "%s is a truncated file\n", file_name ); + return ret; +} + +/* get the next dword from the current resource file */ +static DWORD get_dword(void) +{ + DWORD ret = *(const DWORD *)file_pos; + file_pos += sizeof(DWORD); + if (file_pos > file_end) fatal_error( "%s is a truncated file\n", file_name ); + return ret; +} + +/* get a string from the current resource file */ +static void get_string( struct string_id *str ) +{ + if (*(const WCHAR *)file_pos == 0xffff) + { + get_word(); /* skip the 0xffff */ + str->str = NULL; + str->id = get_word(); + } + else + { + WCHAR *p = xmalloc( (strlenW((const WCHAR*)file_pos) + 1) * sizeof(WCHAR) ); + str->str = p; + str->id = 0; + while ((*p++ = get_word())); + } +} + +/* check the file header */ +/* all values must be zero except header size */ +static int check_header(void) +{ + if (get_dword()) return 0; /* data size */ + if (get_dword() != 32) return 0; /* header size */ + if (get_word() != 0xffff || get_word()) return 0; /* type, must be id 0 */ + if (get_word() != 0xffff || get_word()) return 0; /* name, must be id 0 */ + if (get_dword()) return 0; /* data version */ + if (get_word()) return 0; /* mem options */ + if (get_word()) return 0; /* language */ + if (get_dword()) return 0; /* version */ + if (get_dword()) return 0; /* characteristics */ + return 1; +} + +/* load the next resource from the current file */ +static void load_next_resource( DLLSPEC *spec ) +{ + DWORD hdr_size; + struct resource *res = add_resource( spec ); + + res->data_size = (get_dword() + 3) & ~3; + hdr_size = get_dword(); + if (hdr_size & 3) fatal_error( "%s header size not aligned\n", file_name ); + + res->data = file_pos - 2*sizeof(DWORD) + hdr_size; + get_string( &res->type ); + get_string( &res->name ); + if ((int)file_pos & 2) get_word(); /* align to dword boundary */ + get_dword(); /* skip data version */ + get_word(); /* skip mem options */ + res->lang = get_word(); + get_dword(); /* skip version */ + get_dword(); /* skip characteristics */ + + file_pos = (const char *)res->data + res->data_size; + if (file_pos > file_end) fatal_error( "%s is a truncated file\n", file_name ); +} + +/* load a Win32 .res file */ +int load_res32_file( const char *name, DLLSPEC *spec ) +{ + int fd, ret; + void *base; + struct stat st; + + if ((fd = open( name, O_RDONLY )) == -1) fatal_perror( "Cannot open %s", name ); + if ((fstat( fd, &st ) == -1)) fatal_perror( "Cannot stat %s", name ); + if (!st.st_size) fatal_error( "%s is an empty file\n", name ); +#ifdef HAVE_MMAP + if ((base = mmap( NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0 )) == (void*)-1) +#endif /* HAVE_MMAP */ + { + base = xmalloc( st.st_size ); + if (read( fd, base, st.st_size ) != st.st_size) + fatal_error( "Cannot read %s\n", name ); + } + + file_name = name; + file_pos = base; + file_end = file_pos + st.st_size; + if ((ret = check_header())) + { + while (file_pos < file_end) load_next_resource( spec ); + } + close( fd ); + return ret; +} + +/* compare two unicode strings/ids */ +static int cmp_string( const struct string_id *str1, const struct string_id *str2 ) +{ + if (!str1->str) + { + if (!str2->str) return str1->id - str2->id; + return 1; /* an id compares larger than a string */ + } + if (!str2->str) return -1; + return strcmpW( str1->str, str2->str ); +} + +/* compare two resources for sorting the resource directory */ +/* resources are stored first by type, then by name, then by language */ +static int cmp_res( const void *ptr1, const void *ptr2 ) +{ + const struct resource *res1 = ptr1; + const struct resource *res2 = ptr2; + int ret; + + if ((ret = cmp_string( &res1->type, &res2->type ))) return ret; + if ((ret = cmp_string( &res1->name, &res2->name ))) return ret; + return res1->lang - res2->lang; +} + +/* build the 3-level (type,name,language) resource tree */ +static struct res_tree *build_resource_tree( DLLSPEC *spec ) +{ + int i; + struct res_tree *tree; + struct res_type *type = NULL; + struct res_name *name = NULL; + + qsort( spec->resources, spec->nb_resources, sizeof(*spec->resources), cmp_res ); + + tree = xmalloc( sizeof(*tree) ); + tree->types = NULL; + tree->nb_types = 0; + + for (i = 0; i < spec->nb_resources; i++) + { + if (!i || cmp_string( &spec->resources[i].type, &spec->resources[i-1].type )) /* new type */ + { + type = add_type( tree, &spec->resources[i] ); + name = add_name( type, &spec->resources[i] ); + } + else if (cmp_string( &spec->resources[i].name, &spec->resources[i-1].name )) /* new name */ + { + name = add_name( type, &spec->resources[i] ); + } + else name->nb_languages++; + } + return tree; +} + +/* free the resource tree */ +static void free_resource_tree( struct res_tree *tree ) +{ + int i; + + for (i = 0; i < tree->nb_types; i++) free( tree->types[i].names ); + free( tree->types ); + free( tree ); +} + +/* output a Unicode string */ +static void output_string( FILE *outfile, const WCHAR *name ) +{ + int i, len = strlenW(name); + fprintf( outfile, "0x%04x", len ); + for (i = 0; i < len; i++) fprintf( outfile, ", 0x%04x", name[i] ); + fprintf( outfile, " /* " ); + for (i = 0; i < len; i++) fprintf( outfile, "%c", isprint((char)name[i]) ? (char)name[i] : '?' ); + fprintf( outfile, " */" ); +} + +/* output the resource definitions */ +void output_resources( FILE *outfile, DLLSPEC *spec ) +{ + int i, j, k, nb_id_types; + unsigned int n, offset, data_offset; + struct res_tree *tree; + struct res_type *type; + struct res_name *name; + const struct resource *res; + + if (!spec->nb_resources) return; + + tree = build_resource_tree( spec ); + + /* resource data */ + + for (i = 0, res = spec->resources; i < spec->nb_resources; i++, res++) + { + const unsigned int *p = res->data; + int size = res->data_size / 4; + /* dump data as ints to ensure correct alignment */ + fprintf( outfile, "static const unsigned int res_%d[%d] = {\n ", i, size ); + for (j = 0; j < size - 1; j++, p++) + { + fprintf( outfile, "0x%08x,", *p ); + if ((j % 8) == 7) fprintf( outfile, "\n " ); + } + fprintf( outfile, "0x%08x\n};\n\n", *p ); + } + + /* directory structures */ + + fprintf( outfile, "struct res_dir {\n" ); + fprintf( outfile, " unsigned int Characteristics;\n" ); + fprintf( outfile, " unsigned int TimeDateStamp;\n" ); + fprintf( outfile, " unsigned short MajorVersion, MinorVersion;\n" ); + fprintf( outfile, " unsigned short NumerOfNamedEntries, NumberOfIdEntries;\n};\n\n" ); + fprintf( outfile, "struct res_dir_entry {\n" ); + fprintf( outfile, " unsigned int Name;\n" ); + fprintf( outfile, " unsigned int OffsetToData;\n};\n\n" ); + fprintf( outfile, "struct res_data_entry {\n" ); + fprintf( outfile, " const unsigned int *OffsetToData;\n" ); + fprintf( outfile, " unsigned int Size;\n" ); + fprintf( outfile, " unsigned int CodePage;\n" ); + fprintf( outfile, " unsigned int ResourceHandle;\n};\n\n" ); + + /* resource directory definition */ + + fprintf( outfile, "static struct res_struct{\n" ); + fprintf( outfile, " struct res_dir type_dir;\n" ); + fprintf( outfile, " struct res_dir_entry type_entries[%d];\n", tree->nb_types ); + offset = RESDIR_SIZE( tree->nb_types ); + + for (i = 0, type = tree->types; i < tree->nb_types; i++, type++) + { + offset += RESDIR_SIZE( type->nb_names ); + fprintf( outfile, " struct res_dir name_%d_dir;\n", i ); + fprintf( outfile, " struct res_dir_entry name_%d_entries[%d];\n", i, type->nb_names ); + for (n = 0, name = type->names; n < type->nb_names; n++, name++) + { + offset += RESDIR_SIZE( name->nb_languages ); + fprintf( outfile, " struct res_dir lang_%d_%d_dir;\n", i, n ); + fprintf( outfile, " struct res_dir_entry lang_%d_%d_entries[%d];\n", + i, n, name->nb_languages ); + } + } + + fprintf( outfile, " struct res_data_entry data_entries[%d];\n", spec->nb_resources ); + offset += spec->nb_resources * 4 * sizeof(int); + + for (i = nb_id_types = 0, type = tree->types; i < tree->nb_types; i++, type++) + { + if (type->type->str) + { + type->name_offset = offset | 0x80000000; + offset += (strlenW(type->type->str)+1) * sizeof(unsigned short); + fprintf( outfile, " unsigned short type_%d_name[%d];\n", + i, strlenW(type->type->str)+1 ); + } + else + { + type->name_offset = type->type->id; + nb_id_types++; + } + + for (n = 0, name = type->names; n < type->nb_names; n++, name++) + { + if (name->name->str) + { + name->name_offset = offset | 0x80000000; + offset += (strlenW(name->name->str)+1) * sizeof(unsigned short); + fprintf( outfile, " unsigned short name_%d_%d_name[%d];\n", + i, n, strlenW(name->name->str)+1 ); + } + else name->name_offset = name->name->id; + } + } + + /* resource directory contents */ + + fprintf( outfile, "} resources = {\n" ); + fprintf( outfile, " { 0, 0, 0, 0, %d, %d },\n", tree->nb_types - nb_id_types, nb_id_types ); + + /* dump the type directory */ + offset = RESDIR_SIZE( tree->nb_types ); + fprintf( outfile, " {\n" ); + for (i = 0, type = tree->types; i < tree->nb_types; i++, type++) + { + fprintf( outfile, " { 0x%08x, 0x%08x },\n", type->name_offset, offset | 0x80000000 ); + offset += RESDIR_SIZE( type->nb_names ); + for (n = 0, name = type->names; n < type->nb_names; n++, name++) + offset += RESDIR_SIZE( name->nb_languages ); + } + fprintf( outfile, " },\n" ); + + data_offset = offset; + offset = RESDIR_SIZE( tree->nb_types ); + + /* dump the names and languages directories */ + for (i = 0, type = tree->types; i < tree->nb_types; i++, type++) + { + fprintf( outfile, " { 0, 0, 0, 0, %d, %d }, /* name_%d_dir */\n {\n", + type->nb_names - type->nb_id_names, type->nb_id_names, i ); + offset += RESDIR_SIZE( type->nb_names ); + for (n = 0, name = type->names; n < type->nb_names; n++, name++) + { + fprintf( outfile, " { 0x%08x, 0x%08x },\n", name->name_offset, offset | 0x80000000 ); + offset += RESDIR_SIZE( name->nb_languages ); + } + fprintf( outfile, " },\n" ); + + for (n = 0, name = type->names; n < type->nb_names; n++, name++) + { + fprintf( outfile, " { 0, 0, 0, 0, 0, %d }, /* lang_%d_%d_dir */\n {\n", + name->nb_languages, i, n ); + for (k = 0, res = name->res; k < name->nb_languages; k++, res++) + { + fprintf( outfile, " { 0x%04x, 0x%08x },\n", + res->lang, data_offset + (res - spec->resources) * 4 * sizeof(int) ); + } + fprintf( outfile, " },\n" ); + } + } + + /* dump the resource data entries */ + fprintf( outfile, " {\n" ); + for (i = 0, res = spec->resources; i < spec->nb_resources; i++, res++) + { + fprintf( outfile, " { res_%d, sizeof(res_%d), 0, 0 }, /* %08x */\n", i, i, + data_offset + i * 4 * sizeof(int) ); + } + + /* dump the name strings */ + for (i = 0, type = tree->types; i < tree->nb_types; i++, type++) + { + if (type->type->str) + { + fprintf( outfile, " },\n { " ); + output_string( outfile, type->type->str ); + } + for (n = 0, name = type->names; n < type->nb_names; n++, name++) + { + if (name->name->str) + { + fprintf( outfile, " },\n { " ); + output_string( outfile, name->name->str ); + } + } + } + fprintf( outfile, " }\n};\n\n" ); + free_resource_tree( tree ); +} diff --git a/reactos/tools/winebuild/spec16.c b/reactos/tools/winebuild/spec16.c new file mode 100644 index 00000000000..aae4d8f5b97 --- /dev/null +++ b/reactos/tools/winebuild/spec16.c @@ -0,0 +1,832 @@ +/* + * 16-bit spec files + * + * Copyright 1993 Robert J. Amstadt + * Copyright 1995 Martin von Loewis + * Copyright 1995, 1996, 1997 Alexandre Julliard + * Copyright 1997 Eric Youngdale + * Copyright 1999 Ulrich Weigand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#include +#include + +#include "wine/exception.h" +#include "stackframe.h" +#include "builtin16.h" +#include "module.h" + +#include "build.h" + + +/******************************************************************* + * get_cs + */ +#ifdef __i386__ +static inline unsigned short get_cs(void) +{ + unsigned short res; +#ifdef __GNUC__ + __asm__("movw %%cs,%w0" : "=r"(res)); +#elif defined(_MSC_VER) + __asm { mov res, cs } +#else + res = 0; +#endif + return res; +} +#endif /* __i386__ */ + + +/******************************************************************* + * output_file_header + * + * Output a file header with the common declarations we need. + */ +static void output_file_header( FILE *outfile ) +{ + output_standard_file_header( outfile ); + fprintf( outfile, "extern struct\n{\n" ); + fprintf( outfile, " void *base[8192];\n" ); + fprintf( outfile, " unsigned long limit[8192];\n" ); + fprintf( outfile, " unsigned char flags[8192];\n" ); + fprintf( outfile, "} wine_ldt_copy;\n\n" ); +#ifdef __i386__ + fprintf( outfile, "#define __stdcall __attribute__((__stdcall__))\n\n" ); +#else + fprintf( outfile, "#define __stdcall\n\n" ); +#endif +} + + +/******************************************************************* + * StoreVariableCode + * + * Store a list of ints into a byte array. + */ +static int StoreVariableCode( unsigned char *buffer, int size, ORDDEF *odp ) +{ + int i; + + switch(size) + { + case 1: + for (i = 0; i < odp->u.var.n_values; i++) + buffer[i] = odp->u.var.values[i]; + break; + case 2: + for (i = 0; i < odp->u.var.n_values; i++) + ((unsigned short *)buffer)[i] = odp->u.var.values[i]; + break; + case 4: + for (i = 0; i < odp->u.var.n_values; i++) + ((unsigned int *)buffer)[i] = odp->u.var.values[i]; + break; + } + return odp->u.var.n_values * size; +} + + +/******************************************************************* + * BuildModule16 + * + * Build the in-memory representation of a 16-bit NE module, and dump it + * as a byte stream into the assembly code. + */ +static int BuildModule16( FILE *outfile, int max_code_offset, + int max_data_offset, DLLSPEC *spec ) +{ + int i; + char *buffer; + NE_MODULE *pModule; + SEGTABLEENTRY *pSegment; + OFSTRUCT *pFileInfo; + BYTE *pstr; + ET_BUNDLE *bundle = 0; + ET_ENTRY entry; + + /* Module layout: + * NE_MODULE Module + * OFSTRUCT File information + * SEGTABLEENTRY Segment 1 (code) + * SEGTABLEENTRY Segment 2 (data) + * WORD[2] Resource table (empty) + * BYTE[2] Imported names (empty) + * BYTE[n] Resident names table + * BYTE[n] Entry table + */ + + buffer = xmalloc( 0x10000 ); + memset( buffer, 0, 0x10000 ); + + pModule = (NE_MODULE *)buffer; + pModule->magic = IMAGE_OS2_SIGNATURE; + pModule->count = 1; + pModule->next = 0; + pModule->flags = NE_FFLAGS_SINGLEDATA | NE_FFLAGS_BUILTIN | NE_FFLAGS_LIBMODULE; + pModule->dgroup = 2; + pModule->heap_size = spec->heap_size; + pModule->stack_size = 0; + pModule->ip = 0; + pModule->cs = 0; + pModule->sp = 0; + pModule->ss = 0; + pModule->seg_count = 2; + pModule->modref_count = 0; + pModule->nrname_size = 0; + pModule->modref_table = 0; + pModule->nrname_fpos = 0; + pModule->moveable_entries = 0; + pModule->alignment = 0; + pModule->truetype = 0; + pModule->os_flags = NE_OSFLAGS_WINDOWS; + pModule->misc_flags = 0; + pModule->dlls_to_init = 0; + pModule->nrname_handle = 0; + pModule->min_swap_area = 0; + pModule->expected_version = 0; + pModule->module32 = 0; + pModule->self = 0; + pModule->self_loading_sel = 0; + + /* File information */ + + pFileInfo = (OFSTRUCT *)(pModule + 1); + pModule->fileinfo = (int)pFileInfo - (int)pModule; + memset( pFileInfo, 0, sizeof(*pFileInfo) - sizeof(pFileInfo->szPathName) ); + pFileInfo->cBytes = sizeof(*pFileInfo) - sizeof(pFileInfo->szPathName) + + strlen(spec->file_name); + strcpy( pFileInfo->szPathName, spec->file_name ); + pstr = (char *)pFileInfo + pFileInfo->cBytes + 1; + + /* Segment table */ + + pstr = (char *)(((long)pstr + 3) & ~3); + pSegment = (SEGTABLEENTRY *)pstr; + pModule->seg_table = (int)pSegment - (int)pModule; + pSegment->filepos = 0; + pSegment->size = max_code_offset; + pSegment->flags = 0; + pSegment->minsize = max_code_offset; + pSegment->hSeg = 0; + pSegment++; + + pModule->dgroup_entry = (int)pSegment - (int)pModule; + pSegment->filepos = 0; + pSegment->size = max_data_offset; + pSegment->flags = NE_SEGFLAGS_DATA; + pSegment->minsize = max_data_offset; + pSegment->hSeg = 0; + pSegment++; + + /* Resource table */ + + pstr = (char *)pSegment; + pstr = (char *)(((long)pstr + 3) & ~3); + pModule->res_table = (int)pstr - (int)pModule; + pstr += output_res16_directory( pstr, spec ); + + /* Imported names table */ + + pstr = (char *)(((long)pstr + 3) & ~3); + pModule->import_table = (int)pstr - (int)pModule; + *pstr++ = 0; + *pstr++ = 0; + + /* Resident names table */ + + pstr = (char *)(((long)pstr + 3) & ~3); + pModule->name_table = (int)pstr - (int)pModule; + /* First entry is module name */ + *pstr = strlen( spec->dll_name ); + strcpy( pstr + 1, spec->dll_name ); + strupper( pstr + 1 ); + pstr += *pstr + 1; + *pstr++ = 0; + *pstr++ = 0; + /* Store all ordinals */ + for (i = 1; i <= spec->limit; i++) + { + ORDDEF *odp = spec->ordinals[i]; + WORD ord = i; + if (!odp || !odp->name[0]) continue; + *pstr = strlen( odp->name ); + strcpy( pstr + 1, odp->name ); + strupper( pstr + 1 ); + pstr += *pstr + 1; + memcpy( pstr, &ord, sizeof(WORD) ); + pstr += sizeof(WORD); + } + *pstr++ = 0; + + /* Entry table */ + + pstr = (char *)(((long)pstr + 3) & ~3); + pModule->entry_table = (int)pstr - (int)pModule; + for (i = 1; i <= spec->limit; i++) + { + int selector = 0; + ORDDEF *odp = spec->ordinals[i]; + if (!odp) continue; + + switch (odp->type) + { + case TYPE_CDECL: + case TYPE_PASCAL: + case TYPE_VARARGS: + case TYPE_STUB: + selector = 1; /* Code selector */ + break; + + case TYPE_VARIABLE: + selector = 2; /* Data selector */ + break; + + case TYPE_ABS: + selector = 0xfe; /* Constant selector */ + break; + + default: + selector = 0; /* Invalid selector */ + break; + } + + if ( !selector ) + continue; + + if ( bundle && bundle->last+1 == i ) + bundle->last++; + else + { + pstr = (char *)(((long)pstr + 1) & ~1); + if ( bundle ) + bundle->next = (char *)pstr - (char *)pModule; + + bundle = (ET_BUNDLE *)pstr; + bundle->first = i-1; + bundle->last = i; + bundle->next = 0; + pstr += sizeof(ET_BUNDLE); + } + + /* FIXME: is this really correct ?? */ + entry.type = 0xff; /* movable */ + entry.flags = 3; /* exported & public data */ + entry.segnum = selector; + entry.offs = odp->offset; + memcpy( pstr, &entry, sizeof(ET_ENTRY) ); + pstr += sizeof(ET_ENTRY); + } + *pstr++ = 0; + + /* Dump the module content */ + + pstr = (char *)(((long)pstr + 3) & ~3); + dump_bytes( outfile, (char *)pModule, (int)pstr - (int)pModule, "Module", 0 ); + return (int)pstr - (int)pModule; +} + + +#ifdef __i386__ +/******************************************************************* + * BuildCallFrom16Func + * + * Build a 16-bit-to-Wine callback glue function. + * + * The generated routines are intended to be used as argument conversion + * routines to be called by the CallFrom16... core. Thus, the prototypes of + * the generated routines are (see also CallFrom16): + * + * extern WORD WINAPI PREFIX_CallFrom16_C_word_xxx( FARPROC func, LPBYTE args ); + * extern LONG WINAPI PREFIX_CallFrom16_C_long_xxx( FARPROC func, LPBYTE args ); + * extern void WINAPI PREFIX_CallFrom16_C_regs_xxx( FARPROC func, LPBYTE args, + * CONTEXT86 *context ); + * + * where 'C' is the calling convention ('p' for pascal or 'c' for cdecl), + * and each 'x' is an argument ('w'=word, 's'=signed word, 'l'=long, + * 'p'=linear pointer, 't'=linear pointer to null-terminated string, + * 'T'=segmented pointer to null-terminated string). + * + * The generated routines fetch the arguments from the 16-bit stack (pointed + * to by 'args'); the offsets of the single argument values are computed + * according to the calling convention and the argument types. Then, the + * 32-bit entry point is called with these arguments. + * + * For register functions, the arguments (if present) are converted just + * the same as for normal functions, but in addition the CONTEXT86 pointer + * filled with the current register values is passed to the 32-bit routine. + */ +static void BuildCallFrom16Func( FILE *outfile, const char *profile, const char *prefix ) +{ + int i, pos, argsize = 0; + int short_ret = 0; + int reg_func = 0; + int usecdecl = 0; + int varargs = 0; + const char *args = profile + 7; + const char *ret_type; + + /* Parse function type */ + + if (!strncmp( "c_", profile, 2 )) usecdecl = 1; + else if (!strncmp( "v_", profile, 2 )) varargs = usecdecl = 1; + else if (strncmp( "p_", profile, 2 )) + { + fprintf( stderr, "Invalid function name '%s', ignored\n", profile ); + return; + } + + if (!strncmp( "word_", profile + 2, 5 )) short_ret = 1; + else if (!strncmp( "regs_", profile + 2, 5 )) reg_func = 1; + else if (strncmp( "long_", profile + 2, 5 )) + { + fprintf( stderr, "Invalid function name '%s', ignored\n", profile ); + return; + } + + for ( i = 0; args[i]; i++ ) + switch ( args[i] ) + { + case 'w': /* word */ + case 's': /* s_word */ + argsize += 2; + break; + case 'l': /* long or segmented pointer */ + case 'T': /* segmented pointer to null-terminated string */ + case 'p': /* linear pointer */ + case 't': /* linear pointer to null-terminated string */ + argsize += 4; + break; + } + + ret_type = reg_func? "void" : short_ret ? "unsigned short" : "unsigned int"; + + fprintf( outfile, "typedef %s (%s*proc_%s_t)( ", + ret_type, usecdecl ? "" : "__stdcall ", profile ); + args = profile + 7; + for ( i = 0; args[i]; i++ ) + { + if ( i ) fprintf( outfile, ", " ); + switch (args[i]) + { + case 'w': fprintf( outfile, "unsigned short" ); break; + case 's': fprintf( outfile, "short" ); break; + case 'l': case 'T': fprintf( outfile, "unsigned int" ); break; + case 'p': case 't': fprintf( outfile, "void *" ); break; + } + } + if (reg_func || varargs) + fprintf( outfile, "%svoid *", i? ", " : "" ); + else if ( !i ) + fprintf( outfile, "void" ); + fprintf( outfile, " );\n" ); + + fprintf( outfile, "static %s __stdcall __wine_%s_CallFrom16_%s( proc_%s_t proc, unsigned char *args%s )\n", + ret_type, make_c_identifier(prefix), profile, profile, + reg_func? ", void *context" : "" ); + + fprintf( outfile, "{\n %sproc(\n", reg_func ? "" : "return " ); + args = profile + 7; + pos = !usecdecl? argsize : 0; + for ( i = 0; args[i]; i++ ) + { + if ( i ) fprintf( outfile, ",\n" ); + fprintf( outfile, " " ); + switch (args[i]) + { + case 'w': /* word */ + if ( !usecdecl ) pos -= 2; + fprintf( outfile, "*(unsigned short *)(args+%d)", pos ); + if ( usecdecl ) pos += 2; + break; + + case 's': /* s_word */ + if ( !usecdecl ) pos -= 2; + fprintf( outfile, "*(short *)(args+%d)", pos ); + if ( usecdecl ) pos += 2; + break; + + case 'l': /* long or segmented pointer */ + case 'T': /* segmented pointer to null-terminated string */ + if ( !usecdecl ) pos -= 4; + fprintf( outfile, "*(unsigned int *)(args+%d)", pos ); + if ( usecdecl ) pos += 4; + break; + + case 'p': /* linear pointer */ + case 't': /* linear pointer to null-terminated string */ + if ( !usecdecl ) pos -= 4; + fprintf( outfile, "((char*)wine_ldt_copy.base[*(unsigned short*)(args+%d) >> 3] + *(unsigned short*)(args+%d))", + pos + 2, pos ); + if ( usecdecl ) pos += 4; + break; + + default: + fprintf( stderr, "Unknown arg type '%c'\n", args[i] ); + } + } + if ( reg_func ) + fprintf( outfile, "%s context", i? ",\n" : "" ); + else if (varargs) + fprintf( outfile, "%s args + %d", i? ",\n" : "", argsize ); + fprintf( outfile, " );\n}\n\n" ); +} +#endif + + +/******************************************************************* + * get_function_name + */ +static const char *get_function_name( const ORDDEF *odp ) +{ + static char buffer[80]; + + sprintf( buffer, "%s_%s_%s", + (odp->type == TYPE_PASCAL) ? "p" : + (odp->type == TYPE_VARARGS) ? "v" : "c", + (odp->flags & FLAG_REGISTER) ? "regs" : + (odp->flags & FLAG_RET16) ? "word" : "long", + odp->u.func.arg_types ); + return buffer; +} + + +/******************************************************************* + * Spec16TypeCompare + */ +static int Spec16TypeCompare( const void *e1, const void *e2 ) +{ + const ORDDEF *odp1 = *(const ORDDEF * const *)e1; + const ORDDEF *odp2 = *(const ORDDEF * const *)e2; + int retval; + int type1 = odp1->type; + int type2 = odp2->type; + + if (type1 == TYPE_STUB) type1 = TYPE_CDECL; + if (type2 == TYPE_STUB) type2 = TYPE_CDECL; + + if ((retval = type1 - type2) != 0) return retval; + + type1 = odp1->flags & (FLAG_RET16|FLAG_REGISTER); + type2 = odp2->flags & (FLAG_RET16|FLAG_REGISTER); + + if ((retval = type1 - type2) != 0) return retval; + + return strcmp( odp1->u.func.arg_types, odp2->u.func.arg_types ); +} + + +/******************************************************************* + * output_stub_funcs + * + * Output the functions for stub entry points +*/ +static void output_stub_funcs( FILE *outfile, const DLLSPEC *spec ) +{ + int i; + char *p; + + for (i = 0; i <= spec->limit; i++) + { + ORDDEF *odp = spec->ordinals[i]; + if (!odp || odp->type != TYPE_STUB) continue; + fprintf( outfile, "#ifdef __GNUC__\n" ); + fprintf( outfile, "static void __wine_unimplemented( const char *func ) __attribute__((noreturn));\n" ); + fprintf( outfile, "#endif\n" ); + fprintf( outfile, "static void __wine_unimplemented( const char *func )\n{\n" ); + 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, " } rec;\n\n" ); + fprintf( outfile, " extern void __stdcall RtlRaiseException( struct exc_record * );\n\n" ); + fprintf( outfile, " rec.code = 0x%08x;\n", EXCEPTION_WINE_STUB ); + fprintf( outfile, " rec.flags = %d;\n", EH_NONCONTINUABLE ); + fprintf( outfile, " rec.rec = 0;\n" ); + fprintf( outfile, " rec.params = 2;\n" ); + fprintf( outfile, " rec.info[0] = \"%s\";\n", spec->file_name ); + fprintf( outfile, " rec.info[1] = func;\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}\n\n" ); + break; + } + for (i = 0; i <= spec->limit; i++) + { + ORDDEF *odp = spec->ordinals[i]; + if (!odp || odp->type != TYPE_STUB) continue; + odp->link_name = xrealloc( odp->link_name, strlen(odp->name) + 13 ); + strcpy( odp->link_name, "__wine_stub_" ); + strcat( odp->link_name, odp->name ); + for (p = odp->link_name; *p; p++) if (!isalnum(*p)) *p = '_'; + fprintf( outfile, "static void %s(void) { __wine_unimplemented(\"%s\"); }\n", + odp->link_name, odp->name ); + } +} + + +/******************************************************************* + * BuildSpec16File + * + * Build a Win16 assembly file from a spec file. + */ +void BuildSpec16File( FILE *outfile, DLLSPEC *spec ) +{ + ORDDEF **type, **typelist; + int i, nFuncs, nTypes; + int code_offset, data_offset, module_size, res_size; + unsigned char *data; + char constructor[100], destructor[100]; +#ifdef __i386__ + unsigned short code_selector = get_cs(); +#endif + + /* File header */ + + output_file_header( outfile ); + fprintf( outfile, "extern unsigned short __wine_call_from_16_word();\n" ); + fprintf( outfile, "extern unsigned int __wine_call_from_16_long();\n" ); + fprintf( outfile, "extern void __wine_call_from_16_regs();\n" ); + fprintf( outfile, "extern void __wine_call_from_16_thunk();\n" ); + + data = (unsigned char *)xmalloc( 0x10000 ); + memset( data, 0, 16 ); + data_offset = 16; + + if (!spec->dll_name) /* set default name from file name */ + { + char *p; + spec->dll_name = xstrdup( spec->file_name ); + if ((p = strrchr( spec->dll_name, '.' ))) *p = 0; + } + + output_stub_funcs( outfile, spec ); + + /* Build sorted list of all argument types, without duplicates */ + + typelist = (ORDDEF **)calloc( spec->limit+1, sizeof(ORDDEF *) ); + + for (i = nFuncs = 0; i <= spec->limit; i++) + { + ORDDEF *odp = spec->ordinals[i]; + if (!odp) continue; + switch (odp->type) + { + case TYPE_CDECL: + case TYPE_PASCAL: + case TYPE_VARARGS: + case TYPE_STUB: + typelist[nFuncs++] = odp; + + default: + break; + } + } + + qsort( typelist, nFuncs, sizeof(ORDDEF *), Spec16TypeCompare ); + + i = nTypes = 0; + while ( i < nFuncs ) + { + typelist[nTypes++] = typelist[i++]; + while ( i < nFuncs && Spec16TypeCompare( typelist + i, typelist + nTypes-1 ) == 0 ) + i++; + } + + /* Output CallFrom16 routines needed by this .spec file */ +#ifdef __i386__ + for ( i = 0; i < nTypes; i++ ) + { + char profile[101]; + + strcpy( profile, get_function_name( typelist[i] )); + BuildCallFrom16Func( outfile, profile, spec->file_name ); + } +#endif + + /* Output the DLL functions prototypes */ + + for (i = 0; i <= spec->limit; i++) + { + ORDDEF *odp = spec->ordinals[i]; + if (!odp) continue; + switch(odp->type) + { + case TYPE_CDECL: + case TYPE_PASCAL: + case TYPE_VARARGS: + fprintf( outfile, "extern void %s();\n", odp->link_name ); + break; + default: + break; + } + } + + /* Output code segment */ + + fprintf( outfile, "\n#include \"pshpack1.h\"\n" ); + fprintf( outfile, "\nstatic struct code_segment\n{\n" ); + fprintf( outfile, " struct {\n" ); +#ifdef __i386__ + fprintf( outfile, " unsigned char pushl;\n" ); /* pushl $relay */ + fprintf( outfile, " void *relay;\n" ); + fprintf( outfile, " unsigned char lcall;\n" ); /* lcall __FLATCS__:glue */ + fprintf( outfile, " void *glue;\n" ); + fprintf( outfile, " unsigned short flatcs;\n" ); +#endif + fprintf( outfile, " unsigned short lret;\n" ); /* lret $args */ + fprintf( outfile, " unsigned short args;\n" ); + fprintf( outfile, " unsigned int arg_types[2];\n" ); + fprintf( outfile, " } call[%d];\n", nTypes ); + fprintf( outfile, " struct {\n" ); +#ifdef __i386__ + fprintf( outfile, " unsigned short pushw_bp;\n" ); /* pushw %bp */ + fprintf( outfile, " unsigned char pushl;\n" ); /* pushl $target */ +#endif + fprintf( outfile, " void (*target)();\n" ); + fprintf( outfile, " unsigned short call;\n" ); /* call CALLFROM16 */ + fprintf( outfile, " short callfrom16;\n" ); + fprintf( outfile, " } entry[%d];\n", nFuncs ); + fprintf( outfile, "} code_segment =\n{\n {\n" ); + + code_offset = 0; + + for ( i = 0; i < nTypes; i++ ) + { + char profile[101], *arg; + unsigned int arg_types[2]; + int j, argsize = 0; + + strcpy( profile, get_function_name( typelist[i] )); + if ( typelist[i]->type == TYPE_PASCAL ) + for ( arg = typelist[i]->u.func.arg_types; *arg; arg++ ) + switch ( *arg ) + { + case 'w': /* word */ + case 's': /* s_word */ + argsize += 2; + break; + case 'l': /* long or segmented pointer */ + case 'T': /* segmented pointer to null-terminated string */ + case 'p': /* linear pointer */ + case 't': /* linear pointer to null-terminated string */ + argsize += 4; + break; + } + + /* build the arg types bit fields */ + arg_types[0] = arg_types[1] = 0; + for (j = 0; typelist[i]->u.func.arg_types[j]; j++) + { + int type = 0; + switch(typelist[i]->u.func.arg_types[j]) + { + case 'w': type = ARG_WORD; break; + case 's': type = ARG_SWORD; break; + case 'l': type = ARG_LONG; break; + case 'p': type = ARG_PTR; break; + case 't': type = ARG_STR; break; + case 'T': type = ARG_SEGSTR; break; + } + arg_types[j / 10] |= type << (3 * (j % 10)); + } + if (typelist[i]->flags & FLAG_REGISTER) arg_types[0] |= ARG_REGISTER; + if (typelist[i]->flags & FLAG_RET16) arg_types[0] |= ARG_RET16; + +#ifdef __i386__ + fprintf( outfile, " { 0x68, __wine_%s_CallFrom16_%s, 0x9a, __wine_call_from_16_%s,\n", + make_c_identifier(spec->file_name), profile, + (typelist[i]->flags & FLAG_REGISTER) ? "regs": + (typelist[i]->flags & FLAG_RET16) ? "word" : "long" ); + if (argsize) + fprintf( outfile, " 0x%04x, 0xca66, %d, { 0x%08x, 0x%08x } },\n", + code_selector, argsize, arg_types[0], arg_types[1] ); + else + fprintf( outfile, " 0x%04x, 0xcb66, 0x9090, { 0x%08x, 0x%08x } },\n", + code_selector, arg_types[0], arg_types[1] ); +#else + if (argsize) + fprintf( outfile, " { 0xca66, %d, { 0x%08x, 0x%08x } },\n", + argsize, arg_types[0], arg_types[1] ); + else + fprintf( outfile, " { 0xcb66, 0x9090, { 0x%08x, 0x%08x } },\n", + arg_types[0], arg_types[1] ); +#endif + code_offset += sizeof(CALLFROM16); + } + fprintf( outfile, " },\n {\n" ); + + for (i = 0; i <= spec->limit; i++) + { + ORDDEF *odp = spec->ordinals[i]; + if (!odp) continue; + switch (odp->type) + { + case TYPE_ABS: + odp->offset = LOWORD(odp->u.abs.value); + break; + + case TYPE_VARIABLE: + odp->offset = data_offset; + data_offset += StoreVariableCode( data + data_offset, 4, odp); + break; + + case TYPE_CDECL: + case TYPE_PASCAL: + case TYPE_VARARGS: + case TYPE_STUB: + type = bsearch( &odp, typelist, nTypes, sizeof(ORDDEF *), Spec16TypeCompare ); + assert( type ); + + fprintf( outfile, " /* %s.%d */ ", spec->dll_name, i ); +#ifdef __i386__ + fprintf( outfile, "{ 0x5566, 0x68, %s, 0xe866, %d /* %s */ },\n", +#else + fprintf( outfile, "{ %s, 0xe866, %d, /* %s */ },\n", +#endif + odp->link_name, + (type-typelist)*sizeof(CALLFROM16) - + (code_offset + sizeof(ENTRYPOINT16)), + get_function_name( odp ) ); + + odp->offset = code_offset; + code_offset += sizeof(ENTRYPOINT16); + break; + + default: + fprintf(stderr,"build: function type %d not available for Win16\n", + odp->type); + exit(1); + } + } + + fprintf( outfile, " }\n};\n" ); + + /* Output data segment */ + + dump_bytes( outfile, data, data_offset, "Data_Segment", 0 ); + + /* Build the module */ + + module_size = BuildModule16( outfile, code_offset, data_offset, spec ); + res_size = output_res16_data( outfile, spec ); + + /* Output the DLL descriptor */ + + fprintf( outfile, "#include \"poppack.h\"\n\n" ); + + fprintf( outfile, "static const struct dll_descriptor\n{\n" ); + fprintf( outfile, " unsigned char *module_start;\n" ); + fprintf( outfile, " int module_size;\n" ); + fprintf( outfile, " struct code_segment *code_start;\n" ); + fprintf( outfile, " unsigned char *data_start;\n" ); + fprintf( outfile, " const char *owner;\n" ); + fprintf( outfile, " const unsigned char *rsrc;\n" ); + fprintf( outfile, "} descriptor =\n{\n" ); + fprintf( outfile, " Module,\n" ); + fprintf( outfile, " sizeof(Module),\n" ); + fprintf( outfile, " &code_segment,\n" ); + fprintf( outfile, " Data_Segment,\n" ); + fprintf( outfile, " \"%s\",\n", spec->owner_name ); + fprintf( outfile, " %s\n", res_size ? "resource_data" : "0" ); + fprintf( outfile, "};\n" ); + + /* Output the DLL constructor */ + + sprintf( constructor, "__wine_spec_%s_init", make_c_identifier(spec->file_name) ); + sprintf( destructor, "__wine_spec_%s_fini", make_c_identifier(spec->file_name) ); + output_dll_init( outfile, constructor, destructor ); + + fprintf( outfile, + "void %s(void)\n" + "{\n" + " extern void __wine_register_dll_16( const struct dll_descriptor *descr );\n" + " __wine_register_dll_16( &descriptor );\n" + "}\n", constructor ); + fprintf( outfile, + "void %s(void)\n" + "{\n" + " extern void __wine_unregister_dll_16( const struct dll_descriptor *descr );\n" + " __wine_unregister_dll_16( &descriptor );\n" + "}\n", destructor ); +} diff --git a/reactos/tools/winebuild/spec32.c b/reactos/tools/winebuild/spec32.c new file mode 100644 index 00000000000..222cdabe500 --- /dev/null +++ b/reactos/tools/winebuild/spec32.c @@ -0,0 +1,1057 @@ +/* + * 32-bit spec files + * + * Copyright 1993 Robert J. Amstadt + * Copyright 1995 Martin von Loewis + * Copyright 1995, 1996, 1997 Alexandre Julliard + * Copyright 1997 Eric Youngdale + * Copyright 1999 Ulrich Weigand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#include +#include +#include +#include + +#include "winglue.h" + +#define EXCEPTION_WINE_STUB 0x80000100 /* stub entry point called */ +#define EH_NONCONTINUABLE 0x01 + +#include "build.h" + + +#ifdef __APPLE__ +# define __ASM_SKIP ".space" +#else +# define __ASM_SKIP ".skip" +#endif + +static int string_compare( const void *ptr1, const void *ptr2 ) +{ + const char * const *str1 = ptr1; + const char * const *str2 = ptr2; + return strcmp( *str1, *str2 ); +} + + +/******************************************************************* + * make_internal_name + * + * Generate an internal name for an entry point. Used for stubs etc. + */ +static const char *make_internal_name( const ORDDEF *odp, DLLSPEC *spec, const char *prefix ) +{ + static char buffer[256]; + if (odp->name || odp->export_name) + { + char *p; + sprintf( buffer, "__wine_%s_%s_%s", prefix, spec->file_name, + odp->name ? odp->name : odp->export_name ); + /* make sure name is a legal C identifier */ + for (p = buffer; *p; p++) if (!isalnum(*p) && *p != '_') break; + if (!*p) return buffer; + } + sprintf( buffer, "__wine_%s_%s_%d", prefix, make_c_identifier(spec->file_name), odp->ordinal ); + return buffer; +} + +/******************************************************************* + * declare_weak_function + * + * Output a prototype for a weak function. + */ +static void declare_weak_function( FILE *outfile, const char *ret_type, const char *name, const char *params) +{ + fprintf( outfile, "#ifdef __GNUC__\n" ); + fprintf( outfile, "# ifdef __APPLE__\n" ); + fprintf( outfile, "extern %s %s(%s) __attribute__((weak_import));\n", ret_type, name, params ); + fprintf( outfile, "static %s (*__wine_spec_weak_%s)(%s) = %s;\n", ret_type, name, params, name ); + fprintf( outfile, "#define %s __wine_spec_weak_%s\n", name, name ); + fprintf( outfile, "# else\n" ); + fprintf( outfile, "extern %s %s(%s) __attribute__((weak));\n", ret_type, name, params ); + fprintf( outfile, "# endif\n" ); + fprintf( outfile, "#else\n" ); + fprintf( outfile, "extern %s %s(%s);\n", ret_type, name, params ); + fprintf( outfile, "static void __asm__dummy_%s(void)", name ); + fprintf( outfile, " { asm(\".weak " __ASM_NAME("%s") "\"); }\n", name ); + fprintf( outfile, "#endif\n\n" ); +} + + +/******************************************************************* + * output_debug + * + * Output the debug channels. + */ +static int output_debug( FILE *outfile ) +{ + int i; + + if (!nb_debug_channels) return 0; + qsort( debug_channels, nb_debug_channels, sizeof(debug_channels[0]), string_compare ); + + for (i = 0; i < nb_debug_channels; i++) + fprintf( outfile, "char __wine_dbch_%s[] = \"\\003%s\";\n", + debug_channels[i], debug_channels[i] ); + + fprintf( outfile, "\nstatic char * const debug_channels[%d] =\n{\n", nb_debug_channels ); + for (i = 0; i < nb_debug_channels; i++) + { + fprintf( outfile, " __wine_dbch_%s", debug_channels[i] ); + if (i < nb_debug_channels - 1) fprintf( outfile, ",\n" ); + } + fprintf( outfile, "\n};\n\n" ); + fprintf( outfile, "static void *debug_registration;\n\n" ); + + return nb_debug_channels; +} + + +/******************************************************************* + * output_exports + * + * Output the export table for a Win32 module. + */ +static int output_exports( FILE *outfile, int nr_exports, DLLSPEC *spec ) +{ + int i, fwd_size = 0, total_size = 0; + + if (!nr_exports) return 0; + + fprintf( outfile, "asm(\".data\\n\"\n" ); + fprintf( outfile, " \"\\t.align %d\\n\"\n", get_alignment(4) ); + fprintf( outfile, " \"" __ASM_NAME("__wine_spec_exports") ":\\n\"\n" ); + + /* export directory header */ + + fprintf( outfile, " \"\\t.long 0\\n\"\n" ); /* Characteristics */ + fprintf( outfile, " \"\\t.long 0\\n\"\n" ); /* TimeDateStamp */ + fprintf( outfile, " \"\\t.long 0\\n\"\n" ); /* MajorVersion/MinorVersion */ + fprintf( outfile, " \"\\t.long __wine_spec_exp_names\\n\"\n" ); /* Name */ + fprintf( outfile, " \"\\t.long %d\\n\"\n", spec->base ); /* Base */ + fprintf( outfile, " \"\\t.long %d\\n\"\n", nr_exports ); /* NumberOfFunctions */ + fprintf( outfile, " \"\\t.long %d\\n\"\n", spec->nb_names ); /* NumberOfNames */ + fprintf( outfile, " \"\\t.long __wine_spec_exports_funcs\\n\"\n" ); /* AddressOfFunctions */ + if (spec->nb_names) + { + fprintf( outfile, " \"\\t.long __wine_spec_exp_name_ptrs\\n\"\n" ); /* AddressOfNames */ + fprintf( outfile, " \"\\t.long __wine_spec_exp_ordinals\\n\"\n" ); /* AddressOfNameOrdinals */ + } + else + { + fprintf( outfile, " \"\\t.long 0\\n\"\n" ); /* AddressOfNames */ + fprintf( outfile, " \"\\t.long 0\\n\"\n" ); /* AddressOfNameOrdinals */ + } + total_size += 10 * sizeof(int); + + /* output the function pointers */ + + fprintf( outfile, " \"__wine_spec_exports_funcs:\\n\"\n" ); + for (i = spec->base; i <= spec->limit; i++) + { + ORDDEF *odp = spec->ordinals[i]; + if (!odp) fprintf( outfile, " \"\\t.long 0\\n\"\n" ); + else switch(odp->type) + { + case TYPE_EXTERN: + case TYPE_STDCALL: + case TYPE_VARARGS: + case TYPE_CDECL: + if (!(odp->flags & FLAG_FORWARD)) + { + fprintf( outfile, " \"\\t.long " __ASM_NAME("%s") "\\n\"\n", + (odp->flags & FLAG_REGISTER) ? make_internal_name( odp, spec, "regs" ) + : odp->link_name ); + } + else + { + fprintf( outfile, " \"\\t.long __wine_spec_forwards+%d\\n\"\n", fwd_size ); + fwd_size += strlen(odp->link_name) + 1; + } + break; + case TYPE_STUB: + fprintf( outfile, " \"\\t.long " __ASM_NAME("%s") "\\n\"\n", + make_internal_name( odp, spec, "stub" ) ); + break; + default: + assert(0); + } + } + total_size += (spec->limit - spec->base + 1) * sizeof(int); + + if (spec->nb_names) + { + /* output the function name pointers */ + + int namepos = strlen(spec->file_name) + 1; + + fprintf( outfile, " \"__wine_spec_exp_name_ptrs:\\n\"\n" ); + for (i = 0; i < spec->nb_names; i++) + { + fprintf( outfile, " \"\\t.long __wine_spec_exp_names+%d\\n\"\n", namepos ); + namepos += strlen(spec->names[i]->name) + 1; + } + total_size += spec->nb_names * sizeof(int); + } + + /* output the function names */ + + fprintf( outfile, " \"\\t.text\\n\"\n" ); + fprintf( outfile, " \"__wine_spec_exp_names:\\n\"\n" ); + fprintf( outfile, " \"\\t" __ASM_STRING " \\\"%s\\\"\\n\"\n", spec->file_name ); + for (i = 0; i < spec->nb_names; i++) + fprintf( outfile, " \"\\t" __ASM_STRING " \\\"%s\\\"\\n\"\n", spec->names[i]->name ); + fprintf( outfile, " \"\\t.data\\n\"\n" ); + + if (spec->nb_names) + { + /* output the function ordinals */ + + fprintf( outfile, " \"__wine_spec_exp_ordinals:\\n\"\n" ); + for (i = 0; i < spec->nb_names; i++) + { + fprintf( outfile, " \"\\t" __ASM_SHORT " %d\\n\"\n", + spec->names[i]->ordinal - spec->base ); + } + total_size += spec->nb_names * sizeof(short); + if (spec->nb_names % 2) + { + fprintf( outfile, " \"\\t" __ASM_SHORT " 0\\n\"\n" ); + total_size += sizeof(short); + } + } + + /* output forward strings */ + + if (fwd_size) + { + fprintf( outfile, " \"__wine_spec_forwards:\\n\"\n" ); + for (i = spec->base; i <= spec->limit; i++) + { + ORDDEF *odp = spec->ordinals[i]; + if (odp && (odp->flags & FLAG_FORWARD)) + fprintf( outfile, " \"\\t" __ASM_STRING " \\\"%s\\\"\\n\"\n", odp->link_name ); + } + fprintf( outfile, " \"\\t.align %d\\n\"\n", get_alignment(4) ); + total_size += (fwd_size + 3) & ~3; + } + + /* output relays */ + + if (debugging) + { + for (i = spec->base; i <= spec->limit; i++) + { + ORDDEF *odp = spec->ordinals[i]; + unsigned int j, args, mask = 0; + const char *name; + + /* skip nonexistent entry points */ + if (!odp) goto ignore; + /* skip non-functions */ + if ((odp->type != TYPE_STDCALL) && (odp->type != TYPE_CDECL)) goto ignore; + /* skip norelay and forward entry points */ + if (odp->flags & (FLAG_NORELAY|FLAG_FORWARD)) goto ignore; + + for (j = 0; odp->u.func.arg_types[j]; j++) + { + if (odp->u.func.arg_types[j] == 't') mask |= 1<< (j*2); + if (odp->u.func.arg_types[j] == 'W') mask |= 2<< (j*2); + } + if ((odp->flags & FLAG_RET64) && (j < 16)) mask |= 0x80000000; + + name = odp->link_name; + args = strlen(odp->u.func.arg_types) * sizeof(int); + if (odp->flags & FLAG_REGISTER) name = make_internal_name( odp, spec, "regs" ); + + switch(odp->type) + { + case TYPE_STDCALL: + fprintf( outfile, " \"\\tjmp " __ASM_NAME("%s") "\\n\"\n", name ); + fprintf( outfile, " \"\\tret $%d\\n\"\n", args ); + fprintf( outfile, " \"\\t.long " __ASM_NAME("%s") ",0x%08x\\n\"\n", name, mask ); + break; + case TYPE_CDECL: + fprintf( outfile, " \"\\tjmp " __ASM_NAME("%s") "\\n\"\n", name ); + fprintf( outfile, " \"\\tret\\n\"\n" ); + fprintf( outfile, " \"\\t" __ASM_SHORT " %d\\n\"\n", args ); + fprintf( outfile, " \"\\t.long " __ASM_NAME("%s") ",0x%08x\\n\"\n", name, mask ); + break; + default: + assert(0); + } + continue; + + ignore: + fprintf( outfile, " \"\\t.long 0,0,0,0\\n\"\n" ); + } + } + + fprintf( outfile, " \"\\t.text\\n\"\n" ); + fprintf( outfile, " \"\\t.align %d\\n\"\n", get_alignment(4) ); + fprintf( outfile, ");\n\n" ); + + return total_size; +} + + +/******************************************************************* + * output_stub_funcs + * + * Output the functions for stub entry points +*/ +static void output_stub_funcs( FILE *outfile, DLLSPEC *spec ) +{ + int i; + + for (i = 0; i < spec->nb_entry_points; i++) + { + ORDDEF *odp = &spec->entry_points[i]; + if (odp->type != TYPE_STUB) continue; + fprintf( outfile, "#ifdef __GNUC__\n" ); + fprintf( outfile, "static void __wine_unimplemented( const char *func ) __attribute__((noreturn));\n" ); + fprintf( outfile, "#endif\n\n" ); + 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\n" ); + fprintf( outfile, "static void __wine_unimplemented( const char *func )\n{\n" ); + fprintf( outfile, " struct exc_record rec;\n" ); + fprintf( outfile, " rec.code = 0x%08x;\n", EXCEPTION_WINE_STUB ); + fprintf( outfile, " rec.flags = %d;\n", EH_NONCONTINUABLE ); + fprintf( outfile, " rec.rec = 0;\n" ); + fprintf( outfile, " rec.params = 2;\n" ); + fprintf( outfile, " rec.info[0] = \"%s\";\n", spec->file_name ); + fprintf( outfile, " rec.info[1] = func;\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}\n\n" ); + break; + } + + for (i = 0; i < spec->nb_entry_points; i++) + { + const ORDDEF *odp = &spec->entry_points[i]; + if (odp->type != TYPE_STUB) continue; + fprintf( outfile, "void %s(void) ", make_internal_name( odp, spec, "stub" ) ); + if (odp->name) + fprintf( outfile, "{ __wine_unimplemented(\"%s\"); }\n", odp->name ); + else if (odp->export_name) + fprintf( outfile, "{ __wine_unimplemented(\"%s\"); }\n", odp->export_name ); + else + fprintf( outfile, "{ __wine_unimplemented(\"%d\"); }\n", odp->ordinal ); + } +} + + +/******************************************************************* + * output_register_funcs + * + * Output the functions for register entry points + */ +static void output_register_funcs( FILE *outfile, DLLSPEC *spec ) +{ + const char *name; + int i; + + for (i = 0; i < spec->nb_entry_points; i++) + { + const ORDDEF *odp = &spec->entry_points[i]; + if (odp->type != TYPE_STDCALL && odp->type != TYPE_CDECL) continue; + if (!(odp->flags & FLAG_REGISTER)) continue; + if (odp->flags & FLAG_FORWARD) continue; + name = make_internal_name( odp, spec, "regs" ); + fprintf( outfile, + "asm(\".align %d\\n\\t\"\n" + " \"" __ASM_FUNC("%s") "\\n\\t\"\n" + " \"" __ASM_NAME("%s") ":\\n\\t\"\n" + " \"call " __ASM_NAME("__wine_call_from_32_regs") "\\n\\t\"\n" + " \".long " __ASM_NAME("%s") "\\n\\t\"\n" + " \".byte %d,%d\");\n", + get_alignment(4), + name, name, odp->link_name, + strlen(odp->u.func.arg_types) * sizeof(int), + (odp->type == TYPE_CDECL) ? 0 : (strlen(odp->u.func.arg_types) * sizeof(int)) ); + } +} + + +/******************************************************************* + * output_dll_init + * + * Output code for calling a dll constructor and destructor. + */ +void output_dll_init( FILE *outfile, const char *constructor, const char *destructor ) +{ + fprintf( outfile, "#ifndef __GNUC__\n" ); + fprintf( outfile, "static void __asm__dummy_dll_init(void) {\n" ); + fprintf( outfile, "#endif\n" ); + +#if defined(__i386__) + if (constructor) + { + fprintf( outfile, "asm(\"\\t.section\\t\\\".init\\\" ,\\\"ax\\\"\\n\"\n" ); + fprintf( outfile, " \"\\tcall " __ASM_NAME("%s") "\\n\"\n", constructor ); + fprintf( outfile, " \"\\t.section\\t\\\".text\\\"\\n\");\n" ); + } + if (destructor) + { + fprintf( outfile, "asm(\"\\t.section\\t\\\".fini\\\" ,\\\"ax\\\"\\n\"\n" ); + fprintf( outfile, " \"\\tcall " __ASM_NAME("%s") "\\n\"\n", destructor ); + fprintf( outfile, " \"\\t.section\\t\\\".text\\\"\\n\");\n" ); + } +#elif defined(__sparc__) + if (constructor) + { + fprintf( outfile, "asm(\"\\t.section\\t\\\".init\\\" ,\\\"ax\\\"\\n\"\n" ); + fprintf( outfile, " \"\\tcall " __ASM_NAME("%s") "\\n\"\n", constructor ); + fprintf( outfile, " \"\\tnop\\n\"\n" ); + fprintf( outfile, " \"\\t.section\\t\\\".text\\\"\\n\");\n" ); + } + if (destructor) + { + fprintf( outfile, "asm(\"\\t.section\\t\\\".fini\\\" ,\\\"ax\\\"\\n\"\n" ); + fprintf( outfile, " \"\\tcall " __ASM_NAME("%s") "\\n\"\n", destructor ); + fprintf( outfile, " \"\\tnop\\n\"\n" ); + fprintf( outfile, " \"\\t.section\\t\\\".text\\\"\\n\");\n" ); + } +#elif defined(__powerpc__) +# ifdef __APPLE__ +/* Mach-O doesn't have an init section */ + if (constructor) + { + fprintf( outfile, "asm(\"\\t.mod_init_func\\n\"\n" ); + fprintf( outfile, " \"\\t.align 2\\n\"\n" ); + fprintf( outfile, " \"\\t.long " __ASM_NAME("%s") "\\n\"\n", constructor ); + fprintf( outfile, " \"\\t.text\\n\");\n" ); + } + if (destructor) + { + fprintf( outfile, "asm(\"\\t.mod_term_func\\n\"\n" ); + fprintf( outfile, " \"\\t.align 2\\n\"\n" ); + fprintf( outfile, " \"\\t.long " __ASM_NAME("%s") "\\n\"\n", destructor ); + fprintf( outfile, " \"\\t.text\\n\");\n" ); + } +# else /* __APPLE__ */ + if (constructor) + { + fprintf( outfile, "asm(\"\\t.section\\t\\\".init\\\" ,\\\"ax\\\"\\n\"\n" ); + fprintf( outfile, " \"\\tbl " __ASM_NAME("%s") "\\n\"\n", constructor ); + fprintf( outfile, " \"\\t.section\\t\\\".text\\\"\\n\");\n" ); + } + if (destructor) + { + fprintf( outfile, "asm(\"\\t.section\\t\\\".fini\\\" ,\\\"ax\\\"\\n\"\n" ); + fprintf( outfile, " \"\\tbl " __ASM_NAME("%s") "\\n\"\n", destructor ); + fprintf( outfile, " \"\\t.section\\t\\\".text\\\"\\n\");\n" ); + } +# endif /* __APPLE__ */ +#elif defined(__ALPHA__) + if (constructor) + { + fprintf( outfile, "asm(\"\\t.section\\t\\\".init\\\" ,\\\"ax\\\"\\n\"\n" ); + fprintf( outfile, " \"\\tjsr $26," __ASM_NAME("%s") "\\n\"\n", constructor ); + fprintf( outfile, " \"\\t.section\\t\\\".text\\\"\\n\");\n" ); + } + if (destructor) + { + fprintf( outfile, "asm(\"\\t.section\\t\\\".fini\\\" ,\\\"ax\\\"\\n\"\n" ); + fprintf( outfile, " \"\\tjsr $26," __ASM_NAME("%s") "\\n\"\n", destructor ); + fprintf( outfile, " \"\\t.section\\t\\\".text\\\"\\n\");\n" ); + } +#else +#error You need to define the DLL constructor for your architecture +#endif + fprintf( outfile, "#ifndef __GNUC__\n" ); + fprintf( outfile, "}\n" ); + fprintf( outfile, "#endif\n" ); +} + + +/******************************************************************* + * BuildSpec32File + * + * Build a Win32 C file from a spec file. + */ +void BuildSpec32File( FILE *outfile, DLLSPEC *spec ) +{ + int exports_size = 0; + int nr_exports, nr_imports; + DWORD page_size; + const char *init_func = spec->init_func; + +#ifdef HAVE_GETPAGESIZE + page_size = getpagesize(); +#elif defined(__svr4__) + page_size = sysconf(_SC_PAGESIZE); +#elif defined(_WINDOWS) + { + SYSTEM_INFO si; + GetSystemInfo(&si); + page_size = si.dwPageSize; + } +#else +# error Cannot get the page size on this platform +#endif + + nr_exports = spec->base <= spec->limit ? spec->limit - spec->base + 1 : 0; + resolve_imports( spec ); + output_standard_file_header( outfile ); + + /* Reserve some space for the PE header */ + + fprintf( outfile, "extern char __wine_spec_pe_header[];\n" ); + fprintf( outfile, "#ifndef __GNUC__\n" ); + fprintf( outfile, "static void __asm__dummy_header(void) {\n" ); + fprintf( outfile, "#endif\n" ); + fprintf( outfile, "asm(\".text\\n\\t\"\n" ); + fprintf( outfile, " \".align %d\\n\"\n", get_alignment(page_size) ); + fprintf( outfile, " \"" __ASM_NAME("__wine_spec_pe_header") ":\\t" __ASM_SKIP " 65536\\n\\t\"\n" ); + fprintf( outfile, " \".data\\n\\t\"\n" ); + fprintf( outfile, " \".align %d\\n\"\n", get_alignment(4) ); + fprintf( outfile, " \"" __ASM_NAME("__wine_spec_data_start") ":\\t.long 1\");\n" ); + fprintf( outfile, "#ifndef __GNUC__\n" ); + fprintf( outfile, "}\n" ); + fprintf( outfile, "#endif\n" ); + +#ifdef __APPLE__ + fprintf( outfile, "static char _end[4];\n" ); +#else + fprintf( outfile, "extern char _end[];\n" ); +#endif + + fprintf( outfile, "extern int __wine_spec_data_start[], __wine_spec_exports[];\n\n" ); + +#ifdef __i386__ + fprintf( outfile, "#define __stdcall __attribute__((__stdcall__))\n\n" ); +#else + fprintf( outfile, "#define __stdcall\n\n" ); +#endif + + if (nr_exports) + { + /* Output the stub functions */ + + output_stub_funcs( outfile, spec ); + + fprintf( outfile, "#ifndef __GNUC__\n" ); + fprintf( outfile, "static void __asm__dummy(void) {\n" ); + fprintf( outfile, "#endif /* !defined(__GNUC__) */\n" ); + + /* Output code for all register functions */ + + output_register_funcs( outfile, spec ); + + /* Output the exports and relay entry points */ + + exports_size = output_exports( outfile, nr_exports, spec ); + + fprintf( outfile, "#ifndef __GNUC__\n" ); + fprintf( outfile, "}\n" ); + fprintf( outfile, "#endif /* !defined(__GNUC__) */\n" ); + } + + /* Output the DLL imports */ + + nr_imports = output_imports( outfile, spec ); + + /* Output the resources */ + + output_resources( outfile, spec ); + + /* Output the entry point function */ + + fprintf( outfile, "static int __wine_spec_init_state;\n" ); + fprintf( outfile, "extern int __wine_main_argc;\n" ); + fprintf( outfile, "extern char **__wine_main_argv;\n" ); + fprintf( outfile, "extern char **__wine_main_environ;\n" ); + fprintf( outfile, "extern unsigned short **__wine_main_wargv;\n" ); +#ifdef __APPLE__ + fprintf( outfile, "extern _dyld_func_lookup(char *, void *);" ); + fprintf( outfile, "static void __wine_spec_hidden_init(int argc, char** argv, char** envp)\n" ); + fprintf( outfile, "{\n" ); + fprintf( outfile, " void (*init)(void);\n" ); + fprintf( outfile, " _dyld_func_lookup(\"__dyld_make_delayed_module_initializer_calls\", (unsigned long *)&init);\n" ); + fprintf( outfile, " init();\n" ); + fprintf( outfile, "}\n" ); + fprintf( outfile, "static void __wine_spec_hidden_fini()\n" ); + fprintf( outfile, "{\n" ); + fprintf( outfile, " void (*fini)(void);\n" ); + fprintf( outfile, " _dyld_func_lookup(\"__dyld_mod_term_funcs\", (unsigned long *)&fini);\n" ); + fprintf( outfile, " fini();\n" ); + fprintf( outfile, "}\n" ); + fprintf( outfile, "#define _init __wine_spec_hidden_init\n" ); + fprintf( outfile, "#define _fini __wine_spec_hidden_fini\n" ); +#else + fprintf( outfile, "extern void _init(int, char**, char**);\n" ); + fprintf( outfile, "extern void _fini();\n" ); +#endif + + if (spec->characteristics & IMAGE_FILE_DLL) + { + if (init_func) + fprintf( outfile, "extern int __stdcall %s( void*, unsigned int, void* );\n\n", init_func ); + else + { + declare_weak_function( outfile, "int __stdcall", "DllMain", "void*, unsigned int, void*" ); + init_func = "DllMain"; + } + fprintf( outfile, + "static int __stdcall __wine_dll_main( void *inst, unsigned int reason, void *reserved )\n" + "{\n" + " int ret;\n" + " if (reason == %d && __wine_spec_init_state == 1)\n" + " _init( __wine_main_argc, __wine_main_argv, __wine_main_environ );\n" + " ret = %s ? %s( inst, reason, reserved ) : 1;\n" + " if (reason == %d && __wine_spec_init_state == 1) _fini();\n" + " return ret;\n" + "}\n", + DLL_PROCESS_ATTACH, init_func, init_func, DLL_PROCESS_DETACH ); + init_func = "__wine_dll_main"; + } + else switch(spec->subsystem) + { + case IMAGE_SUBSYSTEM_NATIVE: + if (init_func) + fprintf( outfile, "extern int __stdcall %s( void*, void* );\n\n", init_func ); + else + { + declare_weak_function( outfile, "int __stdcall", "DriverEntry", "void*, void*"); + init_func = "DriverEntry"; + } + fprintf( outfile, + "static int __stdcall __wine_driver_entry( void *obj, void *path )\n" + "{\n" + " int ret;\n" + " if (__wine_spec_init_state == 1)\n" + " _init( __wine_main_argc, __wine_main_argv, __wine_main_environ );\n" + " ret = %s ? %s( obj, path ) : 0;\n" + " if (__wine_spec_init_state == 1) _fini();\n" + " return ret;\n" + "}\n", + init_func, init_func ); + init_func = "__wine_driver_entry"; + break; + case IMAGE_SUBSYSTEM_WINDOWS_GUI: + case IMAGE_SUBSYSTEM_WINDOWS_CUI: + if (init_func) + fprintf( outfile, "extern int %s( int argc, char *argv[] );\n", init_func ); + else + { + declare_weak_function( outfile, "int", "main", "int argc, char *argv[]" ); + declare_weak_function( outfile, "int", "wmain", "int argc, unsigned short *argv[]" ); + declare_weak_function( outfile, "int __stdcall", "WinMain", "void *,void *,char *,int" ); + } + fprintf( outfile, + "\ntypedef struct {\n" + " unsigned int cb;\n" + " char *lpReserved, *lpDesktop, *lpTitle;\n" + " unsigned int dwX, dwY, dwXSize, dwYSize;\n" + " unsigned int dwXCountChars, dwYCountChars, dwFillAttribute, dwFlags;\n" + " unsigned short wShowWindow, cbReserved2;\n" + " char *lpReserved2;\n" + " void *hStdInput, *hStdOutput, *hStdError;\n" + "} STARTUPINFOA;\n" + "extern char * __stdcall GetCommandLineA(void);\n" + "extern void * __stdcall GetModuleHandleA(char *);\n" + "extern void __stdcall GetStartupInfoA(STARTUPINFOA *);\n" + "extern void __stdcall ExitProcess(unsigned int);\n" + "static void __wine_exe_main(void)\n" + "{\n" + " int ret;\n" + " if (__wine_spec_init_state == 1)\n" + " _init( __wine_main_argc, __wine_main_argv, __wine_main_environ );\n" ); + if (init_func) + fprintf( outfile, + " ret = %s( __wine_main_argc, __wine_main_argv );\n", init_func ); + else + fprintf( outfile, + " if (WinMain) {\n" + " STARTUPINFOA info;\n" + " char *cmdline = GetCommandLineA();\n" + " int bcount=0, in_quotes=0;\n" + " while (*cmdline) {\n" + " if ((*cmdline=='\\t' || *cmdline==' ') && !in_quotes) break;\n" + " else if (*cmdline=='\\\\') bcount++;\n" + " else if (*cmdline=='\\\"') {\n" + " if ((bcount & 1)==0) in_quotes=!in_quotes;\n" + " bcount=0;\n" + " }\n" + " else bcount=0;\n" + " cmdline++;\n" + " }\n" + " while (*cmdline=='\\t' || *cmdline==' ') cmdline++;\n" + " GetStartupInfoA( &info );\n" + " if (!(info.dwFlags & 1)) info.wShowWindow = 1;\n" + " ret = WinMain( GetModuleHandleA(0), 0, cmdline, info.wShowWindow );\n" + " }\n" + " else if (wmain) ret = wmain( __wine_main_argc, __wine_main_wargv );\n" + " else ret = main( __wine_main_argc, __wine_main_argv );\n" ); + fprintf( outfile, + " if (__wine_spec_init_state == 1) _fini();\n" + " ExitProcess( ret );\n" + "}\n\n" ); + init_func = "__wine_exe_main"; + break; + } + + /* Output the NT header */ + + /* this is the IMAGE_NT_HEADERS structure, but we cannot include winnt.h here */ + fprintf( outfile, "static const struct image_nt_headers\n{\n" ); + fprintf( outfile, " int Signature;\n" ); + fprintf( outfile, " struct file_header {\n" ); + fprintf( outfile, " short Machine;\n" ); + fprintf( outfile, " short NumberOfSections;\n" ); + fprintf( outfile, " int TimeDateStamp;\n" ); + fprintf( outfile, " void *PointerToSymbolTable;\n" ); + fprintf( outfile, " int NumberOfSymbols;\n" ); + fprintf( outfile, " short SizeOfOptionalHeader;\n" ); + fprintf( outfile, " short Characteristics;\n" ); + fprintf( outfile, " } FileHeader;\n" ); + fprintf( outfile, " struct opt_header {\n" ); + fprintf( outfile, " short Magic;\n" ); + fprintf( outfile, " char MajorLinkerVersion, MinorLinkerVersion;\n" ); + fprintf( outfile, " int SizeOfCode;\n" ); + fprintf( outfile, " int SizeOfInitializedData;\n" ); + fprintf( outfile, " int SizeOfUninitializedData;\n" ); + fprintf( outfile, " void *AddressOfEntryPoint;\n" ); + fprintf( outfile, " void *BaseOfCode;\n" ); + fprintf( outfile, " void *BaseOfData;\n" ); + fprintf( outfile, " void *ImageBase;\n" ); + fprintf( outfile, " int SectionAlignment;\n" ); + fprintf( outfile, " int FileAlignment;\n" ); + fprintf( outfile, " short MajorOperatingSystemVersion;\n" ); + fprintf( outfile, " short MinorOperatingSystemVersion;\n" ); + fprintf( outfile, " short MajorImageVersion;\n" ); + fprintf( outfile, " short MinorImageVersion;\n" ); + fprintf( outfile, " short MajorSubsystemVersion;\n" ); + fprintf( outfile, " short MinorSubsystemVersion;\n" ); + fprintf( outfile, " int Win32VersionValue;\n" ); + fprintf( outfile, " void *SizeOfImage;\n" ); + fprintf( outfile, " int SizeOfHeaders;\n" ); + fprintf( outfile, " int CheckSum;\n" ); + fprintf( outfile, " short Subsystem;\n" ); + fprintf( outfile, " short DllCharacteristics;\n" ); + fprintf( outfile, " int SizeOfStackReserve;\n" ); + fprintf( outfile, " int SizeOfStackCommit;\n" ); + fprintf( outfile, " int SizeOfHeapReserve;\n" ); + fprintf( outfile, " int SizeOfHeapCommit;\n" ); + fprintf( outfile, " int LoaderFlags;\n" ); + fprintf( outfile, " int NumberOfRvaAndSizes;\n" ); + fprintf( outfile, " struct { const void *VirtualAddress; int Size; } DataDirectory[%d];\n", + IMAGE_NUMBEROF_DIRECTORY_ENTRIES ); + fprintf( outfile, " } OptionalHeader;\n" ); + fprintf( outfile, "} nt_header = {\n" ); + fprintf( outfile, " 0x%04x,\n", IMAGE_NT_SIGNATURE ); /* Signature */ +#ifdef __i386__ + fprintf( outfile, " { 0x%04x,\n", IMAGE_FILE_MACHINE_I386 ); /* Machine */ +#elif defined(__powerpc__) + fprintf( outfile, " { 0x%04x,\n", IMAGE_FILE_MACHINE_POWERPC ); /* Machine */ +#elif defined(__ALPHA__) + fprintf( outfile, " { 0x%04x,\n", IMAGE_FILE_MACHINE_ALPHA ); /* Machine */ +#else + fprintf( outfile, " { 0x%04x,\n", IMAGE_FILE_MACHINE_UNKNOWN ); /* Machine */ +#endif + fprintf( outfile, " 0, 0, 0, 0,\n" ); + fprintf( outfile, " sizeof(nt_header.OptionalHeader),\n" ); /* SizeOfOptionalHeader */ + fprintf( outfile, " 0x%04x },\n", spec->characteristics ); /* Characteristics */ + + fprintf( outfile, " { 0x%04x,\n", IMAGE_NT_OPTIONAL_HDR_MAGIC ); /* Magic */ + fprintf( outfile, " 0, 0,\n" ); /* Major/MinorLinkerVersion */ + fprintf( outfile, " 0, 0, 0,\n" ); /* SizeOfCode/Data */ + fprintf( outfile, " %s,\n", init_func ); /* AddressOfEntryPoint */ + fprintf( outfile, " 0, __wine_spec_data_start,\n" ); /* BaseOfCode/Data */ + fprintf( outfile, " __wine_spec_pe_header,\n" ); /* ImageBase */ + fprintf( outfile, " %ld,\n", page_size ); /* SectionAlignment */ + fprintf( outfile, " %ld,\n", page_size ); /* FileAlignment */ + fprintf( outfile, " 1, 0,\n" ); /* Major/MinorOperatingSystemVersion */ + fprintf( outfile, " 0, 0,\n" ); /* Major/MinorImageVersion */ + fprintf( outfile, " %d,\n", spec->subsystem_major ); /* MajorSubsystemVersion */ + fprintf( outfile, " %d,\n", spec->subsystem_minor ); /* MinorSubsystemVersion */ + fprintf( outfile, " 0,\n" ); /* Win32VersionValue */ + fprintf( outfile, " _end,\n" ); /* SizeOfImage */ + fprintf( outfile, " %ld,\n", page_size ); /* SizeOfHeaders */ + fprintf( outfile, " 0,\n" ); /* CheckSum */ + fprintf( outfile, " 0x%04x,\n", spec->subsystem );/* Subsystem */ + fprintf( outfile, " 0,\n" ); /* DllCharacteristics */ + fprintf( outfile, " %d, %ld,\n", /* SizeOfStackReserve/Commit */ + (spec->stack_size ? spec->stack_size : 1024) * 1024, page_size ); + fprintf( outfile, " %d, %ld,\n", /* SizeOfHeapReserve/Commit */ + (spec->heap_size ? spec->heap_size : 1024) * 1024, page_size ); + fprintf( outfile, " 0,\n" ); /* LoaderFlags */ + fprintf( outfile, " %d,\n", IMAGE_NUMBEROF_DIRECTORY_ENTRIES ); /* NumberOfRvaAndSizes */ + fprintf( outfile, " {\n" ); + fprintf( outfile, " { %s, %d },\n", /* IMAGE_DIRECTORY_ENTRY_EXPORT */ + exports_size ? "__wine_spec_exports" : "0", exports_size ); + fprintf( outfile, " { %s, %s },\n", /* IMAGE_DIRECTORY_ENTRY_IMPORT */ + nr_imports ? "&imports" : "0", nr_imports ? "sizeof(imports)" : "0" ); + fprintf( outfile, " { %s, %s },\n", /* IMAGE_DIRECTORY_ENTRY_RESOURCE */ + spec->nb_resources ? "&resources" : "0", + spec->nb_resources ? "sizeof(resources)" : "0" ); + fprintf( outfile, " }\n }\n};\n\n" ); + + /* Output the DLL constructor */ + + fprintf( outfile, + "void __wine_spec_init(void)\n" + "{\n" + " extern void __wine_dll_register( const struct image_nt_headers *, const char * );\n" + " __wine_spec_init_state = 1;\n" + " __wine_dll_register( &nt_header, \"%s\" );\n" + "}\n\n", + spec->file_name ); + + output_dll_init( outfile, "__wine_spec_init_ctor", NULL ); + fprintf( outfile, + "void __wine_spec_init_ctor(void)\n" + "{\n" + " if (__wine_spec_init_state) return;\n" + " __wine_spec_init();\n" + " __wine_spec_init_state = 2;\n" + "}\n" ); +} + + +/******************************************************************* + * BuildDef32File + * + * Build a Win32 def file from a spec file. + */ +void BuildDef32File( FILE *outfile, DLLSPEC *spec ) +{ + const char *name; + int i; + + fprintf(outfile, "; File generated automatically from %s; do not edit!\n\n", + input_file_name ); + + fprintf(outfile, "LIBRARY %s\n\n", spec->file_name); + + fprintf(outfile, "EXPORTS\n"); + + /* Output the exports and relay entry points */ + + for(i = 0; i < spec->nb_entry_points; i++) + { + const ORDDEF *odp = &spec->entry_points[i]; + int is_data = 0; + + if (!odp) continue; + if (odp->name) name = odp->name; + else if (odp->type == TYPE_STUB) name = make_internal_name( odp, spec, "stub" ); + else if (odp->export_name) name = odp->export_name; + else name = make_internal_name( odp, spec, "noname_export" ); + + fprintf(outfile, " %s", name); + + switch(odp->type) + { + case TYPE_EXTERN: + is_data = 1; + /* fall through */ + case TYPE_VARARGS: + case TYPE_CDECL: + /* try to reduce output */ + if(strcmp(name, odp->link_name) || (odp->flags & FLAG_FORWARD)) + fprintf(outfile, "=%s", odp->link_name); + break; + case TYPE_STDCALL: + { + int at_param = strlen(odp->u.func.arg_types) * sizeof(int); + if (!kill_at) fprintf(outfile, "@%d", at_param); + if (odp->flags & FLAG_FORWARD) + { + fprintf(outfile, "=%s", odp->link_name); + } + else if (strcmp(name, odp->link_name)) /* try to reduce output */ + { + fprintf(outfile, "=%s", odp->link_name); + if (!kill_at) fprintf(outfile, "@%d", at_param); + } + break; + } + case TYPE_STUB: + { + if (!kill_at) + { + const char *check = name + strlen(name); + while (name != check && + '0' <= check[-1] && check[-1] <= '9') + { + check--; + } + if (name != check && check != name + strlen(name) && + '@' == check[-1]) + { + fprintf(outfile, "%s", check - 1); + } + } + if (NULL != odp->name) + { + fprintf(outfile, "=%s", make_internal_name( odp, spec, "stub" )); + } + break; + } + default: + assert(0); + } + fprintf( outfile, " @%d", odp->ordinal ); +#if 0 /* MinGW binutils cannot handle this correctly */ + if (!odp->name) fprintf( outfile, " NONAME" ); +#else + if (!odp->name && (odp->type == TYPE_STUB || odp->export_name)) fprintf( outfile, " NONAME" ); +#endif + if (is_data) fprintf( outfile, " DATA" ); +#if 0 + /* MinGW binutils cannot handle this correctly */ + if (odp->flags & FLAG_PRIVATE) fprintf( outfile, " PRIVATE" ); +#endif + fprintf( outfile, "\n" ); + } +} + + +/******************************************************************* + * BuildDebugFile + * + * Build the debugging channels source file. + */ +void BuildDebugFile( FILE *outfile, const char *srcdir, char **argv ) +{ + int nr_debug; + char *prefix, *p; + + while (*argv) + { + if (!parse_debug_channels( srcdir, *argv++ )) exit(1); + } + + output_standard_file_header( outfile ); + nr_debug = output_debug( outfile ); + if (!nr_debug) + { + fprintf( outfile, "/* no debug channels found for this module */\n" ); + return; + } + + if (output_file_name) + { + if ((p = strrchr( output_file_name, '/' ))) p++; + prefix = xstrdup( p ? p : output_file_name ); + if ((p = strchr( prefix, '.' ))) *p = 0; + strcpy( p, make_c_identifier(p) ); + } + else prefix = xstrdup( "_" ); + + /* Output the DLL constructor */ + + fprintf( outfile, + "#ifdef __GNUC__\n" + "void __wine_dbg_%s_init(void) __attribute__((constructor));\n" + "void __wine_dbg_%s_fini(void) __attribute__((destructor));\n" + "#else\n" + "static void __asm__dummy_dll_init(void) {\n", + prefix, prefix ); + +#if defined(__i386__) + fprintf( outfile, "asm(\"\\t.section\\t\\\".init\\\" ,\\\"ax\\\"\\n\"\n" ); + fprintf( outfile, " \"\\tcall " __ASM_NAME("__wine_dbg_%s_init") "\\n\"\n", prefix ); + fprintf( outfile, " \"\\t.section\\t\\\".fini\\\" ,\\\"ax\\\"\\n\"\n" ); + fprintf( outfile, " \"\\tcall " __ASM_NAME("__wine_dbg_%s_fini") "\\n\"\n", prefix ); + fprintf( outfile, " \"\\t.section\\t\\\".text\\\"\\n\");\n" ); +#elif defined(__sparc__) + fprintf( outfile, "asm(\"\\t.section\\t\\\".init\\\" ,\\\"ax\\\"\\n\"\n" ); + fprintf( outfile, " \"\\tcall " __ASM_NAME("__wine_dbg_%s_init") "\\n\"\n", prefix ); + fprintf( outfile, " \"\\tnop\\n\"\n" ); + fprintf( outfile, " \"\\t.section\\t\\\".fini\\\" ,\\\"ax\\\"\\n\"\n" ); + fprintf( outfile, " \"\\tcall " __ASM_NAME("__wine_dbg_%s_fini") "\\n\"\n", prefix ); + fprintf( outfile, " \"\\tnop\\n\"\n" ); + fprintf( outfile, " \"\\t.section\t\\\".text\\\"\\n\");\n" ); +#elif defined(__powerpc__) +# ifdef __APPLE__ + fprintf( outfile, "asm(\"\\t.mod_init_func\\n\"\n" ); + fprintf( outfile, " \"\\t.align 2\\n\"\n" ); + fprintf( outfile, " \"\\t.long " __ASM_NAME("__wine_dbg_%s_init") "\\n\"\n", prefix ); + fprintf( outfile, " \"\\t.text\\n\");\n" ); + fprintf( outfile, "asm(\"\\t.mod_term_func\\n\"\n" ); + fprintf( outfile, " \"\\t.align 2\\n\"\n" ); + fprintf( outfile, " \"\\t.long " __ASM_NAME("__wine_dbg_%s_fini") "\\n\"\n", prefix ); + fprintf( outfile, " \"\\t.text\\n\");\n" ); +# else + fprintf( outfile, "asm(\"\\t.section\\t\\\".init\\\" ,\\\"ax\\\"\\n\"\n" ); + fprintf( outfile, " \"\\tbl " __ASM_NAME("__wine_dbg_%s_init") "\\n\"\n", prefix ); + fprintf( outfile, " \"\\t.section\\t\\\".fini\\\" ,\\\"ax\\\"\\n\"\n" ); + fprintf( outfile, " \"\\tbl " __ASM_NAME("__wine_dbg_%s_fini") "\\n\"\n", prefix ); + fprintf( outfile, " \"\\t.text\\n\");\n" ); +# endif +#elif defined(__ALPHA__) + fprintf( outfile, "asm(\"\\t.section\\t\\\".init\\\" ,\\\"ax\\\"\\n\"\n" ); + fprintf( outfile, " \"\\tjsr $26," __ASM_NAME("__wine_dbg_%s_init") "\\n\"\n", prefix ); + fprintf( outfile, " \"\\t.section\\t\\\".fini\\\" ,\\\"ax\\\"\\n\"\n" ); + fprintf( outfile, " \"\\tjsr $26," __ASM_NAME("__wine_dbg_%s_fini") "\\n\"\n", prefix ); + fprintf( outfile, " \"\\t.section\\t\\\".text\\\"\\n\");\n" ); +#else +#error You need to define the DLL constructor for your architecture +#endif + fprintf( outfile, "}\n#endif /* defined(__GNUC__) */\n\n" ); + + fprintf( outfile, + "void __wine_dbg_%s_init(void)\n" + "{\n" + " extern void *__wine_dbg_register( char * const *, int );\n" + " if (!debug_registration) debug_registration = __wine_dbg_register( debug_channels, %d );\n" + "}\n\n", prefix, nr_debug ); + fprintf( outfile, + "void __wine_dbg_%s_fini(void)\n" + "{\n" + " extern void __wine_dbg_unregister( void* );\n" + " __wine_dbg_unregister( debug_registration );\n" + "}\n", prefix ); + + free( prefix ); +} + + +/******************************************************************* + * BuildPedllFile + * + * Build a PE DLL C file from a spec file. + */ +void BuildPedllFile( FILE *outfile, DLLSPEC *spec ) +{ + int nr_exports; + + nr_exports = spec->base <= spec->limit ? spec->limit - spec->base + 1 : 0; + output_standard_file_header( outfile ); + + if (nr_exports) + { + /* Output the stub functions */ + + output_stub_funcs( outfile, spec ); + } +} diff --git a/reactos/tools/winebuild/utils.c b/reactos/tools/winebuild/utils.c new file mode 100644 index 00000000000..bf71941ee0d --- /dev/null +++ b/reactos/tools/winebuild/utils.c @@ -0,0 +1,415 @@ +/* + * Small utility functions for winebuild + * + * Copyright 2000 Alexandre Julliard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#if !defined(WIN32) +#undef strdup +#endif + +#include +#include +#include +#include +#include + +#include "build.h" + +void *xmalloc (size_t size) +{ + void *res; + + res = malloc (size ? size : 1); + if (res == NULL) + { + fprintf (stderr, "Virtual memory exhausted.\n"); + exit (1); + } + return res; +} + +void *xrealloc (void *ptr, size_t size) +{ + void *res = realloc (ptr, size); + if (res == NULL) + { + fprintf (stderr, "Virtual memory exhausted.\n"); + exit (1); + } + return res; +} + +char *xstrdup( const char *str ) +{ + char *res = strdup( str ); + if (!res) + { + fprintf (stderr, "Virtual memory exhausted.\n"); + exit (1); + } + return res; +} + +char *strupper(char *s) +{ + char *p; + for (p = s; *p; p++) *p = toupper(*p); + return s; +} + +void fatal_error( const char *msg, ... ) +{ + va_list valist; + va_start( valist, msg ); + if (input_file_name) + { + fprintf( stderr, "%s:", input_file_name ); + if (current_line) + fprintf( stderr, "%d:", current_line ); + fputc( ' ', stderr ); + } + else fprintf( stderr, "winebuild: " ); + vfprintf( stderr, msg, valist ); + va_end( valist ); + exit(1); +} + +void fatal_perror( const char *msg, ... ) +{ + va_list valist; + va_start( valist, msg ); + if (input_file_name) + { + fprintf( stderr, "%s:", input_file_name ); + if (current_line) + fprintf( stderr, "%d:", current_line ); + fputc( ' ', stderr ); + } + vfprintf( stderr, msg, valist ); + perror( " " ); + va_end( valist ); + exit(1); +} + +void error( const char *msg, ... ) +{ + va_list valist; + va_start( valist, msg ); + if (input_file_name) + { + fprintf( stderr, "%s:", input_file_name ); + if (current_line) + fprintf( stderr, "%d:", current_line ); + fputc( ' ', stderr ); + } + vfprintf( stderr, msg, valist ); + va_end( valist ); + nb_errors++; +} + +void warning( const char *msg, ... ) +{ + va_list valist; + + if (!display_warnings) return; + va_start( valist, msg ); + if (input_file_name) + { + fprintf( stderr, "%s:", input_file_name ); + if (current_line) + fprintf( stderr, "%d:", current_line ); + fputc( ' ', stderr ); + } + fprintf( stderr, "warning: " ); + vfprintf( stderr, msg, valist ); + va_end( valist ); +} + +/* output a standard header for generated files */ +void output_standard_file_header( FILE *outfile ) +{ + if (input_file_name) + fprintf( outfile, "/* File generated automatically from %s; do not edit! */\n", + input_file_name ); + else + fprintf( outfile, "/* File generated automatically; do not edit! */\n" ); + fprintf( outfile, + "/* This file can be copied, modified and distributed without restriction. */\n\n" ); +} + +/* dump a byte stream into the assembly code */ +void dump_bytes( FILE *outfile, const unsigned char *data, int len, + const char *label, int constant ) +{ + int i; + + fprintf( outfile, "\nstatic %sunsigned char %s[%d] = {", + constant ? "const " : "", label, len ); + for (i = 0; i < len; i++) + { + if (!(i & 7)) fprintf( outfile, "\n " ); + fprintf( outfile, "0x%02x", *data++ ); + if (i < len - 1) fprintf( outfile, "," ); + } + fprintf( outfile, "\n};\n" ); +} + + +/******************************************************************* + * open_input_file + * + * Open a file in the given srcdir and set the input_file_name global variable. + */ +FILE *open_input_file( const char *srcdir, const char *name ) +{ + char *fullname; + FILE *file = fopen( name, "r" ); + + if (!file && srcdir) + { + fullname = xmalloc( strlen(srcdir) + strlen(name) + 2 ); + strcpy( fullname, srcdir ); + strcat( fullname, "/" ); + strcat( fullname, name ); + file = fopen( fullname, "r" ); + } + else fullname = xstrdup( name ); + + if (!file) fatal_error( "Cannot open file '%s'\n", fullname ); + input_file_name = fullname; + current_line = 1; + return file; +} + + +/******************************************************************* + * close_input_file + * + * Close the current input file (must have been opened with open_input_file). + */ +void close_input_file( FILE *file ) +{ + fclose( file ); + free( input_file_name ); + input_file_name = NULL; + current_line = 0; +} + + +/******************************************************************* + * remove_stdcall_decoration + * + * Remove a possible @xx suffix from a function name. + * Return the numerical value of the suffix, or -1 if none. + */ +int remove_stdcall_decoration( char *name ) +{ + char *p, *end = strrchr( name, '@' ); + if (!end || !end[1] || end == name) return -1; + /* make sure all the rest is digits */ + for (p = end + 1; *p; p++) if (!isdigit(*p)) return -1; + *end = 0; + return atoi( end + 1 ); +} + + +/******************************************************************* + * alloc_dll_spec + * + * Create a new dll spec file descriptor + */ +DLLSPEC *alloc_dll_spec(void) +{ + DLLSPEC *spec; + + spec = xmalloc( sizeof(*spec) ); + spec->file_name = NULL; + spec->dll_name = NULL; + spec->owner_name = NULL; + spec->init_func = NULL; + spec->type = SPEC_WIN32; + spec->base = MAX_ORDINALS; + spec->limit = 0; + spec->stack_size = 0; + spec->heap_size = 0; + spec->nb_entry_points = 0; + spec->alloc_entry_points = 0; + spec->nb_names = 0; + spec->nb_resources = 0; + spec->characteristics = 0; + spec->subsystem = 0; + spec->subsystem_major = 4; + spec->subsystem_minor = 0; + spec->entry_points = NULL; + spec->names = NULL; + spec->ordinals = NULL; + spec->resources = NULL; + return spec; +} + + +/******************************************************************* + * free_dll_spec + * + * Free dll spec file descriptor + */ +void free_dll_spec( DLLSPEC *spec ) +{ + int i; + + for (i = 0; i < spec->nb_entry_points; i++) + { + ORDDEF *odp = &spec->entry_points[i]; + free( odp->name ); + free( odp->export_name ); + free( odp->link_name ); + } + free( spec->file_name ); + free( spec->dll_name ); + free( spec->owner_name ); + free( spec->init_func ); + free( spec->entry_points ); + free( spec->names ); + free( spec->ordinals ); + free( spec->resources ); + free( spec ); +} + + +/******************************************************************* + * make_c_identifier + * + * Map a string to a valid C identifier. + */ +const char *make_c_identifier( const char *str ) +{ + static char buffer[256]; + char *p; + + for (p = buffer; *str && p < buffer+sizeof(buffer)-1; p++, str++) + { + if (isalnum(*str)) *p = *str; + else *p = '_'; + } + *p = 0; + return buffer; +} + + +/***************************************************************** + * Function: get_alignment + * + * Description: + * According to the info page for gas, the .align directive behaves + * differently on different systems. On some architectures, the + * argument of a .align directive is the number of bytes to pad to, so + * to align on an 8-byte boundary you'd say + * .align 8 + * On other systems, the argument is "the number of low-order zero bits + * that the location counter must have after advancement." So to + * align on an 8-byte boundary you'd say + * .align 3 + * + * The reason gas is written this way is that it's trying to mimick + * native assemblers for the various architectures it runs on. gas + * provides other directives that work consistantly across + * architectures, but of course we want to work on all arches with or + * without gas. Hence this function. + * + * + * Parameters: + * alignBoundary -- the number of bytes to align to. + * If we're on an architecture where + * the assembler requires a 'number + * of low-order zero bits' as a + * .align argument, then this number + * must be a power of 2. + * + */ +int get_alignment(int alignBoundary) +{ +#if defined(__powerpc__) || defined(__ALPHA__) + + int n = 0; + + switch(alignBoundary) + { + case 2: + n = 1; + break; + case 4: + n = 2; + break; + case 8: + n = 3; + break; + case 16: + n = 4; + break; + case 32: + n = 5; + break; + case 64: + n = 6; + break; + case 128: + n = 7; + break; + case 256: + n = 8; + break; + case 512: + n = 9; + break; + case 1024: + n = 10; + break; + case 2048: + n = 11; + break; + case 4096: + n = 12; + break; + case 8192: + n = 13; + break; + case 16384: + n = 14; + break; + case 32768: + n = 15; + break; + case 65536: + n = 16; + break; + default: + fatal_error("Alignment to %d-byte boundary not supported on this architecture.\n", + alignBoundary); + } + return n; + +#elif defined(__i386__) || defined(__sparc__) + + return alignBoundary; + +#else +#error "How does the '.align' assembler directive work on your architecture?" +#endif +} diff --git a/reactos/tools/winebuild/winebuild.man.in b/reactos/tools/winebuild/winebuild.man.in new file mode 100644 index 00000000000..4f47749fc9e --- /dev/null +++ b/reactos/tools/winebuild/winebuild.man.in @@ -0,0 +1,434 @@ +.\" -*- nroff -*- +.TH WINEBUILD 1 "March 2003" "@PACKAGE_STRING@" "Wine dll builder" +.SH NAME +winebuild \- Wine dll builder +.SH SYNOPSIS +.BI winebuild\ [options]\ [input\ files] +.SH DESCRIPTION +.B winebuild +generates the C and assembly files that are necessary to build a Wine +dll, which is basically a Win32 dll encapsulated inside a Unix +library. +.PP +.B winebuild +has different modes, depending on what kind of file it is asked to +generate. The mode is specified by one of the mode options specified +below. In addition to the mode option, various other command-line +option can be specified, as described in the \fBOPTIONS\fR section. +.SH "MODE OPTIONS" +You have to specify exactly one of the following options, depending on +what you want winebuild to generate. +.TP +.BI \--dll= filename +Build a C file from a .spec file (see \fBSPEC FILE SYNTAX\fR for +details), or from a standard Windows .def file. The resulting C file +must be compiled and linked to the other object files to build a +working Wine dll. +.br +In that mode, the +.I input files +should be the list of all object files that will be linked into the +final dll, to allow +.B winebuild +to get the list of all undefined symbols that need to be imported from +other dlls. +.TP +.BI \--exe= name +Build a C file for the named executable. This is basically the same as +the --dll mode except that it doesn't require a .spec file as input, +since an executable doesn't export functions. The resulting C file +must be compiled and linked to the other object files to build a +working Wine executable, and all the other object files must be listed +as +.I input files. +.TP +.BI \--def= file.spec +Build a .def file from a spec file. This is used when building dlls +with a PE (Win32) compiler. +.TP +.B \--debug +Build a C file containing the definitions for debugging channels. In +that mode the +.I input files +should be a list of C files to search for debug channel +definitions. The resulting C file must be compiled and linked with the +dll. +.TP +.B \--relay16 +Generate the assembly code for the 16-bit relay routines. This is for +Wine internal usage only, you should never need to use this option. +.TP +.B \--relay32 +Generate the assembly code for the 32-bit relay routines. This is for +Wine internal usage only, you should never need to use this option. +.SH OPTIONS +.TP +.BI \-C,\ --source-dir= directory +Change to the specified directory before reading source files. Only +meaningful in +.BR \--debug\ mode. +.TP +.BI \-D\ symbol +Ignored for compatibility with the C compiler. +.TP +.BI \-e,\ --entry= function +Specify the module entry point function; if not specified, the default +is +.B DllMain +for dlls, and +.B WinMain +for executables (if +.B WinMain +is not defined, the standard C +.B main +is used instead). This is only valid for Win32 modules. +.TP +.BI \-f\ flags +Ignored for compatibility with the C compiler. +.TP +.BI \-F,\ --filename= filename +Set the file name of the module. The default is to use the base name +of the spec file (without any extension). +.TP +.B \-h, --help +Display a usage message and exit. +.TP +.BI \-H,\ --heap= size +Specify the size of the module local heap in bytes (only valid for +Win16 modules); default is no local heap. +.TP +.BI \-i,\ --ignore= [-]symbol[,[-]symbol] +Specify a list of symbols that should be ignored when resolving +undefined symbols against the imported libraries. This forces these +symbols to be resolved from the Unix C library (or from another Unix +library linked with the application). If a symbol is prefixed by '-' +it is removed from the list instead of being added; a stand-alone '-' +clears the whole list. +.TP +.BI \-I\ directory +Ignored for compatibility with the C compiler. +.TP +.B \-k, --kill-at +Remove the stdcall decorations from the symbol names in the +generated .def file. Only meaningful in \fB--def\fR mode. +.TP +.BI \-K\ flags +Ignored for compatibility with the C compiler. +.TP +.BI \--ld-cmd= ld-command +Specify the command to use to link the object files; the default is +\fBld\fR. +.TP +.BI \--nm-cmd= nm-command +Specify the command to use to get the list of undefined symbols; the +default is \fBnm\fR. +.TP +.BI \-L,\ --library-path= directory +Append the specified directory to the list of directories that are +searched for import libraries. +.TP +.BI \-l,\ --library= name +Import the specified library, looking for a corresponding +\fIlibname.def\fR file in the directories specified with the \fB-L\fR +option. +.TP +.BI \-d,\ --delay-lib= name +Same as the \fB-l\fR option, but import the specified library in +delayed mode (i.e. the library won't be loaded until a function +imported from it is actually called). +.TP +.BI \-M,\ --main-module= module +Specify that we are building a 16-bit dll, that will ultimately be +linked together with the 32-bit dll specified in \fImodule\fR. Only +meaningful in \fB--dll\fR mode. +.TP +.BI \-N,\ --dll-name= dllname +Set the internal name of the module. It is only used in Win16 +modules. The default is to use the base name of the spec file (without +any extension). This is used for KERNEL, since it lives in +KRNL386.EXE. It shouldn't be needed otherwise. +.TP +.BI \-o,\ --output= file +Set the name of the output file (default is standard output). +.TP +.BI \-r,\ --res= rsrc.res +Load resources from the specified binary resource file. The +\fIrsrc.res\fR can be produced from a source resource file with +.BR wrc(1) +(or with a Windows resource compiler). +.br +This option is only necessary for Win16 resource files, the Win32 ones +can simply listed as +.I input files +and will automatically be handled correctly (though the +.B \-r +option will also work for Win32 files). +.TP +.BI --subsystem= subsystem[:major[.minor]] +Set the subsystem of the executable, which can be one of the following: +.br +.B console +for a command line executable, +.br +.B windows +for a graphical executable, +.br +.B native +for a native-mode dll. +.br +The entry point of a command line executable is a normal C \fBmain\fR +function. A \fBwmain\fR function can be used instead if you need the +argument array to use Unicode strings. A graphical executable has a +\fBWinMain\fR entry point. +.br +Optionally a major and minor subsystem version can also be specified; +the default subsystem version is 4.0. +.TP +.B \--version +Display the program version and exit. +.TP +.B \-w, --warnings +Turn on warnings. +.SH "SPEC FILE SYNTAX" +.SS "General syntax" +A spec file should contain a list of ordinal declarations. The general +syntax is the following: +.PP +.I ordinal functype +.RI [ flags ]\ exportname \ \fB(\fR\ [ args... ] \ \fB) \ [ handler ] +.br +.IB ordinal\ variable +.RI [ flags ]\ exportname \ \fB(\fR\ [ data... ] \ \fB) +.br +.IB ordinal\ extern +.RI [ flags ]\ exportname \ [ symbolname ] +.br +.IB ordinal\ stub +.RI [ flags ]\ exportname +.br +.IB ordinal\ equate +.RI [ flags ]\ exportname\ data +.br +.BI #\ comments +.PP +Declarations must fit on a single line, except if the end of line is +escaped using a backslash character. The +.B # +character anywhere in a line causes the rest of the line to be ignored +as a comment. +.PP +.I ordinal +specifies the ordinal number corresponding to the entry point, or '@' +for automatic ordinal allocation (Win32 only). +.PP +.I flags +is a series of optional flags, preceded by a '-' character. The +supported flags are: +.RS +.TP +.B -norelay +The entry point is not displayed in relay debugging traces (Win32 +only). +.TP +.B -noname +The entry point will be imported by ordinal instead of by name. +.TP +.B -ret16 +The function returns a 16-bit value (Win16 only). +.TP +.B -ret64 +The function returns a 64-bit value (Win32 only). +.TP +.B -i386 +The entry point is only available on i386 platforms. +.TP +.B -register +The function uses CPU register to pass arguments. +.TP +.B -private +The function cannot be imported from other dlls, it can only be +accessed through GetProcAddress. +.SS "Function ordinals" +Syntax: +.br +.I ordinal functype +.RI [ flags ]\ exportname \ \fB(\fR\ [ args... ] \ \fB) \ [ handler ] +.br + +This declaration defines a function entry point. The prototype defined by +.IR exportname \ \fB(\fR\ [ args... ] \ \fB) +specifies the name available for dynamic linking and the format of the +arguments. '@' can be used instead of +.I exportname +for ordinal-only exports. +.PP +.I functype +should be one of: +.RS +.TP +.B stdcall +for a normal Win32 function +.TP +.B pascal +for a normal Win16 function +.TP +.B cdecl +for a Win16 or Win32 function using the C calling convention +.TP +.B varargs +for a Win16 or Win32 function using the C calling convention with a +variable number of arguments +.RE +.PP +.I args +should be one or several of: +.RS +.TP +.B word +(16-bit unsigned value) +.TP +.B s_word +(16-bit signed word) +.TP +.B long +(32-bit value) +.TP +.B double +(64-bit value) +.TP +.B ptr +(linear pointer) +.TP +.B str +(linear pointer to a null-terminated ASCII string) +.TP +.B wstr +(linear pointer to a null-terminated Unicode string) +.TP +.B segptr +(segmented pointer) +.TP +.B segstr +(segmented pointer to a null-terminated ASCII string). +.HP +.RB Only\ ptr ,\ str ,\ wstr ,\ long\ and\ double +are valid for Win32 functions. +.RE +.PP +.I handler +is the name of the actual C function that will implement that entry +point in 32-bit mode. The handler can also be specified as +.IB dllname . function +to define a forwarded function (one whose implementation is in another +dll). If +.I handler +is not specified, it is assumed to be identical to +.I exportname. +.PP +This first example defines an entry point for the 32-bit GetFocus() +call: +.IP +@ stdcall GetFocus() GetFocus +.PP +This second example defines an entry point for the 16-bit +CreateWindow() call (the ordinal 100 is just an example); it also +shows how long lines can be split using a backslash: +.IP +100 pascal CreateWindow(ptr ptr long s_word s_word s_word \\ + s_word word word word ptr) WIN_CreateWindow +.PP +To declare a function using a variable number of arguments, specify +the function as +.B varargs +and declare it in the C file with a '...' parameter for a Win32 +function, or with an extra VA_LIST16 argument for a Win16 function. +See the wsprintf* functions in user.exe.spec and user32.spec for an +example. +.SS "Variable ordinals" +Syntax: +.br +.IB ordinal\ variable +.RI [ flags ]\ exportname \ \fB(\fR\ [ data... ] \ \fB) +.PP +This declaration defines data storage as 32-bit words at the ordinal +specified. +.I exportname +will be the name available for dynamic +linking. +.I data +can be a decimal number or a hex number preceeded by "0x". The +following example defines the variable VariableA at ordinal 2 and +containing 4 ints: +.IP +2 variable VariableA(-1 0xff 0 0) +.PP +This declaration only works in Win16 spec files. In Win32 you should +use +.B extern +instead (see below). +.SS "Extern ordinals" +Syntax: +.br +.IB ordinal\ extern +.RI [ flags ]\ exportname \ [ symbolname ] +.PP +This declaration defines an entry that simply maps to a C symbol +(variable or function). It only works in Win32 spec files. +.I exportname +will point to the symbol +.I symbolname +that must be defined in the C code. Alternatively, it can be of the +form +.IB dllname . symbolname +to define a forwarded symbol (one whose implementation is in another +dll). If +.I symbolname +is not specified, it is assumed to be identical to +.I exportname. +.SS "Stub ordinals" +Syntax: +.br +.IB ordinal\ stub +.RI [ flags ]\ exportname +.PP +This declaration defines a stub function. It makes the name and +ordinal available for dynamic linking, but will terminate execution +with an error message if the function is ever called. +.SS "Equate ordinals" +Syntax: +.br +.IB ordinal\ equate +.RI [ flags ]\ exportname\ data +.PP +This declaration defines an ordinal as an absolute value. +.I exportname +will be the name available for dynamic linking. +.I data +can be a decimal number or a hex number preceeded by "0x". +.SH AUTHORS +.B winebuild +has been worked on by many people over the years. The main authors are +Robert J. Amstadt, Alexandre Julliard, Martin von Loewis, Ulrich +Weigand and Eric Youngdale. Many other Wine developers have +contributed, please check the file Changelog in the Wine distribution +for the complete details. +.SH BUGS +It is not yet possible to use a PE-format dll in an import +specification; only Wine dlls can be imported. +.PP +If you find a bug, please submit a bug report at +.UR http://bugs.winehq.org +.B http://bugs.winehq.org. +.UE +.SH AVAILABILITY +.B winebuild +is part of the wine distribution, which is available through WineHQ, +the +.B wine +development headquarters, at +.UR http://www.winehq.org/ +.B http://www.winehq.org/. +.UE +.SH "SEE ALSO" +.BR wine (1), +.BR wrc (1). diff --git a/reactos/tools/winebuild/winglue.h b/reactos/tools/winebuild/winglue.h new file mode 100644 index 00000000000..ecbb4f86588 --- /dev/null +++ b/reactos/tools/winebuild/winglue.h @@ -0,0 +1,31 @@ +#ifndef _WINGLUE_H +#define _WINGLUE_H + +typedef unsigned char BYTE; +typedef unsigned short WORD; +typedef unsigned long DWORD; +typedef unsigned short WCHAR; + +#define LOBYTE(w) ((BYTE)(w)) +#define HIBYTE(w) ((BYTE)(((WORD)(w)>>8)&0xFF)) + +#define DLL_PROCESS_ATTACH 1 +#define DLL_PROCESS_DETACH 0 + +#define IMAGE_FILE_DLL 8192 +#define IMAGE_SUBSYSTEM_NATIVE 1 +#define IMAGE_SUBSYSTEM_WINDOWS_GUI 2 +#define IMAGE_SUBSYSTEM_WINDOWS_CUI 3 +#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16 +#define IMAGE_FILE_MACHINE_I386 332 +#define IMAGE_NT_SIGNATURE 0x00004550 +#define IMAGE_NT_OPTIONAL_HDR_MAGIC 0x10b + +#ifndef max +#define max(a,b) ((a)>(b)?(a):(b)) +#endif +#ifndef min +#define min(a,b) ((a)<(b)?(a):(b)) +#endif + +#endif /* _WINGLUE_H */