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

비밀번호

댓글 작성자
 




... [151]  152  153  154  155  156  157  158  159  160  161  162  163  164  165  ...
NoWriterDateCnt.TitleFile(s)
1277정성태5/8/201231260오류 유형: 152. cmd.exe - The system cannot write to the specified device. [2]
1276정성태4/28/201223117Phone: 5. 모든 Marketplace에 윈폰 앱을 등록하는 방법 [1]
1275정성태4/28/201226990개발 환경 구성: 150. 프로세스 실행으로 잠긴 파일이지만, 이름은 변경가능하다는 사실! 아셨나요? [7]
1274정성태4/17/201221506Phone: 4. "Holiday Calendar" 윈폰 응용 프로그램 등록
1273정성태4/6/201224706Phone: 3. 윈도우 폰을 위한 Holiyday Calendar 앱 개발파일 다운로드1
1272정성태4/5/201226289오류 유형: 151. ASP.NET - EcbGetUnicodeServerVariables 코드에서 System.AccessViolationException 예외 발생
1271정성태4/3/201228965Math: 6. 동전을 여러 더미로 나누는 경우의 수 세기 [1]
1270정성태3/29/201222933오류 유형: 150. Visual Studio 2010 원격 디버깅 오류 - Kerberos authentication failed
1269정성태3/27/201236743오류 유형: 149. ODP.NET 오류 - The provider is not compatible with the version of Oracle client
1268정성태3/27/201233223오류 유형: 148. WCF svc 호출 시 HTTP Error 404.17 - Not Found [1]
1267정성태3/16/201231191.NET Framework: 314. C++의 inline asm 사용을 .NET으로 포팅하는 방법 [1]파일 다운로드1
1266정성태3/14/201234412개발 환경 구성: 149. RAID 1 구성 시 하드 디스크 장애 발생 해결에 대한 경험담
1265정성태3/13/201224705VC++: 61. 아이태니엄(IA64: Itanium) 에서 겪은 C++ 포인터 연산 문제 [2]
1264정성태3/10/201244116.NET Framework: 313. WELL512 난수 발생 알고리즘 - C# [5]파일 다운로드1
1263정성태3/9/201222918개발 환경 구성: 148. tinyget 사용법
1262정성태3/8/201243787개발 환경 구성: 147. .keystore 파일에 저장된 개인키 추출 방법과 인증기관으로부터 온 공개키를 합친 pfx 파일 만드는 방법 [1]
1261정성태3/7/201224495Phone: 2. 개발자용 윈도우 폰 7 기기 등록하는 방법
1260정성태3/6/201224358Phone: 1. 윈도폰 7 개발자 (회사) 등록하는 방법 [3]
1259정성태3/4/201235844Windows: 57. 새로 추가된 네트워크 커널 디버깅 및 PowerShell 3.0 [1]
1258정성태3/3/201237472개발 환경 구성: 146. SQL Server 2012에 포함된 LocalDB 기능 소개 [3]파일 다운로드1
1257정성태3/3/201225658.NET Framework: 312. Native 스레드와 Managed 스레드 개체의 상태 관계 [1]파일 다운로드1
1256정성태3/3/201229210Math: 5. Euler's totient function - C#파일 다운로드1
1255정성태3/3/201231589Math: 4. 소수 판정 및 소인수 분해 소스 코드 - C# [1]파일 다운로드1
1254정성태3/1/201226554Windows: 56. Windows 8 Consumer Preview를 사용해 보고... [1]
1253정성태3/1/201228031VS.NET IDE: 71. Visual Studio 11 Ultimate 베타 설치 [3]
1252정성태3/1/201225377Windows: 55. 윈도우 8 베타 설치 과정 [1]
... [151]  152  153  154  155  156  157  158  159  160  161  162  163  164  165  ...