This is slightly different from dlltool but I think it should be compatible. The transition is done by first replacing dlltool with its bugs, and fixing them in separate changes.
I based the ARM / AARCH64 implementation on the existing code around, but I have no idea if it is correct, and dlltool also doesn't include any delay load implementation for ARM.
-- v2: winebuild: Put the delay import descriptor in data section. winebuild: Fix import hint value for symbols imported by name. winebuild: Implement import lib generation to replace dlltool. winebuild: Split output_static_lib into output_(import|static)_lib. winebuild: Use asm_name to add underscore prefix in asm_globl. winebuild: Pass the desired symbol name to get_link_name.
From: Rémi Bernon rbernon@codeweavers.com
And rename it to get_abi_name. --- tools/winebuild/build.h | 2 +- tools/winebuild/import.c | 4 ++-- tools/winebuild/spec16.c | 2 +- tools/winebuild/spec32.c | 10 +++++----- tools/winebuild/utils.c | 24 ++++++++++++------------ 5 files changed, 21 insertions(+), 21 deletions(-)
diff --git a/tools/winebuild/build.h b/tools/winebuild/build.h index 9918c2341f4..79a22315b0d 100644 --- a/tools/winebuild/build.h +++ b/tools/winebuild/build.h @@ -273,7 +273,7 @@ extern DLLSPEC *alloc_dll_spec(void); extern void free_dll_spec( DLLSPEC *spec ); extern char *make_c_identifier( const char *str ); extern const char *get_stub_name( const ORDDEF *odp, const DLLSPEC *spec ); -extern const char *get_link_name( const ORDDEF *odp ); +extern const char *get_abi_name( const ORDDEF *odp, const char *name ); extern int sort_func_list( ORDDEF **list, int count, int (*compare)(const void *, const void *) ); extern unsigned int get_alignment(unsigned int align); extern unsigned int get_page_size(void); diff --git a/tools/winebuild/import.c b/tools/winebuild/import.c index b1c14db9cab..3f3775dc3bd 100644 --- a/tools/winebuild/import.c +++ b/tools/winebuild/import.c @@ -627,7 +627,7 @@ static char *create_undef_symbols_file( DLLSPEC *spec ) if (odp->type == TYPE_STUB || odp->type == TYPE_ABS || odp->type == TYPE_VARIABLE) continue; if (odp->flags & FLAG_FORWARD) continue; if (odp->flags & FLAG_SYSCALL) continue; - output( "\t%s %s\n", get_asm_ptr_keyword(), asm_name( get_link_name( odp ))); + output( "\t%s %s\n", get_asm_ptr_keyword(), asm_name( get_abi_name( odp, odp->link_name ))); } for (j = 0; j < extra_ld_symbols.count; j++) output( "\t%s %s\n", get_asm_ptr_keyword(), asm_name(extra_ld_symbols.str[j]) ); @@ -1393,7 +1393,7 @@ void output_syscalls( DLLSPEC *spec ) for (i = 0; i < count; i++) { ORDDEF *odp = syscalls[i]; - const char *name = get_link_name(odp); + const char *name = get_abi_name(odp, odp->link_name); unsigned int id = (spec->syscall_table << 12) + i;
output( "\t.align %d\n", get_alignment(16) ); diff --git a/tools/winebuild/spec16.c b/tools/winebuild/spec16.c index 8d4f96ea323..ea5fb44986c 100644 --- a/tools/winebuild/spec16.c +++ b/tools/winebuild/spec16.c @@ -738,7 +738,7 @@ static void output_module16( DLLSPEC *spec ) output( ".L__wine_%s_%u:\n", spec->c_name, i ); output( "\tpushw %%bp\n" ); output( "\tpushl $%s\n", - asm_name( odp->type == TYPE_STUB ? get_stub_name( odp, spec ) : get_link_name( odp ))); + asm_name( odp->type == TYPE_STUB ? get_stub_name( odp, spec ) : get_abi_name( odp, odp->link_name ))); output( "\tcallw .L__wine_spec_callfrom16_%s\n", get_callfrom16_name( odp ) ); } output( ".L__wine_spec_code_segment_end:\n" ); diff --git a/tools/winebuild/spec32.c b/tools/winebuild/spec32.c index c09e58084cc..c445a2440d2 100644 --- a/tools/winebuild/spec32.c +++ b/tools/winebuild/spec32.c @@ -470,7 +470,7 @@ void output_exports( DLLSPEC *spec ) } else { - output( "\t%s %s\n", func_ptr, asm_name( get_link_name( odp ))); + output( "\t%s %s\n", func_ptr, asm_name( get_abi_name( odp, odp->link_name ))); } break; case TYPE_STUB: @@ -592,14 +592,14 @@ void output_exports( DLLSPEC *spec ) if (UsePIC) { output( "\tcall %s\n", asm_name("__wine_spec_get_pc_thunk_eax") ); - output( "1:\tjmp *__imp_%s-1b(%%eax)\n", asm_name( get_link_name( odp ))); + output( "1:\tjmp *__imp_%s-1b(%%eax)\n", asm_name( get_abi_name( odp, odp->link_name ))); needs_get_pc_thunk = 1; } - else output( "\tjmp *__imp_%s\n", asm_name( get_link_name( odp ))); + else output( "\tjmp *__imp_%s\n", asm_name( get_abi_name( odp, odp->link_name ))); break; case CPU_x86_64: output( "\t.byte 0x48,0x8d,0xa4,0x24,0x00,0x00,0x00,0x00\n" ); /* hotpatch prolog */ - output( "\tjmp *__imp_%s(%%rip)\n", asm_name( get_link_name( odp ))); + output( "\tjmp *__imp_%s(%%rip)\n", asm_name( get_abi_name( odp, odp->link_name ))); break; default: assert(0); @@ -1325,7 +1325,7 @@ void output_def_file( DLLSPEC *spec, int import_only ) if (odp->flags & FLAG_FORWARD) output( "=%s", odp->link_name ); else if (strcmp(name, odp->link_name)) /* try to reduce output */ - output( "=%s", get_link_name( odp )); + output( "=%s", get_abi_name( odp, odp->link_name )); break; } case TYPE_STUB: diff --git a/tools/winebuild/utils.c b/tools/winebuild/utils.c index 4f1bdf7cd2d..eb75aaed50b 100644 --- a/tools/winebuild/utils.c +++ b/tools/winebuild/utils.c @@ -728,28 +728,28 @@ const char *get_stub_name( const ORDDEF *odp, const DLLSPEC *spec ) }
/* return the stdcall-decorated name for an entry point */ -const char *get_link_name( const ORDDEF *odp ) +const char *get_abi_name( const ORDDEF *odp, const char *name ) { static char *buffer; char *ret;
- if (target.cpu != CPU_i386) return odp->link_name; + if (target.cpu != CPU_i386) return name;
switch (odp->type) { case TYPE_STDCALL: if (is_pe()) { - if (odp->flags & FLAG_THISCALL) return odp->link_name; - if (odp->flags & FLAG_FASTCALL) ret = strmake( "@%s@%u", odp->link_name, get_args_size( odp )); - else if (!kill_at) ret = strmake( "%s@%u", odp->link_name, get_args_size( odp )); - else return odp->link_name; + if (odp->flags & FLAG_THISCALL) return name; + if (odp->flags & FLAG_FASTCALL) ret = strmake( "@%s@%u", name, get_args_size( odp )); + else if (!kill_at) ret = strmake( "%s@%u", name, get_args_size( odp )); + else return name; } else { - if (odp->flags & FLAG_THISCALL) ret = strmake( "__thiscall_%s", odp->link_name ); - else if (odp->flags & FLAG_FASTCALL) ret = strmake( "__fastcall_%s", odp->link_name ); - else return odp->link_name; + if (odp->flags & FLAG_THISCALL) ret = strmake( "__thiscall_%s", name ); + else if (odp->flags & FLAG_FASTCALL) ret = strmake( "__fastcall_%s", name ); + else return name; } break;
@@ -758,13 +758,13 @@ const char *get_link_name( const ORDDEF *odp ) { int args = get_args_size( odp ); if (odp->flags & FLAG_REGISTER) args += get_ptr_size(); /* context argument */ - ret = strmake( "%s@%u", odp->link_name, args ); + ret = strmake( "%s@%u", name, args ); } - else return odp->link_name; + else return name; break;
default: - return odp->link_name; + return name; }
free( buffer );
From: Rémi Bernon rbernon@codeweavers.com
--- tools/winebuild/utils.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/tools/winebuild/utils.c b/tools/winebuild/utils.c index eb75aaed50b..f5928965c36 100644 --- a/tools/winebuild/utils.c +++ b/tools/winebuild/utils.c @@ -1016,8 +1016,7 @@ const char *asm_globl( const char *func ) break; case PLATFORM_MINGW: case PLATFORM_WINDOWS: - buffer = strmake( "\t.globl %s%s\n%s%s:", target.cpu == CPU_i386 ? "_" : "", func, - target.cpu == CPU_i386 ? "_" : "", func ); + buffer = strmake( "\t.globl %s\n%s:", asm_name( func ), asm_name( func ) ); break; default: buffer = strmake( "\t.globl %s\n\t.hidden %s\n%s:", func, func, func );
From: Rémi Bernon rbernon@codeweavers.com
--- tools/winebuild/build.h | 3 ++- tools/winebuild/import.c | 12 ++++++------ tools/winebuild/main.c | 4 ++-- 3 files changed, 10 insertions(+), 9 deletions(-)
diff --git a/tools/winebuild/build.h b/tools/winebuild/build.h index 79a22315b0d..86115e05d25 100644 --- a/tools/winebuild/build.h +++ b/tools/winebuild/build.h @@ -305,7 +305,8 @@ extern void output_module( DLLSPEC *spec ); extern void output_stubs( DLLSPEC *spec ); extern void output_syscalls( DLLSPEC *spec ); extern void output_imports( DLLSPEC *spec ); -extern void output_static_lib( DLLSPEC *spec, struct strarray files ); +extern void output_import_lib( DLLSPEC *spec, struct strarray files ); +extern void output_static_lib( const char *output_name, struct strarray files, int create ); extern void output_exports( DLLSPEC *spec ); extern int load_res32_file( const char *name, DLLSPEC *spec ); extern void output_resources( DLLSPEC *spec ); diff --git a/tools/winebuild/import.c b/tools/winebuild/import.c index 3f3775dc3bd..a650401e711 100644 --- a/tools/winebuild/import.c +++ b/tools/winebuild/import.c @@ -1552,7 +1552,7 @@ static void assemble_files( const char *prefix ) }
/* build a library from the current asm files and any additional object files in argv */ -static void build_library( const char *output_name, struct strarray files, int create ) +void output_static_lib( const char *output_name, struct strarray files, int create ) { struct strarray args;
@@ -1682,16 +1682,16 @@ static void build_unix_import_lib( DLLSPEC *spec ) }
/* output an import library for a Win32 module and additional object files */ -void output_static_lib( DLLSPEC *spec, struct strarray files ) +void output_import_lib( DLLSPEC *spec, struct strarray files ) { if (is_pe()) { - if (spec) build_windows_import_lib( output_file_name, spec ); - if (files.count || !spec) build_library( output_file_name, files, !spec ); + build_windows_import_lib( output_file_name, spec ); + if (files.count) output_static_lib( output_file_name, files, 0 ); } else { - if (spec) build_unix_import_lib( spec ); - build_library( output_file_name, files, 1 ); + 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 cfccee6cf8b..cbe1e4669c1 100644 --- a/tools/winebuild/main.c +++ b/tools/winebuild/main.c @@ -669,10 +669,10 @@ int main(int argc, char **argv) case MODE_IMPLIB: if (!spec_file_name) fatal_error( "missing .spec file\n" ); if (!parse_input_file( spec )) break; - output_static_lib( spec, files ); + output_import_lib( spec, files ); break; case MODE_STATICLIB: - output_static_lib( NULL, files ); + output_static_lib( output_file_name, files, 1 ); break; case MODE_BUILTIN: if (!files.count) fatal_error( "missing file argument for --builtin option\n" );
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).
From: Rémi Bernon rbernon@codeweavers.com
This fixes incorrect hint values generated by dlltool: instead of the index in the name table, it used the ordinal value, which almost always ends up in a hint lookup failure and a fallback to binary search. --- tools/winebuild/import.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/winebuild/import.c b/tools/winebuild/import.c index df826d87ec7..711c8e0cee6 100644 --- a/tools/winebuild/import.c +++ b/tools/winebuild/import.c @@ -1910,7 +1910,7 @@ static void build_windows_import_lib( const char *lib_name, DLLSPEC *spec ) 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.short %d\n", odp->hint ); output( "\t%s "%s"\n", get_asm_string_keyword(), name ); }
From: Rémi Bernon rbernon@codeweavers.com
This fixes incorrect .text section flags for any module using delay imports. The use of a custom .text$2 section as dlltool is doing causes the section to be flagged as DATA, and ends up with the .text section being writable, which triggers the anti tamper used in Forza Horizon. --- tools/winebuild/import.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/tools/winebuild/import.c b/tools/winebuild/import.c index 711c8e0cee6..bf208f6a009 100644 --- a/tools/winebuild/import.c +++ b/tools/winebuild/import.c @@ -1724,7 +1724,7 @@ static void build_windows_import_lib( const char *lib_name, DLLSPEC *spec ) output_function_size( delay_load ); output_gnu_stack_note();
- output( "\n\t.section ".text$2"\n" ); + output( "\t.data\n" ); output( ".L__wine_delay_import_desc:\n" ); output( "\t.long 1\n" ); /* DllAttributes */ output_rva( "%s\n", asm_name( import_name ) ); /* DllNameRVA */ @@ -1735,7 +1735,6 @@ static void build_windows_import_lib( const char *lib_name, DLLSPEC *spec ) 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() ); }
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=124916
Your paranoid android.
=== debian11 (build log) ===
Use of uninitialized value $Flaky in addition (+) at /home/testbot/lib/WineTestBot/LogUtils.pm line 720, <$LogFile> line 24701. Use of uninitialized value $Flaky in addition (+) at /home/testbot/lib/WineTestBot/LogUtils.pm line 720, <$LogFile> line 24701. Use of uninitialized value $Flaky in addition (+) at /home/testbot/lib/WineTestBot/LogUtils.pm line 720, <$LogFile> line 24701.
So it looks like the MSVC-like import libs are actually important, LLD seems to be using the COFF IMPORT_OBJECT_HEADER archive members to figure the DLL name, and decide which lib should be ignored and delay loaded instead (matching the -Wl,--delay-load flags).
Generating a GNU-like import lib like here, makes it generate immediate imports for everything, as we pass it `.cross.a` files for all libraries. We could instead probably pass it `.delay.a` files, like we do with GCC but that would defeat the purpose of using the delay-load flag.
I also changed the section alignment flags in favor of `.align` directives instead, and the referencing of the DLL name symbol from the first archive object, which LLD was also complaining about (though it's not a problem anyway, it may be in the future if we want to implement MSVC-like archives).
I didn't try to generate MSVC-like archive because I don't know how well they behave when mixed with GNU-like archives. It should be doable but they may differ too, so I think as a first step it's best to keep as close as we can to GNU dlltool.
Generating a GNU-like import lib like here, makes it generate immediate imports for everything, as we pass it `.cross.a` files for all libraries.
I have a change to LLD to make it generate delay-load imports for GNU-style import libraries, but it's kind of a mess. If you want, I can show it to you.
On Wed Oct 12 19:28:19 2022 +0000, Chip Davis wrote:
Generating a GNU-like import lib like here, makes it generate
immediate imports for everything, as we pass it `.cross.a` files for all libraries. I have a change to LLD to make it generate delay-load imports for GNU-style import libraries, but it's kind of a mess. If you want, I can show it to you.
I'm not sure it's necessary. I think it's possible to mix GNU-style import libs and MSVC-style import libs, and so maybe MSVC-style would be the way forward for Wine libs. I was only afraid of doing the transition too quickly.
Instead, I think replacing GNU dlltool while keeping its behavior is a good starting point, and will allow us to migrate to MSVC-style later, assuming that it indeed works.
I have a change to LLD to make it generate delay-load imports for GNU-style import libraries, but it's kind of a mess. If you want, I can show it to you.
Oh, I considered trying to do that at some point a few years ago, but decided not to, because I was afraid that it would be quite messy, yeah...
I think it's possible to mix GNU-style import libs and MSVC-style import libs
LLD can handle both types mixed (except for delay loaded stuff), GNU ld can handle both types too (but it has had some amount of occasional bugs with MSVC style libs; some were fixed recently in git, past the 2.39 branch too), but GNU ld probably can't do delay loads with MSVC style import libs either.