Re: [PATCH 2/2] x86: fix bug in emit_jni_trampoline()

2009-07-06 Thread Tomek Grabiec
2009/7/6 Pekka Enberg :
> On Mon, 2009-07-06 at 01:16 +0200, Tomek Grabiec wrote:
>> We cannot overwrite a register which is not saved (ESI) because this
>> will lead to a corruption in JIT code. The proper solution is
>> to copy the call arguments.
>>
>> Signed-off-by: Tomek Grabiec 
>
> I'm confused. What exactly is the problem here and why do we need to
> copy arguments?
>

We need to push struct vm_jni_env * before calling JNI target. When
trampoline is called, it contains a return address on top
of the stack. Therefore we need to pop the return address and then
push the JNI env pointer, then call the target, and on return
cleanup JNI env argument and restore return address.

The problem is that we cannot scratch any register in jni_trampoline
that liveness analysis is not aware of being scratched.
Therefore we cannot scratch any caller saved register. Linveness
analysis assumes that all callee saved registers are scratched
but we cannot use any of those either because they can be scratched in
the JNI target function.

The approach in the patch simply copies call arguments in trampoline
and pushes JNI env pointer on top of
 them and calls the target.

One alternative solution:
For each native method call we could reserve the space on stack for
one word above call arguments to which
the return address can be saved and restored later

>> ---
>>  arch/x86/emit-code.c    |   37 +++--
>>  include/jit/compiler.h  |    3 ++-
>>  include/jit/emit-code.h |    3 ++-
>>  jit/trampoline.c        |    7 ---
>>  4 files changed, 39 insertions(+), 11 deletions(-)
>>
>> diff --git a/arch/x86/emit-code.c b/arch/x86/emit-code.c
>> index 236ba0e..c4ba179 100644
>> --- a/arch/x86/emit-code.c
>> +++ b/arch/x86/emit-code.c
>> @@ -1389,21 +1389,46 @@ void emit_trampoline(struct compilation_unit *cu,
>>  }
>>
>>  void emit_jni_trampoline(struct buffer *buf, struct vm_jni_env *jni_env,
>> -                      void *target)
>> +                      void *target, struct vm_method *method)
>>  {
>> +     unsigned long args_size;
>> +
>>       jit_text_lock();
>>
>>       buf->buf = jit_text_ptr();
>>
>> -     /* save return address into caller-saved register */
>> -     __emit_pop_reg(buf, REG_ESI);
>> +     __emit_push_reg(buf, REG_ESI);
>> +     __emit_push_reg(buf, REG_EDI);
>> +     __emit_push_reg(buf, REG_ECX);
>> +
>> +     /*
>> +      * Make a copy of call arguments
>> +      */
>> +     __emit_mov_reg_reg(buf, REG_ESP, REG_ESI);
>> +     __emit_add_imm_reg(buf, (3 + 1) * sizeof(unsigned long), REG_ESI);
>> +
>> +     args_size = sizeof(unsigned long) + (method->args_count + 1);
>> +
>> +     __emit_add_imm_reg(buf, -args_size, REG_ESP);
>> +     __emit_mov_reg_reg(buf, REG_ESP, REG_EDI);
>> +
>> +     __emit_mov_imm_reg(buf, method->args_count + 1, REG_ECX);
>> +
>> +     /* CLD */
>> +     emit(buf, 0xfc);
>> +
>> +     /* REP MOVSD */
>> +     emit(buf, 0xf3);
>> +     emit(buf, 0xa5);
>>
>>       __emit_push_imm(buf, (unsigned long) jni_env);
>>       __emit_call(buf, target);
>> -     __emit_add_imm_reg(buf, 4, REG_ESP);
>> +     __emit_add_imm_reg(buf, args_size + sizeof(unsigned long), REG_ESP);
>> +
>> +     __emit_pop_reg(buf, REG_ECX);
>> +     __emit_pop_reg(buf, REG_EDI);
>> +     __emit_pop_reg(buf, REG_ESI);
>>
>> -     /* return to caller*/
>> -     __emit_push_reg(buf, REG_ESI);
>>       emit_ret(buf);
>>
>>       jit_text_reserve(buffer_offset(buf));
>> diff --git a/include/jit/compiler.h b/include/jit/compiler.h
>> index 8584954..e2609ef 100644
>> --- a/include/jit/compiler.h
>> +++ b/include/jit/compiler.h
>> @@ -14,6 +14,7 @@ struct compilation_unit;
>>  struct expression;
>>  struct statement;
>>  struct buffer;
>> +struct vm_method;
>>
>>  struct fixup_site {
>>       /* Compilation unit to which relcall_insn belongs */
>> @@ -62,7 +63,7 @@ void *jit_magic_trampoline(struct compilation_unit *);
>>  struct jit_trampoline *alloc_jit_trampoline(void);
>>  struct jni_trampoline *alloc_jni_trampoline(void);
>>  struct jit_trampoline *build_jit_trampoline(struct compilation_unit *);
>> -struct jni_trampoline *build_jni_trampoline(void *);
>> +struct jni_trampoline *build_jni_trampoline(struct vm_method *, void *);
>>  void free_jit_trampoline(struct jit_trampoline *);
>>  void free_jni_trampoline(struct jni_trampoline *);
>>
>> diff --git a/include/jit/emit-code.h b/include/jit/emit-code.h
>> index 07c41bf..291955f 100644
>> --- a/include/jit/emit-code.h
>> +++ b/include/jit/emit-code.h
>> @@ -8,6 +8,7 @@ struct buffer;
>>  struct insn;
>>  struct vm_object;
>>  struct vm_jni_env;
>> +struct vm_method;
>>
>>  enum emitter_type {
>>       NO_OPERANDS = 1,
>> @@ -39,6 +40,6 @@ extern void emit_body(struct basic_block *, struct buffer 
>> *);
>>  extern void backpatch_branch_target(struct buffer *buf, struct insn *insn,
>>                                   unsigned long target_offset);
>>  extern void emit_jni_trampoline(struct buffer *buf, struct vm_jni_env 
>> *jni_en

Re: [PATCH 2/2] x86: fix bug in emit_jni_trampoline()

2009-07-06 Thread Pekka Enberg
On Mon, 2009-07-06 at 01:16 +0200, Tomek Grabiec wrote:
> We cannot overwrite a register which is not saved (ESI) because this
> will lead to a corruption in JIT code. The proper solution is
> to copy the call arguments.
> 
> Signed-off-by: Tomek Grabiec 

I'm confused. What exactly is the problem here and why do we need to
copy arguments?

> ---
>  arch/x86/emit-code.c|   37 +++--
>  include/jit/compiler.h  |3 ++-
>  include/jit/emit-code.h |3 ++-
>  jit/trampoline.c|7 ---
>  4 files changed, 39 insertions(+), 11 deletions(-)
> 
> diff --git a/arch/x86/emit-code.c b/arch/x86/emit-code.c
> index 236ba0e..c4ba179 100644
> --- a/arch/x86/emit-code.c
> +++ b/arch/x86/emit-code.c
> @@ -1389,21 +1389,46 @@ void emit_trampoline(struct compilation_unit *cu,
>  }
>  
>  void emit_jni_trampoline(struct buffer *buf, struct vm_jni_env *jni_env,
> -  void *target)
> +  void *target, struct vm_method *method)
>  {
> + unsigned long args_size;
> +
>   jit_text_lock();
>  
>   buf->buf = jit_text_ptr();
>  
> - /* save return address into caller-saved register */
> - __emit_pop_reg(buf, REG_ESI);
> + __emit_push_reg(buf, REG_ESI);
> + __emit_push_reg(buf, REG_EDI);
> + __emit_push_reg(buf, REG_ECX);
> +
> + /*
> +  * Make a copy of call arguments
> +  */
> + __emit_mov_reg_reg(buf, REG_ESP, REG_ESI);
> + __emit_add_imm_reg(buf, (3 + 1) * sizeof(unsigned long), REG_ESI);
> +
> + args_size = sizeof(unsigned long) + (method->args_count + 1);
> +
> + __emit_add_imm_reg(buf, -args_size, REG_ESP);
> + __emit_mov_reg_reg(buf, REG_ESP, REG_EDI);
> +
> + __emit_mov_imm_reg(buf, method->args_count + 1, REG_ECX);
> +
> + /* CLD */
> + emit(buf, 0xfc);
> +
> + /* REP MOVSD */
> + emit(buf, 0xf3);
> + emit(buf, 0xa5);
>  
>   __emit_push_imm(buf, (unsigned long) jni_env);
>   __emit_call(buf, target);
> - __emit_add_imm_reg(buf, 4, REG_ESP);
> + __emit_add_imm_reg(buf, args_size + sizeof(unsigned long), REG_ESP);
> +
> + __emit_pop_reg(buf, REG_ECX);
> + __emit_pop_reg(buf, REG_EDI);
> + __emit_pop_reg(buf, REG_ESI);
>  
> - /* return to caller*/
> - __emit_push_reg(buf, REG_ESI);
>   emit_ret(buf);
>  
>   jit_text_reserve(buffer_offset(buf));
> diff --git a/include/jit/compiler.h b/include/jit/compiler.h
> index 8584954..e2609ef 100644
> --- a/include/jit/compiler.h
> +++ b/include/jit/compiler.h
> @@ -14,6 +14,7 @@ struct compilation_unit;
>  struct expression;
>  struct statement;
>  struct buffer;
> +struct vm_method;
>  
>  struct fixup_site {
>   /* Compilation unit to which relcall_insn belongs */
> @@ -62,7 +63,7 @@ void *jit_magic_trampoline(struct compilation_unit *);
>  struct jit_trampoline *alloc_jit_trampoline(void);
>  struct jni_trampoline *alloc_jni_trampoline(void);
>  struct jit_trampoline *build_jit_trampoline(struct compilation_unit *);
> -struct jni_trampoline *build_jni_trampoline(void *);
> +struct jni_trampoline *build_jni_trampoline(struct vm_method *, void *);
>  void free_jit_trampoline(struct jit_trampoline *);
>  void free_jni_trampoline(struct jni_trampoline *);
>  
> diff --git a/include/jit/emit-code.h b/include/jit/emit-code.h
> index 07c41bf..291955f 100644
> --- a/include/jit/emit-code.h
> +++ b/include/jit/emit-code.h
> @@ -8,6 +8,7 @@ struct buffer;
>  struct insn;
>  struct vm_object;
>  struct vm_jni_env;
> +struct vm_method;
>  
>  enum emitter_type {
>   NO_OPERANDS = 1,
> @@ -39,6 +40,6 @@ extern void emit_body(struct basic_block *, struct buffer 
> *);
>  extern void backpatch_branch_target(struct buffer *buf, struct insn *insn,
>   unsigned long target_offset);
>  extern void emit_jni_trampoline(struct buffer *buf, struct vm_jni_env 
> *jni_env,
> - void *target);
> + void *target, struct vm_method *method);
>  
>  #endif /* JATO_EMIT_CODE_H */
> diff --git a/jit/trampoline.c b/jit/trampoline.c
> index 0de2db5..42f3afa 100644
> --- a/jit/trampoline.c
> +++ b/jit/trampoline.c
> @@ -65,7 +65,8 @@ static void *jit_native_trampoline(struct compilation_unit 
> *cu)
>   add_cu_mapping((unsigned long)ret, cu);
>  
>   if (!method->jni_trampoline)
> - method->jni_trampoline = build_jni_trampoline(ret);
> + method->jni_trampoline =
> + build_jni_trampoline(method, ret);
>  
>   return buffer_ptr(method->jni_trampoline->objcode);
>   }
> @@ -151,13 +152,13 @@ struct jit_trampoline *build_jit_trampoline(struct 
> compilation_unit *cu)
>   return trampoline;
>  }
>  
> -struct jni_trampoline *build_jni_trampoline(void *target)
> +struct jni_trampoline *build_jni_trampoline(struct vm_method *vmm, void 
> *target)
>  {
>   struct jni_trampoline *trampoline;
>  
>