tags:

views:

887

answers:

6

I am reading Microsoft's CRT source code, and I can come up with the following code, where the function __initstdio1 will be executed before main() routine.

The question is, how to execute some code before entering the main() routine in VC (not VC++ code)?

#include <stdio.h>

#pragma section(".CRT$XIC",long,read)

int __cdecl __initstdio1(void);

#define _CRTALLOC(x) __declspec(allocate(x))

_CRTALLOC(".CRT$XIC") static pinit = __initstdio1;

int z = 1;

int __cdecl __initstdio1(void) {
    z = 10;
    return 0;
}

int main(void) {
    printf("Some code before main!\n");
    printf("z = %d\n", z);
    printf("End!\n");
    return 0;
}

The output will be:

Some code before main!
z = 10
End!

However, I am not able to understand the code.

I have done some google on .CRT$XIC but no luck is found. Can some expert explain above code segment to me, especially the followings:

  1. What does this line _CRTALLOC(".CRT$XIC") static pinit = __initstdio1; mean? What is the significance of the variable pinit?
  2. During compilation the compiler (cl.exe) throws a warning saying as below:

Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved.

stdmacro.c
stdmacro.c(9) : warning C4047: 'initializing' : 'int' differs in levels of indirection from 'int (__
cdecl *)(void)'
Microsoft (R) Incremental Linker Version 9.00.30729.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:stdmacro.exe
stdmacro.obj

What is the corrective action needs to be done to remove the warning message?

Thanks in advance.


Added:

I have modified the code and give type to pinit as _PIFV. Now the warning message is gone.

The new code is as follows:

#include <stdio.h>

#pragma section(".CRT$XIC1",long,read)

int __cdecl __initstdio1(void);

typedef int  (__cdecl *_PIFV)(void);

#define _CRTALLOC(x) __declspec(allocate(x))

_CRTALLOC(".CRT$XIC1") static _PIFV pinit1 = __initstdio1;

int z = 1;

int __cdecl __initstdio1(void) {
    z = 100;

    return 0;
}

int main(void) {
    printf("Some code before main!\n");
    printf("z = %d\n", z);
    printf("End!\n");
    return 0;
}
+3  A: 

In C++ at least, you don't need all that implementation specific stuff:

#include <iostream>

struct A {
   A() { std::cout << "before main" << std::endl; }
};

A a;

int main() {
   std::cout << "in main" << std::endl;
}
anon
This is a good idea.But your code can pass compilation in C++ only; not in C.
yinyueyouge
The question is tagged C++
mouviciel
+3  A: 

This is what _CRTALLOC is defined as:

extern _CRTALLOC(".CRT$XIA") _PVFV __xi_a[];
extern _CRTALLOC(".CRT$XIZ") _PVFV __xi_z[];// C initializers
extern _CRTALLOC(".CRT$XCA") _PVFV __xc_a[];
extern _CRTALLOC(".CRT$XCZ") _PVFV __xc_z[];// C++ initializers

It's a table of things to pre-initialise, of which a pointer to your function __initstdio1 is placed.

This page described CRT initialisation:

http://msdn.microsoft.com/en-us/library/bb918180.aspx

Mark Ingram
+1  A: 

There's some information here (search for CRT). The significance of variable pinit is none, it's just a piece of data placed in the executable, where the runtime can find it. However, I would advise you to give it a type, like this:

_CRTALLOC(".CRT$XIC") static void (*pinit)()=...

The linker warning probably just warns you you have a function that has int return type, but doesn't return anything (probably you'd better change the return type to void).

jpalecek
+3  A: 

A simple way to do this.

#include <iostream>

int before_main()
{
    std::cout << "before main" << std::endl;
    return 0;
}

static int n = before_main();

void main(int argc, char* argv[])
{
    std::cout << "in main" << std::endl;
}
Shino C G
problems may arise when before_main() depends on data created in another function that runs before main(). The order in what such functions run is not defined. This, in particular, means that global data before_main() creates may be overwritten by runtime routines that initialize global data to 0s
dmityugov
This is C++, not C.
RBerteig
replace std::count << with puts() and it will be C
dmityugov
+1  A: 

Even in C, there is a need for some code to be run before main() is entered, if only to transform the command line into the C calling convention. In practice, the standard library needs some initialization, and the exact needs can vary from compile to compile.

The true program entry point is set at link time, and is usually in a module named something like crt0 for historical reasons. As you've found, the source to that module is available in the crt sources.

To support initializations that are discovered at link time, a special segment is used. Its structure is a list of function pointers of fixed signature, which will be iterated early in crt0 and each function called. This same array (or one very much like it) of function pointers is used in a C++ link to hold pointers to constructors of global objects.

The array is filled in by the linker by allowing every module linked to include data in it, which are all concatenated together to form the segment in the finished executable. The only significance to the variable pinit is that it is declared (by the _CRTALLOC() macro) to be located in that segment, and is initialized to the address of a function to be called during the C startup.

Obviously, the details of this are extremely platform-specific. For general programming, you are probably better served by wrapping your initialization and your current main inside a new main():

int main(int argc, char **argv) {
    early_init();
    init_that_modifies_argv(&argc, &argv);
    // other pre-main initializations...
    return real_main(argc,argv);
}

For special purposes, modifying the crt0 module itself or doing compiler-specific tricks to get additional early initialization functions called can be the best answer. For example, when building embedded systems that run from ROM without an operating system loader, it is common to need to customize the behavior of the crt0 module in order to have a stack at all on which to push the parameters to main(). In that case, there may be no better solution than to modify crt0 to initialize the memory hardware to suit your needs.

RBerteig
A: 

I wrote an award-winning article about this on CodeGuru a while ago...

===Jac

Jac Goudsmit