views:

79

answers:

3

In an C program, I need to re-initialize all global variables as they where when the program starts for tests purpose.

I want to reproduce the data copy from Load Memory Address, LMA to VMA (run-time address) done by GCC libraries with a reinitialization function. For example, if foo variables are declared as global and initialized. And if my re-initialization function is re_init():

#include <stdio.h> 
int foo1 = 42;
int foo2 = 777;

int main(){
    foo1 = 0;
    foo2 = 0;
    re_init();
    printf("foo1:%d and foo2:%d",foo1,foo2);
    return 0;
}

then I want to have as an output :

foo1:42 and foo2:777

I believe that the right way to do this is to the default linker file and maybe the startup code that copy initiation values to RAM. So, with GCC (cygwin), what should I do to achieve this?

Edit: This page seems o have more precision on it : http://sources.redhat.com/binutils/docs-2.12/ld.info/Output-Section-LMA.html#Output%20Section%20LMA

+3  A: 

I don't know exactly how cygwin does this, but in general, the data section is not copied from its LMA to its VMA; rather, the relevant chunk of the executable file is memory-mapped into RAM at the desired VMA by the kernel, and then the dynamic linker executes any relocations that point at the data section.

To reinitialize the data section from the contents of the executable, therefore, you're going to have to duplicate enough of the dynamic linker and kernel-side executable loader to: find the executable file (this is not necessarily argv[0]); parse its headers and locate the data section; destroy the old mapping and recreate it at the appropriate VMA; carry out all the relocations again; and then deal with all of the fallout caused by your having yanked the C library's runtime state out from under it (it's not only your own global variables in the data section, things like stdout and malloc's master allocation tables are there as well).

Do some searches on "unexec" and "undump", which are solving a similar (but not the same) problem, that might get you code you can recycle.

Zack
Zack had more votes : answer accepted. I would appreciate an answer as clear as Steven Schlansker though.
djondal
+2  A: 

The path you're going is fraught with portability problems, bugs, and sadness.

Best way to fix this: globals are bad! I would factor all my code out to store state in a "context" struct, which is passed around. That way resetting the "state" is just manufacturing a new "context" struct.

Steven Schlansker
And sadness... Interesting!
djondal
I'm getting sad just thinking about it! :-p
Steven Schlansker
A: 

Here's another idea for a solution, although I still think that eliminating the global state is a better one.

Define all your globals in a header file via macros:

globals.h:

DEFINE_GLOBAL(int, foo, 5);
DEFINE_GLOBAL(char, bar, 'x');

main.c:

#include <stdio.h>

#define DEFINE_GLOBAL(type, name, initialvalue) type name = initialvalue
#include "globals.h"
#undef DEFINE_GLOBAL

void resetState() {
    #define DEFINE_GLOBAL(type, name, initialvalue) name = initialvalue
    #include "globals.h"
    #undef DEFINE_GLOBAL
}

I haven't tested this, so it might need some syntax correction - but I think the concept is sound.

Steven Schlansker
I agree that global state is another name for pain and suffering on a code quality level. It is the anti-encapsulation concept. But I am not dealing with someone else's design here... Unfortunately I have hundreds of global variable to re-initialize.
djondal
Perhaps you could write a quick script to read in the files and spit out the appropriate reinitialization commands? i.e. scan for all globals, and generate a function to reset them all?
Steven Schlansker