crosire / blink

A tool which allows you to edit source code of any MSVC C++ project live at runtime
BSD 2-Clause "Simplified" License
1.09k stars 81 forks source link

Question about relocations and static local variables #17

Closed ddovod closed 5 years ago

ddovod commented 5 years ago

Hi. Suppose we have this StaticVar.cpp file:

#include <cstdio>

static int localStaticVar = 10;

void printAndIncrement()
{
    printf("%d", localStaticVar++);
}

We're calling printAndIncrement each frame. We want to reload this file for some reason. We're expecting to get this output:

10
11
12
13
CODE RELOAD
14
15
...

On linux we cannot use relocation data from the StaticVar.cpp.o because such a relocations are 32-bit (probably compiler takes into account the "locality" of localStaticVar relative to printAndIncrement), and in general we cannot insert the address of old localStaticVar into the new version of printAndIncrement. How blink deal with such cases? Thank you!

suVrik commented 5 years ago

Hello! Take a look at #12

There's code that successfully finds local static variable usages in reloaded object file (it's mentioned in #12), but currently this code changes such usages for incorrect addresses.

ddovod commented 5 years ago

Hey! Thank you, I've read that issue, but my question is more about this. Suppose we have a GetAndIncrement.cpp file:

static int localStaticVariable = 0;

int getAndIncrement()
{
    return localStaticVariable++;
}

If we compile it to the object file, we will have smth like this: getandincrement Take a look at mov instruction at 0x08000044. Second operand is the address of the localStaticVariable which will be filled by the linker when relocations (on linux it is R_X86_64_32S) will be applied. Then we're touching this file, recompiling, linking it into .dll, loading and fixing the address of the localStaticVariable in the .dll in a way where new getAndIncrement function will use old version of localStaticVariable, exactly what you're talking about in #12. The problem here is second operand of mov instruction is relative 32-bit address of localStaticVariable, but since this variable lives in the executable and getAndIncrement function lives in the .dll, probably the distance between them in the memory will be way more than a 32-bit number. So the question is - is there any guarantee on windows that second operand of mov instruction in this case is 64-bit address?

crosire commented 5 years ago

blink handles this by allocating the memory region for the new object file sections as close as possible to the existing code in memory. This way the relative offset should still fit into a 32-bit value. Windows has APIs to allocate memory at a specific virtual address which allow this: https://github.com/crosire/blink/blob/55f2534f1933af0c70eeab5ce106fb888e51c0cc/source/blink_linker.cpp#L40 https://github.com/crosire/blink/blob/55f2534f1933af0c70eeab5ce106fb888e51c0cc/source/blink_linker.cpp#L199

ddovod commented 5 years ago

I see, thank you very much!