8/04/2009

08-04-09 - CINIT

Two questions I can't find answers to :

1. Is there a way to tell from a piece of code that you are being called from cinit ? eg. in C++ when a constructor causes some code to run, and that calls some function, and then I get called, is there anything I can check to see that I'm currently in cinit, not main?

(obviously a very evil thing I could do is run a stack trace and see what's at the top of the stack). I can't find anything in C that I can check, because the C stdlib is initialized before me, so to my cinit code it looks just like I'm in the app run.

The reason I want this is mainly for asserting & validation - I want to make sure that my own cinit code isn't calling certain things (such as memory allocation) so I want to put in checks like ASSERT( ! in_cinit() );

2. Is there a way to disallow cinit code in certain modules? For example, having cinit stuff in any library is very unsafe because you have to be wary that your OBJ could get dropped from the link and the cinit stuff will not be run, plus you have order of run issues, it's something I can handle fine in my own projects, but not something I want to force on clients. So I want to make sure that my actual deliverable libraries have no cinit stuff - but I do want cinit stuff in my test apps, so I don't want to just break it entirely. I'd really like a compiler error so I know I did a booboo right when I write it.

6 comments:

Бранимир Караџић said...

Did you try pragma init_seg on MSVC (or init_priority attribute on GCC)?

Something like:

#pragma init_seg(lib) // this will be initialized before user consturctors, but after CRT libs
bool g_cinit = true;

int main...
{
g_cinit = false;
...
}

Bret said...

The other way around might be easier, maybe?

// BSS should be initialized to zero
bool g_main_has_started;

int main (...) {
g_main_has_started = true;
...
}

cbloom said...

Yeah, obviously if I can write something at the start of main() it's trivial.

I want this to be in a library and have it work for clients and not require them to write anything in main. It should work automatically using only cinit, not relying on any code in main.

ryg said...

Heavily depends on the compiler and platform, obviously.

For VC++, you have the init_seg stuff. Stuff all your lib code into init_seg(lib). Then you can do:

--------------

static bool initFinished = false;

#pragma init_seg(lib)
// your static initializers
#pragma init_seg(user)
struct __InitDone { __InitDone() { initFinished = true; } } __InitDoneInst;

--------------

Disallowing cinit code: No way to make the compiler reject this that I'm aware of, at least for VC++. But you can test whether the compiled object files contain any initializers, since they're put into special sections:

dumpbin myfile.obj /sections | find ".CRT$"

Look at crt0init.c / crtexe.c in the CRT source code to see how it works. The linker sorts all sections starting with ".CRT$" alphabetically then merges them into one big ".CRT" section, which is then merged into the const data section (that's the "#pragma comment(linker, "/merge:.CRT=.rdata")" in crtinit.c). The $XCA and $XCZ names are used to get labels that point at the start and end of the respective sublists, initializers are put somewhere inbetween. (Normally $XCU for C++ constructors with user-level priority).

If you want something to get executed just after all cinit has been completed, put a function pointer into section .CRT$XCY.

This automatic section-merging is a general linker thing and not tied to the .CRT stuff. If you put a $ in a section name, the linker will always use the remainder of the name as a sort key then merge everything.

ryg said...

Addendum: Less bothersome version of above (no need to stuff everything into init_seg(lib)), including a small test case:

// ---- code stars here
#include <stdio.h>

// ---- start of relevant part

static bool inCInit = false;

static void __cdecl startCInit() { inCInit = true; }
static void __cdecl endCInit() { inCInit = false; }

// the required VC++/linker voodoo
#pragma section(".CRT$XCB",long,read)
#pragma section(".CRT$XCY",long,read)

typedef void (__cdecl *FuncPtr)();

__declspec(allocate(".CRT$XCB")) FuncPtr __startPtr__[] = { startCInit };
__declspec(allocate(".CRT$XCY")) FuncPtr __endPtr__[] = { endCInit };

// ---- end of relevant part

static void func()
{
printf("func called from %s\n",inCInit ? "cinit" : "normal program flow");
}

struct A
{
A() { func(); }
} a;

int main()
{
A b;
func();
}

// ---- code ends here

Now let's see how much Blogger will mutilate this...

cbloom said...

Awesome!

BTW I did some investigation and it seems MSVC sticks everything in init_seg user by default; eg. libs don't get put in init_seg(lib) unless you manually tell it to do so.

I always try to make my static C++ initialization stuff be initialize-on-use so it's not order dependent, but one thing I discovered by doing dumpbins is that the damn float constants become initializers in cinit. eg. something like

const double c = 0.5 + 0.1;

becomes a cinit call.

old rants