Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
In dit artikel wordt beschreven hoe de crt de globale status initialiseert in systeemeigen code.
De linker bevat standaard de CRT-bibliotheek, die een eigen opstartcode biedt. Deze opstartcode initialiseert de CRT-bibliotheek, roept globale initialisatiefuncties aan en roept vervolgens de door de gebruiker geleverde main functie aan voor consoletoepassingen.
Het is mogelijk, hoewel niet aanbevolen, om te profiteren van Microsoft-specifiek Linker-gedrag om uw eigen globale initialisatoren in een specifieke volgorde in te voegen. Deze code is niet draagbaar en wordt geleverd met enkele belangrijke opmerkingen.
Een globaal object initialiseren
Houd rekening met de volgende C++-code (C staat deze code niet toe omdat er geen functie-aanroep in een constante expressie is toegestaan).
int func(void)
{
return 3;
}
int gi = func();
int main()
{
return gi;
}
Volgens de C/C++-standaard moet func() worden aangeroepen voordat main() wordt uitgevoerd. Maar wie noemt het zo?
Een manier om de aanroeper te bepalen, is door een onderbrekingspunt in func()te stellen, fouten in de toepassing op te sporen en de stack te onderzoeken. Het is mogelijk omdat de CRT-broncode is opgenomen in Visual Studio.
Wanneer u door de functies op de stapel bladert, ziet u dat de CRT een lijst met functiepointers aanroept. Deze functies zijn vergelijkbaar met func() of constructors voor klasseninstanties.
De CRT haalt de lijst met functiepointers op uit de Microsoft C++-compiler. Wanneer de compiler een globale initialisatiefunctie ziet, wordt er een dynamische initialisatie gegenereerd in de .CRT$XCU sectie waarin CRT de sectienaam staat en XCU de groepsnaam. Als u een lijst met dynamische initialisatieprogramma's wilt ophalen, voert u de opdracht dumpbin /all main.objuit en zoekt u vervolgens in de .CRT$XCU sectie. De opdracht is alleen van toepassing wanneer main.cpp wordt gecompileerd als een C++-bestand, niet als een C-bestand. Dit moet er ongeveer uitzien als in dit voorbeeld:
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))
De CRT definieert twee aanwijzers:
-
__xc_ain.CRT$XCA -
__xc_zin.CRT$XCZ
Geen van beide groepen heeft andere symbolen gedefinieerd, behalve __xc_a en __xc_z.
Wanneer de linker nu verschillende .CRT subsecties (het deel na de $) leest, worden deze in één sectie gecombineerd en alfabetisch gerangschikt. Dit betekent dat de door de gebruiker gedefinieerde globale initialisatieprogramma's (die de Microsoft C++-compiler inlegt .CRT$XCU) altijd .CRT$XCA na en vóór .CRT$XCZkomen.
De sectie moet er ongeveer uitzien als in dit voorbeeld:
.CRT$XCA
__xc_a
.CRT$XCU
Pointer to Global Initializer 1
Pointer to Global Initializer 2
.CRT$XCZ
__xc_z
De CRT-bibliotheek gebruikt zowel __xc_a als __xc_z om het begin en einde van de globale initialisatorenlijst te bepalen vanwege de manier waarop ze in het geheugen worden ingedeeld nadat het image is geladen.
Linkerfuncties voor initialisatie
De C++-standaard biedt geen conforme manier om relatieve volgorde op te geven voor vertaaleenheden voor een door de gebruiker geleverde globale initialisatiefunctie. Aangezien de Microsoft-linker echter de .CRT subsecties alfabetisch bestelt, is het mogelijk om te profiteren van deze volgorde om de initialisatievolgorde op te geven. We raden deze Microsoft-specifieke techniek niet aan en het kan in een toekomstige release worden onderbroken. We hebben het alleen gedocumenteerd om ervoor te zorgen dat u geen code maakt die moeilijk te diagnosticeren is.
Om problemen in uw code te voorkomen, hebben we vanaf Visual Studio 2019 versie 16.11 standaard twee nieuwe waarschuwingen toegevoegd: C5247 en C5248. Schakel deze waarschuwingen in om problemen te detecteren bij het maken van uw eigen initializers.
U kunt initialisatoren toevoegen aan ongebruikte gereserveerde sectienamen om ze in een specifieke relatieve volgorde te creëren ten opzichte van de door de compiler gegenereerde dynamische initialisatoren.
#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;
De namen .CRT$XCT en .CRT$XCV worden momenteel niet gebruikt door de compiler of de CRT-bibliotheek, maar er is geen garantie dat ze in de toekomst ongebruikt blijven. En uw variabelen kunnen mogelijk nog steeds worden weggeoptimaliseerd door de compiler. Houd rekening met mogelijke technische, onderhouds- en draagbaarheidsproblemen voordat u deze techniek gaat gebruiken.
Zie ook
_initterm, _initterm_e
C Runtime-bestanden (CRT) en C++ Standaardbibliotheek (STL) .lib