Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일

정적 라이브러리 참조 시 "LNK2019 unresolved external symbol '...' referenced in function" 오류 발생

우연히 접하게 된 라이브러리 하나가 재미있는 구조로 되어 있습니다. ^^ 기본적으로는 C 정적 라이브러리인데, Win32 DLL로도 컴파일이 가능하게 소스코드가 유연하게 구성되어 있는데요.

실제로 간단하게 하나 만들어 보겠습니다. 정적 라이브러리 용도로 Win32 프로젝트를 선택해 다음과 같이 함수 하나를 추가해 둡니다.

// ================= My.h =============== 
#pragma once

#if defined(_MSC_VER) && defined(_USRDLL)
# ifdef LIB_EXPORTS
#  define LIB_DLLEXTERN __declspec(dllexport)
# else 
#  define LIB_DLLEXTERN __declspec(dllimport)
# endif
#else
# define LIB_DLLEXTERN 
#endif

#ifdef __cplusplus
extern "C"
{
#endif

LIB_DLLEXTERN int MyFunc(int input);

#ifdef __cplusplus
};
#endif

// ================= My.c =============== 

#include "My.h"

LIB_DLLEXTERN int MyFunc(int input)
{
    return input + 5;
}

보시는 바와 같이, Win32 정적 프로젝트(이름: StaticLib)이므로 LIB_DLLEXTERN은 빈 매크로로 설정됩니다. 반면 만약 이 소스코드를 Win32 DLL 프로젝트에 추가해 LIB_DLLEXTERN 상수와 함께 컴파일하면 __declspec(dllexport) 영향으로 MyFunc 함수가 외부로 노출이 가능해집니다.

이 함수를 이용하는 Win32 Console EXE 프로젝트를 만들어 테스트 해보면 당연히 잘 컴파일이 됩니다. (정적으로 링크됩니다.)

#include "stdafx.h"

#include "../StaticLib/My.h"

int main()
{
    int result = MyFunc(5);
    printf("%d\n", result);
    return 0;
}




문제는, 위의 정적 라이브러리를 Win32 DLL 프로젝트에서 참조할 때 발생합니다.

#include "stdafx.h"
#include "Win32Lib.h"

#include "../StaticLib/My.h"

WIN32LIB_API int fnWin32Lib(void)
{
    int result = MyFunc(5);
    return result;
}

StaticLib 정적 라이브러리를 추가한 Win32 DLL 프로젝트를 빌드하면 다음과 같은 링크 오류가 발생합니다.

Creating library C:\staticlib\x64\Debug\Win32Lib.lib and object C:\staticlib\x64\Debug\Win32Lib.exp
Win32Lib.obj : error LNK2019: unresolved external symbol __imp_MyFunc referenced in function "int __cdecl fnWin32Lib(void)" (?fnWin32Lib@@YAHXZ)
C:\staticlib\x64\Debug\Win32Lib.dll : fatal error LNK1120: 1 unresolved externals

문제를 간단히 했으니 쉽게 유추할 수 있지만, 수많은 소스 코드 파일을 담은 3rd-party 라이브러리를 정적 링크할 때 저런 오류가 나오면 도대체 뭐가 잘못된 것인지 ^^; 헤맬 수밖에 없습니다.

원인은 간단합니다. My.h 헤더 파일을 include한 Win32 DLL 프로젝트는 _USRDLL 상수가 정의되어 있으므로 __declspec(dllimport) 구문이 정의되어 버립니다.

#pragma once

#if defined(_MSC_VER) && defined(_USRDLL)
# ifdef LIB_EXPORTS
#  define LIB_DLLEXTERN __declspec(dllexport)
# else 
#  define LIB_DLLEXTERN __declspec(dllimport)
# endif
#else
# define LIB_DLLEXTERN 
#endif

#ifdef __cplusplus
extern "C"
{
#endif

LIB_DLLEXTERN int MyFunc(int input);

#ifdef __cplusplus
};
#endif

따라서, DLL 프로젝트 측에서는 "__declspec(dllimport) int MyFunc(int input);" 시그니처를 가진 함수를 찾으려고 시도하지만 링커는 정적 라이브러리 파일(.lib)로부터 그 함수를 찾을 수 없으므로 LNK2019 오류를 내뱉는 것입니다.

문제의 원인을 좀 더 자세히 설명해보면!

"__declspec(dllimport)"로 인해 calling convention이 바뀌는 것은 아닙니다. 단지 dllimport가 지정된 경우 Visual C++ 컴파일러는 그것이 DLL로부터 export된 함수라고 가정하고 DLL 컴파일 시에 생성된 Import Library의 정보를 바탕으로 링크를 시도하는데 이때 해당 .lib 파일에 정의된 이름이 "__imp_[function]" 과 같은 식으로 정의됩니다. 하지만, 우리가 제공했던 .lib 파일은 DLL의 Import Lib 파일이 아닌 순수 정적 링크를 위한 .lib 파일이기 때문에 __imp_.... 식의 이름을 포함하고 있지 않아 LNK2019 오류로 이어진 것입니다.

어찌 보면, 정적 라이브러리 파일과 (DLL의) Import 라이브러리 파일의 확장자를 동일하게 해버려서 온 혼란의 하나겠지요!

해결 방법은, 추가하려는 정적 파일의 헤더 파일을 수정하거나... 아니면 다음과 같이 임시로 _USRDLL 상수를 해제해 버리면 됩니다.

#include "stdafx.h"
#include "Win32Lib.h"

#undef _USRDLL
#include "../StaticLib/My.h"

WIN32LIB_API int fnWin32Lib(void)
{
    int result = MyFunc(5);
    return result;
}

(첨부한 파일은 이 글의 재현 코드입니다.)




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]







[최초 등록일: ]
[최종 수정일: 7/10/2021]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 166  167  168  169  170  171  172  173  [174]  175  176  177  178  179  180  ...
NoWriterDateCnt.TitleFile(s)
652정성태1/22/200922777.NET Framework: 121. WPF - PrintTicket provider failed to bind to printer.
651정성태1/20/200920027.NET Framework: 120. 타입이 다른 배열끼리의 변환
650정성태1/19/200931780COM 개체 관련: 21. C/C++ 프로젝트에 /clr 옵션 적용으로 인한 COM 개체 사용 오류
649정성태1/18/200929375Windows: 38. Q1U UMPC에 Windows 7 베타 설치하기
648정성태1/18/200928086Windows: 37. Windows PE를 USB 메모리에 적용
647정성태1/18/200938224Windows: 36. Windows PE ISO 이미지 만들기 [1]
646정성태1/18/200931146디버깅 기술: 23. COMPLUS_ZapDisable - JIT 최적화 코드 생성 제어 [1]
645정성태1/11/200929952Windows: 35. 서명되지 않은 드라이버 로딩 방법
644정성태1/11/200921055Windows: 34. VPC 설치 후기 [2]
643정성태1/10/200926496Windows: 33. Windows 7 베타와 VMA 충돌 [1]
642정성태1/8/200925204개발 환경 구성: 34. Sysinternals의 모든 툴을 한번에 업데이트 하는 방법 [1]
641정성태1/7/200922352기타: 27. D820 - A09 바이오스 업데이트 프로그램 패치 [2]
640정성태1/4/200923993Team Foundation Server: 29. ClickOnce 응용 프로그램 배포를 Team Build에 추가.
639정성태1/4/200921998Team Foundation Server: 28. PFX 코드 서명을 포함한 프로젝트의 팀 빌드 실패 - MSB4018
638정성태1/3/200925076.NET Framework: 119. WPF - 의존 속성 정의에서 XamlParseException 발생하는 예 [2]
637정성태1/1/200927269기타: 26. 2008년 인기 순위 정리
636정성태12/31/200822397.NET Framework: 118. 2진 검색을 이용한 리스트 정렬 삽입파일 다운로드1
635정성태12/29/200824952오류 유형: 66. 파일 암호화 오류 - Recovery policy configured for this system contains invalid recovery certificate
634정성태12/29/200839310기타: 25. 가상 키보드 관련 정리 [4]
633정성태12/20/200824832기타: 24. RMClock for x64 [2]
632정성태12/19/200833480기타: 23. D820 - 배터리 없이 바이오스 업데이트 방법 [2]파일 다운로드1
631정성태12/10/200842124VC++: 36. Detours 라이브러리를 이용한 Win32 API - Sleep 호출 가로채기 [3]
630정성태12/9/200822908.NET Framework: 117. WPF - TreeView에서 항목이 펼쳐질 때 Cursors.Wait 사용파일 다운로드1
629정성태12/7/200832070.NET Framework: 116. 소켓 연결 시간 제한
628정성태12/6/200820991.NET Framework: 115. Marshal 타입 관련 2가지 자원 해제 메서드파일 다운로드1
627정성태12/6/200823371VS.NET IDE: 58. VS.NET IDE 팁 - 커서 위치 이동 [1]
... 166  167  168  169  170  171  172  173  [174]  175  176  177  178  179  180  ...