32비트 컴파일할 땐 문제 없던 C++/CLI 코드가 64비트로 컴파일하자 링킹 오류를 내고 죽었다. 다음은 실제 프로젝트 코드에서 나온 오류 메시지 그대로이다. 물론 알아볼 수 없게 소스 코드 이름이나 프로젝트 이름을 모두 바꿔치기 했다.
>Compiling resources... 2>Linking... 2>Generating code 2>Finished generating code 2>SampleLibrary.lib(SourceCode1.obj) : error LNK2016: Absolute symbol '@comp.id' used as target of REL32 relocation 2>SampleLibrary.lib(SourceCode2.obj) : error LNK2016: Absolute symbol '@comp.id' used as target of REL32 relocation 2>SampleLibrary.lib(SourceCode3.obj) : error LNK2016: Absolute symbol '@comp.id' used as target of REL32 relocation 2>SampleLibrary.lib(SourceCode4.obj) : error LNK2016: Absolute symbol '@comp.id' used as target of REL32 relocation 2>SampleLibrary.lib(SourceCode5.obj) : error LNK2016: Absolute symbol '@comp.id' used as target of REL32 relocation 2>LINK : fatal error LNK1165: link failed because of fixup errors 2>Build log was saved at "file://d:\src\trunk\src\projectname\x64\Release\BuildLog.htm" 2>ProjectName - 6 error(s), 0 warning(s) ========== Build: 1 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
MSDN 라이브러리는 LNK2016 오류를 이렇게 설명한다.
절대 기호 ‘symbol’이(가) target 재배치 대상으로 사용됩니다.
Absolute symbol ‘symbol’ used as target of target relocation절대 기호가 올바른 재배치 대상이 아닙니다. 컴파일러 공급업체에 문의할 수 있습니다.
마이크로소프트 고객지원 사이트에선 추가 정보를 제공한다.
When you try to link to an application or to a DLL, and the application or the DLL has more than 32,767 exports,
여기서 감이 온다. 특별한 이유가 있어서 링커 옵션에 Keep Unreferenced Data (/OPT:NOREF) 옵션과 Remove Redundant COMDATs (/OPT:ICF)을 넣었다. 닷넷 런타임에다가 32비트 라이브러리, 64비트 라이브러리까지 모두 불러오다 보니 심볼이 너무 많았나 보다.
고객지원 사이트에선 해결책까지 언급했다.
A supported hotfix is now available from Microsoft, but it is only intended to correct the problem that this article describes. Apply it only to systems that are experiencing this specific problem.
To resolve this problem, contact Microsoft Product Support Services to obtain the hotfix. For a complete list of Microsoft Product Support Services telephone numbers and information about support costs, visit the following Microsoft Web site:
핫픽스가 있긴 한데, 직접 연락해야 준다는 뜻이다. 하여 이메일로 핫픽스를 요청했다. 답신이 오면 다시 테스트해볼 일이다. 핫픽스로도 문제가 해결 안 되면, 최적화 옵션을 바꾸고 다른 해결책을 모색해봐야 한다.
추가 정보. Visual Studio 2008 Orcas 베타를 받아서 해보니 링킹 문제가 사라졌다. 새 버전에선 문제가 해결됐나 보다.
(2007.07.16) 해결했다. 링커를 손보지 않고 프로그래밍 차원에서 해결했다(고 들었다). MSDN 라이브러리를 보면 selectany에 대해 이렇게 말한다.
At link time, if multiple definitions of a COMDAT are seen, the linker picks one and discards the rest. If the linker option /OPT:REF (Optimizations) is selected, then COMDAT elimination will occur to remove all the unreferenced data items in the linker output.
Constructors and assignment by global function or static methods in the declaration do not create a reference and will not prevent /OPT:REF elimination. Side effects from such code should not be depended on when no other references to the data exist.
For dynamically initialized, global objects, selectany will discard an unreferenced object’s initialization code, as well.
A global data item can normally be initialized only once in an EXE or DLL project. selectany can be used in initializing global data defined by headers, when the same header appears in more than one source file. selectany is available in both the C and C++ compilers.
요약하자면 selectany
키워드를 쓰면 참조되지 않는 코드는 제거된다는 말이다. selectany
키워드가 쓰인 코드를 모두 extern
으로 선언하고, 내부 구현을 cpp 파일에 옮겨 놓으니 문제가 해결됐다(고 한다).
프로젝트 중간에 끼어든 터라 자료 조사를 주로 하는 탓에 문제를 제대로 파악하기가 쉽지 않다. 전체 소스 코드를 살펴볼 시간이 많지 않다 보니 엉뚱한 데 초점을 맞추기 십상이다. 이번 건만 해도 링커 쪽을 조사해달라는 요청을 받아서 키워드 쪽을 볼 생각은 하지 못했었다. 어쩌다가 selectany
키워드가 쓰인 걸 봤기 때문에, 문제를 잡아낼 수 있었다. 이를테면 운이 좋았던 셈이다.