ddovod / jet-live

c++ hot code reload for linux and macos
MIT License
412 stars 23 forks source link

gcc anonymous globals relocation failure #53

Open Hexlord opened 2 months ago

Hexlord commented 2 months ago

It seems that gcc, unlike clang, does not export symbols from anonymous namespaces with -Wl,-export-dynamic, or does it in a way that jet-live does not recognize

namespace {
struct MyStruct {
    int my_field = 0;
};
MyStruct global;
}

global.my_field = 123;
// hot-reload of this module happens
assert(global.my_field == 123); // fails (seems to be filled with zeroes/default initialized).

Would love to investigate what causes this, but there are no jet-live logs coming out related to this, so any pointers are appreciated!

Upd.: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=31462 seems to be the old change in gcc that caused this

ddovod commented 2 months ago

Hello. I've faced several issues related exclusively to gcc (see cmake/jet_live_setup.cmake, there's -falign-functions=16 added specifically for gcc), but I've had no chance to investigate it deeper. I believe there's some gcc option that will make gcc behave in the same way as clang, so I would suggest to try to find one. Unfortunately I don't have time to investigate this myself, but I would be thankful if you share your findings here

ddovod commented 2 months ago

And also, are you sure you've implemented ILiveListener::onLog function? There could be some logs related to unknown relocations and other failures. Talking about gcc flags I believe you need something that will change either symbols visibility or symbols placement (segment, section) or even both

Hexlord commented 2 months ago

Hello, yep I have noticed the gcc specifics, I dug in a little more,

g++ -g -fPIC -c main.cpp && g++ -g -shared -o main.so main.o && objdump -g main.so | grep St

for

struct St1 {
    int field = 0;
};
St1 St1Instance;

namespace {

struct St2 {
    int field = 0;
};
St2 St2Instance;

}

int main() {
    return 0;
}

correctly returns the debug symbols

sasha@sasha:~/gcc_bugs/symbols$ g++ -g -fPIC -c main.cpp && g++ -g -shared -o main.so main.o && objdump -g main.so | grep St
    <2f>   DW_AT_name        : St1
    <4b>   DW_AT_name        : (indirect string, offset: 0xad): St1Instance
    <66>   DW_AT_name        : St2
    <7b>   DW_AT_name        : (indirect string, offset: 0x0): St2Instance
 Line Number Statements:
  0x00000000 53743249 6e737461 6e636500 474e5520 St2Instance.GNU 
  0x000000a0 6e006669 656c6400 6d61696e 00537431 n.field.main.St1
Full related dump ``` <1><60>: Abbrev Number: 6 (DW_TAG_namespace) <61> DW_AT_export_symbols: 1 <61> DW_AT_sibling : <0x87> <2><65>: Abbrev Number: 1 (DW_TAG_structure_type) <66> DW_AT_name : St2 <6a> DW_AT_byte_size : 4 <6a> DW_AT_decl_file : 1 <6a> DW_AT_decl_line : 8 <6b> DW_AT_decl_column : 8 <6b> DW_AT_sibling : <0x7a> <3><6f>: Abbrev Number: 2 (DW_TAG_member) <70> DW_AT_name : (indirect string, offset: 0xa2): field <74> DW_AT_decl_file : 1 <74> DW_AT_decl_line : 9 <75> DW_AT_decl_column : 9 <75> DW_AT_type : <0x43> <79> DW_AT_data_member_location: 0 <3><79>: Abbrev Number: 0 <2><7a>: Abbrev Number: 7 (DW_TAG_variable) <7b> DW_AT_name : (indirect string, offset: 0x0): St2Instance <7f> DW_AT_decl_file : 1 <80> DW_AT_decl_line : 11 <81> DW_AT_decl_column : 5 <82> DW_AT_type : <0x65> <86> DW_AT_declaration : 1 <2><86>: Abbrev Number: 0 <1><87>: Abbrev Number: 8 (DW_TAG_variable) <88> DW_AT_specification: <0x7a> <8c> DW_AT_location : 9 byte block: 3 28 40 0 0 0 0 0 0 (DW_OP_addr: 4028) ```

I am not sure if I am looking at the correct thing, as I have little idea about all these things, but I don't think it is a gcc regression after the mentioned commit ILiveListener::onLog is indeed implemented, it does spam some logs about dep files not being found, but it always did that (it does that for weird dependency headers), nothing new at the moment or after the hot reload of a problematic file (anonymous global variable).

I will try and add debug traps into jet-live to see if it ever finds my global symbol (I'll give it a unique name) when I get the time! Please do let me know if there are more ways I could debug this

ddovod commented 2 months ago

You should look at symbols defined in .bss and .data sections, not debug symbols. For more context you can look into DefaultSymbolsFilter::shouldTransferElfSymbol function. You can use objdump or any other tool that shows the content of sections in ELF binaries

Hexlord commented 2 months ago

You should look at symbols defined in .bss and .data sections, not debug symbols.

Ah, right, g++ -g -fPIC -Wl,-export-dynamic -MD -c main.cpp && g++ -g -Wl,-export-dynamic -MD -shared -o main.so main.o && objdump -t main.so yields about the same result as clang for following code (both globals in .bss):

main.cpp ```c++ struct St1 { int field = 0; }; St1 St1Instance; namespace { struct St2 { int field = 0; }; St2 St2Instance; } int main() { St1Instance.field++; St2Instance.field++; return 0; } ```

Also as for logging - my bad, there actually were logs, I have not seen them because it takes another jetLive->update() to display them (because after the hot reload the logs of the hot reload are unprocessed events), but I could not previously reach that because of segfault immediately after hot reload, now I update() twice in a row, and see the logs, and there are a bunch of WTF-s:

```c++ [JetLive Info]: Success: Render2D.cpp [JetLive Debug]: Trying to reload code... [JetLive Info]: Linking... [JetLive Debug]: Link command: /usr/local/gcc-14.2.0/bin/g++-14.2 -fPIC -shared -g -Wl,-Ttext-segment,0x10169000 -Wl,-z,max-page-size=0x1000 -Wl,-export-dynamic -Wl,-soname,lib_reload1.so -o lib_reload1.so "/home/sasha/se/cmake-build-debug/Core/CMakeFiles/SECore.dir/Modules/Render/Render2D.cpp.o" [JetLive Info]: Linked successfully [JetLive Debug]: Opening /home/sasha/se/cmake-build-debug/lib_reload1.so... [JetLive Debug]: Library opened successfully [JetLive Debug]: Loading symbols from /home/sasha/se/cmake-build-debug/lib_reload1.so... [JetLive Debug]: Symbols loaded [JetLive Debug]: Loading exported symbols list... [JetLive Debug]: Done, total exported symbols: 485 in 1 files [JetLive Debug]: Loading link-time relocations... [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: _ZN2SE12_GLOBAL__N_113StateRender2DE was relocated [JetLive Debug]: _ZN2SE12_GLOBAL__N_113StateRender2DE was relocated [JetLive Debug]: _ZN2SE12_GLOBAL__N_113StateRender2DE was relocated [JetLive Debug]: _ZN2SE12_GLOBAL__N_113StateRender2DE was relocated [JetLive Debug]: _ZN2SE12_GLOBAL__N_113StateRender2DE was relocated [JetLive Debug]: _ZN2SE12_GLOBAL__N_113StateRender2DE was relocated [JetLive Debug]: _ZN2SE12_GLOBAL__N_113StateRender2DE was relocated [JetLive Debug]: _ZN2SE12_GLOBAL__N_113StateRender2DE was relocated [JetLive Debug]: Done, relocated: 8/8 [JetLive Debug]: Hooking functions... [JetLive Debug]: Done, hooked: 510/510 [JetLive Debug]: Copying statics from old code to new one... [JetLive Debug]: Done, copied: 3/3 [JetLive Info]: Code reloaded Signal: SIGSEGV (Segmentation fault) ```

I hacked in some code to try to look up the symbols in other sections:

ElfProgramInfoLoader.cpp ```c++ if (symFound == symbolsInSections[sectionIndex].end()) { uint32_t k = 0; for (; k < elfFile.sections.size(); k++) { symFound = symbolsInSections[k].find(symRelAddr); if(symFound != symbolsInSections[k].end()) { context->events->addLog(LogSeverity::kDebug, std::string("Symbol is in ") + std::to_string(k) + " instead of " + std::to_string(sectionIndex) + " section"); break; } } if(k == elfFile.sections.size()) { context->events->addLog(LogSeverity::kError, "WTF"); } continue; } ```

And it seems like it did something (might be something wrong), but it was futile:

```c++ [JetLive Info]: Success: Render2D.cpp [JetLive Debug]: Trying to reload code... [JetLive Info]: Linking... [JetLive Debug]: Link command: /usr/local/gcc-14.2.0/bin/g++-14.2 -fPIC -shared -g -Wl,-Ttext-segment,0x10174000 -Wl,-z,max-page-size=0x1000 -Wl,-export-dynamic -Wl,-soname,lib_reload1.so -o lib_reload1.so "/home/sasha/se/cmake-build-debug/Core/CMakeFiles/SECore.dir/Modules/Render/Render2D.cpp.o" [JetLive Info]: Linked successfully [JetLive Debug]: Opening /home/sasha/se/cmake-build-debug/lib_reload1.so... [JetLive Debug]: Library opened successfully [JetLive Debug]: Loading symbols from /home/sasha/se/cmake-build-debug/lib_reload1.so... [JetLive Debug]: Symbols loaded [JetLive Debug]: Loading exported symbols list... [JetLive Debug]: Done, total exported symbols: 485 in 1 files [JetLive Debug]: Loading link-time relocations... [JetLive Debug]: Symbol is in 1101 instead of 432 section [JetLive Debug]: Symbol is in 1227 instead of 432 section [JetLive Debug]: Symbol is in 722 instead of 432 section [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Debug]: Symbol is in 1227 instead of 432 section [JetLive Debug]: Symbol is in 1227 instead of 432 section [JetLive Debug]: Symbol is in 1227 instead of 432 section [JetLive Debug]: Symbol is in 722 instead of 432 section [JetLive Debug]: Symbol is in 1227 instead of 432 section [JetLive Debug]: Symbol is in 1227 instead of 432 section [JetLive Debug]: Symbol is in 1226 instead of 432 section [JetLive Debug]: Symbol is in 1227 instead of 432 section [JetLive Debug]: Symbol is in 1227 instead of 432 section [JetLive Error]: WTF [JetLive Debug]: Symbol is in 1227 instead of 432 section [JetLive Debug]: Symbol is in 1227 instead of 432 section [JetLive Debug]: Symbol is in 1227 instead of 432 section [JetLive Debug]: Symbol is in 1227 instead of 432 section [JetLive Error]: WTF [JetLive Debug]: Symbol is in 1101 instead of 432 section [JetLive Debug]: Symbol is in 1227 instead of 432 section [JetLive Debug]: Symbol is in 1101 instead of 432 section [JetLive Debug]: Symbol is in 1226 instead of 432 section [JetLive Debug]: Symbol is in 1227 instead of 432 section [JetLive Debug]: Symbol is in 1227 instead of 432 section [JetLive Error]: WTF [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Symbol is in 1227 instead of 432 section [JetLive Debug]: Symbol is in 1227 instead of 432 section [JetLive Debug]: Symbol is in 1227 instead of 432 section [JetLive Debug]: Symbol is in 1227 instead of 432 section [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Debug]: Symbol is in 1101 instead of 432 section [JetLive Debug]: Symbol is in 1101 instead of 432 section [JetLive Debug]: Symbol is in 1101 instead of 432 section [JetLive Debug]: Symbol is in 1226 instead of 432 section [JetLive Debug]: Symbol is in 1226 instead of 432 section [JetLive Debug]: Symbol is in 1227 instead of 432 section [JetLive Debug]: Symbol is in 1227 instead of 432 section [JetLive Debug]: Symbol is in 1227 instead of 432 section [JetLive Debug]: Symbol is in 1227 instead of 432 section [JetLive Debug]: Symbol is in 722 instead of 432 section [JetLive Debug]: Symbol is in 722 instead of 432 section [JetLive Error]: WTF [JetLive Error]: WTF [JetLive Debug]: Symbol is in 1227 instead of 432 section [JetLive Debug]: Symbol is in 1227 instead of 432 section [JetLive Debug]: Symbol is in 1227 instead of 432 section [JetLive Debug]: Symbol is in 1227 instead of 432 section [JetLive Debug]: Symbol is in 1227 instead of 432 section [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: _ZN2SE12_GLOBAL__N_113StateRender2DE was relocated [JetLive Debug]: _ZN2SE12_GLOBAL__N_113StateRender2DE was relocated [JetLive Debug]: _ZN2SE12_GLOBAL__N_113StateRender2DE was relocated [JetLive Debug]: _ZN2SE12_GLOBAL__N_113StateRender2DE was relocated [JetLive Debug]: _ZN2SE12_GLOBAL__N_113StateRender2DE was relocated [JetLive Debug]: _ZN2SE12_GLOBAL__N_113StateRender2DE was relocated [JetLive Debug]: _ZN2SE12_GLOBAL__N_113StateRender2DE was relocated [JetLive Debug]: _ZN2SE12_GLOBAL__N_113StateRender2DE was relocated [JetLive Debug]: Done, relocated: 8/8 [JetLive Debug]: Hooking functions... [JetLive Debug]: Done, hooked: 510/510 [JetLive Debug]: Copying statics from old code to new one... [JetLive Debug]: Done, copied: 3/3 [JetLive Info]: Code reloaded Signal: SIGSEGV (Segmentation fault) ```

Lastly I tried this objdump -t /home/sasha/se/cmake-build-debug/Core/CMakeFiles/SECore.dir/Modules/Render/Render2D.cpp.o | grep StateRender2D And noticed the abnormality (no .bss entry for my global variable in anonymous namespace):

0000000000000000 l     F .text  00000000000000b7 _ZN2SE12_GLOBAL__N_120FRenderStateRender2DaSEOS1_
00000000000034e0 l     F .text  00000000000000d4 _ZN2SE12_GLOBAL__N_120FRenderStateRender2DD2Ev
00000000000034e0 l     F .text  00000000000000d4 _ZN2SE12_GLOBAL__N_120FRenderStateRender2DD1Ev
0000000000000000 l     O .data  0000000000000058 _ZN2SE12_GLOBAL__N_113StateRender2DE

if we do the same for my other global variable that is outside of anonymouse namespace in the same file: objdump -t /home/sasha/se/cmake-build-debug/Core/CMakeFiles/SECore.dir/Modules/Render/Render2D.cpp.o | grep IterationNodes Then there is .bss entry for it:

0000000000000000 g     O .bss   0000000000000010 _ZN2SE5Impll14IterationNodesE

So this could be a gcc issue where it does not export the global variable into .bss, however I have not been able to come up with minimal reproducable example (when I try to assemble one, anonymous namespace does get exported), it could potentially be a compiler bug, I shall investigate it further. Please note that moving it outside of anonymous namespace fixes the issue, even though it still does not appear in .bss: objdump -t /home/sasha/se/cmake-build-debug/Core/CMakeFiles/SECore.dir/Modules/Render/Render2D.cpp.o | grep StateRender2D

0000000000000000 l    d  .text._ZN2SE5Impll20FRenderStateRender2DaSEOS1_    0000000000000000 .text._ZN2SE5Impll20FRenderStateRender2DaSEOS1_
0000000000000000 l    d  .text._ZN2SE5Impll20FRenderStateRender2DD2Ev   0000000000000000 .text._ZN2SE5Impll20FRenderStateRender2DD2Ev
0000000000000000 l       .group 0000000000000000 _ZN2SE5Impll20FRenderStateRender2DD5Ev
0000000000000000  w    F .text._ZN2SE5Impll20FRenderStateRender2DaSEOS1_    00000000000000b7 _ZN2SE5Impll20FRenderStateRender2DaSEOS1_
0000000000000000  w    F .text._ZN2SE5Impll20FRenderStateRender2DD2Ev   00000000000000d4 _ZN2SE5Impll20FRenderStateRender2DD2Ev
0000000000000000  w    F .text._ZN2SE5Impll20FRenderStateRender2DD2Ev   00000000000000d4 _ZN2SE5Impll20FRenderStateRender2DD1Ev
0000000000000000 g     O .data  0000000000000058 _ZN2SE5Impll13StateRender2DE

However, the Jet-Live output changes from [JetLive Debug]: Done, relocated: 8/8 to [JetLive Debug]: Done, relocated: 0/0 - as if it no longer even tries to relocate (which might be correct, idk), and there is much more [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code spam as well:

```c++ [JetLive Info]: Success: Render2D.cpp [JetLive Debug]: Trying to reload code... [JetLive Info]: Linking... [JetLive Debug]: Link command: /usr/local/gcc-14.2.0/bin/g++-14.2 -fPIC -shared -g -Wl,-Ttext-segment,0x10153000 -Wl,-z,max-page-size=0x1000 -Wl,-export-dynamic -Wl,-soname,lib_reload1.so -o lib_reload1.so "/home/sasha/se/cmake-build-debug/Core/CMakeFiles/SECore.dir/Modules/Render/Render2D.cpp.o" [JetLive Info]: Linked successfully [JetLive Debug]: Opening /home/sasha/se/cmake-build-debug/lib_reload1.so... [JetLive Debug]: Library opened successfully [JetLive Debug]: Loading symbols from /home/sasha/se/cmake-build-debug/lib_reload1.so... [JetLive Debug]: Symbols loaded [JetLive Debug]: Loading exported symbols list... [JetLive Debug]: Done, total exported symbols: 489 in 1 files [JetLive Debug]: Loading link-time relocations... [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Relocation UNKNOWN is not possible in PIC code [JetLive Debug]: Done, relocated: 0/0 [JetLive Debug]: Hooking functions... [JetLive Debug]: Done, hooked: 510/510 [JetLive Debug]: Copying statics from old code to new one... [JetLive Debug]: Done, copied: 3/3 [JetLive Info]: Code reloaded ```

This is objdump for clang file with struct in anonymous namespace (for clang it always works):

0000000000000000 l     F .text  0000000000000305 _ZN2SE12_GLOBAL__N_120FRenderStateRender2DD2Ev
0000000000000000 l     O .data  0000000000000058 _ZN2SE12_GLOBAL__N_113StateRender2DE
0000000000023280 l     F .text  00000000000002eb _ZN2SE12_GLOBAL__N_120FRenderStateRender2DaSEOS1_

this is for clang with struct outside of anonymous namespace:

0000000000000000 l    d  .text._ZN2SE5Impll20FRenderStateRender2DD2Ev   0000000000000000 .text._ZN2SE5Impll20FRenderStateRender2DD2Ev
0000000000000000 l    d  .text._ZN2SE5Impll20FRenderStateRender2DaSEOS1_    0000000000000000 .text._ZN2SE5Impll20FRenderStateRender2DaSEOS1_
0000000000000000  w    F .text._ZN2SE5Impll20FRenderStateRender2DD2Ev   0000000000000305 _ZN2SE5Impll20FRenderStateRender2DD2Ev
0000000000000000 g     O .data  0000000000000058 _ZN2SE5Impll13StateRender2DE
0000000000000000  w    F .text._ZN2SE5Impll20FRenderStateRender2DaSEOS1_    00000000000002eb _ZN2SE5Impll20FRenderStateRender2DaSEOS1_

Finally the code file in question, though it is of no interest

Render2D.cpp ```c++ #include "Render2D.h" #include #include #include #include "Modules/EntityBook/EntityBook.h" #include "Modules/Fonts/FontGeom.h" #include "Modules/Primitives/PrimitivesTypes.h" #include "Modules/Primitives/PrivatePrimitiveTypes.h" #include "Modules/Resources/Resources.h" #include "Modules/Transform/TransformComponents.h" #include "Render.h" namespace SE { namespace { struct FRenderStateRender2D { FUniqueBuffer QuadVB; FUniqueBuffer QuadIB; FUniqueBuffer InstanceVB; FUniqueBuffer Constants; FVersionedShader ShaderVS; FVersionedShader ShaderFS; FUniquePipelineState PipelineState; FUniqueRenderTarget RenderTarget; FUniqueDescriptor Descriptor; FUniqueSampler Sampler; FUniqueProfiler Profiler; }; FRenderStateRender2D StateRender2D; } namespace Impll{ struct FInputVS { f32vec2 Position; f32vec2 UV; }; struct FRenderConstants { f32vec4 SwapchainHalfSizeInv; }; struct FUIIterationNode { entt Entity; FUIIterationNode *Next = nullptr; }; TStableHeapArray IterationNodes = Array::WithMaxSize(65536); void RenderUI(); void RenderEntityUI( u32 WhiteTextureId, const CUITransform *UITransform, const CColoredRect *ColoredRect, const CTexturedRect *TexturedRect, const CColor *Color, const CWorldColorTransform *WorldColorTransform, const CUIStyle *Style, const CUINode *Node, const CBorderColor *BorderColor, const CTexture *Texture, const CDynamicText *Text); void RenderEntity2D( entt Entity, u32 InInstanceIndex, u32 WhiteTextureId, const CUITransform *UITransform, const CLocalTransform *LocalTransform, const CWorldTransform *WorldTransform, const CColoredRect *ColoredRect, const CTexturedRect *TexturedRect, const CColor *Color, const CWorldColorTransform *WorldColorTransform, const CTexture *Texture, const CDynamicText *Text); } // namespace using namespace Impll; void MRender2DInit() { StateRender2D.PipelineState = Rhi.CreatePipelineState(); StateRender2D.RenderTarget = Rhi.CreateRenderTarget(); StateRender2D.Profiler = Rhi.CreateProfiler("Render2D"); FRhiDepthStencilState DepthStencilState = {}; DepthStencilState.DepthTestEnable = false; DepthStencilState.DepthWrite = false; DepthStencilState.Compare = ERhiCompareOp::Always; FRhiRasterizationState RasterizationState = {}; RasterizationState.CullMode = ERhiCullMode::None; RasterizationState.PolygonMode = ERhiPolygonMode::Solid; RasterizationState.FrontFace = ERhiFrontFace::Clockwise; FRhiInputAssemblyState InputAssemblyState = {}; InputAssemblyState.Topology = ERhiPrimitiveTopology::Triangle; FRhiBlendState BlendState = {}; BlendState.Enable = false; BlendState.AttachmentStates.Add(FRhiAttachmentState{ FRhiColorMask().WithR().WithG().WithB().WithA(), true, ERhiBlendFactor::SrcAlpha, ERhiBlendFactor::OneMinusSrcAlpha, ERhiBlendOp::Add, ERhiBlendFactor::One, ERhiBlendFactor::OneMinusSrcAlpha, ERhiBlendOp::Add}); FRhiVertexInputState VertexInputState = {}; VertexInputState.InputAttributes = { FRhiInputAttribute{ERhiVertexSemantics::Position, 0, 0, ERhiFormat::R32G32_FLOAT, offsetof(FInputVS, Position)}, FRhiInputAttribute{ERhiVertexSemantics::Texcoord, 1, 0, ERhiFormat::R32G32_FLOAT, offsetof(FInputVS, UV)}, FRhiInputAttribute{ERhiVertexSemantics::Texcoord, 2, 1, ERhiFormat::R32G32B32A32_FLOAT, offsetof(FRender2DInstanceVS, PositionAndSize)}, FRhiInputAttribute{ERhiVertexSemantics::Texcoord, 3, 1, ERhiFormat::R32G32B32A32_FLOAT, offsetof(FRender2DInstanceVS, UVPositionAndSize)}, FRhiInputAttribute{ERhiVertexSemantics::Texcoord, 4, 1, ERhiFormat::R32_FLOAT, offsetof(FRender2DInstanceVS, Rotation)}, FRhiInputAttribute{ERhiVertexSemantics::Texcoord, 5, 1, ERhiFormat::R32_UINT, offsetof(FRender2DInstanceVS, TextureId)}, FRhiInputAttribute{ERhiVertexSemantics::Texcoord, 6, 1, ERhiFormat::R32_UINT, offsetof(FRender2DInstanceVS, SamplerId)}, // FRhiInputAttribute{ERhiVertexSemantics::Texcoord, 7, 1, ERhiFormat::R32_UINT, offsetof(FRender2DInstanceVS, EntityId)}, FRhiInputAttribute{ERhiVertexSemantics::Texcoord, 8, 1, ERhiFormat::R32G32B32A32_FLOAT, offsetof(FRender2DInstanceVS, Color)}}; VertexInputState.InputBindings = { FRhiInputBinding{0, sizeof(FInputVS), ERhiVertexInputRate::Vertex}, FRhiInputBinding{1, sizeof(FRender2DInstanceVS), ERhiVertexInputRate::Instance}}; { TFrameArray Vertices = { {{-0.5, -0.5}, {-0.5, 0.5}}, {{0.5, -0.5}, {0.5, 0.5}}, {{0.5, 0.5}, {0.5, -0.5}}, {{-0.5, 0.5}, {-0.5, -0.5}}}; TFrameArray Indices = {0, 1, 2, 0, 2, 3}; StateRender2D.QuadVB = Rhi.CreateBuffer( FRhiBufferDesc{"UIQuadVB", FRhiBufferUsage().WithVertex(), ERhiBufferMemoryUsage::Rendering, ERhiTransferPriority::AlwaysRelevant}); StateRender2D.QuadVB->Update(ToBytes(Vertices)); StateRender2D.QuadIB = Rhi.CreateBuffer( FRhiBufferDesc{"UIQuadIB", FRhiBufferUsage().WithIndex(), ERhiBufferMemoryUsage::Rendering, ERhiTransferPriority::AlwaysRelevant}); StateRender2D.QuadIB->Update(ToBytes(Indices)); } StateRender2D.InstanceVB = Rhi.CreateBuffer( FRhiBufferDesc{"UIInstanceVB", FRhiBufferUsage().WithVertex(), ERhiBufferMemoryUsage::Rendering, ERhiTransferPriority::PerFrame}); StateRender2D.Constants = Rhi.CreateBuffer( FRhiBufferDesc{"UIConstants", FRhiBufferUsage().WithConstantBuffer(), ERhiBufferMemoryUsage::Rendering, ERhiTransferPriority::PerFrame}); StateRender2D.ShaderVS.Shader = ResourcesBook.Shader("Engine/Shader/UI/UI.hlsl", "MainVS", FRhiShaderStage().WithVertex(), {}); StateRender2D.ShaderFS.Shader = ResourcesBook.Shader("Engine/Shader/UI/UI.hlsl", "MainPS", FRhiShaderStage().WithFragment(), {}); StateRender2D.Sampler = Rhi.CreateSampler(RhiNearestClamp); StateRender2D.PipelineState->SetDepthStencilState(DepthStencilState); StateRender2D.PipelineState->SetRasterizationState(RasterizationState); StateRender2D.PipelineState->SetVertexInputState(VertexInputState); StateRender2D.PipelineState->SetBlendState(BlendState); StateRender2D.PipelineState->SetInputAssemblyState(InputAssemblyState); OnPreClearECS([] { StateRender2D = {}; }); } void PrintColoredTree(entt Entity, u32 Depth = 0) { if(!Depth) { Outputf("Tree:\n"); } if(Entity.Has()) { Outputf("{}{}\n", String::Repeat("-", Depth), Entity); } Entity.Children([&](entt Child) { PrintColoredTree(Child, Depth + 1); }); } void MRender2DSubmitST() { Profiled("MRender2DSubmitST"); var InstanceCount = Render2DInstanceCount.Raw(); Render2DInstanceCount.Raw() = 0; if(!StateRender2D.QuadVB->IsRenderable() || !StateRender2D.QuadIB->IsRenderable()) { return; } var[MetaVS, VersionVS] = ResourcesBook.ShaderMeta(StateRender2D.ShaderVS.Shader); var[MetaFS, VersionFS] = ResourcesBook.ShaderMeta(StateRender2D.ShaderFS.Shader); if(!MetaVS || !MetaFS) { return; } if(VersionVS != StateRender2D.ShaderVS.Version || VersionFS != StateRender2D.ShaderFS.Version) { StateRender2D.ShaderVS.Version = VersionVS; StateRender2D.ShaderFS.Version = VersionFS; var Meta = *MetaVS; Meta += *MetaFS; Meta.UpdateHash(); StateRender2D.Descriptor = Rhi.CreateDescriptor(Meta); StateRender2D.PipelineState->SetShaders( {FRhiShaderStage().WithVertex(), ResourcesBook.ShaderPointer(StateRender2D.ShaderVS.Shader)}, {FRhiShaderStage().WithFragment(), ResourcesBook.ShaderPointer(StateRender2D.ShaderFS.Shader)}); } { Profiled("Update"); alignas(16) FRenderConstants Constants; var HalfSizeInv = Rhi.Swapchain()->Size32Inv * 2; Constants.SwapchainHalfSizeInv.X = HalfSizeInv.X; Constants.SwapchainHalfSizeInv.Y = HalfSizeInv.Y; StateRender2D.Constants->Update(AsBytes(Constants)); StateRender2D.InstanceVB->Update(AsBytes(TArrayView(*Render2DInstances, InstanceCount))); } { StateRender2D.Descriptor->BindBuffer("Constants", StateRender2D.Constants); TStaticArray Samplers = {*StateRender2D.Sampler}; StateRender2D.Descriptor->BindSamplers("MaterialSamplers", View(Samplers)); StateRender2D.Descriptor->BindTextures("MaterialTextures", ResourcesBook.TextureSparse.ViewElements()); } StateRender2D.RenderTarget->Clear(); StateRender2D.RenderTarget->Set( 0, Rhi.Swapchain()->GetCurrentTexture(), FRhiTextureRange{ERhiTextureDimension::Texture2D, 0, 1, 0, 1}, FRhiColorAttachment{}); var Commands = Rhi.CreateFrameCommand(ERhiQueueFamily::Graphics); Commands->Begin(StateRender2D.Profiler); Commands->BeginMarker("UI"); Commands->ResourceStateTransition(FRhiTextureStateTransition{ Rhi.Swapchain()->GetCurrentTexture(), *Rhi.Swapchain()->GetCurrentTexture()->GetRenderAllocationIndex(), FRhiTextureRange{ERhiTextureDimension::Texture2D, 0, 1, 0, 1}, ERhiResourceState::Present, ERhiResourceState::RenderTarget}); StateRender2D.QuadVB->ConsumeOwnershipTransfer(Commands); StateRender2D.QuadIB->ConsumeOwnershipTransfer(Commands); for(ref Texture: ResourcesBook.TextureSparse) { Texture->SyncPendingOps(Commands); } Commands->SetViewport(Rhi.Swapchain()->Size.X, Rhi.Swapchain()->Size.Y); Commands->SetScissor(Rhi.Swapchain()->Size.X, Rhi.Swapchain()->Size.Y); Commands->BeginRenderPass(StateRender2D.RenderTarget); Commands->BindVertexBuffer(0, StateRender2D.QuadVB); Commands->BindIndexBuffer(StateRender2D.QuadIB); Commands->BindVertexBuffer(1, StateRender2D.InstanceVB); Commands->BindDescriptor(StateRender2D.Descriptor); Commands->BindPipelineState(StateRender2D.PipelineState); Commands->DrawIndexed(6, InstanceCount); Commands->EndRenderPass(); Commands->ResourceStateTransition(FRhiTextureStateTransition{ Rhi.Swapchain()->GetCurrentTexture(), *Rhi.Swapchain()->GetCurrentTexture()->GetRenderAllocationIndex(), FRhiTextureRange{ERhiTextureDimension::Texture2D, 0, 1, 0, 1}, ERhiResourceState::RenderTarget, ERhiResourceState::Present}); Commands->EndMarker(); Commands->End(StateRender2D.Profiler); { Profiled("Submit"); Rhi.Queue()->Submit(ERhiQueueFamily::Graphics, FRhiSubmitInfo{.CmdBuffers = {Commands}}); } } void MRender2DWorldMT() { u32 RenderCounter = 0; var WhiteTextureId = ResourcesBook.TextureId(CommonResources.WhiteTexture); EntityQuery([&](entt Entity, const CRenderLayer &RenderLayer) { if(Likely(RenderLayer.LayerIndex != Meta::MaxValue)) { ref Layer = PrimitiveState.Layers[RenderLayer.LayerIndex]; ref SubLayer = Layer.SubLayers[RenderLayer.ToSubLayer()]; RenderCounter += RenderLayer.InstanceCount; var[UITransform, LocalTransform, WorldTransform, ColoredRect, TexturedRect, Color, WorldColorTransform, Texture, Text] = Entity.Get< const CUITransform, const CLocalTransform, const CWorldTransform, const CColoredRect, const CTexturedRect, const CColor, const CWorldColorTransform, const CTexture, const CDynamicText>(); RenderEntity2D( Entity, SubLayer.Counter->fetch_add(RenderLayer.InstanceCount, std::memory_order::relaxed), WhiteTextureId, UITransform, LocalTransform, WorldTransform, ColoredRect, TexturedRect, Color, WorldColorTransform, Texture, Text); } }); Render2DInstanceCount->fetch_add(RenderCounter, std::memory_order::relaxed); } void MRender2DWorldFinalizeST() { Profiled("MRender2DWorldFinalizeST"); for(ref Layer: PrimitiveState.Layers) { for(ref SubLayer: Layer.SubLayers) { VerboseAssert(SubLayer.Counter.Raw() >= SubLayer.InstanceCount); var Offset = SubLayer.Counter.Raw() - SubLayer.InstanceCount; var Slice = TArraySlice(*Render2DInstances + Offset, SubLayer.InstanceCount); // This is required to avoid order jittering due to table mutations. Array::SortArray(Slice, [](const FRender2DInstanceVS &A, const FRender2DInstanceVS &B) { return A.EntityId < B.EntityId; }); } } } void MRender2DUIST() { Profiled("MRender2DUIST"); RenderUI(); } namespace Impll{ void RenderUI() { var WhiteTextureId = ResourcesBook.TextureId(CommonResources.WhiteTexture); IterationNodes.Clear(); var Head = &IterationNodes.Add({EntityBook.UIRoot, nullptr}); while(Head) { var Entity = Head->Entity; Head = Head->Next; var[UITransform, ColoredRect, TexturedRect, Color, WorldColorTransform, UIRenderNode, Style, Node, BorderColor, Texture, Text] = Entity.Get< const CUITransform, const CColoredRect, const CTexturedRect, const CColor, const CWorldColorTransform, const CUIRenderNode, const CUIStyle, const CUINode, const CBorderColor, const CTexture, const CDynamicText>(); RenderEntityUI(WhiteTextureId, UITransform, ColoredRect, TexturedRect, Color, WorldColorTransform, Style, Node, BorderColor, Texture, Text); if(Entity == EntityBook.Editor.HierarchyPanel) { MHierarchyPanelST(); } for(var Index = (i32)UIRenderNode->Children.GetSize() - 1; Index >= 0; --Index) { Head = &IterationNodes.Add({UIRenderNode->Children[Index], Head}); } } FatalCheckf(Render2DInstanceCount.Raw() <= MaxRender2DInstances, "MaxRender2DInstances must be increased"); } void RenderEntityUI( u32 WhiteTextureId, const CUITransform *UITransform, const CColoredRect *ColoredRect, const CTexturedRect *TexturedRect, const CColor *Color, const CWorldColorTransform *WorldColorTransform, const CUIStyle *Style, const CUINode *Node, const CBorderColor *BorderColor, const CTexture *Texture, const CDynamicText *Text) { Profiled("RenderEntityUI"); // Shape. if(!(ColoredRect || TexturedRect || Text || Style)) { return; } var AABB = f64aabb ::FromPositionAndSize(UITransform->Position, UITransform->Size); AABB.Clip(Node->AABB); var AABBPosition = AABB.ComputePosition(); var AABBSize = AABB.ComputeSize(); if(Math::IsAlmostZeroAny(AABBSize)) { return; } if(ColoredRect || TexturedRect) { ref Instance = Render2DInstances[Render2DInstanceCount.Raw()++]; Instance.PositionAndSize.X = AABBPosition.X; Instance.PositionAndSize.Y = AABBPosition.Y; Instance.PositionAndSize.Z = AABBSize.X; Instance.PositionAndSize.W = AABBSize.Y; var UVPosition = (f64vec2{AABBPosition.X - UITransform->Position.X, UITransform->Position.Y - AABBPosition.Y}) / UITransform->Size + f64vec2{0.5, 0.5}; var UVSize = AABBSize / UITransform->Size; Instance.UVPositionAndSize.X = UVPosition.X; Instance.UVPositionAndSize.Y = UVPosition.Y; Instance.UVPositionAndSize.Z = UVSize.X; Instance.UVPositionAndSize.W = UVSize.Y; Instance.Rotation = UITransform->Rotation; if(TexturedRect) { Instance.TextureId = ResourcesBook.TextureId(Texture->Texture); } else { Instance.TextureId = WhiteTextureId; } Instance.SamplerId = 0; var FinalColor = Color ? Color->Color : f64color4{1.0, 1.0, 1.0, 1.0}; if(WorldColorTransform) { FinalColor *= WorldColorTransform->GetColor(); } Instance.Color = Math::Crush(FinalColor); } // Text. if(Text) { for(ref Glyph: Text->FontText.Glyphs) { var InGlyphPosition = UITransform->Position + Glyph.Position; var InGlyphSize = Glyph.Size; var GlyphAABB = f64aabb ::FromPositionAndSize(InGlyphPosition, InGlyphSize); GlyphAABB.Clip(Node->AABB); var Size = GlyphAABB.ComputeSize(); if(!Math::IsAlmostZeroAny(Size)) { var Position = GlyphAABB.ComputePosition(); ref Instance = Render2DInstances[Render2DInstanceCount.Raw()++]; Instance.PositionAndSize.X = Position.X; Instance.PositionAndSize.Y = Position.Y; Instance.PositionAndSize.Z = Size.X; Instance.PositionAndSize.W = Size.Y; var UVPosition = (Position - InGlyphPosition) / InGlyphSize * Glyph.UVSize + Glyph.UVPosition; var UVSize = Size / InGlyphSize * Glyph.UVSize; Instance.UVPositionAndSize.X = UVPosition.X; Instance.UVPositionAndSize.Y = UVPosition.Y; Instance.UVPositionAndSize.Z = UVSize.X; Instance.UVPositionAndSize.W = UVSize.Y; Instance.Rotation = UITransform->Rotation; var FinalColor = Glyph.Color; if(Color) { FinalColor *= Color->Color; } if(WorldColorTransform) { FinalColor *= WorldColorTransform->GetColor(); } Instance.TextureId = ResourcesBook.TextureId(Glyph.CustomTexture); Instance.SamplerId = 0; Instance.Color = Math::Crush(FinalColor); } } } // Borders. if(Style) { var InnerLeft = UI::ResolveValueIfPoints(Style->InnerBorder[EYGEdge::Left]); var InnerTop = UI::ResolveValueIfPoints(Style->InnerBorder[EYGEdge::Top]); var InnerRight = UI::ResolveValueIfPoints(Style->InnerBorder[EYGEdge::Right]); var InnerBottom = UI::ResolveValueIfPoints(Style->InnerBorder[EYGEdge::Bottom]); var OuterLeft = UI::ResolveValueIfPoints(Style->OuterBorder[EYGEdge::Left]); var OuterTop = UI::ResolveValueIfPoints(Style->OuterBorder[EYGEdge::Top]); var OuterRight = UI::ResolveValueIfPoints(Style->OuterBorder[EYGEdge::Right]); var OuterBottom = UI::ResolveValueIfPoints(Style->OuterBorder[EYGEdge::Bottom]); if(InnerLeft || InnerTop || InnerRight || InnerBottom || OuterLeft || OuterTop || OuterRight || OuterBottom) { var FinalColor = BorderColor ? BorderColor->Color : f64color4{1.0, 1.0, 1.0, 1.0}; if(WorldColorTransform) { FinalColor *= WorldColorTransform->GetColor(); } var InstanceColor = Math::Crush(FinalColor); var ProcessBorder = [&](f64 Value, f64vec2 HalfPositionDirection, f64vec2 SizeDirection, f64 LengthOffset, f64 PositionDirectionOffset, f64 SizeDirectionOffset) { if(!Math::IsAlmostZero(Value)) { var InPosition = UITransform->Position + f64vec2{HalfPositionDirection.X * UITransform->Size.X, HalfPositionDirection.Y * UITransform->Size.Y} + (PositionDirectionOffset + Value) * HalfPositionDirection + (SizeDirection * SizeDirectionOffset); var InSize = Math::Abs(HalfPositionDirection) * Value * 2.0 + f64vec2{SizeDirection.X * (UITransform->Size.X + LengthOffset), SizeDirection.Y * (UITransform->Size.Y + LengthOffset)}; if(Math::IsAboveZeroExclusive(InSize.X) && Math::IsAboveZeroExclusive(InSize.Y)) { var AABB = f64aabb ::FromPositionAndSize(InPosition, InSize); AABB.Clip(Node->AABB); var Size = AABB.ComputeSize(); if(!Math::IsAlmostZeroAny(Size)) { var Position = AABB.ComputePosition(); ref Instance = Render2DInstances[Render2DInstanceCount.Raw()++]; Instance.PositionAndSize.X = Position.X; Instance.PositionAndSize.Y = Position.Y; Instance.PositionAndSize.Z = Size.X; Instance.PositionAndSize.W = Size.Y; Instance.UVPositionAndSize.X = 0.0; Instance.UVPositionAndSize.Y = 0.0; Instance.UVPositionAndSize.Z = 0.0; Instance.UVPositionAndSize.W = 0.0; Instance.Rotation = 0.0; Instance.TextureId = WhiteTextureId; Instance.SamplerId = 0; Instance.Color = InstanceColor; } } } }; var InnerHeightOffset = InnerTop + InnerBottom; ProcessBorder(InnerLeft, {-0.5, 0.0}, {0.0, 1.0}, -InnerHeightOffset, -2.0 * InnerLeft, (InnerTop - InnerBottom) * -0.5); ProcessBorder(InnerTop, {0.0, 0.5}, {1.0, 0.0}, 0.0, -2.0 * InnerTop, 0.0); ProcessBorder(InnerRight, {0.5, 0.0}, {0.0, 1.0}, -InnerHeightOffset, -2.0 * InnerRight, (InnerTop - InnerBottom) * -0.5); ProcessBorder(InnerBottom, {0.0, -0.5}, {1.0, 0.0}, 0.0, -2.0 * InnerBottom, 0.0); var OuterWidthOffset = OuterLeft + OuterRight; ProcessBorder(OuterLeft, {-0.5, 0.0}, {0.0, 1.0}, 0.0, 0.0, 0.0); ProcessBorder(OuterTop, {0.0, 0.5}, {1.0, 0.0}, OuterWidthOffset, 0.0, (OuterRight - OuterLeft) * 0.5); ProcessBorder(OuterRight, {0.5, 0.0}, {0.0, 1.0}, 0.0, 0.0, 0.0); ProcessBorder(OuterBottom, {0.0, -0.5}, {1.0, 0.0}, OuterWidthOffset, 0.0, (OuterRight - OuterLeft) * 0.5); } } } void RenderEntity2D( entt Entity, u32 InInstanceIndex, u32 WhiteTextureId, const CUITransform *UITransform, const CLocalTransform *LocalTransform, const CWorldTransform *WorldTransform, const CColoredRect *ColoredRect, const CTexturedRect *TexturedRect, const CColor *Color, const CWorldColorTransform *WorldColorTransform, const CTexture *Texture, const CDynamicText *Text) { var InstanceIndex = InInstanceIndex; // Shape. if(ColoredRect || TexturedRect) { ref Instance = Render2DInstances[InstanceIndex++]; if(UITransform) { Instance.PositionAndSize.X = UITransform->Position.X; Instance.PositionAndSize.Y = UITransform->Position.Y; Instance.PositionAndSize.Z = UITransform->Size.X; Instance.PositionAndSize.W = UITransform->Size.Y; Instance.Rotation = UITransform->Rotation; } else if(WorldTransform) { Instance.PositionAndSize.X = WorldTransform->GetPosition().X; Instance.PositionAndSize.Y = WorldTransform->GetPosition().Y; if(LocalTransform) { Instance.PositionAndSize.Z = LocalTransform->GetScale().X; Instance.PositionAndSize.W = LocalTransform->GetScale().Y; } else { Instance.PositionAndSize.Z = 1.0; Instance.PositionAndSize.W = 1.0; } Instance.Rotation = 0.0; } else { Prevent(); } Instance.UVPositionAndSize.X = 0.5; Instance.UVPositionAndSize.Y = 0.5; Instance.UVPositionAndSize.Z = 1.0; Instance.UVPositionAndSize.W = 1.0; if(TexturedRect) { Instance.TextureId = ResourcesBook.TextureId(Texture->Texture); } else { Instance.TextureId = WhiteTextureId; } Instance.SamplerId = 0; Instance.EntityId = Entity.Id; var FinalColor = Color ? Color->Color : f64color4{1.0, 1.0, 1.0, 1.0}; if(WorldColorTransform) { FinalColor *= WorldColorTransform->GetColor(); } Instance.Color = Math::Crush(FinalColor); } // Text. if(Text) { for(ref Glyph: Text->FontText.Glyphs) { ref Instance = Render2DInstances[InstanceIndex++]; if(UITransform) { Instance.PositionAndSize.X = UITransform->Position.X + Glyph.Position.X; Instance.PositionAndSize.Y = UITransform->Position.Y + Glyph.Position.Y; Instance.PositionAndSize.Z = Glyph.Size.X; Instance.PositionAndSize.W = Glyph.Size.Y; Instance.Rotation = UITransform->Rotation; } else if(WorldTransform) { Instance.PositionAndSize.X = WorldTransform->GetPosition().X + Glyph.Position.X; Instance.PositionAndSize.Y = WorldTransform->GetPosition().Y + Glyph.Position.Y; Instance.PositionAndSize.Z = Glyph.Size.X; Instance.PositionAndSize.W = Glyph.Size.Y; Instance.Rotation = 0.0; } else { Prevent(); } Instance.UVPositionAndSize.X = Glyph.UVPosition.X; Instance.UVPositionAndSize.Y = Glyph.UVPosition.Y; Instance.UVPositionAndSize.Z = Glyph.UVSize.X; Instance.UVPositionAndSize.W = Glyph.UVSize.Y; Instance.TextureId = ResourcesBook.TextureId(Glyph.CustomTexture); Instance.SamplerId = 0; Instance.EntityId = Entity.Id; var FinalColor = Glyph.Color; if(Color) { FinalColor *= Color->Color; } if(WorldColorTransform) { FinalColor *= WorldColorTransform->GetColor(); } Instance.Color = Math::Crush(FinalColor); } } } } // namespace void RenderColoredRect2D( u32 WhiteTextureId, const f64vec2 &InPosition, const f64vec2 &InSize, const f64vec2 &InAlignment, const f64color4 &InColor, const f64aabb &InAABB) { ref Instance = Render2DInstances[Render2DInstanceCount.Raw()++]; var AABB = f64aabb::FromPositionAndSize(InPosition + InSize * InAlignment, InSize); AABB.Clip(InAABB); var Position = AABB.ComputePosition(); var Size = AABB.ComputeSize(); Instance.PositionAndSize.X = Position.X; Instance.PositionAndSize.Y = Position.Y; Instance.PositionAndSize.Z = Size.X; Instance.PositionAndSize.W = Size.Y; Instance.Rotation = 0.0; Instance.UVPositionAndSize.X = 0.0; Instance.UVPositionAndSize.Y = 0.0; Instance.UVPositionAndSize.Z = 0.0; Instance.UVPositionAndSize.W = 0.0; Instance.TextureId = WhiteTextureId; Instance.SamplerId = 0; Instance.Color = Math::Crush(InColor); } void RenderText2D( const f64vec2 &InPosition, const f64aabb &InAABB, TArrayView Glyphs) { for(ref Glyph: Glyphs) { ref Instance = Render2DInstances[Render2DInstanceCount.Raw()++]; var GlyphPosition = InPosition + Glyph.Position; var GlyphSize = Glyph.Size; var AABB = f64aabb::FromPositionAndSize(GlyphPosition, GlyphSize); AABB.Clip(InAABB); var Position = AABB.ComputePosition(); var Size = AABB.ComputeSize(); Instance.PositionAndSize.X = Position.X; Instance.PositionAndSize.Y = Position.Y; Instance.PositionAndSize.Z = Size.X; Instance.PositionAndSize.W = Size.Y; var UVPosition = (Position - GlyphPosition) / GlyphSize * Glyph.UVSize + Glyph.UVPosition; var UVSize = Size / GlyphSize * Glyph.UVSize; Instance.UVPositionAndSize.X = UVPosition.X; Instance.UVPositionAndSize.Y = UVPosition.Y; Instance.UVPositionAndSize.Z = UVSize.X; Instance.UVPositionAndSize.W = UVSize.Y; Instance.Rotation = 0.0; Instance.TextureId = ResourcesBook.TextureId(Glyph.CustomTexture); Instance.SamplerId = 0; Instance.Color = Math::Crush(Glyph.Color); } } } // namespace SE ```

Thanks a lot for the heads up on .bss section! I also tried playing around with shouldTransferElfSymbol (changing it here and there), but to no avail, I think if we are not in the .bss section there is nothing we can do after that

ddovod commented 2 months ago

Thank you for thorough investigation! I didn't touch this code for a couple of years already so I can miss something, but what I remember is the global variables (globals, statics, in and out of anonymous namespaces etc) can be located either in .bss or in .data sections. Most likely globals are put in .data and statics in .bss section, but I'm not sure how gcc treats globals in anon namespaces.

The last thing that you could try is to compile the same sample code with gcc and with clang and check how it looks like under objdump and what are differences. I'm almost sure that this is some specifics of gcc, but following the common sense I assume that if there's some global variable (specifically in anon namespace), it should be stored somewhere, and if gcc is not storing it neither in .data nor in .bss sections, it should store is somewhere else, and if you'll manage to find this place (section name), you can adjust the code of jet-live and make it work as it should.

And also you can try to run tests suite with both gcc and clang and check the difference between reports, this could probably help with investigations as well.

I'll let you know if I eventually come up with more ideas

Hexlord commented 2 months ago

In my case there are no static variables, unless they are qualified as such automatically when they are not getting used outside of CU, I have little idea, I will keep on the lookout for both sections then

I will take a loot at the tests suite! Thank you for the quick response

Yeah I have updated my comment, in fact both clang and gcc produce an equal .data symbol for the case of anonymous namespace: 0000000000000000 l O .data 0000000000000058 _ZN2SE12_GLOBAL__N_113StateRender2DE So the problem must be somewhere else (like the section format / whatever) What worries me is that with clang, for both the case of global inside anonymous space and outside of it, jet-live outputs [JetLive Debug]: Done, relocated: 0/0 But for gcc, for the case of global inside anonymous space it outputs a different string (for the case outside of anon namespace the output is the same): [JetLive Debug]: Done, relocated: 8/8 Given the .data symbol for the global variable is identical, it probably means that jet-live tries to relocate something that it should not relocate? I will try and simplify the code file until there is nothing remaining but the repro, extract it into separate app and step through jet-live to determine exactly where the divergence happens, I think this will be the fastest way to figure it out without learning everything about elf format like you did when writing this very useful library ^^

ddovod commented 2 months ago

it probably means that jet-live tries to relocate something that it should not relocate?

Probably yes, and probably there're some logic errors, so the minimal sample with error would be helpful here

Hexlord commented 2 months ago

Ok so not a real repro (it does not corrupt memory), but if you modify the example like this:

SimpleCommandInterpreter.cpp ```c++ #include "SimpleCommandInterpreter.hpp" namespace { struct FGlobal { void *A = nullptr; void *B = nullptr; }; FGlobal Global; } SimpleCommandInterpreter::SimpleCommandInterpreter(int currentCommandsCounter) : m_currentCommandsCounter(currentCommandsCounter) { // Each of these two lines adds +1 WTF to the output Global.A = (void *)1; Global.B = (void *)2; } int SimpleCommandInterpreter::getCurrentCommandsCounter() const { return m_currentCommandsCounter; } std::string SimpleCommandInterpreter::runCommand(const std::string& command) { auto totalCommands = std::to_string(++m_currentCommandsCounter); std::string result; // Implement your commands here if (command == "Hello") { result = "Hi there! "; // These three lines together add one WTF to the output result += std::to_string((unsigned long long)Global.A); result += " "; result += std::to_string((unsigned long long)Global.B); } else { result = "Sorry, I don't know what '" + command + "' means. Fix it in runtime in 'SimpleCommandInterpreter::runCommand'"; } return "ttl: " + totalCommands + " > " + result; } ```

And output changes from

```c++ Hello ttl: 1 > Hi there! [I]: Compiling: SimpleCommandInterpreter.cpp [I]: Success: SimpleCommandInterpreter.cpp reload [I]: Trying to reload code... [I]: Linking... [D]: Link command: /usr/local/gcc-14.2.0/bin/g++-14.2 -fPIC -shared -g -Wl,-Ttext-segment,0xa48000 -Wl,-z,max-page-size=0x1000 -Wl,-export-dynamic -Wl,-soname,lib_reload1.so -o lib_reload1.so "/home/sasha/jet-live/cmake-build-debug-gcc/example/CMakeFiles/example.dir/src/SimpleCommandInterpreter.cpp.o" [I]: Linked successfully [D]: Opening /home/sasha/jet-live/cmake-build-debug-gcc/lib_reload1.so... [D]: Library opened successfully [D]: Loading symbols from /home/sasha/jet-live/cmake-build-debug-gcc/lib_reload1.so... [D]: Symbols loaded [D]: Loading exported symbols list... [D]: Done, total exported symbols: 22 in 1 files [D]: Loading link-time relocations... [D]: Done, relocated: 0/0 [D]: Hooking functions... [D]: Done, hooked: 22/22 [D]: Copying statics from old code to new one... [D]: Done, copied: 3/3 [I]: Code reloaded ```

To

```c++ Hello ttl: 1 > Hi there! 1 2 [I]: Compiling: SimpleCommandInterpreter.cpp [I]: Success: SimpleCommandInterpreter.cpp reload [I]: Trying to reload code... [I]: Linking... [D]: Link command: /usr/local/gcc-14.2.0/bin/g++-14.2 -fPIC -shared -g -Wl,-Ttext-segment,0xa48000 -Wl,-z,max-page-size=0x1000 -Wl,-export-dynamic -Wl,-soname,lib_reload1.so -o lib_reload1.so "/home/sasha/jet-live/cmake-build-debug-gcc/example/CMakeFiles/example.dir/src/SimpleCommandInterpreter.cpp.o" [I]: Linked successfully [D]: Opening /home/sasha/jet-live/cmake-build-debug-gcc/lib_reload1.so... [D]: Library opened successfully [D]: Loading symbols from /home/sasha/jet-live/cmake-build-debug-gcc/lib_reload1.so... [D]: Symbols loaded [D]: Loading exported symbols list... [D]: Done, total exported symbols: 30 in 1 files [D]: Loading link-time relocations... [E]: WTF [E]: WTF [E]: WTF [D]: _ZN12_GLOBAL__N_16GlobalE was relocated [D]: Done, relocated: 1/1 [D]: Hooking functions... [D]: Done, hooked: 30/30 [D]: Copying statics from old code to new one... [D]: Done, copied: 3/3 [I]: Code reloaded ```

Then after hot-reload with gcc you will see 3x WTF outputs This makes me wonder if jet live confuses uses of anonymous global variables with their declarations, or something like that,

But after we add -mcmodel=large, we get

```c++ Hello ttl: 1 > Hi there! 1 2 [I]: Compiling: SimpleCommandInterpreter.cpp [I]: Success: SimpleCommandInterpreter.cpp reload [I]: Trying to reload code... [I]: Linking... [D]: Link command: /usr/local/gcc-14.2.0/bin/g++-14.2 -fPIC -shared -g -Wl,-Ttext-segment,0xa49000 -Wl,-z,max-page-size=0x1000 -Wl,-export-dynamic -Wl,-soname,lib_reload1.so -o lib_reload1.so "/home/sasha/jet-live/cmake-build-debug-gcc/example/CMakeFiles/example.dir/src/SimpleCommandInterpreter.cpp.o" [I]: Linked successfully [D]: Opening /home/sasha/jet-live/cmake-build-debug-gcc/lib_reload1.so... [D]: Library opened successfully [D]: Loading symbols from /home/sasha/jet-live/cmake-build-debug-gcc/lib_reload1.so... [D]: Symbols loaded [D]: Loading exported symbols list... [D]: Done, total exported symbols: 30 in 1 files [D]: Loading link-time relocations... [E]: Relocation R_X86_64_GOTOFF64 is not possible in PIC code [E]: Relocation R_X86_64_GOTOFF64 is not possible in PIC code [E]: Relocation R_X86_64_GOTOFF64 is not possible in PIC code [E]: Relocation R_X86_64_GOTOFF64 is not possible in PIC code [D]: Done, relocated: 0/0 [D]: Hooking functions... [D]: Done, hooked: 30/30 [D]: Copying statics from old code to new one... [D]: Done, copied: 4/4 [I]: Code reloaded ```

So basically setting this for the application fixes the issue

set(CMAKE_C_FLAGS   "${CMAKE_C_FLAGS}   -mcmodel=large")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcmodel=large")

I discovered that flag when I was debugging by clang issue with jet live long ago, I think it was a compile error of some sorts due to linking with big libraries So the fix is to apply it to gcc build as well, simple as that, even though I still have little understanding what exactly goes wrong Thanks again for the quick responses, I think I would have lost motivation fairly quickly in chasing that and would just live with it, if not for your interest! Hope everything is alright

I could make a pr for it if you wish, but I am not sure if this is a true fix, it could just happen to fix it for my use cases

ddovod commented 2 months ago

That's interesting and could make sense. Thank you very much again, I'll try to investigate it more and probably create some tests that explicitly gets benefit from this flag