devkitPro / 3dstools

52 stars 19 forks source link

Crash when running generated 3dsx #9

Open squalldc opened 3 years ago

squalldc commented 3 years ago

Hi,

I get a crash when running the generated 3dsx file on a real 3ds as well as in Citra. I'm not sure if it's something I'm doing or some limitation in the 3dsx format.

It seems to be relocation related as it crashes at runtime when trying to get the address of a c++ member function pointer.

I've uploaded the just the test elf here (let me know if you need the smdh and romfs folder as well): https://anonfiles.com/ycnfj2ndu8/test_7z

Is there anything I can do to help look into this? Thanks,

fincs commented 3 years ago

Can you post the source code of your project? Member function pointers have no special relocation treatment.

squalldc commented 3 years ago

At the moment, the source isn't ready for release yet (it's quite large and all over the place) unfortunately.

squalldc commented 3 years ago

I've uploaded the test source code. Not sure if this results in the exact same issue (seems similar) but I've trimmed out a lot of the code so you can just build it like the other 3ds samples:

https://anonfiles.com/XfE6Xfnbu2/3ds_bug_7z

Basically, main.c just calls this one function now:

        void BugTest(bool set) {
            CNF_PARAMS current = ConfigureParams;
            ConfigureParams.System.bFastForward = (set != 0);
            if (ConfigureParams.System.bFastForward) {
                ConfigureParams.Screen.nFrameSkips = 5;
            } else {
                ConfigureParams.Screen.nFrameSkips = 0;
            }
            Change_CopyChangedParamsToConfiguration(&current, &ConfigureParams, false);
squalldc commented 3 years ago

Forgot to mention that the CIA seems to work fine with no issues:

https://youtu.be/ojhbTrj3eTw

fincs commented 3 years ago

We've identified the cause of this issue after some testing. Unfortunately, it involves broken assumptions made by some of the infrastructure that is used to support hax 2.x; specifically the problem arises with certain (legal) code optimizations GCC generates (if you're curious, the problematic code is in IoMem_VoidRead). The mere presence of this code in the binary is enough to produce an invalid 3DSX executable that fails to load (it doesn't even get to the point where it finishes loading). Fixing this will require us to consider dropping support for hax 2.x as a supported homebrew environment, leaving Luma3DS' Rosalina as the only remaining supported entrypoint. No decision has been taken yet, though.

With that said, we've also spotted some problematic practices in your source code tree. I'm not sure if they apply to your actual project or just the test case you posted, but generally it's not a good idea to replace an existing project's buildsystem with something else, it's best to make the existing buildsystem work directly. It's also not a good idea to drop a random version of a library into a project (especially when it's something complex like SDL). We happen to supply a 3ds-sdl package through devkitPro pacman, although it's SDL 1.x based. Also, I can't exactly tell which version you attempted to get working, supposedly Hatari requires SDL2 now, which is definitely not available for 3DS (at least for now). Either way, we can be reached for further advice and guidance.

squalldc commented 3 years ago

Thanks for looking into this.

Yeah the test code was just put together quickly to try to get a smaller reproduction of the crash. The actual project doesn't use the same buildsystem or sdl library in the test case.

Just curious, does the issue arise when function pointers are stored in static arrays and comparing those values to function addresses at runtime?

fincs commented 3 years ago

sdl library in the test case.

Which SDL library are you using then?

Just curious, does the issue arise when function pointers are stored in static arrays and comparing those values to function addresses at runtime?

It has to do with an optimization GCC does when accessing an array with an index that is offsetted by a constant. GCC saves one instruction by adding the offset (which in this case is negative) directly to the pointer that is stored as a spill immediate in the code. This later doesn't play nice with 3DSX's relocation processing, since it ends up highly out of bounds.

squalldc commented 3 years ago

Thanks for the explanation, it makes a lot of sense now.

Hatari currently still supports SDL 1.x, but this will be deprecated soon at which time I'll need to switch to SDL 2. I was using Vice3DS's SDL library (although I found their mutex code still has some bugs). I did notice devkitPro has it's own version when I upgraded the toolchain while looking into this crash issue, so I'll be switching over to your version of SDL 1.x.

fincs commented 3 years ago

For what's worth, we rewrote the synchronization primitive code (and fixed a couple of other related bugs). While SDL on 3DS is still not exactly ideal, it should hopefully be a bit more stable now.