이전 포스팅에서 컴퓨터는 우리의 언어를 이해하지 못하고 그로 인해 우리의 언어를 컴퓨터의 언어로 바꿔주는 과정인 컴파일에 대해 알아보았다. 그리고 컴파일 과정을 통해 목적파일 ( .o, .obj ) 파일이 만들어진다는 것도 배웠다. 이번 포스팅에서는 이 목적파일들이 실행파일 .exe로 변환되는 과정인 링킹에 대해 알아보겠다.
컴파일(Complie) 이란
우리는 일상속에서 우리의 언어로 소통하고 상호작용한다. 하지만 우리의 언어로 컴퓨터와 소통을 시도한다면 아마 거의 불가능할 것이다. AI가 성행하고 있는 현재, 우리의 언어로 소통할 수
joungdev.tistory.com
프로그래밍에서의 링크(Link), 링킹(Linking)
링크를 이전에 들어보신 분들이라면 아마 C언어를 통해 들어보았을 것이다. C의 빌드 과정에서는 빠짐 없이 언급되는 것이 링크, 링킹이다. 아마 Java를 공부하실 분들이라면 조금은 생소하실 수도 있다. 자바를 공부하다 보면 JVM에 집중하게 되어 링크 과정은 간과하고 넘어가는 경우가 허다하다(물론 그렇지 않은 경우도 있을 것이다) . 하지만 링크 과정은 모든 프로그래밍 언어에서 일어나기 때문에 잘 알아두는 것이 좋다. 물론 자바의 링크 과정과 C언어의 링크 과정은 다른 점이 많다. 오늘 포스팅에서는 가장 일반적인 형태의 링크에 대해 알아볼 것이다.
링크 그리고 링커
링크(Linking)는 컴파일된 코드(예: 오브젝트 파일 *.obj, 또는 *.o 파일)를 결합하여 실행 파일 (*.exe)을 만드는 과정이다. 이 과정에서는 여러 개의 코드 파일, 라이브러리, 외부 함수 등을 하나의 실행 가능한 파일로 결합한다. 이때 링크를 수행하는 프로그램을 링커라고 부른다. 링커는 프로그램에서 사용되는 여러 개의 오브젝트 파일과 라이브러리 파일을 하나의 실행 파일로 결합하는 역할을 한다. 링커가 수행하는 작업은 다음과 같다.
1. 심볼 결합
- 함수나 변수의 주소를 해결하여 서로 연결한다. 예를 들어, main() 함수에서 호출하는 다른 함수들이 어느 파일에 정의되어 있는 지를 찾는다.
2. 재배치
- 각 오브젝트 파일이 독립적으로 컴파일되었기 때문에, 이들을 하나의 실행 파일로 합칠 때 메모리 주소를 재조정한다. 함수와 변수의 실제 메모리 주소를 결정하는 작업이다.
3. 라이브러리 연결
- 필요한 외부 라이브러리(예: C 표준 라이브러리 libc, 수학 라이브러리 libm 등)을 연결한다.
링커는 이것 외에도 여러 활동을 통해 내부적으로 다양한 역할을 수행한다. 이에 대한 내용은 너무 깊기 떄문에 관심이 있으신 분들은 따로 찾아보는 것도 좋을 것 같다. 나중에 포스팅으로 다룰 수 있으면 다뤄보도록 하겠다.
링크의 종류
링크는 그 역할은 고정되어 있지만 종류가 크게 두 가지로 나뉜다. 두 가지 방식으로 링크가 이루어진다.
정적 링크 (Static Linking)
- 프로그램을 빌드할 때 필요한 라이브러리의 코드를 실행 파일에 미리 포함을 시킨다. 따라서, 실행 파일이 독립적으로 실행될 수 있으며, 실행 중에 라이브러리를 추가로 로드할 필요가 없다.
gcc -o hello main.o foo.o -lm
위 명령은 main.o, foo.o 라는 오브젝트 파일을 결합하고, 수학 함수들이 포함되 libm 라이브러리를 정적으로 연결하여 최종 실행 파일 hello를 만든다.
동적 링크 (Dynamic Linking)
- 실행 파일을 만들 때 라이브러리 코드는 포함하지 않고, 프로그램 실행 중에 필요한 라이브러리를 동적으로 로드한다. 실행 파일 크기가 작고, 여러 프로그램이 동일한 라이브러리를 공유할 수 있어 메모리 효율이 좋다.
gcc -o hello main.o foo.o -lm
명령어는 같지만 동적 링크의 경우에는 libm 라이브러리가 실행 중에 동적으로 로드된다. 따라서 프로그램을 실행할 때 해당 라이브러리가 시스템에 설치되어야 libm 라이브러리를 연결할 수 있다.
컴파일과 링크의 차이점
간혹, 컴파일과 링크의 차이점을 구분하지 못하는 경우가 있다. 이전 포스팅 컴파일에 관한 내용을 보고 오면 좋겠지만 간단하게 설명하자면 컴파일(Complie)은 우리가 작성한 소스 코드를 오브젝트 파일로 변환하는 과정이다.
main.c => main.o
이때, 컴파일러는 각 파일을 개별적으로 처리하여 기계어 코드로 변환하지만, 함수 호출 등을 처리할 때 어떤 함수가 정의되어 있는 지는 모른다. 이 과정을 링크(Linking) 과정에서 해결하는 것이다. 오브젝트 파일과 라이브러리들을 결합하여 최종 실행 파일로 만들고 이 과정에서 각 오브젝트 파일들이 서로 연결되고, 외부 라이브러리에서 필요한 함수나 변수들은 연결한다. 즉, 컴파일과 링크는 연속적인 과정이지만, 컴파일과 링크는 구분해서 기억하는 것이 좋다. (물론 경계가 모호한 부분도 있다)
오류
이런한 링크 과정으로 인해 링크 과정에서 발생하는 오류도 다양하다. 몇 가지만 알아보도록 하겠다.
미해결 심볼
프로그램에서 사용된 함수나 변수가 정의되지 않았거나, 잘못된 라이브러리를 사용한 경우 발생
중복 정의
여러 파일에서 동일한 함수나 변수가 중복 정의되었을 때 발생하는 문제
라이브러리 누락
동적 링크를 사용할 때 필요한 라이브러리가 시스템에 없으면 오류가 발생
링크는 프로그램을 실행 가능한 형태로 만드는 데 중요한 과정이다. 컴파일로 변환된 기계어를 결합하여 프로그램을 완성하는 작업이니만큼 그럴 수 밖에 없다. 링크 자체는 포스팅에서 다룬 것처럼 여러 프로그래밍언어에서 일반적인 형태이다. 하지만 링크 과정은 각 환경에 따라 사용되는 링크 방식이나 구체적인 절차가 다를 수 있다. 따라서, 포스팅의 내용을 토대로 언어를 학습하실 때 링크의 과정을 알아보는 것도 좋을 것 같다.
'기본 개념' 카테고리의 다른 글
빌드(Build)란 - 컴파일에서 실행까지 (1) | 2025.01.17 |
---|---|
컴파일(Complie) 이란 (0) | 2025.01.13 |
파싱(Parsing) 이란 (0) | 2025.01.09 |
브라우저란 (0) | 2025.01.08 |