CRT 초기화
이 문서에서는 CRT가 네이티브 코드에서 전역 상태를 초기화하는 방법을 설명합니다.
기본적으로 링커는 자체 시작 코드를 제공하는 CRT 라이브러리를 포함합니다. 이 시작 코드는 CRT 라이브러리를 초기화하고 전역 이니셜라이저를 호출한 다음 콘솔 애플리케이션에 대해 사용자 제공 main
함수를 호출합니다.
권장되지는 않지만 Microsoft 관련 링커 동작을 활용하여 고유한 전역 이니셜라이저를 특정 순서로 삽입할 수 있습니다. 이 코드는 이식 가능하지 않으며 몇 가지 중요한 주의 사항이 함께 제공됩니다.
전역 개체 초기화
다음 C++ 코드를 고려합니다(C는 상수 식에서 함수 호출을 허용하지 않으므로 이 코드를 허용하지 않음).
int func(void)
{
return 3;
}
int gi = func();
int main()
{
return gi;
}
C/C++ 표준에 따라 func()
을 실행하기 전에 main()
를 호출해야 합니다. 하지만 누가 호출할까요?
호출자를 확인하는 한 가지 방법은 중단점을 func()
설정하고, 애플리케이션을 디버그하고, 스택을 검사하는 것입니다. CRT 소스 코드가 Visual Studio에 포함되어 있기 때문에 가능합니다.
스택에서 함수를 찾아보면 CRT가 함수 포인터 목록을 호출하는 것을 볼 수 있습니다. 이러한 함수는 클래스 인스턴스의 func()
생성자 또는 유사합니다.
CRT는 Microsoft C++ 컴파일러에서 함수 포인터 목록을 가져옵니다. 컴파일러에 전역 이니셜라이저가 표시되면 섹션 이름 및 XCU
그룹 이름이 있는 CRT
섹션에서 동적 이니셜라이저 .CRT$XCU
를 생성합니다. 동적 이니셜라이저 목록을 얻으려면 명령을 dumpbin /all main.obj
실행한 다음 섹션을 검색합니다 .CRT$XCU
. 이 명령은 C 파일이 아닌 C++ 파일로 컴파일된 경우에만 적용됩니다 main.cpp
. 이 예제와 유사해야 합니다.
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))
CRT는 두 포인터를 정의합니다.
.CRT$XCA
의__xc_a
.CRT$XCZ
의__xc_z
두 그룹에는 다음을 제외한 __xc_a
__xc_z
다른 기호가 정의되어 있지 않습니다.
이제 링커가 다양한 .CRT
하위 섹션(부분 뒤 $
)을 읽을 때 한 섹션에 결합하고 사전순으로 정렬합니다. 즉, Microsoft C++ 컴파일러가 배치하는 사용자 정의 전역 이니셜라이저는 .CRT$XCU
항상 뒤와 .CRT$XCA
그 앞에 .CRT$XCZ
옵니다.
섹션은 다음 예제와 유사합니다.
.CRT$XCA
__xc_a
.CRT$XCU
Pointer to Global Initializer 1
Pointer to Global Initializer 2
.CRT$XCZ
__xc_z
CRT 라이브러리는 이미지를 로드한 후 메모리에 배치되는 방식 때문에 전역 이니셜라이저 목록의 시작과 끝을 둘 다 __xc_a
__xc_z
사용하고 결정합니다.
초기화를 위한 링커 기능
C++ 표준은 사용자가 제공한 전역 이니셜라이저에 대해 번역 단위 간에 상대적 순서를 지정하는 규칙적인 방법을 제공하지 않습니다. 그러나 Microsoft 링커는 하위 섹션을 .CRT
사전순으로 주문하므로 이 순서를 활용하여 초기화 순서를 지정할 수 있습니다. 이 Microsoft 관련 기술은 권장하지 않으며 향후 릴리스에서 중단될 수 있습니다. 진단하기 어려운 방법으로 끊어진 코드를 만들지 못하도록 하기 위해서만 문서화했습니다.
Visual Studio 2019 버전 16.11부터 코드의 문제를 방지하기 위해 C5247 및 C5248이라는 두 가지 새로운 경고를 기본 경고로 추가했습니다. 사용자 고유의 이니셜라이저를 만들 때 문제를 감지하려면 이러한 경고를 사용하도록 설정합니다.
사용하지 않는 예약된 섹션 이름에 이니셜라이저를 추가하여 컴파일러에서 생성된 동적 이니셜라이저에 대한 특정 상대 순서로 만들 수 있습니다.
#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;
이름은 .CRT$XCT
.CRT$XCV
현재 컴파일러 또는 CRT 라이브러리에서 사용되지 않지만 나중에 사용하지 않을 것이라는 보장은 없습니다. 또한 컴파일러에서 변수를 최적화할 수 있습니다. 이 기술을 채택하기 전에 잠재적인 엔지니어링, 유지 관리 및 이식성 문제를 고려합니다.
참고 항목
_initterm, _initterm_e
C 런타임(CRT) 및 C++ STL(표준 라이브러리) .lib
파일