Actually, trying to modify my code to move everything to shared memory is quite tedious AND error prone. There are global variables, STLs with custom memory allocators, class members that also need handling, etc. The problem is if any of these is allocated wrongly or I forget to move them to shared memory, it will be near impossible to debug my way out of it.
So, I am considering a different approach. In my client, I also track module loads and their sections. I think it might be possible to change the write sections of the client module to have MAP_SHARED, which would cause them to be shared with any child process after fork. That should take care of any global variables in my client. Something similar for memory allocated by the client would also make sense. At least that's the basic idea.
Two questions:
1. Where does DR allocate memory when the client calls new, ram_mem_alloc, global_alloc, etc? I know it keeps it separate from the application, but no other info given. I can also see that my client module and the `libdynamorio.so` modules have huge footers after their last sections, at least compared to all other modules. Are these regions used for the allocation?
2. Since there is no way to directly change a memory to have MAP_SHARED, I will have to resort to something like the following, would this work as a sequence?
// After simulator is already allocated but BEFORE fork
void* old_sim_base = existing_simulator_address;
size_t sim_size = simulator_size;
// Create shared backing
int memfd = memfd_create("sim", 0);
ftruncate(memfd, sim_size);
// Map shared region at temporary location
void* temp = mmap(NULL, sim_size, PROT_READ|PROT_WRITE,
MAP_SHARED, memfd, 0);
// Copy existing data
memcpy(temp, old_sim_base, sim_size);
// Now the trick - atomically replace old with new
void* new_base = mremap(temp, sim_size, sim_size,
MREMAP_MAYMOVE | MREMAP_FIXED, old_sim_base);
// Now old_sim_base points to shared memory with same contentThanks a lot for your help.