Open Rhys-T opened 6 days ago
Nice job finding the mechanism (I knew of the crash, but not why it happened). That's some weird logic in Allegro.
Here's the alternative (based on global variables) that I used in a pinch last time I hit this:
int al_run_allegro(scope int delegate() user_main)
{
__gshared int delegate() user_main2;
user_main2 = user_main;
extern(C) static int main_runner(int argc, char** argv)
{
version(OSX)
{
thread_attachThis();
rt_moduleTlsCtor();
}
auto main_ret = user_main2();
version(OSX)
{
thread_detachThis();
rt_moduleTlsDtor();
}
return main_ret;
}
return al_run_main(0, null, &main_runner);
}
I tried patching that version into the copy of the library I was building Lix with, and so far it seems to work perfectly. (Other than the game suddenly not being Retina-capable, but it's not the library's fault that I haven't fully set up the Info.plist file.) Thanks!
I think the reason Allegro ignores the original argv
is that on macOS, dropped or double-clicked documents don't get passed via argv
- and sometimes the app gets an extra argument that looks like -psn0123456
, which seems to just be used to set its 'process serial number' for some mostly-deprecated Mac APIs - so changing it to just the app and the document helps make things look as consistent across different platforms as possible. (Still seems weird that argv[0]
ends up as the entire .app bundle and not the executable, though…)
Is using a global like this going to work as the official fix? I'm not that familiar with D, or Allegro for that matter, but I figured the fake argv
trick was there for a reason. Are there any thread-safety concerns with using the global? Is there any way that a (non-broken) program could have multiple al_run_allegro
s going at once, fighting over the variable?
I was trying to get my macOS build of Lix packaged up as a proper double-clickable .app bundle, but for some reason, even though the executable ran by itself, as soon as I tried it as part of the bundle it would get a SIGSEGV in
_D8allegro56system14al_run_allegroFMDFZiZ11main_runnerUiPPaZi
(which demangles toint extern(C)main_runner(int, char**) allegro5.system.al_run_allegro(scope int() delegate)
). I did some digging, and I think I see what's going on.If I'm reading this library's
system.d
correctly, it's giving the underlyingal_run_main
call a fakeargv
, with the address of the game's callback function stored where the characters ofargv[0]
would normally be.main_runner
then casts the pointer back the other way to find and invoke the callback function. The problem is that on macOS, if Allegro detects that the program is running from a .app bundle, it ignores theargc
/argv
it was passed and instead creates its own fake one containing the path to the app bundle[^notexe] and the path to the document dropped on the app, if any:https://github.com/liballeg/allegro5/blob/5114ee60410c9aac6a0f23402570eb41a4cc9095/src/macosx/osx_app_delegate.m#L158-L192
So instead of getting the callback pointer,
main_runner
ends up using the first 8 bytes of the bundle path as an address, and crashes.[^notexe]: Just the path to the bundle, not all the way to the executable, for some reason.