4 messages in net.java.dev.jna.usersRe: [jna-users] Using setProtected un...
FromSent OnAttachments
Jonathan NewellJan 8, 2008 6:50 pm 
Jonathan NewellJan 9, 2008 5:50 am 
Timothy WallJan 12, 2008 11:29 am 
Timothy WallJan 21, 2008 1:45 am 
Actions with this message:
Paste this link in email or IM:
Paste this link in email or IM:
Atom feed for this thread
Paste this URL into your reader:
Subject:Re: [jna-users] Using setProtected under win32Actions...
From:Timothy Wall (twal@dev.java.net)
Date:Jan 12, 2008 11:29:08 am
List:net.java.dev.jna.users

On Jan 9, 2008, at 8:50 AM, Jonathan Newell wrote:

I'm using JNA to talk to a legacy windows dll that I have to use, but that I don't really trust. Another developer spent a couple of months putting together a JNI wrapper to the dll, but this is clunky and a bit flakey, so I was delighted when I was able to replicate the whole thing in JNA inside a day. Unfortunately the external dll seems to have several bugs which can cause it to throw Exception Access Violations under various repeatable but unavoidable situations, and I don't have any way to fix the underlying code. Seeing my java program disappear in a heap of dust because the external dll has a bug is quite upsetting, so I was keen to use the protected mode (it is an analytics package that I am using where each call through JNA may result in a second or more of computation time so the protection overhead is more than acceptable).

The basic scenario I'm running is that the dll has various (1000s) plugins, some of which work and some of which don't - I want to be able to detect those that don't via an exception in java and drive on (logging the fact that a specific plugin was bad).

I've checked out and built trunk (revision 439), but after calling Native.setProtected(true) the vm still crashes when making the call through to the external dll. Looking at the code, this seems to be in the dispatch method:

(in dispatch.c) PSTART(); ffi_call(&cif, FFI_FN(func), resP, ffi_values); PEND(); if (preserve_last_error) { update_last_error(env, GET_LAST_ERROR()); }

The win32 specific protection code is very different to the other protection code, and I wonder why this is the case. In particular I'm not convinced that the atempt to unwind the stack is very useful, if I'm reading it right it will only go back one frame which means that if the exception occurs more than one frame down from where the exception handler is installed then it will restart too deep in the stack.

You may be right; I'm no expert in MS structured exception handling. I was trying to effect a try { } catch(...) { } that would work with GCC. A reasonable test for this would be to force an illegal access deeper on the stack (I believe the current test calls a JNI method which immediately forces an illegal access), without any subroutine calls).

I've reworked the code to use setjmp/longjmp (as with the other exception handling) and it seems to work better:

(in protect.h) #ifdef _WIN32 #include <excpt.h> #include <setjmp.h>

static jmp_buf context;

static EXCEPTION_DISPOSITION __cdecl exc_handler(struct _EXCEPTION_RECORD* exception_record, void *establisher_frame, struct _CONTEXT *context_record, void* dispatcher_context) { longjmp(context, exception_record->ExceptionCode); }

#define PROTECTED_START() \ EXCEPTION_REGISTRATION ex_reg; \ int _error = 0; \ if (PROTECT) { \ ex_reg.handler = exc_handler; \ asm volatile ("movl %%fs:0, %0" : "=r" (ex_reg.prev)); \ asm volatile ("movl %0, %%fs:0" : : "r" (&ex_reg)); \ if ((_error = setjmp(context) != 0)) { \ goto _exc_caught; \ } \ }

#define PROTECTED_END(ONERR) do { \ if (!_error) \ goto _remove_handler; \ _exc_caught: \ ONERR; \ _remove_handler: \ if (PROTECT) { asm volatile ("movl %0, %%fs:0" : : "r" (ex_reg.prev)); } \ } while(0)

#else...

This still uses the windows structured exception handling. The only issue I find is that I have to call Native.setPreserveLastError (false) if I want to actually receive the error raised in ONERR, otherwise the error preserving after the ffi_call actually wipes out the error (it is not a safe post-exception JNI operation).

The error update should probably be moved to within the protected block to avoid calling it post-exception. The main problem with setjmp/longjmp is that it requires a global jump buffer/environment, and thus is not thread-safe.

I'd love some comments on the above, specifically whether people think it is a safe approach. I'm not a C programmer (in fact I've basically had to learn C from scratch over the past 24 hours to do this), so I might be missing something obvious or doing something really stupid, apologies in advance if this is the case (I specifically apologize if I've misunderstood the existing stack unwinding code, it was well beyond me and I may be doing it a disservice!).

Well, you've at least got better critical thinking skills regarding the C language than many programmers I've met who've been doing it for years.