If you run the game in applet mode it will cause the entire firmware to fatal. That will give you an error code which you can search through in here.
https://switchbrew.org/wiki/Error_codes#Fatal_Errors
Hopefully that will be enough.
If that is not enough you can use addr2line. Credits to
@shchmue for explaining how this works in the AtlasNX dev chat.
To paraphrase what was said in the chat logs addr2line will take an address and tell you which line of code it maps to. You just have to take the last address from the stack before the crash and see which line maps to it.
You use this command:
Bash:
aarch64-none-elf-addr2line -e yourprogram.elf -f -p -C -a youroffset
Replace "yourprogram" with the name of your program (E.g in my case when I build Amiigo I get Amiigo.nro, so if I ran this on Amiigo I would put Amiigo.elf instead of yourprogram.elf)
Replace youroffset with the address. E.g 0x6d604
To get the address take the value in the PC register (program counter) and subtract the base address of your program. How do you get those values? When your program crashes a dump of all that info will be generated in sdmc:/atmosphere/fatal_reports (this info will also be on the fatal error screen but imo it's much easier to copy paste it than type it all out manually).
I have put a screenshot of a crash report below. In this case take PC (000000485d056714) and subtract Start Address (000000485ce00000) this will result in (0x)256714. This crash was generated by a system app but if we pretend it came from Amiigo and I wanted to know which line of code caused the crash I would type
Bash:
aarch64-none-elf-addr2line -e Amiigo.elf -f -p -C -a 0x256714
If the line of code that caused the crash came from some library you're using you might need to look at a different address. Each line in the stack trace refers to the return address of each function on the stack. If I recall correctly it is the exact same process to obtain those addresses as it is to get the line that it crashed on. I.e ReturnAddress[XX] - Start Address.
This might sound difficult but it really isn't. The biggest pain in the ass is trying to copy over the crash log after you crashed. For that reason I usually stick to using error codes and trying to deduce what caused the crash from those, but if you get really stuck using addr2line can be very useful.
Edit: You mentioned NX-Link wasn't working. For debugging using NX-Link make sure that you're calling all of the init functions that you need. I can't remember exactly what they are but I think you need to init the sockets system module and NXLink itself. Then you need to send the NRO to the Switch using NXLINK with the -S argument.
Edit 2: Going off of what I have in Amiigo
https://github.com/CompSciOrBust/Amiigo/blob/master/source/main.cpp#L9
I think you need to call socketInitializeDefault() and nxlinkStdio(), possibly nifmInitialize(NifmServiceType_User) but I don't think that's needed. Be sure to call socketExit() before the app closes. When I send the app to my switch I use:
Make sure that you have NX-Link open in HB Menu on your Switch by pressing Y. If NX-Link can not find your Switch you may need to provide the IP address using the -a argument.
Edit 3: If I had a random guess about why your game is crashing at stat up it is because you're probably forgetting to initialize something. From my own experience using SDL2 on Switch it is likely one of the following: TTF_Init(), plInitialize(), romfsInit().
TTF is for font loading, I think that's also used on Windows so probably not that if you game works fine on PC. plInitialize is for loading Nintendo's system font, probably not that if you're not using it. romfsInit is for loading files from RomFS. I think this is the most likely culprit if you're trying to embed assets in to the NRO file itself instead of loading them from the SD Card.