Partilhar via


Inicialização CRT

Este artigo descreve como o CRT inicializa o estado global no código nativo.

Por padrão, o vinculador inclui a biblioteca CRT, que fornece seu próprio código de inicialização. Esse código de inicialização inicializa a biblioteca CRT, chama inicializadores globais e, em seguida, chama a função fornecida main pelo usuário para aplicativos de console.

É possível, embora não recomendado, aproveitar o comportamento do vinculador específico da Microsoft para inserir seus próprios inicializadores globais em uma ordem específica. Este código não é portátil e vem com algumas ressalvas importantes.

Inicializando um objeto global

Considere o seguinte código C++ (C não permitirá esse código porque não permite uma chamada de função em uma expressão constante).

int func(void)
{
    return 3;
}

int gi = func();

int main()
{
    return gi;
}

De acordo com o padrão C/C++, func() deve ser chamado antes main() de ser executado. Mas quem a chama?

Uma maneira de determinar o chamador é definir um ponto de interrupção no func(), depurar o aplicativo e examinar a pilha. É possível porque o código-fonte CRT está incluído no Visual Studio.

Ao navegar pelas funções na pilha, você verá que o CRT está chamando uma lista de ponteiros de função. Essas funções são semelhantes a func(), ou construtores para instâncias de classe.

O CRT obtém a lista de ponteiros de função do compilador Microsoft C++. Quando o compilador vê um inicializador global, ele gera um inicializador dinâmico na .CRT$XCU seção onde CRT é o nome da seção e XCU é o nome do grupo. Para obter uma lista de inicializadores dinâmicos, execute o comando dumpbin /all main.obje pesquise a .CRT$XCU seção. O comando só se aplica quando main.cpp é compilado como um arquivo C++, não um arquivo C. Deve ser semelhante a este exemplo:

SECTION HEADER #6
.CRT$XCU name
       0 physical address
       0 virtual address
       4 size of raw data
     1F2 file pointer to raw data (000001F2 to 000001F5)
     1F6 file pointer to relocation table
       0 file pointer to line numbers
       1 number of relocations
       0 number of line numbers
40300040 flags
         Initialized Data
         4 byte align
         Read Only

RAW DATA #6
  00000000: 00 00 00 00                                      ....

RELOCATIONS #6
                                               Symbol    Symbol
Offset    Type              Applied To         Index     Name
--------  ----------------  -----------------  --------  -------
00000000  DIR32             00000000           C         ??__Egi@@YAXXZ (void __cdecl `dynamic initializer for 'gi''(void))

O CRT define dois ponteiros:

  • __xc_a no .CRT$XCA
  • __xc_z no .CRT$XCZ

Nenhum dos grupos tem quaisquer outros símbolos definidos, exceto __xc_a e __xc_z.

Agora, quando o vinculador lê várias .CRT subseções (a parte após o $), ele as combina em uma seção e as ordena em ordem alfabética. Isso significa que os inicializadores globais definidos pelo usuário (que o compilador Microsoft C++ coloca em .CRT$XCU) sempre vêm depois de .CRT$XCA e antes de .CRT$XCZ.

A seção deve ser semelhante a este exemplo:

.CRT$XCA
            __xc_a
.CRT$XCU
            Pointer to Global Initializer 1
            Pointer to Global Initializer 2
.CRT$XCZ
            __xc_z

A biblioteca CRT usa ambos __xc_a e __xc_z para determinar o início e o fim da lista global de inicializadores devido à maneira como eles são dispostos na memória depois que a imagem é carregada.

Recursos do vinculador para inicialização

O padrão C++ não fornece uma maneira conforme de especificar a ordem relativa entre unidades de tradução para um inicializador global fornecido pelo usuário. No entanto, como o vinculador da Microsoft ordena as .CRT subseções em ordem alfabética, é possível aproveitar essa ordem para especificar a ordem de inicialização. Não recomendamos esta técnica específico da Microsoft, e poderá deixar de funcionar em uma versão futura. Nós o documentamos apenas para impedir que você crie código quebrado de maneiras difíceis de diagnosticar.

Para ajudar a evitar problemas em seu código, a partir da versão 16.11 do Visual Studio 2019, adicionamos dois novos avisos desativados por padrão: C5247 e C5248. Habilite esses avisos para detetar problemas ao criar seus próprios inicializadores.

Você pode adicionar inicializadores a nomes de seção reservados não utilizados para criá-los em uma ordem relativa específica aos inicializadores dinâmicos gerados pelo compilador:

#pragma section(".CRT$XCT", read)
// 'i1' is guaranteed to be called before any compiler generated C++ dynamic initializer
__declspec(allocate(".CRT$XCT")) type i1 = f;

#pragma section(".CRT$XCV", read)
// 'i2' is guaranteed to be called after any compiler generated C++ dynamic initializer
__declspec(allocate(".CRT$XCV")) type i2 = f;

Os nomes .CRT$XCT e .CRT$XCV não são usados pelo compilador ou pela biblioteca CRT no momento, mas não há garantia de que eles permanecerão sem uso no futuro. E as suas variáveis ainda podem ser otimizadas ou eliminadas pelo compilador. Considere os possíveis problemas de engenharia, manutenção e portabilidade antes de adotar essa técnica.

Ver também

_initterm, _initterm_e
Ficheiros de tempo de execução de C (CRT) e C++ Standard Library (STL) .lib