Empty Game/Room Caption/Var Construction Recursion

Reporter: RobertBColton  |  Status: open  |  Last Modified: June 17, 2019, 10:16:04 PM

This is happening for me on Windows in compile mode. Steps to reproduce:

  • Create a new game with one object (no room!)
  • Compile an exe of it (opening the exe will crash)
  • Add to the create event a call to string_height("");
  • Compile an exe again, opening it this time will not crash
  • <
So, I finally managed to get to the bottom of it. It's room_caption and current_caption global initialization. I figured it out by removing -s from the SHELL Makefile for compile mode, then adding -g3 -ggdb to get the symbols. I did a full clean rebuild and got the below stack trace. Note the length 12 is because I changed the two vars to initialize to "hello rusky".

#18569 0x0000000000535593 in var::var<char [12]>(char const (&) [12]) [clone .constprop.0] ()
#18570 0x0000000000535593 in var::var<char [12]>(char const (&) [12]) [clone .constprop.0] ()
#18571 0x0000000000535593 in var::var<char [12]>(char const (&) [12]) [clone .constprop.0] ()
#18572 0x000000000053603d in global constructors keyed to 65535_0_SHELLmain.o.70040 ()
#18573 0x0000000000485b32 in __do_global_ctors ()
    at E:/mingwbuild/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/gccmain.c:67
#18574 0x0000000000485b8f in __main ()
    at E:/mingwbuild/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/gccmain.c:83
#18575 0x000000000040137f in __tmainCRTStartup ()
    at E:/mingwbuild/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c:329
#18576 0x00000000004014db in WinMainCRTStartup ()
    at E:/mingwbuild/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c:195


It's not clear to me why calling string_height fixes anything. It's also not clear to me that we're seeing recursion. Sounds like an undefined access that adding another method call is masking. I suggest room_caption is being initialized to room[0].caption, which is OOB. Maybe declaring that create event pushes another string in the way. Either way, the problem is likely with how room_caption is initialized. Not with recursion between two constructors.

Here's the thing, I deleted my earlier comment. If you change string_height to draw_text that also works, as well as just about any other function in GSfont.cpp except for draw_set_font or draw_get_font which do not fix it. Rusky suggested it might just be functions that take var, but I tried show_message, array_length_1d, and string which do not fix it. Pretty weird, but I think it's just related to the fact that this only occurs with -flto which must be separating the code segments into Enclaves.

Now let me explain why I think it's recursion other than the stack being 20,000 frames of var construction. If I add an overload to variant_string_wrapper that takes const char* then the recursion goes away but I end up with a memmove segfault, which I think is caused by the release just below that.

struct variant_string_wrapper : std::string {


That's a weird problem to encounter. And that solution shouldn't work. It seems GCC is up to its old antics of trying to hop from type A to type B via var. That isn't legal and I don't understand how it's doing that. Can you get line numbers?

Right, no I don't think I can get line numbers because it only occurs with -flto which destroys that. I mean, it's giving line numbers, they just happen to be in linker-separated modules instead of the original line numbers. If you have an idea of what to add to the -g3 -ggdb I used to get the original line numbers, please share.

@JoshDreamland here's something to add, std::string has no virtual destructor, and thus deriving from it is not recommended.


Ok, so I decided to do some printing from the constructors to get us some more information and see all the variables involved here as well as exactly which constructors are running. I discovered that as far as strings go, it is just the caption basically. However, I also realized that we are casting the room caption every step to std::string and checking if it's empty to set the window caption.
if (!((std::string)enigma_user::room_caption).empty())

I had to comment that out for a second in order to see first the constructors that are being run.

********* EXECUTE:

rvalue_ref string variant_string_wrapper() this be the room caption
variant(const char *str) this be the room caption
rvalue_ref string variant_string_wrapper() this be the current caption
variant(const char *str) this be the current caption
Initializing audio system...

I then gave this a go in compile mode and the stack trace was totally different. I even uncommented the setting of the room caption again and got the same.

Thread 1 received signal SIGSEGV, Segmentation fault.
0x00000000005339d0 in enigma::propagate_locals(enigma::object_planar*) [clone .cold] ()
(gdb) bt
#0  0x00000000005339d0 in enigma::propagate_locals(enigma::object_planar*) [clone .cold] ()
#1  0x0000000000992850 in ?? ()
Backtrace stopped: previous frame inner to this frame (corrupt stack?)

Once I set the two caption vars back to empty string literals, I was able to get back to the original issue.

#0  0x0000000000532c62 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) [clone .isra.0] [clone .lto_priv.6] ()
#1  0x0000000000532cc4 in var::var<char [1]>(char const (&) [1]) [clone .constprop.0] ()

So it just seems it's something to do with the linker optimization not liking vars constructed from empty string literals.

Please sign in to post comments, or you can view this issue on GitHub.