Egloos | Log-in
F/OSS study
F/OSS study
[binutils] 정적 라이브러리의 구조 - ar, ranlib
ar : 2.19.1


ar은 정적 라이브러리를 만드는 데 사용하는 도구이다.

사실 ar은 archive 파일을 만드는 도구이며
반드시 라이브러리를 만들 때만 사용할 수 있는 것은 아니다.
일반적인 파일들을 묶어 archive로 만들 때도 물론 사용할 수 있다.

물론 이런 경우 대부분은 tar를 사용하겠지만
tar와 ar은 이름도 한 끝 차이(?!)인 만큼
기본적인 기능은 거의 비슷하다.

다음과 같은 텍스트 파일이 있을 때 (각각의 크기는 12 바이트이다.)

a.txt:
content of a

b.txt:
content of b

이를 ar을 이용하여 다음과 같이 c.a 라는 파일로 묶을 수 있다.

$ ar r c.a a.txt b.txt

c.a 파일을 살펴보면 다음과 같이 구성됨을 볼 수 있다.
(참고로 1000은 지금 사용 중인 계정의 uid와 gid이다)

$ xxd c.a
0000000: 213c 6172 6368 3e0a 612e 7478 742f 2020  !<arch>.a.txt/ 
0000010: 2020 2020 2020 2020 3132 3437 3939 3138          12479918
0000020: 3831 2020 3130 3030 2020 3130 3030 2020  81  1000  1000 
0000030: 3130 3036 3434 2020 3132 2020 2020 2020  100644  12     
0000040: 2020 600a 636f 6e74 656e 7420 6f66 2061    `.content of a
0000050: 622e 7478 742f 2020 2020 2020 2020 2020  b.txt/         
0000060: 3132 3437 3939 3138 3938 2020 3130 3030  1247991898  1000
0000070: 2020 3130 3030 2020 3130 3036 3434 2020    1000  100644 
0000080: 3132 2020 2020 2020 2020 600a 636f 6e74  12        `.cont
0000090: 656e 7420 6f66 2062                      ent of b

ar은 metadata를 ASCII 문자를 이용하여 저장하므로 한 눈에 파일의 구조가 들어온다.
먼저 이 파일이 ar로 만들어 졌음을 나타내는 magic number ("!<arch>\n") 가 나오고
그리고 파일 이름 (/로 끝난다), 생성 시간 (timestamp), uid, gid, mode, size 정보가 차례로 나온다.
마지막으로 metadata의 마지막을 알리는 magic number ("`\n")가 나온 후에
실제 파일 데이터가 나온다. 이 후는 각 파일마다 반복된다.

이 정보는 binutils 소스의 include/aout/ar.h 에 다음과 같이 정의되어 있다.
(참고로 \012는 8진수 이므로 \xA, 10, '\n'과 동일함)

/* Note that the usual '\n' in magic strings may translate to different
   characters, as allowed by ANSI.  '\012' has a fixed value, and remains
   compatible with existing BSDish archives. */

#define ARMAG  "!<arch>\012"    /* For COFF and a.out archives.  */
#define ARMAGB "!<bout>\012"    /* For b.out archives.  */
#define ARMAGT "!<thin>\012"    /* For thin archives.  */
#define SARMAG 8
#define ARFMAG "`\012"

...

struct ar_hdr
{
  char ar_name[16];        /* Name of this member.  */
  char ar_date[12];        /* File mtime.  */
  char ar_uid[6];        /* Owner uid; printed as decimal.  */
  char ar_gid[6];        /* Owner gid; printed as decimal.  */
  char ar_mode[8];        /* File mode, printed as octal.   */
  char ar_size[10];        /* File size, printed as decimal.  */
  char ar_fmag[2];        /* Should contain ARFMAG.  */
};

따라서 ar은 기본적으로 a.out 형식을 사용한다는 것을 알 수 있다.
(보면 알 수 있듯이 파일 이름의 길이가 16바이트로 고정되어 있으므로
이보다 긴 이름의 파일이 사용되면 다른 형식을 이용하는 것 같다.)

object 파일(.o)들을 라이브러리로 만들 때도 이와 동일한 형식으로 저장된다.
(하지만 우분투 9.04에 기본으로 설치되는 binutils (2.19.1-0ubuntu3)의 경우
ar r 명령 만을 실행해도 기본적으로 s 옵션을 추가한 것처럼 symbol index를 생성하였다.
명시적으로 (대문자) S 옵션을 주어 실행하면 index 생성을 금지할 수 있으니
이를 이용하여 텍스트 파일의 경우와 동일한 형태로 생성되는지 확인할 수 있다.)

정적 라이브러리의 경우에는 단순히 object 파일을 합치는 것 이외에도
위에서 언급한 symbol index를 생성하는 작업이 필요하다.
(이는 라이브러리 내에 정의된 심볼들을 빨리 찾아서 링크 속도를 높이기 위한 목적이다.)

이를 위해서는 ar 실행 시 s 옵션을 주거나 ranlib 프로그램을 실행하면 되는데
사실 ar과 ranlib은 동일한 소스에서 flag 하나만 다르게 설정하여 컴파일 하는
거의 동일한 프로그램이니 ar 만을 살펴보기로 하겠다.
(또한 위에서 언급한대로 ar r 만 실행해도 ar rs를 실행한 것과 동일한 결과가 나왔다.)

symbol index가 어떻게 구성되는지 알아보기 위해
다음과 같은 간단한 예제 프로그램을 만들어 보자.
(test1에서는 d1과 f1이라는 심볼이 만들어지고, test2에서는 f2가 만들어 질 것이다.)

test1.c:
int d1;
void f1 (void) { }

test2.c:
void f2 (void) { }

이를 컴파일 한 후 다음과 같이 libartest.a 라는 라이브러리로 만든다.

$ gcc -c test1.c
$ gcc -c test2.c
$ ar rs libartest.a test1.o test2.o

libartest.a 파일의 구조를 들여다보자.

$ xxd -l160 libartest.a
0000000: 213c 6172 6368 3e0a 2f20 2020 2020 2020  !<arch>./      
0000010: 2020 2020 2020 2020 3132 3437 3939 3432          12479942
0000020: 3534 2020 3020 2020 2020 3020 2020 2020  54  0     0    
0000030: 3020 2020 2020 2020 3236 2020 2020 2020  0       26     
0000040: 2020 600a 0000 0003 0000 005e 0000 005e    `........^...^
0000050: 0000 034a 6631 0064 3100 6632 0000 7465  ...Jf1.d1.f2..te
0000060: 7374 312e 6f2f 2020 2020 2020 2020 3132  st1.o/        12
0000070: 3437 3834 3936 3739 2020 3130 3030 2020  47849679  1000 
0000080: 3130 3030 2020 3130 3036 3434 2020 3638  1000  100644  68
0000090: 3720 2020 2020 2020 600a 7f45 4c46 0101  7       `..ELF..

앞의 magic number 부분은 (당연히) 동일하다.
대신 test1.o 파일이 0x5e 부분에서 시작하며 그 앞에 새로운 파일(?)이 추가되었음을 볼 수 있다.
(참고로 ar에서는 archive 내에 포함된 파일들을 멤버라고 부른다)
test1.o 앞에 추가된 멤버도 동일한 형식으로 구성되므로 위의 헤더 형식에 따라 살펴보면
  • ar_name : / (즉, 이름이 없다)
  • ar_date : 1247994254 (= 2009-07-19 18:04:14)
  • ar_uid : 0
  • ar_gid : 0
  • ar_mode : 0
  • ar_size : 26
  • ar_fmag : ARFMAG (= 0x600a)
다른 건 별 의미가 없고 size 정보에만 관심을 가지면 된다.
헤더 이후에 실제 데이터가 시작되는 위치가 0x44이니
여기에 26(= 0x1a)을 더하면 0x5e가 된다. (아까 살펴본 test1.o 의 시작 위치와 동일하다!)

0x44 부터의 26 바이트를 좀 더 자세히 살펴보자.
0000 0003 0000 005e 0000 005e 0000 034a 6631 0064 3100 6632 0000

우선 3과 5e 라는 정보가 눈에 띈다.
(이 정보는 big endian 32bit 정수형으로 저장되는 듯 하다.)

우선 3은 라이브러리 내의 심볼 개수이다.
위에서 살펴보았듯이 test1.o에는 2개, test2.o에는 1개의 심볼이 존재한다.

그 다음에는 3개의(!) 32bit 정수(big endian)가 나오는데
각각 0x5e, 0x5e, 0x34a에 해당한다. (뭔가 느낌이 오지 않는가!?)
0x5e는 test1.o 파일 정보가 시작되는 위치이다.
그렇다면 0x34a는 test2.o 파일 정보가 시작되는 위치라고 볼 수 있을 것이다.

다음과 같이 확인할 수 있다. (편의상 헤더 부분 만 보기로 한다.)

$ xxd -s0x34a -l60 libartest.a
000034a: 7465 7374 322e 6f2f 2020 2020 2020 2020  test2.o/       
000035a: 3132 3437 3834 3737 3035 2020 3130 3030  1247847705  1000
000036a: 2020 3130 3030 2020 3130 3036 3434 2020    1000  100644 
000037a: 3636 3820 2020 2020 2020 600a            668       `.

역시나 생각대로다.

그 이후에는 해당 심볼의 이름이 NULL-terminated string 형태로 나온다.
마지막 임을 나타내기 위해 NULL 문자가 추가적으로 사용된 것 같다.
"f1" (0x66, 0x31, 0x00), "d1" (0x64, 0x31, 0x00), "f2" (0x66, 0x32, 0x00), \0

이제 프로그램 빌드 과정에서 libartest.a가 링크된다면
링커는 필요한 심볼을 먼저 index에서 찾은 후 (string match)
발견되면 해당 위치에 있는 offset 값을 통해 멤버의 시작 위치(헤더)를 찾고
해당 object 파일 만을 추출하여 프로그램에 링크시킬 것이라고 짐작할 수 있다. (별도의 dependency가 없는 경우)
by namhyung | 2009/07/19 18:31 | System | 트랙백(2) | 핑백(1) | 덧글(0)
트랙백 주소 : http://studyfoss.egloos.com/tb/5049029
☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]
Tracked from tomato_house at 2010/02/09 17:10

제목 : [binutils] 정적 라이브러리의 구조 - ar..
[binutils] 정적 라이브러리의 구조 - ar, ranlib...more

Tracked from at 2014/03/11 00:23

제목 : garcinia cambogia
line3...more

Linked at SAY YOU SEY ME~ .. at 2011/03/13 16:26

... ib 유틸리티가 필요. $&gt;ar rcv libio.a read.o write.oa -read.oa -wirte.o$&gt;ranlib libio.ahttp://studyfoss.egloos.com/5049029 참고. &lt;예제2&gt;TARGET = &lt;- link 후 생성되는 실행 file all : $(TARGET)$(T ... more

:         :

:

비공개 덧글

◀ 이전 페이지 다음 페이지 ▶

카테고리
General
Application
System
Kernel
Book
Tips
태그
elf SMP script blktrace perf sed awk compiler scm C computer-architecture binutils linux block-layer memory CARM vcs gcc git documentation synchronization glibc x86 CAaQA3 build algorithm kernel emacs bash patch
전체보기
이글루 파인더

최근 등록된 덧글
informsi yang bagus dan be..
by pordanaia at 08/05
Informsi yang bagus dan b..
by jamalsu at 08/01
as inforasi yang menarik http..
by jamalsu at 07/22
최근 등록된 트랙백
Tod's Ferrari Homme
by Tods Pas Cher,Kodak did ..
Mocassin Femme
by Mocassins Homme, I got so..
natural garcinia cambogia
by
rss

skin by jiinny


X