카테고리

asm (27) bootloader_x86_grub (1) C (92) compile (11) config (76) CPP (13) CSS (1) debugging (7) gimp (1) Go (1) html (1) Java (1) JavaScript (1) kernel (19) LibreOffice (3) Linux system progamming (21) MFC (1) opencv (4) OpenGL (1) PHP (1) Python (4) qemu (29) shell (3) socket (7) troubleshooting (2) ubuntu18.04 (2) windows (1)

2018/12/16

static library 정적 라이브러리 링커 해석 구조 정리

1. 정적 라이브러리 아카이브

정적 라이브러리
관련 재배치 가능한 개체 파일을 아카이브라는 인덱스가 있는 하나의 단일 파일로 연결한 후 결합한다.

하나 이상의 아카이브에서 심볼를 찾아 확인 되지 않은 외부 참조를 해결 하려고 링커를 업데이트 한다.

아카이브 멤버 파일에서 참조의 내용을 확인하고 이를 실행 파일과 연결 한다.

프로세스를 빠르게 동작 시키기 위해  .a 파일에는 모든 파일의 심볼에 대한 인덱스가 포함되어 있다.

atoi.c -> Translator -> atoi.o
printf.c -> Translator -> printf.o
...
random.c -> Translator -> random.o

atio.o, printf.o random.o -> Archiver(ar)
shell> ar -rs libc.a atio.o printf.o ... random.o

libc.a == C standard library

ar 명령어 사용방법 메뉴얼 참고.
shell> man ar

shell> mkdir temp
shell> cd temp
shell> ar -x /usr/lib/x86_64-linux-gnu/libc.a
shell> cd temp
shell> ls | wc
shell> ls | grep printf.o
shell> cat mymath.h
double myadd(double, double);
double mysub(double, double);
double mymul(double, double);
double mydiv(double, double);

shell>cat myadd.c
double myadd(double a, double b){
return a + b;
}

shell>cat mysub.c
double mysub(double a, double b){
return a - b;
}

shell> cat mymul(doubla a, double b){
return a * b;
}

shell> cat mydiv(double a, double b){
if (b != 0)
return a / b;
return 0;
}

shell> tree
.
├── driver
│   ├── lee_test.c
├── myadd.c
├── mydiv.c
├── mymath.h
├── mymul.c
├── mysub.c

오브젝트 파일로 컴파일 한다.
shell> gcc -c *.c

정적 라이브러리 생성한다
shell> ar -rs libarifmath.a myadd.o mysub.o mydiv.o mymul.o

생성된 오브젝트 파일을 확인한다.
shell> ar -t libarifmath.a
myadd.o
mysub.o
mydiv.o
mymul.o

shell> tree
.
├── driver
│   ├── lee_test.c
├── libarifmath.a
├── myadd.c
├── myadd.o
├── mydiv.c
├── mydiv.o
├── mymath.h
├── mymul.c
├── mymul.o
├── mysub.c
└── mysub.o

shell> cd driver
shell> cat lee_test.c

#include <stdio.h>
#include <mymath.h>
int main(){
   double x, y;
   printf("Enter first number: ");
   scanf("%lf",&x);
   printf("Enter second number: ");
   scanf("%lf",&y);
   double ans1 = myadd(x,y);
   double ans2 = mysub(x,y);
   double ans3 = mymul(x,y);
   double ans4 = mydiv(x,y);
   printf("a + b = %7.2lf\n",ans1);
   printf("a - b = %7.2lf\n",ans2);
   printf("a * b = %7.2lf\n",ans3);
   printf("a / b = %7.2lf\n",ans4);
   return 0;
}

링커 병합을 이해학 위해 컴파일
shell> gcc -c lee_test.c
lee_test.c:8:10: fatal error: mymath.h: 그런 파일이나 디렉터리가 없습니다
 #include <mymath.h>
          ^~~~~~~~~~
compilation terminated.

링커에게 병합에 필요한 추가 정보 제공 -I../ include 경로를 지정해 추가한다.
shell> gcc -c lee_test.c -I../

shell> tree
.
├── driver
│   ├── lee_test.c
│   └── prog1.o
├── libarifmath.a
├── myadd.c
├── myadd.o
├── mydiv.c
├── mydiv.o
├── mymath.h
├── mymul.c
├── mymul.o
├── mysub.c
└── mysub.o

추가된 헤더 정보 확인
shell> nm porg1.o
                 U _GLOBAL_OFFSET_TABLE_
                 U __isoc99_scanf
                 U __stack_chk_fail
0000000000000000 T main
                 U myadd
                 U mydiv
                 U mymul
                 U mysub
                 U printf

실행 파일 컴파일
shell> gcc prog1.o -o lee_test

prog1.o: In function `main':
lee_test.c:(.text+0x80): undefined reference to `myadd'
lee_test.c:(.text+0xa4): undefined reference to `mysub'
lee_test.c:(.text+0xc8): undefined reference to `mymul'
lee_test.c:(.text+0xec): undefined reference to `mydiv'
collect2: error: ld returned 1 exit status
헤더 정보는 추가 했으나, 헤더가 참고 해야할 라이브러리가 없음으로 컴파일 실패.

생성된 헤더 정보를 가지고 있는 정적라이브러리 .a 추가해 컴파일 한다.
shell> gcc prog1.o -o lee_test -larifmath
/usr/bin/ld: cannot find -larifmath
collect2: error: ld returned 1 exit status
링커가 기본 경로를 찾고 있기 때문에 만들지 못한다.

정적 라이브러리 경로 지정 후 컴파일
shell> gcc prog1.o -o lee_test -larifmath -L../
shell> tree
.
├── driver
│   ├── lee_test
│   ├── lee_test.c
│   └── prog1.o
├── libarifmath.a
├── myadd.c
├── myadd.o
├── mydiv.c
├── mydiv.o
├── mymath.h
├── mymul.c
├── mymul.o
├── mysub.c
└── mysub.o

2. 정적 라이브러리와 링크
add.o sub.o div.o main.o : Archiver -> libarifmath.a -> sub.o -> Linker(ld) -> driver(prog1) : 전부 링크된 실행 가능한 객체파일

derver.c mymath.h : Translators(cpp, ccl, as) -> driver.o : Relocatable object file -> Linker(ld) -> driver(prog1) -> 전부 링크된 실행 가능한 객체파일

libc.a : 표준 C 정적 라이브러리 -> printf.o -> Linker(ld) -> driver(prog1) -> 전부 링크된 실행 가능한 객체파일

3. 라이브러리 관리(ar 유틸리티)
"ar"은 라이브러리 관리를 할 수 있는 도구이다.
3.1 만들기
 -r -> 새로운 아카이브 생성
  shell> ar -r libfirst.a file1.o file2.o

 -q -> 객체 파일을 기존의 아카이브에 추가
  shell> ar -q libfirst.a file3.o

 -d -> 기존 아카이브에서 객체 모듈 삭제
  shell> ar -d libfirst.a file2.o

3.2 추출 물
 -x -> PWD에 객체 모듈을 추출
  shell> ar -x /usr/lib/libm.a

3.3 디스플레이
 -t -> 아카이브의 목차 표시
  shee> ar -t /usr/lib/libm.a

외부 참조를 해결하기 위한 링커 알고리즘:
scanf.o 파일 및 명령 줄의 순서에 따라 파일을 검사한다.
검사 중에 현재 해석 되지 않은 참조 리스트를 유지한다.
새로운 .o 또는 .a 파일 obj가 발견 되면 목록에서 확인되지 않은 참조를 obj에 정의된 심볼과 비교하여 해결 하려고 한다.

문제:
명령 행 순서 문제
명령 줄 끝에 라이브러리 배치

4. 정적 라이브러리 한계
4.1 정적 연결의 한계:

실행 파일의 크기가 큼.
디스크에 저장된 실행 파일에서 중복(복제)

메모리의 실행 파일을 복제 한다. scanf 함수를 사용하여 10개의 C-프로그램을 실행 한다고 가정 하면 scanf 함수의 10개 사본이 메모리에 있다.

시스템 라이브러리의 사소한 버그 수정은 각 응용 프로그램을 명시적으로 다시 링크(컴파일)해야 한다.

4.2 현대 솔루션: 공유 라이브러리
로드 시간 또는 런타임 중 동적로드 및 링크되는 코드 데이터가 포함 된 개체 파일

UNIX 세계에서는 공유객체 (. so).
MS 세계에서 동적 라이브러리 또는 dll 이라고 함

댓글 없음:

댓글 쓰기