From: Rémi Bernon rbernon@codeweavers.com
Introducing a new --force-dlltool flag, enabled when -Wl,--delay-load linker flag is supported to keep support for MSVC-like import libs, as generated by LLVM dlltool. --- tools/makedep.c | 1 + tools/winebuild/build.h | 1 + tools/winebuild/import.c | 319 ++++++++++++++++++++++++++++++- tools/winebuild/main.c | 7 + tools/winebuild/winebuild.man.in | 4 + 5 files changed, 327 insertions(+), 5 deletions(-)
diff --git a/tools/makedep.c b/tools/makedep.c index d7e045bdd6e..d0adc949448 100644 --- a/tools/makedep.c +++ b/tools/makedep.c @@ -3313,6 +3313,7 @@ static void output_import_lib( struct makefile *make ) output( "\t%s%s -b %s -w --implib -o $@", cmd_prefix( "BUILD" ), tools_path( make, "winebuild" ), crosstarget ); if (make->is_win16) output_filename( "-m16" ); + if (delay_load_flag) output_filename( "--force-dlltool" ); output_filename( "--export" ); output_filename( spec_file ); output_filenames_obj_dir( make, make->crossimplib_files ); diff --git a/tools/winebuild/build.h b/tools/winebuild/build.h index 86115e05d25..74aa5f86ad1 100644 --- a/tools/winebuild/build.h +++ b/tools/winebuild/build.h @@ -356,6 +356,7 @@ extern int kill_at; extern int verbose; extern int link_ext_symbols; extern int force_pointer_size; +extern int force_dlltool; extern int unwind_tables; extern int use_msvcrt; extern int unix_lib; diff --git a/tools/winebuild/import.c b/tools/winebuild/import.c index a650401e711..df826d87ec7 100644 --- a/tools/winebuild/import.c +++ b/tools/winebuild/import.c @@ -1581,8 +1581,8 @@ void output_static_lib( const char *output_name, struct strarray files, int crea } }
-/* create a Windows-style import library */ -static void build_windows_import_lib( const char *lib_name, DLLSPEC *spec ) +/* create a Windows-style import library using dlltool */ +static void build_dlltool_import_lib( const char *lib_name, DLLSPEC *spec ) { struct strarray args; char *def_file; @@ -1625,6 +1625,314 @@ static void build_windows_import_lib( const char *lib_name, DLLSPEC *spec ) spawn( args ); }
+/* create a Windows-style import library */ +static void build_windows_import_lib( const char *lib_name, DLLSPEC *spec ) +{ + char *dll_name = encode_dll_name( spec->file_name ), *delay_load, *import_desc, *import_name; + struct strarray objs = empty_strarray; + int i, total, by_name; + const char *name; + + /* make sure assemble_files doesn't strip suffixes */ + for (i = 0; i < strlen( dll_name ); ++i) if (dll_name[i] == '.') dll_name[i] = '_'; + + delay_load = strmake( "__wine_delay_load_%s", dll_name ); + import_desc = strmake( "__wine_import_%s_desc", dll_name ); + import_name = strmake( "__wine_import_%s_name", dll_name ); + + new_output_as_file(); + + if (strendswith( lib_name, ".delay.a" )) + { + output( "\t.text\n" ); + output( "\n\t.align %d\n", get_alignment( get_ptr_size() )); + output( "%s\n", asm_globl( delay_load ) ); + + output_cfi( ".cfi_startproc" ); + switch (target.cpu) + { + case CPU_i386: + output( "\tpushl %%ecx\n" ); + output_cfi( ".cfi_adjust_cfa_offset 4" ); + output( "\tpushl %%edx\n" ); + output_cfi( ".cfi_adjust_cfa_offset 4" ); + output( "\tpushl %%eax\n" ); + output_cfi( ".cfi_adjust_cfa_offset 4" ); + output( "\tpushl $.L__wine_delay_import_desc\n" ); + output( "\tcall ___delayLoadHelper2@8\n" ); + output_cfi( ".cfi_adjust_cfa_offset -4" ); + output( "\tpopl %%edx\n" ); + output_cfi( ".cfi_adjust_cfa_offset -4" ); + output( "\tpopl %%ecx\n" ); + output_cfi( ".cfi_adjust_cfa_offset -4" ); + output( "\tjmp *%%eax\n" ); + break; + case CPU_x86_64: + output_cfi( ".seh_proc %s", asm_name( delay_load ) ); + output( "\tsub $0x48, %%rsp\n" ); + output_cfi( ".cfi_adjust_cfa_offset 0x48" ); + output_cfi( ".seh_stackalloc 0x48" ); + output_cfi( ".seh_endprologue" ); + output( "\tmov %%rcx, 0x40(%%rsp)\n" ); + output( "\tmov %%rdx, 0x38(%%rsp)\n" ); + output( "\tmov %%r8, 0x30(%%rsp)\n" ); + output( "\tmov %%r9, 0x28(%%rsp)\n" ); + output( "\tmov %%rax, %%rdx\n" ); + output( "\tlea .L__wine_delay_import_desc(%%rip), %%rcx\n" ); + output( "\tcall __delayLoadHelper2\n" ); + output( "\tmov 0x28(%%rsp), %%r9\n" ); + output( "\tmov 0x30(%%rsp), %%r8\n" ); + output( "\tmov 0x38(%%rsp), %%rdx\n" ); + output( "\tmov 0x40(%%rsp), %%rcx\n" ); + output( "\tadd $0x48, %%rsp\n" ); + output_cfi( ".cfi_adjust_cfa_offset -0x48" ); + output( "\tjmp *%%rax\n" ); + output_cfi( ".seh_endproc" ); + break; + case CPU_ARM: + output( "\tpush {r0-r3, FP, LR}\n" ); + output( "\tmov r1, IP\n" ); + output( "\tldr r0, 1f\n" ); + output( "\tldr r0, [r0]\n" ); + output( "\tbl __delayLoadHelper2\n" ); + output( "\tmov IP, r0\n" ); + output( "\tpop {r0-r3, FP, LR}\n" ); + output( "\tbx IP\n" ); + output( "1:\t.long .L__wine_delay_import_desc\n" ); + break; + case CPU_ARM64: + output( "\tstp x29, x30, [sp, #-80]!\n" ); + output( "\tmov x29, sp\n" ); + output( "\tstp x0, x1, [sp, #16]\n" ); + output( "\tstp x2, x3, [sp, #32]\n" ); + output( "\tstp x4, x5, [sp, #48]\n" ); + output( "\tstp x6, x7, [sp, #64]\n" ); + output( "\tmov x1, x16\n" ); + output( "\tadrp x0, .L__wine_delay_import_desc\n" ); + output( "\tadd x0, x0, #.L__wine_delay_import_desc\n" ); + output( "\tbl __delayLoadHelper2\n" ); + output( "\tmov x16, x0\n" ); + output( "\tldp x0, x1, [sp, #16]\n" ); + output( "\tldp x2, x3, [sp, #32]\n" ); + output( "\tldp x4, x5, [sp, #48]\n" ); + output( "\tldp x6, x7, [sp, #64]\n" ); + output( "\tldp x29, x30, [sp], #80\n" ); + output( "\tbr x16\n" ); + break; + } + output_cfi( ".cfi_endproc" ); + output_function_size( delay_load ); + output_gnu_stack_note(); + + output( "\n\t.section ".text$2"\n" ); + output( ".L__wine_delay_import_desc:\n" ); + output( "\t.long 1\n" ); /* DllAttributes */ + output_rva( "%s\n", asm_name( import_name ) ); /* DllNameRVA */ + output_rva( ".L__wine_delay_import_handle\n" ); /* ModuleHandleRVA */ + output_rva( ".L__wine_delay_import_IAT\n" ); /* ImportAddressTableRVA */ + output_rva( ".L__wine_delay_import_INT\n" ); /* ImportNameTableRVA */ + output( "\t.long 0\n"); /* BoundImportAddressTableRVA */ + output( "\t.long 0\n"); /* UnloadInformationTableRVA */ + output( "\t.long 0\n"); /* TimeDateStamp */ + + output( "\t.data\n" ); + output( ".L__wine_delay_import_handle:\n" ); + output( "\t%s 0\n", get_asm_ptr_keyword() ); + } + + output( "\n\t.section ".idata$2"\n" ); + output( "\n\t.align 4\n" ); + output( "%s\n", asm_globl( import_desc ) ); + output_rva( ".L__wine_import_names" ); /* OriginalFirstThunk */ + output( "\t.long 0\n" ); /* TimeDateStamp */ + output( "\t.long 0\n" ); /* ForwarderChain */ + output_rva( "%s", asm_name( import_name ) ); /* Name */ + output_rva( ".L__wine_import_addrs" ); /* FirstThunk */ + + output( "\n\t.section ".idata$4"\n" ); + output( "\n\t.align 4\n" ); + output( ".L__wine_import_names:\n" ); /* OriginalFirstThunk head */ + if (strendswith( lib_name, ".delay.a" )) + { + output( "\t%s 0\n", get_asm_ptr_keyword() ); /* OriginalFirstThunk tail */ + output( ".L__wine_delay_import_INT:\n" ); + } + + output( "\n\t.section ".idata$5"\n" ); + output( "\n\t.align 4\n" ); + output( ".L__wine_import_addrs:\n" ); /* FirstThunk head */ + if (strendswith( lib_name, ".delay.a" )) + { + output( "\t%s 0\n", get_asm_ptr_keyword() ); /* FirstThunk tail */ + output( ".L__wine_delay_import_IAT:\n" ); + } + + /* reference name symbol from tail object */ + output( "\n\t.globl %s\n", asm_name( import_name ) ); + + /* _head suffix to keep this object sections first */ + assemble_files( strmake( "%s_head", dll_name ) ); + strarray_addall( &objs, as_files ); + as_files = empty_strarray; + + new_output_as_file(); + + output( "\n\t.section ".idata$7"\n" ); + output( "\n\t.align 4\n" ); + output( "%s\n", asm_globl( import_name ) ); + output( "\t%s "%s"\n", get_asm_string_keyword(), spec->file_name ); + output( "\n\t.section ".idata$4"\n" ); + output( "\n\t.align 4\n" ); + output( "\t%s 0\n", get_asm_ptr_keyword() ); /* OriginalFirstThunk tail */ + output( "\n\t.section ".idata$5"\n" ); + output( "\n\t.align 4\n" ); + output( "\t%s 0\n", get_asm_ptr_keyword() ); /* FirstThunk tail */ + + /* _tail suffix to keep this object sections last */ + assemble_files( strmake( "%s_tail", dll_name ) ); + strarray_addall( &objs, as_files ); + as_files = empty_strarray; + + for (i = total = 0; i < spec->nb_entry_points; i++) + { + const ORDDEF *odp = &spec->entry_points[i]; + const char *abi_name; + char *imp_name; + + if (odp->name) name = odp->name; + else if (odp->export_name) name = odp->export_name; + else continue; + + if (odp->flags & FLAG_PRIVATE) continue; + total++; + + /* C++ mangled names cannot be imported */ + if (strpbrk( name, "?@" )) continue; + + switch(odp->type) + { + case TYPE_VARARGS: + case TYPE_CDECL: + case TYPE_STDCALL: + by_name = odp->name && !(odp->flags & FLAG_ORDINAL); + abi_name = get_abi_name( odp, name ); + imp_name = strmake( "%s_imp_%s", target.cpu != CPU_i386 ? "_" : "", + asm_name( abi_name ) ); + + new_output_as_file(); + + output( "\n\t.text\n" ); + output( "\n\t.align %d\n", get_alignment( get_ptr_size() )); + output( "%s\n", asm_globl( abi_name ) ); + + switch (target.cpu) + { + case CPU_i386: + output( "\tjmp *%s\n", asm_name( imp_name ) ); + if (strendswith( lib_name, ".delay.a" )) + { + output( "\t.L__wine_delay_import:\n" ); + output( "\tmov $%s,%%eax\n", asm_name( imp_name ) ); + output( "\tjmp %s\n", asm_name( delay_load ) ); + } + break; + case CPU_x86_64: + output( "\tjmp *%s(%%rip)\n", asm_name( imp_name ) ); + if (strendswith( lib_name, ".delay.a" )) + { + output( "\t.L__wine_delay_import:\n" ); + output( "\tlea %s(%%rip),%%rax\n", asm_name( imp_name ) ); + output( "\tjmp %s\n", asm_name( delay_load ) ); + } + break; + case CPU_ARM: + output( "\tldr IP, 1f\n"); + output( "\tldr PC, [IP]\n"); + if (strendswith( lib_name, ".delay.a" )) + { + output( "\t.L__wine_delay_import:\n" ); + output( "\tldr IP, 1f\n"); + output( "\tldr IP, [IP]\n"); + output( "\tb %s\n", asm_name( delay_load ) ); + } + output( "1:\t.long %s\n", asm_name( imp_name ) ); + break; + case CPU_ARM64: + output( "\tadrp x16, %s\n", arm64_page( asm_name( imp_name ) ) ); + output( "\tadd x16, x16, #%s\n", arm64_pageoff( asm_name( imp_name ) ) ); + output( "\tbr x16\n" ); + if (strendswith( lib_name, ".delay.a" )) + { + output( "\t.L__wine_delay_import:\n" ); + output( "\tadrp x16, %s\n", arm64_page( asm_name( imp_name ) ) ); + output( "\tadd x16, x16, #%s\n", arm64_pageoff( asm_name( imp_name ) ) ); + output( "\tb %s\n", asm_name( delay_load ) ); + } + break; + } + + /* reference head object to always pull its sections */ + output( "\n\t.section ".idata$7"\n" ); + output( "\n\t.align 4\n" ); + output_rva( "%s", asm_name( import_desc ) ); + + output( "\n\t.section ".idata$4"\n" ); + output( "\n\t.align 4\n" ); + if (by_name) + { + output_rva( ".L__wine_import_name" ); + if (get_ptr_size() == 8) output( "\t.long 0\n" ); + } + else + { + if (get_ptr_size() == 4) output( "\t.long 0x8000%04x\n", odp->ordinal ); + else output( "\t.quad 0x800000000000%04x\n", odp->ordinal ); + } + + output( "\n\t.section ".idata$5"\n" ); + output( "\n\t.align 4\n" ); + output( "%s\n", asm_globl( imp_name ) ); + if (strendswith( lib_name, ".delay.a" )) + output( "\t%s .L__wine_delay_import\n", get_asm_ptr_keyword() ); + else if (by_name) + { + output_rva( ".L__wine_import_name" ); + if (get_ptr_size() == 8) output( "\t.long 0\n" ); + } + else + { + if (get_ptr_size() == 4) output( "\t.long 0x8000%04x\n", odp->ordinal ); + else output( "\t.quad 0x800000000000%04x\n", odp->ordinal ); + } + + if (by_name) + { + output( "\n\t.section ".idata$6"\n" ); + output( "\n\t.align 2\n" ); + output( "\t.L__wine_import_name:\n" ); + output( "\t.short %d\n", odp->ordinal ); + output( "\t%s "%s"\n", get_asm_string_keyword(), name ); + } + + free( imp_name ); + break; + + default: + break; + } + } + + /* _syms suffix to keep these objects sections in between _head and _tail */ + assemble_files( strmake( "%s_syms", dll_name ) ); + strarray_addall( &objs, as_files ); + as_files = objs; + + free( delay_load ); + free( import_desc ); + free( import_name ); + free( dll_name ); +} + /* create a Unix-style import library */ static void build_unix_import_lib( DLLSPEC *spec ) { @@ -1684,14 +1992,15 @@ static void build_unix_import_lib( DLLSPEC *spec ) /* output an import library for a Win32 module and additional object files */ void output_import_lib( DLLSPEC *spec, struct strarray files ) { - if (is_pe()) + if (is_pe() && force_dlltool) { - build_windows_import_lib( output_file_name, spec ); + build_dlltool_import_lib( output_file_name, spec ); if (files.count) output_static_lib( output_file_name, files, 0 ); } else { - build_unix_import_lib( spec ); + if (is_pe()) build_windows_import_lib( output_file_name, spec ); + else build_unix_import_lib( spec ); output_static_lib( output_file_name, files, 1 ); } } diff --git a/tools/winebuild/main.c b/tools/winebuild/main.c index cbe1e4669c1..506b88029d8 100644 --- a/tools/winebuild/main.c +++ b/tools/winebuild/main.c @@ -40,6 +40,7 @@ int display_warnings = 0; int kill_at = 0; int verbose = 0; int link_ext_symbols = 0; +int force_dlltool = 0; int force_pointer_size = 0; int unwind_tables = 0; int use_msvcrt = 0; @@ -208,6 +209,7 @@ static const char usage_str[] = " -f FLAGS Compiler flags (-fPIC and -fasynchronous-unwind-tables are supported)\n" " -F, --filename=DLLFILE Set the DLL filename (default: from input file name)\n" " --fake-module Create a fake binary module\n" +" --force-dlltool Force using dlltool to generate import library\n" " -h, --help Display this help message\n" " -H, --heap=SIZE Set the heap size for a Win16 dll\n" " -I DIR Ignored for C flags compatibility\n" @@ -257,6 +259,7 @@ enum long_options_values LONG_OPT_EXTERNAL_SYMS, LONG_OPT_FAKE_MODULE, LONG_OPT_FIXUP_CTORS, + LONG_OPT_FORCE_DLLTOOL, LONG_OPT_LARGE_ADDRESS_AWARE, LONG_OPT_LDCMD, LONG_OPT_NMCMD, @@ -290,6 +293,7 @@ static const struct long_option long_options[] = { "data-only", 0, LONG_OPT_DATA_ONLY }, { "external-symbols", 0, LONG_OPT_EXTERNAL_SYMS }, { "fake-module", 0, LONG_OPT_FAKE_MODULE }, + { "force-dlltool", 0, LONG_OPT_FORCE_DLLTOOL }, { "large-address-aware", 0, LONG_OPT_LARGE_ADDRESS_AWARE }, { "ld-cmd", 1, LONG_OPT_LDCMD }, { "nm-cmd", 1, LONG_OPT_NMCMD }, @@ -493,6 +497,9 @@ static void option_callback( int optc, char *optarg ) case LONG_OPT_FAKE_MODULE: fake_module = 1; break; + case LONG_OPT_FORCE_DLLTOOL: + force_dlltool = 1; + break; case LONG_OPT_EXTERNAL_SYMS: link_ext_symbols = 1; break; diff --git a/tools/winebuild/winebuild.man.in b/tools/winebuild/winebuild.man.in index 2637b5beb5f..35a3162553c 100644 --- a/tools/winebuild/winebuild.man.in +++ b/tools/winebuild/winebuild.man.in @@ -138,6 +138,10 @@ Create a fake PE module for a dll or exe, instead of the normal assembly or object file. The PE module contains the resources for the module, but no executable code. .TP +.B --force-dlltool +Force using dlltool to generate the import library, instead of using the +builtin import library generation support. +.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).