오브젝트 파일와 링킹
프로그램의 빌드 과정은
크게 컴파일과 링킹과정으로 나누어지게됩니다
위 그림처럼 *.c 파일이 컴파일과정을 통해서 오브젝트파일이 생성이 되게 되고
링킹을 통해서 오브젝트파일들을 합치고 하나의 실행파일을 만들게 됩니다.
오브젝트 파일
오브젝트 파일은 약 3가지정도의 형태로 구분을 할수가 있다고 합니다.
· 재배치 가능한 오브젝트 파일(relocatable object file). compile-time에 다른 재배치 가능한 오브젝트 파일과 통합할 수 있어 하나의 실행파일이 생성될 수 있는 형태의 바이너리 코드와 데이타를 포함한다.
· 실행 오브젝트 파일(executable object file). 메모리에 직접 로딩되어 실행될 수 있는 형태의 바이너리 코드와 데이타를 포함한다.
· 공유 오브젝트 파일(shared object file). load-time이나 run-time에 동적으로 메모리에 로드되고 링크될 수 있는 특수한 타입의 재배치 가능한 오브젝트 파일.
그리고 컴파일러와 어셈블러에 의해서 보통 재배치가능한 오브젝트 파일과 공유 오브젝트 파일을 생성하게 되고
그 오브젝트 파일들을 하나로 모아서 실행파일을 생성하게 된다고 합니다.
실행가능한파일,재배치가능한 오브젝트 파일은
window에선 PE파일형태로 되었기고 공유오브젝트파일일 경우 DLL로 불립니다
그리고 리눅스에서는 ELF를 사용한다고 합니다, 그리고 먼저는 리눅스에서 쓰는 ELF파일을 먼저 살펴봅니다
ELF구조
ELF Header |
.text |
.rodata |
.data |
.bss |
.symtab |
.rel.text |
.rel.data |
.debug |
.line |
.strtab |
ELF(executable and linking format)파일은 위와 같이 해더와 섹션으로 구성이 되어있고
헤데: 파일구성에 대한 정보들
각섹션들은
.text: 컴파일된 프로그램의 실행코드를 담고있음
.rodata: 읽기전용데이터 및 pritnf에서 쓰이는format string등이 저장되어 있고
.data: 전역변수들
.bss: 초기화 되지않은전역변수이 저장되는 영역이고, 실제 오브젝트파일에는 아무공간도 차지 않고있음
.symtab: 프로그램 내에서 정의되거나 참조되는 전역변수와 함수에 대한 정보를 담고 있는 심볼 테이블
.debug : 컴파일때 -g옵셥을 줬을때 생성되며, 지역및 전역 변수를 엔트리로 가지는 디버깅 심볼테이블
.strtab : .symtab과 .debug 섹션에 있는 심볼 테이블들의 스트링 테이블
등으로 구성되어있습니다.
그래서 링킹(Linking)이란
이 오브젝트 파일들을 모아 통합하여 하나의 실행파일로 만들어줍니다.
오브젝트 파일들을 통합해서 라이브러리로 만들고 그걸 메인 프로그램과 링킹을합니다
그리고
링킹에는
static linking과 Dynamic linking 두가지가 있다
static linking
스태틱 링킹, 정적 링킹은
프로그램에서 외부 함수,서브루틴,클래스등을 쓰게 되면 해당되는 정적라이브러리와 함께
하나의 실행파일을 만들게 됩니다,
예를 들어 test.c 이 코드에서 prinf함수를 쓴다고 하면
링킹과정에서 printf함수코드가 들어있는 라이브러리 코드를 가져와서
test.o파일과 합쳐서 하나의 실행파일로 만들게됩니다 그렇다보니
프로그램의 크기가 커지고, a 와 b라는 프로그램이 printf함수를 쓴다고 한다면
a,b둘다에게 이 라이브러리가 들어가 실행파일을 만들기때문에
실행파일이 메모리에 올라갈때에 같은 기능을하는 함수들이 메모리에 수없이 차지게 하게 됨으로
자원낭비가 심해지게 됩니다
그렇기 때문에 다이나믹 링킹이 있게 됩니다.
Dynamic Linking
동적링킹은 Dynamic linking은
정적링킹와 다르게 링커가 라이브러리 파일의 실행코드들을 실행파일에 넣지않고
실행파일이 run-time시에 라이브러리에 있는 함수를 호출하게 되면 그때에
링커가 해당되는 라이브러리 파일을 실행파일의 메모리주소에 로딩을 시키고 해당함수를 실행하게됩니다
예를 들어서 test.o 메인오브젝트 파일과 link.so(공유라이브러리)를 링크를 하게되면
만들지는 실행파일에는 link.so의 코드들이 함께있지 않고 run-time시에 함수,서브루틴들을
참조할수있게 심볼테이블및 재배치정보만을 담고 링킹이 됩니다.
그리고 GNU glibc기반 시스템에서는, ELF 바이너리 실행파일의 시작은 프로그램로더가 적재되고 실행된 후에 합니다
리눅스 시스템에서는, 이 로더는 /lib/ld-linux.so.X(여기서 X는 버전 숫자)라는 이름이 붙는다. 이 로더는 프로그램에서 쓰이는 다른 모든 공유 라이브러리를 찾아주고 적재시켜주는 역할을 합니다
즉 실행파일을 실행해서 메모리에 로딩이 되면 이 링킹 로더에게 제어권을 넘겨주고 이 링킹로더가
공유라이브러리를 메인프로그램 메모리 주소영역에다가 매핑을 시키게 됩니다.
그리고 실행파일내에서 이러한 외부 동적라이브러리의 있는 함수를 실행하게 되면
PLT테이블을 참고 하게되고 여기서 GOT를 참조해서 실제 실행파일의 메모리에 로딩되어있는
해당 함수의 주소를 알아와서 실행을 하게됩니다.
참고자료 출처:
https://www.linuxjournal.com/article/6463 - Linkers and Loaders
https://rootfriend.tistory.com/entry/링크Linkers-와-로더Loaders [A Kind of Magic]