동적, 정적 라이브러리 차이점에 대해서 알아 보기로 하자.

정적 라이브러리는 프로그램과 같이 컴파일 & 링크 되어 실제 오브젝트 파일 내에 포함 되어서 실행 되는 라이브러리이다. 라이브러리와 프로그램이 합체(?) 되기 때문에 정적 라이브러리와 링크 하면 프로그램 용량이 늘어나는 현상을 볼 수 있다.

프로그램과 합체가 되면 단점도 생기는데 라이브러리가 업데이트 되었을 때 그 라이브러리를 사용한 프로그램의 업데이트 버전을 어떻게 배포 하느냐 이거다-_-.. 업데이트가 쉽지 않다.(반면에 속도는 빠르겠지) 이래서 DLL이라고 불리우는 동적 라이브러리가 탄생하게 되었는데 프로그램에 합체 된 정적 라이브러리를 밖으로 꺼내 놓기 시작했다. 이게 바로 동적 라이브러리이다. 이러면 아주 편하게 수정 & 배포를 할 수 있으리라 생각 했겠지만 다른 문제점이 있으니.. 속도가 느려지는 단점이 있다. 동적 로더에게 질의를 날려서 라이브러리 찾고 메모리에 올리고 콜하고블라블라 아무튼 복잡해 진다.(이 과정은 다음에 포스팅)

그럼 어떻게 동작하는지 메모좀 해볼까..

아래 프로그램으로 동적 라이브러리를 들여다 보자. 어래 동적 라이브러리가 어딧어? 하시는 분들도 있겠지만 printf는 glibc에 포함된 동적 라이브러리라는 사실. 파일 분석의 흥미진진함을 더해주기 위해 전역변수도 몇개 추가 했다.

사용자 삽입 이미지

main.c 소스!

main 프로그램을 생성 후 nm 명령어를 사용해서 어떤 심볼들이 사용 되었는지 한번 보자.

사용자 삽입 이미지

숫자     알파벳     심볼명

으로 이루어진 것을 볼 수 있다.
앞의 16진수는 대략 가상 주소 겠고.. 알파벳은 찾아봐야겠고;; 마지막은 심볼명을 의미한다.

T : Text 영역(code)
U : 정의 안된 심볼
B : 초기화 안된 데이터(BSS)
D : 초기화 된 데이터
R : Read only data

자 여기서 중요한건 U로 표시된 undefined 심볼이다. 즉 좌측 캡춰 화면에 printf@@GLIBC_2.0 으로 되어있는.. 뭔지 모르겠지만 가상 주소도 안붙어 있네-_-; 이거이 동적 라이브러리와 관련좀 있는 것으로 추정된다.


그외에 전역 변수로 지정한 gVal~~~ 변수들은 B로 되어있는 것을 볼 수 있다. 여기서 아~ gValInit은 0으로 초기화 되어있는데 왜 초기화 안된 데이터 영역에 있나? 하실 수도 있겠지만 0으로 초기화 된 것들도 BSS 영역으로 갑니다. 만약 gValInit 변수에 100을 넣었으면 D라고 표시 됩니다.

readelf 명령어를 사용해 더 자세히 들여다 보자.
사용자 삽입 이미지

readelf -a main

음.. Entry point address는 0x8048310 이군.. gdb로 분석할 때 이곳에서 부터 시작하면 되겠다. 중요한건 이 부분이 아니고 Dynamic section 쪽에 보면 이 프로그램을 실행 시키기 위해 필요한 동적 라이브러리 목록을 볼 수 있다. NEEDEED가 보일때까지 스크롤 해보자. 슉슉

0x00000001 (NEEDED)                     Shared library: [libc.so.6]

자 이 부분 중요하다. 정적 라이브러리와 다르게 동적 라이브러리는 실행 프로그램에 포함 되는 것이 아니라고 했다. 그러면 정적 라이브러리 같이 합체(?) 되는 것도 아닌데 어떻게 이 프로그램이 동적 라이브러리와 연관이 지어지느냐 답은 요기 있다. 컴파일러가 이 프로그램은 이런이런 라이브러리를 사용 합니다. 라고 NEEDED 로 실행 파일에 적어 놓는다. 로더가 아~~ 얘는 이런 라이브러리를 사용하는군.. 이라고 생각하게 되고 이 정보를 어떻게 든 참고해서 프로그램 런타임시에 사용 될 것이라고 생각 된다.


.. 다 적을려고 했는데 너무 피곤하다. 너무 바쁘고 ㅠㅠ To be continue 해야겠다... 다음엔 gdb로 런타임상에서 동적 라이브러리가 어떻게 동작하는지 알아보겠따~~ GOT, PLT 기대하시라

Creative Commons License
Creative Commons License
2009/11/20 00:49 2009/11/20 00:49

[로그인][오픈아이디란?]

SVN 설치 및 설정이 모두 되어있다고 SVN 디렉토리 안에 있는 Project라는 저장소를 만들려고 한다고 가정

# pwd
-> /home/svn

/* 저장소 생성 */
# svnadmin create --fs-type fsfs Project
# svn checkout svn://Domain/Project


/* 서버 open */
# svnserve -d -r ~/svn

/* 권한 주기 */
# cd svn/Project/conf
# vim svnserv.conf
 

아래 옵션 해제
anon-access = read
auth-access = write
password-db = passwd

# vim passwd
ID = passwd

Checkout
# svn checkout svn://Domain/Project

ADD
# svn add filename

Commit
# svn commit

serve close
# killall svnserve         
Creative Commons License
Creative Commons License
2009/10/23 22:29 2009/10/23 22:29

[로그인][오픈아이디란?]

공유 라이브러리가 사용된 실행파일에는 필요한 공유 라이브러리에 대한 정보를 갖고 있다. 그 정보는 ELF의 동적 섹션의 NEEDED 에 기록 되어있다. 예를 들어 ls 의 경우 objdump 명령을 사용하면 다음과 같이 확인해 볼 수 있다.

objdump -p /bin/ls
readelf -d /bin/ls

사용자 삽입 이미지

공유 라이브러리 정보 확인

 위에서 만든 main 파일을 objdump -p 옵션으로 실행 시켜보았다. libfoo.so.0 과 libc.so.6 이 필요한 실행 파일이라는 것을 알 수 있다. 이 공유 라이브러리는 /usr/lib 이나 환경변수 LD_LIBRARY_PATH 에 설정 되어있는 경로를 참조한다. 또한 /etc/ld.so.cache 에 기록된 정보가 있다면 그것을 참조한다. /etc/ld.so.cache sms /etc/ld.so.conf 의 설정을 이용해 ldconfig 를 실행할 때 갱신 된다.

Creative Commons License
Creative Commons License
2009/06/09 15:14 2009/06/09 15:14

[로그인][오픈아이디란?]

정적 라이브러리

정적 라이브러리란 여러 프로그램에서 사용 되는 함수를 포함하는 오브젝트 파일을 하나의 파일로 다룰 수 있도록 정리해 놓은 것이다. 프로그램을 작성할 때 각각의 소소파일을 컴파일하고 링크해서 하나의 실행 가능한 파일을 생성한다. 이때 다른 프로그램에서도 사용될 만한 모듈이 여러 개의 오브젝트 파일로 나뉘어 있으면 이것들을 한 덩어리로 다루기가 번거로워 진다. 그래서 생각해낸 것이 아카이브 파일이다. 여러개의 오브젝트 파일을 하나의 파일로 모아놓은 것이다. ar 명령어를 사용해 여러 개의 오브젝트 파일을 하나의 아카이브 파일로 합칠 수 있다. OS 따라 ranlib 를 사용하면 이 아카이브 파일 내의 오브젝트가 제공하는 심볼 정보를 해시화해서, 심볼을 제공하는 오브젝트 파일을 효율적으로 검색할 수 있게 된다. 이와 같은 아카이브 파일을 정적 라이브러리라 한다.

# gcc -c -o foo.o foo.c
# gcc -c -o bar.o bar.c
# ar ruv libfoo.a foo.o bar.o

라이브러리 내용은 ar 명령어로 확인 가능

 libfoo.a 가 생성 되면 아카이브 파일이 생성된 것이다. 정적 라이브러리를 링크할 경우, 링커는 다른 오브젝트 파일에서 정의되지 않은 심볼을 찾아 지정된 정적 라이브러리에서 해당 심볼을 정의하고 있는 오브젝트 파일의 사본을 추출해서 실행 파일 내에 포함 시킨다.

# gcc -L./ -o main main.o -lfoo

 정적 라이브러리를 링크해서 생성된 실행 바이너리를 실행할 경우에는 정적 라이브러리가 없어도 관계 없다. 필요한 코드는 실행 바이너리에 복사되어 포함되어 있기 때문이다.

사용자 삽입 이미지

클릭해서 보세요

위 화면은 main.c 에서 아카이브 파일 내에 있는 SUM 함수를 호출하고 컴파일한 결과이다. 라이브러리를 지정해 주지 않으면 SUM 은 정의되지 않은 심볼이라고 해서 에러가 난다. 라이브러리 경로를 지정해주고 사용할 라이브러리를 적어주면 컴파일 완료

공유 라이브러리

 공유 라이브러리는 공유 된다는 점에서 정적 라이브러리와 다르다. OS 의 가상 메모리 관리 시스템이 진보함에 따라, 하나의 파일을 mmap, semget 등을 이용해 여러 프로세스에서 메모리를 공유, 참조 할 수 있게 되었다. 이를 활용할 수 있도록 한 것이 공유 라이브러리이다. 과거 OS 는 이 공유 메모리가 거대하게 클 경우에도 프로그램이 실행하기전 로드해야 했기 때문에 비효율적이었지만 현재 OS 는 일단 메모리맵을 설정해 두고 그 메모리 내용이 참조 되었을 때 로드하는 형태를 띄고 있다.

gcc -fPIC -c -o foo.o foo.c
gcc -fPIC -c -o bar.o bar.c
gcc -shared -Wl, -soname, libfoo.so.0 -o libfoo.so foo.o bar.o

일반적으로 -shared -Wl, -soname 옵션을 사용해 SONAME을 지정한다.

공유 라이브러리도 정적 라이브러리와 똑같이 링크 시킨다.

gcc -L./ -o main main.o -lfoo

실질적인 내부 처리는 main.o 내에 정의 되지 않은 심볼이 공유 오브젝트에 정의 되어 있다면 정적 라이브러리와 같이 코드를 복사 하는 것이 아니라. 공유 오브젝트의 SONAME 을 실행 파일의 NEEDED 의 설정할 뿐이다. 공유 라이브러리를 사용하는 실행파일을 실행한 경우에는 동적 링커 로더(ld.so) 가 NEEDED 의 정보를 이용해 공유 라이브러리를 찾아내어, 실행 시에 해당 프로세스의 메모리맵을 조작해서 공유 라이브러리와 실행 바이너리가 같은 프로세스 공간을 사용하도록 한다.

이러한 공유 메모리는 실행파일의 크기, 메모리 사용공간에 대해서도 매우 유리하다. PIC 코드로 생성해 두면 코드 부분이 어느 주소에 위치하더라도 변경할 필요가 없기 때문에, 공유 라이브러리를 하나의 물리적인 메모리 페이지에 읽어들이는 것만으로 각각의 메모리 공간에 있는 프로세스에서 공유 라이브러리의 메모리 페이지를 공유할 수 있게 된다. 이러한 공유 메모리의 사용은 라이브러리 패치 시에도 굉장히 유용하다. 데몬과 같이 장시간 실행 하고 있는 프로그램의 경우에는 이전의 공유 라이브러리가 이미 메모리에 로드 되어있으므로 다시 실행 시키는 것이다.

Creative Commons License
Creative Commons License
2009/06/09 15:10 2009/06/09 15:10

[로그인][오픈아이디란?]

ELF 는 실행 가능한 바이너리 또는 오브젝트 파일 등의 형식을 규정한 것입니다. ELF 파일은 ELF 헤더가 맨 앞에 위치하고, 프로그램 헤더 테이블과 섹션 헤더 테이블이 그 뒤에 위치한다. (사실 더 복잡한 구조지만 큰 구조가 그렇다는 이야기)

ELF 헤더는 파일이 ELF 포맷일 경우 파일 맨앞에 반드시 존재하며, readelf -h 명령어로 elf의 헤더를 볼 수 있습니다. 명령어 참~ 쉽죠? read elf 입니다. 기억하기 쉬워요. 파일 분석시 굉장히 많이 사용하는 명령어 입니다.

사용자 삽입 이미지

Entry point Address - 시작하는 가상주소를 나타낸다.



프로그램 헤더는 readelf -l '파일명' 으로 섹션 헤더는 readelf -S '파일명'으로 확인 가능하다.

사용자 삽입 이미지

shstrtab


위 그림에서 빨간색 네모로 쳐진 shstrtab 의 type 항목을 보면 String table 이라는 것을 알 수 있다. base address 는 0x00ff0c 크기는 0x0000c7 인 것을 알 수 있다. od --skip-bytes 0x00ff0c --read-bytes 0xc7 -t x1z -A x zip 이라고 입력하면 shstrtab 의 내용을 볼 수 있다.

심볼 테이블은 readelf -s 파일명으로 볼 수 있다.

배운 명령어
  • readelf -h (ELF 헤더 확인) 
  • readelf -l (Program 헤더 확인) 
  • readelf -S (Section 헤더 확인)
  • readelf -s (심볼 테이블 확인)
  • od --skip-bytes 0x00ff0c --read-bytes 0xc7 -t x1z -A x zip

Creative Commons License
Creative Commons License
2009/06/03 15:42 2009/06/03 15:42

[로그인][오픈아이디란?]