From cf619dd419ca371602d44f19236cf557b222eddf Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Fri, 21 Jun 2019 12:08:44 +0200 Subject: [PATCH] winebuild: Add -import entry point flag to generate hotpatchable import thunks. Signed-off-by: Alexandre Julliard --- tools/make_specfiles | 1 + tools/winebuild/build.h | 27 +++++----- tools/winebuild/import.c | 5 ++ tools/winebuild/parser.c | 1 + tools/winebuild/spec32.c | 92 +++++++++++++++++++++++++------- tools/winebuild/winebuild.man.in | 7 +++ 6 files changed, 100 insertions(+), 33 deletions(-) diff --git a/tools/make_specfiles b/tools/make_specfiles index c2fa929db5e..7b9115e298a 100755 --- a/tools/make_specfiles +++ b/tools/make_specfiles @@ -591,6 +591,7 @@ sub update_spec_file($) $flags = $parent{flags}; $flags =~ s/-ordinal\s*// if $descr{ordinal} eq "@"; $flags =~ s/-noname\s*// if $descr{ordinal} eq "@"; + $flags =~ s/-import\s*//; if ($descr{flags} =~ /-private/) # preserve -private flag { $flags = "-private " . $flags unless $flags =~ /-private/; diff --git a/tools/winebuild/build.h b/tools/winebuild/build.h index ad6e2a8289d..43a0dd20e82 100644 --- a/tools/winebuild/build.h +++ b/tools/winebuild/build.h @@ -168,21 +168,22 @@ struct strarray }; /* entry point flags */ -#define FLAG_NORELAY 0x01 /* don't use relay debugging for this function */ -#define FLAG_NONAME 0x02 /* don't export 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_REGISTER 0x10 /* use register calling convention */ -#define FLAG_PRIVATE 0x20 /* function is private (cannot be imported) */ -#define FLAG_ORDINAL 0x40 /* function should be imported by ordinal */ -#define FLAG_THISCALL 0x80 /* use thiscall calling convention */ -#define FLAG_FASTCALL 0x100 /* use fastcall calling convention */ +#define FLAG_NORELAY 0x0001 /* don't use relay debugging for this function */ +#define FLAG_NONAME 0x0002 /* don't export function by name */ +#define FLAG_RET16 0x0004 /* function returns a 16-bit value */ +#define FLAG_RET64 0x0008 /* function returns a 64-bit value */ +#define FLAG_REGISTER 0x0010 /* use register calling convention */ +#define FLAG_PRIVATE 0x0020 /* function is private (cannot be imported) */ +#define FLAG_ORDINAL 0x0040 /* function should be imported by ordinal */ +#define FLAG_THISCALL 0x0080 /* use thiscall calling convention */ +#define FLAG_FASTCALL 0x0100 /* use fastcall calling convention */ +#define FLAG_IMPORT 0x0200 /* export is imported from another module */ -#define FLAG_FORWARD 0x200 /* function is a forwarded name */ -#define FLAG_EXT_LINK 0x400 /* function links to an external symbol */ -#define FLAG_EXPORT32 0x800 /* 32-bit export in 16-bit spec file */ +#define FLAG_FORWARD 0x1000 /* function is a forwarded name */ +#define FLAG_EXT_LINK 0x2000 /* function links to an external symbol */ +#define FLAG_EXPORT32 0x4000 /* 32-bit export in 16-bit spec file */ -#define FLAG_CPU(cpu) (0x01000 << (cpu)) +#define FLAG_CPU(cpu) (0x10000 << (cpu)) #define FLAG_CPU_MASK (FLAG_CPU(CPU_LAST + 1) - FLAG_CPU(0)) #define FLAG_CPU_WIN64 (FLAG_CPU(CPU_x86_64) | FLAG_CPU(CPU_ARM64)) #define FLAG_CPU_WIN32 (FLAG_CPU_MASK & ~FLAG_CPU_WIN64) diff --git a/tools/winebuild/import.c b/tools/winebuild/import.c index 7cf065577b0..8c32b738a23 100644 --- a/tools/winebuild/import.c +++ b/tools/winebuild/import.c @@ -838,6 +838,11 @@ static void output_immediate_imports(void) for (j = 0; j < import->nb_imports; j++) { struct import_func *func = &import->imports[j]; + if (i) + { + if (func->name) output( "__imp_%s:\n", asm_name( func->name )); + else if (func->export_name) output( "__imp_%s:\n", asm_name( func->export_name )); + } if (func->name) output( "\t%s .L__wine_spec_import_data_%s_%s-.L__wine_spec_rva_base\n", get_asm_ptr_keyword(), import->c_name, func->name ); diff --git a/tools/winebuild/parser.c b/tools/winebuild/parser.c index bec9f523eb8..3191efaf9ea 100644 --- a/tools/winebuild/parser.c +++ b/tools/winebuild/parser.c @@ -70,6 +70,7 @@ static const char * const FlagNames[] = "ordinal", /* FLAG_ORDINAL */ "thiscall", /* FLAG_THISCALL */ "fastcall", /* FLAG_FASTCALL */ + "import", /* FLAG_IMPORT */ NULL }; diff --git a/tools/winebuild/spec32.c b/tools/winebuild/spec32.c index f1dedcccc95..855e6d8739f 100644 --- a/tools/winebuild/spec32.c +++ b/tools/winebuild/spec32.c @@ -379,9 +379,11 @@ static void output_relay_debug( DLLSPEC *spec ) void output_exports( DLLSPEC *spec ) { int i, fwd_size = 0; + int needs_imports = 0; int needs_relay = has_relays( spec ); int nr_exports = spec->base <= spec->limit ? spec->limit - spec->base + 1 : 0; const char *func_ptr = (target_platform == PLATFORM_WINDOWS) ? ".rva" : get_asm_ptr_keyword(); + const char *name; if (!nr_exports) return; @@ -430,6 +432,13 @@ void output_exports( DLLSPEC *spec ) output( "\t%s .L__wine_spec_forwards+%u\n", func_ptr, fwd_size ); fwd_size += strlen(odp->link_name) + 1; } + else if ((odp->flags & FLAG_IMPORT) && (target_cpu == CPU_x86 || target_cpu == CPU_x86_64)) + { + name = odp->name ? odp->name : odp->export_name; + if (name) output( "\t%s %s_%s\n", func_ptr, asm_name("__wine_spec_imp"), name ); + else output( "\t%s %s_%u\n", func_ptr, asm_name("__wine_spec_imp"), i ); + needs_imports = 1; + } else if (odp->flags & FLAG_EXT_LINK) { output( "\t%s %s_%s\n", func_ptr, asm_name("__wine_spec_ext_link"), odp->link_name ); @@ -503,32 +512,75 @@ void output_exports( DLLSPEC *spec ) /* output relays */ - if (target_platform == PLATFORM_WINDOWS) + if (needs_relay) { - if (!needs_relay) return; - output( "\t.data\n" ); - output( "\t.align %d\n", get_alignment(get_ptr_size()) ); - } - else - { - output( "\t.align %d\n", get_alignment(get_ptr_size()) ); - output( ".L__wine_spec_exports_end:\n" ); - if (!needs_relay) + if (target_platform == PLATFORM_WINDOWS) { - output( "\t%s 0\n", get_asm_ptr_keyword() ); - return; + output( "\t.data\n" ); + output( "\t.align %d\n", get_alignment(get_ptr_size()) ); } + else + { + output( "\t.align %d\n", get_alignment(get_ptr_size()) ); + output( ".L__wine_spec_exports_end:\n" ); + } + + output( ".L__wine_spec_relay_descr:\n" ); + output( "\t%s 0xdeb90002\n", get_asm_ptr_keyword() ); /* magic */ + output( "\t%s 0\n", get_asm_ptr_keyword() ); /* relay func */ + output( "\t%s 0\n", get_asm_ptr_keyword() ); /* private data */ + output( "\t%s __wine_spec_relay_entry_points\n", get_asm_ptr_keyword() ); + output( "\t%s .L__wine_spec_relay_entry_point_offsets\n", get_asm_ptr_keyword() ); + output( "\t%s .L__wine_spec_relay_args_string\n", get_asm_ptr_keyword() ); + + output_relay_debug( spec ); + } + else if (target_platform != PLATFORM_WINDOWS) + { + output( "\t.align %d\n", get_alignment(get_ptr_size()) ); + output( ".L__wine_spec_exports_end:\n" ); + output( "\t%s 0\n", get_asm_ptr_keyword() ); } - output( ".L__wine_spec_relay_descr:\n" ); - output( "\t%s 0xdeb90002\n", get_asm_ptr_keyword() ); /* magic */ - output( "\t%s 0\n", get_asm_ptr_keyword() ); /* relay func */ - output( "\t%s 0\n", get_asm_ptr_keyword() ); /* private data */ - output( "\t%s __wine_spec_relay_entry_points\n", get_asm_ptr_keyword() ); - output( "\t%s .L__wine_spec_relay_entry_point_offsets\n", get_asm_ptr_keyword() ); - output( "\t%s .L__wine_spec_relay_args_string\n", get_asm_ptr_keyword() ); + /* output import thunks */ - output_relay_debug( spec ); + if (!needs_imports) return; + output( "\t.text\n" ); + for (i = spec->base; i <= spec->limit; i++) + { + ORDDEF *odp = spec->ordinals[i]; + if (!odp) continue; + if (!(odp->flags & FLAG_IMPORT)) continue; + + name = odp->name ? odp->name : odp->export_name; + + output( "\t.align %d\n", get_alignment(4) ); + output( "\t.long 0x90909090,0x90909090\n" ); + if (name) output( "%s_%s:\n", asm_name("__wine_spec_imp"), name ); + else output( "%s_%u:\n", asm_name("__wine_spec_imp"), i ); + output_cfi( ".cfi_startproc" ); + + switch (target_cpu) + { + case CPU_x86: + output( "\t.byte 0x8b,0xff,0x55,0x8b,0xec,0x5d\n" ); /* hotpatch prolog */ + 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 ))); + needs_get_pc_thunk = 1; + } + else output( "\tjmp *__imp_%s\n", asm_name( get_link_name( odp ))); + break; + case CPU_x86_64: + output( "\t.byte 0x48\n" ); /* hotpatch prolog */ + output( "\tjmp *__imp_%s(%%rip)\n", asm_name( get_link_name( odp ))); + break; + default: + assert(0); + } + output_cfi( ".cfi_endproc" ); + } } diff --git a/tools/winebuild/winebuild.man.in b/tools/winebuild/winebuild.man.in index e9386f9e0d6..1b9b1b088b5 100644 --- a/tools/winebuild/winebuild.man.in +++ b/tools/winebuild/winebuild.man.in @@ -315,6 +315,13 @@ The function uses the calling convention (first two parameters in %ecx/%edx registers on i386). .TP +.B -import +The function is imported from another module. This can be used instead +of a +.I forward +specification when an application expects to find the function's +implementation inside the dll. +.TP .RE .BI -arch= cpu\fR[\fB,\fIcpu\fR] The entry point is only available on the specified CPU