링크 에러에 대한 고찰

요즘 한가하게 블로그에 글을 쓸 시간이 없음에도 불구, 머리 좀 식힐 겸 생각했던 이야기 하나.

 

옛날 옛적에

C언어는 대학교 입학해서 처음으로 배웠다. 나는 컴퓨터 공학과나 전산학과로 입학한 사람이 아니어서 그냥 혼자서 야매로 공부를 했다. Borland C/C++ 를 좀 만지기도 했으나 주로 Visual C++ 위에서 열심히 코딩 했다. 컴파일 에러는 그럭저럭 C 책 뒤져 가며 잡을 수 있었다. 그런데 나를 정말로 좌절케 했던 것은 바로 LNK2001 에러. VC++ 쓰는 사람이라면 이 에러로 고생 안 해 본 사람은 없을 것이다.

요즘은 고교 과정에서 수준 높은 영어 단어를 가르치는 지는 모르겠으나, 대학교 막 입학한 나에게 ‘unresolved’라는 단어는 매우 생소했다. 사전을 찾아보니 ‘미해결된’ 이라는 뜻이었다. External, 이건 외부라는 뜻이고, symbol 은 상징/기호라는 뜻이니까 그렇다면 “미해결된 외부 기호”. 도대체 이게 무슨 뜻?? 게다가 이 에러가 어디에서 일어났는지도 알 수가 없었다. 내가 작성하고 있던 파일은 main.c 였는데 갑자기 .obj라는 파일은 뭐야? 도대체 “Linking…”은 또 무슨 소릴까? 어렸을 때 MS-DOS 디렉토리에서 LINK.EXE라는 프로그램은 본 것 같았는데.

그런데 LNK2001 에러와 쌍벽을 이루는 에러가 또 있나니 그 이름은 LNK2005.

LNK2001과 LNK2005의 콤보 어택은 수 많은 초보 C/C++ 프로그래머들을 당혹하게 만든 주범이다. 태어나서 GW-BASIC, QBasic만 만져 본 나로서는 이해하기 참 힘든 에러였다.

 

초보자를 위한 링크 에러 설명

초보자를 위해 링크 에러에 대해 최대한 간략히 설명하면: C/C++ 컴파일러는 소스 파일 하나를 독립적으로 ‘컴파일’한다. 그런데 컴파일할 때는 함수 원형 선언만 있고 구현이 없어도 문제 없이 컴파일을 할 수 있다. 단순히 함수 원형의 리턴 및 파라미터 타입만 맞으면 아무런 문제 없이 컴파일은 진행된다. 이렇게 개별 파일의 컴파일 작업이 끝나면 최종 실행 파일을 만들기 위해 모으는 작업, 즉 ‘링킹’을 한다. 이 때 하는 일이 ‘심볼’들을 찾아 연결하는 작업이다. 심볼이라는 단어는 컴파일러의 관점에서 나온 단어다. 컴파일러는 함수 및 변수 같은 것을 ‘심볼 테이블’로 관리하기 때문에 나온 말이다.

링크 과정에서는 각각의 소스 파일이 사용한 ‘심볼’을 해결해야 한다. 쉽게 말하면 사용하고 있는 함수의 실제 몸통을 어디선가 찾아야만 한다. 만약 이 몸통을 못 찾거나 두 개 이상 찾으면 링킹은 실패하고 각각 VC++에서 LNK2001, LNK2005에러가 발생한다. printf 같은 경우는 기본 라이브러리에 이미 그 구현이 있기 때문에 그냥 알아서 되는 것 뿐이다.

위의 경우에는 foo() 라는 함수에 대한 구현이 어떠한 소스 파일에서도 찾을 수가 없으면 LNK2001 에러가 나고, 두 개 이상 파일에서 foo() 함수 구현을 찾으면 LNK2005 에러가 된다.

 

링크 에러의 HCI 적인 고찰

왜 이 링크 에러를 어렵게 느낄까? 한번 사용자 경험 측면에서 이야기 해보자. 일반적인 컴파일러 에러의 형태는 컴파일러 종류에 상관없이 매우 비슷하다.

너무나 당연히 “어느 소스 파일의 어느 라인에서” 이라는 정보가 들어가 있다. 그래서 main.c 파일의 6번째 줄로 가면 이 에러를 잡을 수 있다라는 것을 알려 준다. 그런데 링크 에러는? 맨 위 그림을 보면 “어디에서”에 해당하는 정보가 없다. VC++ 컴파일러가 멍청해서 그런 것일까? 아니다. 대부분이 다 그러하다.

gcc 메세지는 더 무시무시하다. 희한한 이름의 .o 파일에서 문제가 있다고 한다. test.c 라는 파일은 보이는데 “.text+0xa”라는 또 알 수 없는 말이 있다. 링크 에러가 컴파일 에러에 비해 매우 어렵게 느껴지는 것은 이렇게 에러가 어디서 났는지를 쉽게 알려 주지 않기 때문이다. 만약에 컴파일러가

어떤 소스 파일의 몇 번째 줄에서 부른 이 함수의 구현 내용을 찾지 못했습니다.

라고 이야기 해주면 어떨까? 단언컨데 훨씬 링크에러를 쉽게 이해할 수 있을 것이다.

test.c:6: link error: cannot find the definition of the function ‘foo’. Where is the body?

test.c:6: 링크 에러: 함수 ‘foo’의 정의가 없어. 어디다 뒀냐? 라이브러리 링크는 제대로 했냐?

대충 이 정도만 되어도 훨씬 쉬울 것이다. VC++나 기타 에디터의 도움이 있다면 바로 링크 에러를 만든 소스 파일 위치로 점프할 수 있다. 컴파일러 에러는 점점 친절하게 진화하고 있다. 그러나 링크 에러 메세지는 어렵다. 초보자 뿐만 아니라 경험 많은 C/C++ 프로그래머도 링크 에러가 나오면 쉽지 않다.

 

그래도 링크 에러는 어렵다

혼자 만든 프로그램에서 겪는 링크 에러는 쉽게 잡을 수 있다. 그러나 규모가 큰 소프트웨어를 개발할 때, 특히 다른 사람들이 만든 라이브러리를 써야 할 때 겪는 링크 에러는 매우 어렵다.

FileZilla FTP 클라이언트 소스를 받아서 수정을 좀 하려고 했다. 다운로드 받아 보니 친절하게도 VC++ 솔루션 파일이 있었다. 열어서 컴파일해봤다. 컴파일 에러가 무수히 나온다. 필요한 wxWidgets도 깔아주고 컴파일러 에러를 다 잡았다. 이제 링크 에러가 수 천 개 뜬다… 이 쯤이야 하면서 잡아간다. 많이 잡았다. 그런데 열 몇 개 정도는 끝내 못 잡고 말았다. 각종 외부 라이브러리를 많이 쓰다 보니 링킹이 너무 어려운 것이었다. 결국 알고 보니 VC++에서는 링킹이 안 된다는 것이었다. 거기서 시키는 대로 mingw를 깔고 겨우 해결할 수 있었다. 그것도 조금만 라이브러리 버전 등을 실수해도 수 많은 링크 에러를 만나게 된다.

C와 C++ 언어 사이에서도 extern “C” 같은 것을 모르면 링크 에러로 고생한다. extern “C”나 C++ 함수의 name mangling에 대한 자세한 설명은 귀찮아서 생략한다. C++ 클래스 멤버 함수에서 링크 에러가 발생하면 괴상한 문자들로 가득 찬 함수 이름에 좌절한다. 함수 호출 규약(calling convention)도 신경 써야 한다.

링크 에러는 파일 질라 같이 여러 라이브러리를 가져다 쓸 때 특히 어렵다. DLL과 static library가 섞여 있을 때도 어렵다. 모든 외부 라이브러리를 일관되게 링크를 시켜야 한다.  가장 좋은 예는 Codejock의 GUI 툴킷에서 찾아볼 수 있다. 이 툴킷을 static library 형태로 쓸 때 특히 문제다. 이 라이브러리에서도 분명 printf와 같은 기본 C/C++ 함수를 부를 것이다. MFC 같은 C++ 라이브러리도 쓴다. 그래서 내가 만드는 프로그램에 같이 포함시킬 때 주의를 기울여야 한다. 어떤 녀석은 기본 C/C++ 라이브러리를 DLL로 쓰도록 했고, 어떤 녀석은 그러하지 않다면 여지 없이 LNK2001 혹은 LNK2009 폭탄을 맞는다.

대표적으로 위 그림과 같은 런타임 라이브러리 사용을 어떻게 할 것인가를 잘 맞춰야 한다. 때에 따라서는 별도로 libc.lib, msvcrt.lib와 같은 기본 라이브러리가 자동으로 링크되지 않도록 해야 한다. 정말 겪어 보지 않고서는 모르는 문제다.

이런 걸 보면 명시적인 링킹 과정이 없어 보이는 C#이나 Java는 참으로 편리한 언어인 것 같다. 그래도 적어도 링크 에러 메세지를 조금만 가다듬어도 “체감 난이도”는 훨씬 줄어들 것이다.

by object | 2008/11/22 22:26 | 컴퓨터 | 트랙백(1) | 핑백(1) | 덧글(13)
트랙백 주소 : http://minjang.egloos.com/tb/2146607
☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]
Tracked from 그니 at 2010/03/24 17:02

제목 : 링크 에러에 대한 고찰
링크 에러에 대한 고찰...more

Linked at 친절한 임베디드 시스템 개발자.. at 2010/07/24 14:32

... 대로 동작시키기 위한 힌트와 비결 - Make를 디버깅한다 ⓡ gcc 컴파일러 이야기 ⓡ 링크 에러에 대한 고찰 4) OS를 만듭시다. 어때요~ 참 쉽죠? ⓐ Platform과 Bootup&n ... more

Commented by 아라크네 at 2008/11/22 22:54
사족인진 모르겠습니다만 gcc의 에러 메시지는 gcc driver 스크립트에서 임시 위치에 목적 파일을 뱉어 내고 링크하다 보니 이름이 알 수 없게 나오는 것 뿐입니다. 그래도 함수+오프셋 식으로 나오는 건 솔직히 별 도움은 안 되는 것 같은데 없애던지 했으면 좋겠어요.
Commented by object at 2008/11/23 03:51
네. 저도 어떤 기술적인 이유로 그런 것이 나올 것이라는 걸 잘 압니다. 인텔 컴파일러 같은 경우는 자세한 최적화 출력도 볼 수가 있는데 그 땐 더 난해한 메세지가 나오죠. 문제는 99.9%의 사용자에게 불필요한 정보를 굳이 보여줄 필요가 있냐 이거죠.
Commented by tamasii at 2008/11/22 23:00
저도 저 메시지에 굉장히 고생이 많았지요.
컴파일은 되면서 ... 쳇 ... 하면서 많이 삐뚫어졌어요. ^^;;;;
Commented by JOHN_DOE at 2008/11/23 00:16
함수가 수십 수백개 되면 완전 머리가 터져버리겠네요 ㄱ-
Commented by xeraph at 2008/11/23 00:20
ㅋㅋㅋ HCI적 고찰에 동감합니다.
Commented by ㄹㄹㄹ at 2008/11/23 01:39
한 2년전에 외부 라이브러리랑 MFC 같이쓰다가 crt 문제때문에 정말 욕나왔던 기억이 생생합니다
Commented by 세라비 at 2008/11/23 04:22
어디 템플릿 에러만 하겠어요? :) 컴파일러들의 에러 메시지가 분명 시간이 지나면서 개선되어가는 것은 사실인 듯. 표준에 있는 필수 기능들 구현하느라 바쁘다 이제 좀 여유가 생긴 건지 최근 수년에 와서 더 그런 것 같고...
Commented by object at 2008/11/23 04:40
템플릿 관련 에러, 링크 에러... 모두 초보자에게는 난감할 따름이죠.
Commented by Lohengrin at 2008/11/23 07:13
제가 겪었던 힘들었던 링크 에러 중에 하나는 a.DLL에서 CRT를 부를때 해당 버젼의 CRT가 없는데 에러 메시지는 a.DLL을 로드할 수 없다고 뜨는 에러였습니다. a.DLL이 해당 폴더에 있는데 왜 못 읽는 거지? 라고 하면서 한참 헤맨 기억이 나네요. depends 사용법 등을 모르고 시작해서 시간 많이 잡아 먹었죠;
Commented by jhumwhale at 2008/11/24 10:18
문법 처음 배우는 사람에게는 컴파일 에러도
텍사스 소떼 만큼의 짜증이 밀려오죠.


뭐... 나중에는 수많은 삽질을 통해 그럭저럭 해결책이 나오고,
그만큼의 시스템 이해도를 위한 단초를 제공하기는 했지만요... :p

이공계 기피 시대, 더군다나 컴퓨터 공학과 지원자가 줄어드는 마당에
한때는 C 언어 시간에 문법 뿐 만아니라, 에러 메시지에 대한 대처법도 같이 배워야 한다고 생각했었습니다..
Commented by 가짜집시 at 2008/11/24 10:21
확실히, 에러를 내기 때문에 초보자-가 아니라, 무슨 에러가 어떻게 났는지 모르기 때문에 초보자- 인듯.
Commented by daybreaker at 2008/11/24 21:45
저도 C언어를 주력으로 사용하지는 않지만 오픈소스 프로젝트들 가져다가 빌드할 때면 항상 저 링크 에러 때문에 고생하곤 했습니다. 아 정말...ㅠㅠ;
Commented by orchistro at 2008/12/10 03:04
재미있는 글 잘 읽었습니다.
library 파일에서 외부 symbol 을 참조하고, 그 symbol 을 찾을 수 없는 경우 등이 있기 때문에, 링커가 "어떤 소스 파일의 몇 번째 줄에서" 를 출력해 주는 것은 거의 불가능해 보입니다. ( '0')
그냥 재미있는 상상(?)인데, 괜한 딴지를 걸었군요... 너무 개의치 마세요 :-)

덧. HCI 가... Human Computer Interaction 의 약자로도 쓰이는군요.
Host Controller Interface 로 알고 있어서... '응?' 했습니다 :-)

:         :

:

비공개 덧글

<< 이전 페이지 다음 페이지 >>





by 김민장 2008 이글루스 TOP 100
최근 등록된 덧글
개발자 입장에서의 수많은 ..
by Jiyoon at 02/04
저도 아들 돌잔치때 돌잡이 ..
by 박상욱 at 01/18
미국 대학원 원서 작성중에 p..
by 태클사이야 at 01/13
TO: 박PD 로그인 하지 않아..
by 박응용 at 01/10
http://gigglehd.com/zbx..
by dhunter at 12/28
우와.. 좋네요. 태반이 ..
by 윤광배 at 12/17
항상 좋은 글 잘 보고 있습니..
by y2k at 11/23
글이 좋아서 제 블로그에 담..
by 쏭섭 at 11/23
최근 등록된 트랙백
조엘 스폴스키의 강연 (Sta..
by 인덕원칸타타
[Redis] sds.c를 분..
by 조급하지말고 천천히
메뉴릿
이글루 파인더

website counter

Add to Google

rss

skin by 이글루스