/* check if dll will output relay thunks */ int has_relays( DLLSPEC *spec ) { int i; if (target_cpu != CPU_x86 && target_cpu != CPU_x86_64) return 0; for (i = spec->base; i <= spec->limit; i++) { ORDDEF *odp = spec->ordinals[i]; if (needs_relay( odp )) return 1; } return 0; }
/******************************************************************* * output_relay_debug * * Output entry points for relay debugging */ static void output_relay_debug( FILE *outfile, DLLSPEC *spec ) { unsigned int i, j, args, flags; /* first the table of entry point offsets */ fprintf( outfile, "\t%s\n", get_asm_rodata_section() ); fprintf( outfile, "\t.align %d\n", get_alignment(4) ); fprintf( outfile, ".L__wine_spec_relay_entry_point_offsets:\n" ); for (i = spec->base; i <= spec->limit; i++) { ORDDEF *odp = spec->ordinals[i]; if (needs_relay( odp )) fprintf( outfile, "\t.long .L__wine_spec_relay_entry_point_%d-__wine_spec_relay_entry_points\n", i ); else fprintf( outfile, "\t.long 0\n" ); } /* then the table of argument types */ fprintf( outfile, "\t.align %d\n", get_alignment(4) ); fprintf( outfile, ".L__wine_spec_relay_arg_types:\n" ); for (i = spec->base; i <= spec->limit; i++) { ORDDEF *odp = spec->ordinals[i]; unsigned int mask = 0; if (needs_relay( odp )) { for (j = 0; j < 16 && 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); } } fprintf( outfile, "\t.long 0x%08x\n", mask ); } /* then the relay thunks */ fprintf( outfile, "\t.text\n" ); fprintf( outfile, "__wine_spec_relay_entry_points:\n" ); fprintf( outfile, "\tnop\n" ); /* to avoid 0 offset */ for (i = spec->base; i <= spec->limit; i++) { ORDDEF *odp = spec->ordinals[i]; if (!needs_relay( odp )) continue; fprintf( outfile, "\t.align %d\n", get_alignment(4) ); fprintf( outfile, ".L__wine_spec_relay_entry_point_%d:\n", i ); if (odp->flags & FLAG_REGISTER) fprintf( outfile, "\tpushl %%eax\n" ); else fprintf( outfile, "\tpushl %%esp\n" ); args = strlen(odp->u.func.arg_types); flags = 0; if (odp->flags & FLAG_RET64) flags |= 1; if (odp->type == TYPE_STDCALL) flags |= 2; fprintf( outfile, "\tpushl $%u\n", (flags << 24) | (args << 16) | (i - spec->base) ); if (UsePIC) { fprintf( outfile, "\tcall %s\n", asm_name("__wine_spec_get_pc_thunk_eax") ); fprintf( outfile, "1:\tleal .L__wine_spec_relay_descr-1b(%%eax),%%eax\n" ); } else fprintf( outfile, "\tmovl $.L__wine_spec_relay_descr,%%eax\n" ); fprintf( outfile, "\tpushl %%eax\n" ); if (odp->flags & FLAG_REGISTER) { fprintf( outfile, "\tcall *8(%%eax)\n" ); } else { fprintf( outfile, "\tcall *4(%%eax)\n" ); if (odp->type == TYPE_STDCALL) fprintf( outfile, "\tret $%u\n", args * get_ptr_size() ); else fprintf( outfile, "\tret\n" ); } } }
/******************************************************************* * output_relay_debug * * Output entry points for relay debugging */ static void output_relay_debug( DLLSPEC *spec ) { int i, j; unsigned int pos, args, flags; /* first the table of entry point offsets */ output( "\t%s\n", get_asm_rodata_section() ); output( "\t.align %d\n", get_alignment(4) ); output( ".L__wine_spec_relay_entry_point_offsets:\n" ); for (i = spec->base; i <= spec->limit; i++) { ORDDEF *odp = spec->ordinals[i]; if (needs_relay( odp )) output( "\t.long .L__wine_spec_relay_entry_point_%d-__wine_spec_relay_entry_points\n", i ); else output( "\t.long 0\n" ); } /* then the table of argument types */ output( "\t.align %d\n", get_alignment(4) ); output( ".L__wine_spec_relay_arg_types:\n" ); for (i = spec->base; i <= spec->limit; i++) { ORDDEF *odp = spec->ordinals[i]; unsigned int mask = 0; if (needs_relay( odp )) { for (j = pos = 0; pos < 16 && j < odp->u.func.nb_args; j++) { switch (odp->u.func.args[j]) { case ARG_STR: mask |= 1 << (2 * pos++); break; case ARG_WSTR: mask |= 2 << (2 * pos++); break; case ARG_INT64: case ARG_DOUBLE: pos += 8 / get_ptr_size(); break; case ARG_INT128: pos += (target_cpu == CPU_x86) ? 4 : 1; break; default: pos++; break; } } } output( "\t.long 0x%08x\n", mask ); } /* then the relay thunks */ output( "\t.text\n" ); output( "__wine_spec_relay_entry_points:\n" ); output( "\tnop\n" ); /* to avoid 0 offset */ for (i = spec->base; i <= spec->limit; i++) { ORDDEF *odp = spec->ordinals[i]; if (!needs_relay( odp )) continue; output( "\t.align %d\n", get_alignment(4) ); output( ".L__wine_spec_relay_entry_point_%d:\n", i ); output_cfi( ".cfi_startproc" ); args = get_args_size(odp) / get_ptr_size(); flags = 0; switch (target_cpu) { case CPU_x86: if (odp->type == TYPE_THISCALL) /* add the this pointer */ { output( "\tpopl %%eax\n" ); output( "\tpushl %%ecx\n" ); output( "\tpushl %%eax\n" ); flags |= 2; } if (odp->flags & FLAG_REGISTER) output( "\tpushl %%eax\n" ); else output( "\tpushl %%esp\n" ); output_cfi( ".cfi_adjust_cfa_offset 4" ); if (odp->flags & FLAG_RET64) flags |= 1; output( "\tpushl $%u\n", (flags << 24) | (args << 16) | (i - spec->base) ); output_cfi( ".cfi_adjust_cfa_offset 4" ); if (UsePIC) { output( "\tcall %s\n", asm_name("__wine_spec_get_pc_thunk_eax") ); output( "1:\tleal .L__wine_spec_relay_descr-1b(%%eax),%%eax\n" ); } else output( "\tmovl $.L__wine_spec_relay_descr,%%eax\n" ); output( "\tpushl %%eax\n" ); output_cfi( ".cfi_adjust_cfa_offset 4" ); if (odp->flags & FLAG_REGISTER) { output( "\tcall *8(%%eax)\n" ); } else { output( "\tcall *4(%%eax)\n" ); output_cfi( ".cfi_adjust_cfa_offset -12" ); if (odp->type == TYPE_STDCALL || odp->type == TYPE_THISCALL) output( "\tret $%u\n", args * get_ptr_size() ); else output( "\tret\n" ); } break; case CPU_x86_64: output( "\tsubq $40,%%rsp\n" ); output_cfi( ".cfi_adjust_cfa_offset 40" ); switch (args) { default: output( "\tmovq %%%s,72(%%rsp)\n", is_float_arg( odp, 3 ) ? "xmm3" : "r9" ); /* fall through */ case 3: output( "\tmovq %%%s,64(%%rsp)\n", is_float_arg( odp, 2 ) ? "xmm2" : "r8" ); /* fall through */ case 2: output( "\tmovq %%%s,56(%%rsp)\n", is_float_arg( odp, 1 ) ? "xmm1" : "rdx" ); /* fall through */ case 1: output( "\tmovq %%%s,48(%%rsp)\n", is_float_arg( odp, 0 ) ? "xmm0" : "rcx" ); /* fall through */ case 0: break; } output( "\tleaq 40(%%rsp),%%r8\n" ); output( "\tmovq $%u,%%rdx\n", (flags << 24) | (args << 16) | (i - spec->base) ); output( "\tleaq .L__wine_spec_relay_descr(%%rip),%%rcx\n" ); output( "\tcallq *8(%%rcx)\n" ); output( "\taddq $40,%%rsp\n" ); output_cfi( ".cfi_adjust_cfa_offset -40" ); output( "\tret\n" ); break; default: assert(0); } output_cfi( ".cfi_endproc" ); } }
/******************************************************************* * output_relay_debug * * Output entry points for relay debugging */ static void output_relay_debug( DLLSPEC *spec ) { int i; unsigned int j, args, flags; /* first the table of entry point offsets */ output( "\t%s\n", get_asm_rodata_section() ); output( "\t.align %d\n", get_alignment(4) ); output( ".L__wine_spec_relay_entry_point_offsets:\n" ); for (i = spec->base; i <= spec->limit; i++) { ORDDEF *odp = spec->ordinals[i]; if (needs_relay( odp )) output( "\t.long .L__wine_spec_relay_entry_point_%d-__wine_spec_relay_entry_points\n", i ); else output( "\t.long 0\n" ); } /* then the table of argument types */ output( "\t.align %d\n", get_alignment(4) ); output( ".L__wine_spec_relay_arg_types:\n" ); for (i = spec->base; i <= spec->limit; i++) { ORDDEF *odp = spec->ordinals[i]; unsigned int mask = 0; if (needs_relay( odp )) { for (j = 0; j < 16 && 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); } } output( "\t.long 0x%08x\n", mask ); } /* then the relay thunks */ output( "\t.text\n" ); output( "__wine_spec_relay_entry_points:\n" ); output( "\tnop\n" ); /* to avoid 0 offset */ for (i = spec->base; i <= spec->limit; i++) { ORDDEF *odp = spec->ordinals[i]; if (!needs_relay( odp )) continue; output( "\t.align %d\n", get_alignment(4) ); output( ".L__wine_spec_relay_entry_point_%d:\n", i ); args = strlen(odp->u.func.arg_types); flags = 0; switch (target_cpu) { case CPU_x86: if (odp->flags & FLAG_REGISTER) output( "\tpushl %%eax\n" ); else output( "\tpushl %%esp\n" ); if (odp->flags & FLAG_RET64) flags |= 1; output( "\tpushl $%u\n", (flags << 24) | (args << 16) | (i - spec->base) ); if (UsePIC) { output( "\tcall %s\n", asm_name("__wine_spec_get_pc_thunk_eax") ); output( "1:\tleal .L__wine_spec_relay_descr-1b(%%eax),%%eax\n" ); } else output( "\tmovl $.L__wine_spec_relay_descr,%%eax\n" ); output( "\tpushl %%eax\n" ); if (odp->flags & FLAG_REGISTER) { output( "\tcall *8(%%eax)\n" ); } else { output( "\tcall *4(%%eax)\n" ); if (odp->type == TYPE_STDCALL) output( "\tret $%u\n", args * get_ptr_size() ); else output( "\tret\n" ); } break; case CPU_x86_64: output( "\tmovq %%rcx,8(%%rsp)\n" ); output( "\tmovq %%rdx,16(%%rsp)\n" ); output( "\tmovq %%r8,24(%%rsp)\n" ); output( "\tmovq %%r9,32(%%rsp)\n" ); output( "\tmovq %%rsp,%%r8\n" ); output( "\tmovq $%u,%%rdx\n", (flags << 24) | (args << 16) | (i - spec->base) ); output( "\tleaq .L__wine_spec_relay_descr(%%rip),%%rcx\n" ); output( "\tsubq $40,%%rsp\n" ); output( "\tcallq *%u(%%rcx)\n", (odp->flags & FLAG_REGISTER) ? 16 : 8 ); output( "\taddq $40,%%rsp\n" ); output( "\tret\n" ); break; default: assert(0); } } }