모든 라이브러리나 프레임워크는 사용된 기술이나 프로그램 언어와 상관없이 특정한 기능을 제공 == 응용프로그램 인터페이스(API)
라이브러리를 사용하려면 API를 사용해야함.
API는 두 소프트웨어 컴포넌트가 서로를 사용하거나 서로 기능을 제공할 때 수용하는 규칙(=표준)
ABI vs API
- API: 두 소프트웨어의 컴포넌트 간 호환성을 보장해 기능적으로 계속 협력
- ABI: 두 프로그램이 기계 수준의 며열ㅇ어 수준에서 호환 가능하도록 두 프로그램의 목적 파일을 함께 사용할 수 있게 함.
ABI는
1. 대상 아키텍처의 명령어 집합
2. 기존의 자료형과 크기 및 정렬 규칙
3. 함수 호출 규칙
4. 유닉스 계열 시스템에서 시스템 호출 방식 정의
5. 사용된 목적 파일 형식
6. C++ 컴파일러로 생성한 목적 파일의 네임 맹글링, 가상 테이블 레이아웃
리눅스와 다른 유닉스 계열 운영체제 = ELF
OS X(maxOS 및 iOS) = Mach-O
마이크로소프트 윈도우 = PE
컴파일 파이프라인에서 어셈블리 단계의 결과물
C 언어 프로젝트의 임시 결과물로 간주, 이후 최종 결과물을 만드는 주재료
재배치 가능한 목적 파일에서는 컴파일된 변환 단위와 관련해 다음 항목을 찾을 수 있다.
1. 변환 단위에서 발견되는 함수에 대해 생성된 기계 수준의 명령어(코드)
2. 변환 단위에서 선언된, 초기화된 전역 변숫값(데이터)
3. 변환 단위에서 정의된 심벌 및 참조된 심벌을 모두 포함하는 심벌테이블
재배치 가능한? 링커가 수행하는 과정에서 비롯됨. 링커는 재배치 가능한 목적 파일 여러개를 한데 모아서 더 큰 목적 파일을 형성. 하나의 재배치 가능한 목적 파일에서 나타나는 기계 수준의 명령어는 다른 재배치 가능한 목적 파일에서 나온 기계 수준의 명령어 다음에 위치. -> 명령어가 이동할 수 있거나 재배치 가능 하다는 점을 의미.
따라서, 재배치 가능한 목적 파일에서 명령어는 주소를 갖지 않는다. 링크를 단계를 거치고 나서 명령어는 주소를 가진다.
readelf -hSl funcs.o
유틸리티를 사용해서 funcs.o
목적 파일의 ELF 내용을 확인 할 수 있다.
재배치 가능한 목적 파일은 11개의 섹션이 있음.
.text
섹션은 변환 단위를 위한 모든 기계 수준의 명령어를 포함
.data
와 .bss
섹션은 초기화된 전역 변숫값과 초기화되지 않은 전역 변수에 필요한 바이트의 숫자를 각각 포함.
.symtab
섹션은 심벌 테이블을 포함
재배치 가능한 목적 파일의 심벌은 어떠한 최종 혹은 절대 주소를 갖지 않으며, 주소는 링크 단계에서 결정
재배치 가능한 목적 파일처럼 실행 가능한 목적 파일도 기계 수준의 명령어, 초기화된 전역 변숫값, 심벌 테이블과 같은 항목을 가진다. 그러나! 정렬은 다를 수 있음.
ELF 실행 가능한 목적 파일에는 더 많은 섹션이 있다. 이들은 몇몇 세그먼트와 함께 있음.
각 세그먼트는 많은 섹션(0~그 이상)으로 구성, 섹션은 내용에 따라 세그먼트에 놓임.
기계 수준의 명령어를 포함하는 모든 섹션은 같은 세그먼트에 들어간다.
readelf -hSl ex3_1.out
의 ELF 내용 출력을 볼 때 알아야할 사항
INTERP
와 같은 특정 세그먼트를 갖는 공유 목적 파일. 로더 프로그램이 이 세그먼트를 이용해 실행 가능한 목적 파일을 로드함.INTERP
는 위에서 설명. 두 번째는 TEXT
세그먼트 = 기계 수준의 명령어를 갖는 섹션을 모두 포함. 세 번째는 DATA
세그먼트 = 전역 변수를 초기화할 때 사용할 모든 값과 다른 초기 구조체를 포함. 네 번째 세그먼트는 동적 링크와 관련한 정보를 찾을 수 있는 섹션을 나타냄. 동적 링크와 관련한 정보로는 실해의 일부로 로드해야 하는 공유 목적 파일이 있음.여러 개의 재배치 가능한 목적 파일을 링크한다는 것은 주어진 재배치 가능한 목적 파일에서 모든 유사한 섹션을 모아서 더 큰 섹션을 형성하고, 이렇게 모은 섹션을 실행 혹은 공유 목적 파일로 만드는 일이다. 그러므로, 링크를 거쳐야 심벌이 마무리 되고 변하지 않는 주소를 가짐.
실행 가능한 목적 파일에서 주소는 절대적이다. 그러나 공유 목적에서는 상대 주솟값이 절대적.
상대 주솟값
- 메모리 공간에서 사용자 영역이 시작되는 주소 번지를 0번지로 변경하여 사용하는 주소 지정 방식
readelft -s ex3_1.out
명령어를 사용해 심벌 테이블을 볼 수 있음.
하나의 실행 가능한 목적 파일에 2개의 다른 심벌 테이블이 있음. 첫 번째 심벌테이블인 .dynsm
은 실행 파일을 로드할 때 해결할 심벌을 포함. 두 번째 심벌 테이블인 .symtab
은 동적 심벌 테이블에서 가져온, 미해결된 심벌과 함께 해결된 심벌도 모두 포함. 즉, 두 번재 심벌 테이블은 동적 테이블의 미해결 심벌도 포함한다.
심벌 테이블에서 해결된 심벌은 링크 단계에서 획득한 주소에 해당하는 절대 주소를 가진다.
C 프로젝트에서 얻을 수 있는 결과물 중 하나
재배치 가능한 목적 파일에서 만들어지는 유닉스 아카이브이다.
정적 라이브러리 그 자체는 목적 파일로 볼 수 없음. 목적 파일을 위한 컨테이너
정적 라이브러리는 유닉스의 ar 유틸리티로 파일을 아카이브
링커가 링크 단계에서 정적 라이브러리를 사용하려 할 때, 우선 재배치 가능한 목적 파일에서 정적 라이브러리를 추출하려고 시도. 다음 거기서 정의되지 않은 심벌을 찾아 해결
정적 라이브러리는 하나의 아카이브에 이전의 재배치 가능한 목적 파일 모두를 포함
main 함수가 없으면, 정적 라이브러리를 만드는 재배치 가능한 목적 파일로 두거나 아카이브로 둘 수 있다.
ar t opt/~~~/~~.a
명령어를 사용해 아카이브 파일의 내용을 볼 수 있다.
C 언어 라이브러리 사용하려면 정적 라이브러리 파일과 함께 라이브러리가 제공하는 선언에 접근해야함. 이 선언은 라이브러리의 공용 인터페이스(일반 적으로 라이브러리의 API라고 간주)
컴파일 단계(컴파일러가 자료형의 존재나 함수 시그니처 등을 알아야 하는 단계)에서는 선언 필요. 이 내용은 헤더에 있음.
링크 이후에 정적 라이브러리 파일에 의존하는 것은 아무것도 없음. 모든 것은 실행 파일 그 자체의 내부에 임베드되어 있음. = 자기 자신만으로도 실행 가능
작은 이진 파일을 가질 수는 있지만 공유 라이브러리를 사용해야함. = 외부의 공유 라이브러리가 없거나 찾을 수 없느 ㄴ경우에 최종 이진 파일은 완전 X 실행 될 수 X = 이진 파일의 크기와 이진 파일이 갖는 의존성 사이에 트레이드 오프 존재
재사용을 위한 라이브러리를 만드는 또 다른 방식
그 자체가 실행 파일에 해당하지 X
실행을 위한 프로세스가 로딩될 때 가져와서 로드되어야 함.
정적 라이브러리가 실행 파일의 일부이므로, 링커는 주어진 재배치 가능한 파일의 모든 것을 최종 실행 파일에 넣는다. = 링커는 정의도지 않은 심벌을 감지하고, 정의가 필요하며, 이 를 주어진 재배치 가능한 목적 파일에서 찾고자하고, 그렇게 출력되는 최종 파일에 모든 것을 집어 넣는다.
동적 라이브러리의 경우 정의되지 않은 심벌 가질 수 있음. 이는 링크단계에서 해결 X. 실행 파일이 로드되고 실행을 시작할 때 이 심벌을 검색
= 정의되지 않은 동적 심벌을 찾고자 할 때 다른 종류의 링크 단계가 필요. 실행 파일을 로드하고 프로세스로 실행될 준비를 할 때, 동적 링커 혹은 간단히는 로더가 이 링크를 수행
정의되지 않은 동적 심벌은 공유 목적 파일에서 로드해야함. 공유 목적 파일은 정적 라이브러리 파일의 자매 격인 파일.
프로세스를 로드하고 실행할 때, 공유 목적 파일이 로드되며 프로세스가 접근할 수 있는 메모리 지역에 이 파일이 연결. 이 절차는 동적(or 로더)에 의해 수행, 동적 링커는 실행 파일을 로드하고 실행
ELF 실행 가능한 목적 파일과 ELF 공유 목적 파일간의 차이
gcc -c hello.c -fPIC -o hello.o
의 -fPIC는 위치 독립적인 코드를 나타냄.
재배치 가능한 목적 파일이 위치 독립적이라면, 파일 안의 명령어가 고정 주소를 갖지 않는다는 의미. 대신 상대주소를 가짐. 다른 프로세스에서 다른 주소를 가질 수 있다. 공유 목적 파일을 사용하는 방식 때문에 이는 필수 조건.
공유 목적 파일을 만들려면 컴파일러 필요. 공유 목적 파일은 단순한 아카이브이며, 공유 목ㅈ거 파일ㅇ느 그 자체로 목적 파일이다. 재배치 가능한 목적 파일을 만들려면 사용한 것과 동일한 ㄹ이커 프로그램응로 생성해야함.
-shared
옵션을 gcc에 전달 = 재배치 가능한 목적 파일에서 공유 목적 파일을 생성하도록 지시
동적 라이브러리와 링크
동적 라이브러리는 최종 실행 파일에 임베드되지 않는다. 실행할 때 라이브러리 로드.
만약 지정 경로에 정적 라이브러리와 공유 목적 파일이 둘 다 존재, gcc는 공유 목적 파일을 선택해 프로그램과 링크
프로그램 로더 기본 탐색 경로에서 .so
파일을 찾을 수 없을 떄 오류 발생. 이를 해결하려면 환경변수 LB_LIBRARY_PATH
가 올바를 경로를 가리키도록 업데이트해야함. LB_LIBRARY_PATH=/opt/geometry ./ex3_3.out
로 실행 가능
로더 프로그램이 자동으로 로드하지 않는 방식. 공유 라이브러리 내부에서 찾을 수 있는 심벌을 이용하기 전, 공유 목적 파일을 수동으로 로드하는 함수를 개발자가 사용할 수 있음.
gcc -shared 2d.o 3d.o trigon.o -lm -i libgeometry.so
에서 -m
옵션은 링커가 libm.so
와 관련된 공유 목적 파일을 링크하도록 함. libgeometry.so
를 수동으로 로드할 때, 이 파일의 의존성이 어느 정도는 자동으로 로드되어야 하기 때문.
코드에서 dlopen
과 dlsym
(= 함수 포인터 반환, 대상 함수를 불러오는데에 쓰임)이라는 함수로 공유 목적 파일을 로드하는 방법 확인
필요할 때 libgeometry.so
파일을 수동으로 로드하려면
gcc examples_chapter3_4.c -ldl -o ex3_4.out
로 컴파일
같은 라이브러리의 별도 실행 or 별도 버전을 위한 공유 목적 파일을 별개로 만드려 할 때 = 지연된 로딩에 해당. 공유 목적 파일을 자유롭게 로드.
자동으로 로드 = 자유도 감소