Re: LuaJIT, ObjectiveC, @throw in lua_atpanic, clang, infinite recursion

  • From: Mike Pall <mike-1206@xxxxxxxxxx>
  • To: luajit@xxxxxxxxxxxxx
  • Date: Wed, 27 Jun 2012 14:28:34 +0200

Konstantin Osipov wrote:
> We should be using catch(...) (provided we use external unwinding,
> and we do):
> 
> -2----------------------------------
> @try {
>         lua_call(L, lua_gettop(L) - 1, LUA_MULTRET);
> } @catch (ClientError *e) { /* for exceptions thrown by C code */
>     ...
> } @catch (id e) { /* for Lua errors */
>     const char *msg = lua_tostring(L, -1);
>     ...
> }
> ------------------------------------
> 
> Is snippet #2 correct?

Yes.

[That's assuming '@catch (id e)' catches any non-ObjC exception,
just like 'catch (...)' in C++. ]

> We do not use lua_pcall at all, and lua_call ends up in atpanic handler
> in case of any call to luaL_error()/error().
> 
> I guess you're saying that we must run our code in protected
> environment regardless of whether we choose external or internal
> stack unwinding?
> 
> The reason we don't do it, is that in this case any native C++
> exception is converted to a Lua error with text "C++ exception",
> whereas we'd like it to reach our @catch block instead.

I realize that. You need *some* kind of catch frame, whether it's
lua_pcall or a C++/ObjC catch. The above snippet should catch the
error just fine and the atpanic handler should never be invoked.

First, make absolutely sure you've compiled LuaJIT with unwind
tables and with native exceptions:

  nm src/lj_err.o | grep _Unwind_RaiseException
  objdump -h src/lj_err.o | grep eh_frame

Both outputs should be non-empty.

Then there's only one way the atpanic handler might be invoked:
the call to _Unwind_RaiseException() returns unexpectedly. This
only happens when the unwind info is defective for some function
on the current C stack. That's usually caused by some intervening
piece of C code not having the proper unwind info.

You can verify this is the problem by printing something after the
call to err_raise_ext() near line 453 of lj_err.c.

If yes, then you'll need to find out which piece of C code doesn't
have unwind tables. objdump -x or readelf -wf might be of help.
Every *.o file needs to have a (non-empty) .eh_frame section.

[Don't confuse this with .debug_frame: GDB may use either one to
print a backtrace. But exception unwinding only uses .eh_frame.]


BTW: I've found some more info on 32 bit OSX and ObjC exceptions:
  
http://www.cocoabuilder.com/archive/cocoa/212027-exceptions-in-objective-call-stack.html
Maybe the info is dated and Apple changed their mind. Not sure.

You can find out for sure by throwing an ObjC exception and trying
to catch it with C++ for each platform. If that doesn't work, then
it won't work with LuaJIT either and we'll need to find out what
exception model the ObjC runtime uses on that platform.

--Mike

Other related posts: