반응형


AIX에서 실행파일을 만들거나 수행할 때, 공유 오브젝트와 라이브러리는 두 단계로 사용한다.


1. 링크할 때, 링커 에디터(ld 명령)는 실행파일에서 참조하는 정의되지 않은 심볼을 해석하기 위해 공유 오브젝트와 라이브러리를 찾는다. 만약 공유 오브젝트와 라이브러리가 참조된 모든 심볼을 가지고 있다면, 실행파일의 XCOFF헤더의 로더섹션은 그 공유 오브젝트나 라이브러리에 대한 참조를 포함하고 있어야 한다. 다시 말해서 심볼은 공유 오브젝트나 라이브러리에서 반출 (export)되고 실행파일로 반입 (import)된다.


2. 프로그램을 로드할 때, 시스템 로더 (새로운 프로세스를 시작하는 커널 구성요소)는 실행파일의 XCOFF 헤더 정보를 읽은 다음 참조한 공유 라이브러리의 위치를 찾는다. 참조하는 모든 공유 오브젝트와 라이브러리를 찾으면, 실행파일을 시작할 수 있다. 다음 시스템 로더는 실행파일의 여러 섹션을 해당되는 프로세스 주소 공간의 분할된 세그먼트로 로드한다. 프로그램에서 처음 참조하는 오브젝트라면 우선 공유 오브젝트의 프로그램 텍스트와 라이브러리를 전체 시스템 메모리로 로드하고, 추후 다른 프로그램들이 이를 공통으로 이용할 수 있게 된다.


오브젝트, 라이브러리와 어플리케이션 프로그램 코드를 링크시키기 위해 AIX에서는 몇가지의 링크 방법을 지원하고 있으며  이 기법들은 서로 상충되는 기법이 아니다.(단 lazy loading과 런타임 링킹의 경우는 제외한다.) 따라서 한가지 이상의 링킹 방법으로 실행파일을 만들 수 있다.


링킹 기법        심볼해석        실볼 rebound        모듈 로딩                링커 옵션            해당 섹션

디폴트                 링크시                  N/A               프로그램 로드시                    N/A                 AIX 디폴트 랭킹

정적                    링크시                  N/A               프로그램 로드시                 -bstatic               정적링킹

lazy loading         링크시                  N/A                       N/A                          -blazy                lazy loading

런타임                 링크시         프로그램 로드시        프로그램 로드시                 -brtl                   런타임링킹

동적로딩              런타임시           런타임시                   런타임시                       N/A                 동적 로딩


a. 정적으로 링킹된 오브젝트와 아카이브 멤버의 프로그램 텍스트는 실행파일 안에 포함된다.

b. lazy loading은 디폴트 링킹의 변종이다. 참조하는 공유 모듈을 로드하는 시간만을 제외하고, 다른 모든 점은 디폴트 링킹과 아주 흡사하다.

c. 동적 로딩은 링커 옵션이나 오브젝트 파일의 타입보다는 서브루틴에서 제공하는 일종의 프로그래밍 기법으로 볼 수 있다.


다음 옵션을 사용하지 않는 한 실행파일을 만들 때는 디폴트 링킹 방법을 사용하게 된다.


링킹 방식             옵션

정적 링킹              -bstatic

lazy loading          -blazy

런타임 링킹           -brtl


링커는 입력으로 두 종류의 파일(오브젝트 파일과 라이브러리)을 처리할 수 있다. AIX에서 오브젝트 파일과 라이브러리에 들어있는 아카이브 멤버는 모두 정적 파일일 수도 있고 동적 파일일 수도 있다. 정적 파일이던 동적파일이던 간에 디폴트 링킹에서는 링커에게 동일한 방법으로 파일이름을 지정한다. 정적 오브젝트는 항상 정적으로 링크되며, 실행파일 안에 포함된다. 공유 오브젝트는 보통 동적으로 링크되므로, 공유 라이브러리 코드는 실행파일 안에 포함되지 않는다. 그러나 공유 오브젝트는 정적으로 링크시킬 수 있다. 공유 오브젝트를 링크하면, 공유 프로그램 텍스트가 시스템 공유 라이브러리 세그먼트로 로드되고 참조하는 모든 프로세스에서 공유하게 된다.


공유 오브젝트가 미리 메모리에 로드되어 있지 않다면, 공유 오브젝트를 필요로 하는 프로그램을 처음 실행할 때 메모리에 로드하게 된다. 필요로 하는 페이지의 위치에 따라 VMM (virtual memory manager:가상 메모리 관리자)이 로딩과정을 수행하고, 이때 로드되는 페이지의 실제 크기는 현재 VMM의 옵션 설정에 따라 결정된다. VMM의 옵션은 AIX5L 버전 5.2 이상에서는 vmo 에서 설정하고, 그 외 버전의 AIX에서는 vmtune 명령으로 설정한다.


특히 공유 라이브러리를 C++ 로 작성한 경우에는 라이브러리끼리 서로 참조를 많이 하게 된다. 이런 경우는 페이지 요청이 있을 때마다 라이브러리를 로드하기보다는, 프로그램을 시작할 때 한꺼번에 필요한 라이브러리를 모두 메모리로 읽어 들이는 편이 더 빠르다.


이런 경우를 위해 환경변수를 다음처럼 설정한다.

LDR_CNTRL=PREREAD_SHLIB


1) Staic Linking

링커에서 공유 오브젝트와 라이브러리를 어떻게 다룰지 결정할 수 있도록 AIX에서는 -bdynamic과 -bstatic 옵션을 지원한다(-bdynamic, -bstatic 링커 옵션은 AIX 4.3부터 지원). 이 옵션은 토글옵션이며 같은 링커 명령에서 반복해서 사용할 수 있다. -bdynamic 옵션은 디폴트 옵션이며 공유 오브젝트를 공유하는 상태로 처리한다. 반면 -bstatic 옵션을 사용하게 되면 참조하는 모든 오브젝트를 정적으로 링크하고 공유 오브젝트도 정적으로 링크한다. 


xlc -o a.out main.o -lone -static func1.o -ltwo -bdynamic -lthree -bstatic -lfour -bdynamic

예를 들어 위와 같은 명령으로 프로그램을 만들게 되면 func1.o, -ltwo, -lfour 는 정적 오브젝트로 처리하고 -lone, -lthree 는 공유 오브젝트로 처리한다. 따라서 오브젝트 모듈 func1.o, 라이브러리 libone.a 와 libfour.a 에서 참조한 아카이브 멤버는 main.o와 정적으로 링크된다.


-bstatic 옵션을 사용하면 형식적으로 -bdynamic 옵션을 링크 명령의 제일 마지막에 써줘야 링커에서 시스템 라이브러리를 공유 오브젝트로 다루게 된다. 만약 -bdynamic 옵션을 제일 마지막에 써주지 않으면 시스템 라이브러리를 정적으로 링크하게 되므로 최종 실행파일의 크기가 훨씬 커지고, 특정 버전의 시스템 라이브러리와 정적으로 링크하기 때문에 다른 버전의 AIX에서는 프로그램이 수행되지 않을 수 있다. 명령문 제일 뒷 부분에 -bdynamic 옵션을 써줘야 libc.a 같은 시스템 라이브러리를 공유 오브젝트로 처리한다. 단 공유 오브젝트나 공유 아카이브 멤버를 정적으로 링크하는 방법은 64bit 개발환경에서 지원하지 않는다.


2) Lazy Loading

Lazy loading은 디폴트 링킹을 약간 바꾼 변종이며, 모듈내 함수를 실제로 실행할 때까지 모듈의 로딩을 늦추는 방법이다. 모듈을 로드할 때 시스템 로더는 디폴트로 연관관계가 있는 모듈을 모두 함께 로드한다. -blazy 링커 옵션을 써서 모듈을 링킹시키면 모듈 안의 함수를 실제로 호출할 때 모듈을 로드하지만, 심볼 해석은 링크할 때 이미 수행한다.


실행파일을 만들 때 런타임 링커 (-brtl 옵션 사용)를 지정하지 않은 경우에만 lazy loading이 동작한다. 참조하는 모듈정보가 모두 함수호출일 때만 모듈을 lazy loading 방식으로 로드할 수 있다. 만약 모듈 내의 변수를 참조하는 경우라면, 모듈은 정상적인 방법으로 로드된다.


예를 들어 main()에서 myfunc1()을 호출하고, myfunc1()은 libone.so 공유 모듈에 정의되어 있다고 하자. 그리고 myfunc1()이 공유 모듈 libtwo.so 의 myfunc2()를 조건에 따라 호출할 가능성이 있다면, libtwo.so는 lazy loading할 모듈에 해당한다. 만약 myfunc1()이 myfunc2()를 호출하지 않는다면, libtwo.so는 로드되지 않는다. 그러나 myfunc1()이 myfunc2()를 호출하면 lazy loading 코드가 load() 함수를 수행하여 libtwo.so를 로드하고, 함수 디스크립터를 수정해서 함수를 호출할 수 있도록 한다. 만약 libtwo.so를 로드할 수 없게 된다면 lazy loader의 에러 처리기(error-handler)를 호출하게 된다.


lazy loading을 사용한다고 해서 프로그램에 영향을 주지는 않지만, 다음 세가지의 예외상황이 있다.

1. 모듈이 다른 순서로 로드될 수 있기 때문에, 모듈의 로드 순서에 의존하는 프로그램의 경우에는 영향을 받는다. 심지어 어떤 경우는 모듈이 로드되지 않는 경우도 있으므로 주의하자.

2. lazy loading을 사용한다면 함수포인터를 비교하는 경우에 주의해야 한다. 보통 함수는 공유하는 주소영역을 가지므로 함수 포인터를 비교하여 동일한 함수를 참조하고 있는지 알아낼 수 있다. 그러나 모듈을 lazy loading하면 늦게 로드된 모듈의 함수주소는 다른 모듈에서 계산한 결과와 달라질 수 있다. 함수 포인터를 비교하고 이에 영향을 받는 프로그램은 lazy loading을 사용하면 안 된다.

3. 모듈을 상대경로 값으로 로드하고 프로그램이 작업 디렉토리를 바꾼 경우, 필요한 모듈을 찾지 못해서 로드하지 못할 수 있다. lazy loading을 사용하려면 링크할 때 필요한 모듈을 모두 절대경로로 지정해줘야 한다.


lazy loading을 사용할 지 여부는 링크시 결정하며 모듈에 따라 다르다. 한 개의 프로그램에서는 lazy loading을 사용하는 모듈과 lazy loading을 사용하지 않는 모듈을 섞어서 사용할 수 있다. 한 개의 모듈을 링크할 때 다른 모듈에 있는 변수를 참조한다면 이 모듈은 당연히 lazy loading이 안 된다. 만약 모듈에 대한 참조가 모두 함수호출에 관련된 참조라면, 이 모듈은 모두 lazy loading이 가능하다.


3) Runtime Linking

 AIX 디폴트 링킹”에서 설명했듯 AIX에서 디폴트, 정적, lazy loading 링킹 방법을 사용하여 실행파일을 만들면 링크할 때 모든 심볼을 해석(resolve)한다. 미처 해석되지 못한 심볼은 일단 실행파일을 만들고 나면 다시 바인딩되지 않는다. 그러나 런타임 링킹에서는 링크할 때가 아니라 프로그램을 로드할 때 참조하는 심볼을 해석할 수 있도록 한다. 이 기능은 프로그램을 일단 시작한 다음 정의되지 않았거나 공유 모듈내의 연기되지 않은(non-deferred) 심볼을 해석할 수 있도록 한다. 그리고 런타임 정의( ex)프로그램을 링크할 때 미처 함수 정의를 알지 못한 경우)와 심볼 재바인딩 기능을 제공한다. 예를 들어 main()은 libfunc1.so의 func1()을 호출하고 다음 libfunc2.so의 func2()를 호출하는 경우를 생각해보자. libfunc1.so와 libfunc2.so는 런타임링킹을 지원한다고 할 때, main()에서 또 다른 func2()함수를 정의해서 사용하며, 원래의 libfunc2.so의 func2() 정의를 무시하도록 할 수 있다. 이때 주 어플리케이션도 런타임 링킹을 지원하도록 만들어야 한다. 필요한 모듈만 런타임 링킹 라이브러리로 링크하는 방법만으로는 충분하지 않다. 모듈뿐 아니라 어플리케이션 내 함수도 런타임 링킹이 가능해야 한다.


AIX에서는 런타임 링킹을 사용한다고 하더라도, 연기된(deferred) 심볼을 제외한 모든 심볼은 프로그램을 로드할 때 해석해야 한다. 그러나 다른 UNIX OS에서는 함수심볼 해석을 실제 함수를 호출할 때까지 연기하는 경우가 있다.(변수참조는 로드할 때 해석된다.) 이 경우 심볼을 참조하는 모듈이 로드된 다음 함수정의가 로드된다.


런타임 링킹을 사용하려면 다음 사항을 알고 있어야 한다.

1. 런타임 링킹을 사용하려면 -brtl 옵션으로 런타임 링크 라이브러리(/usr/lib/librtl.a)를 지정해줘야 한다. -brtl 옵션과 -blazy 옵션은 함께 사용할 수 없다.

2. 오직 런타임 링킹 공유 오브젝트만 런타임 링킹시킬 수 있다. 런타임 공유 오브젝트가 아닌 오브젝트와 아카이브 멤버는 디폴트 링크 방식으로 링크해야 한다.

3. 런타임 링킹 공유 오브젝트는 주요 코드와 링크시키기 이전에 미리 만들어 놓아야 한다. 

4. 런타임 링킹 공유 오브젝트는 libname.so와 같은 파일이름을 사용해야 한다. 이런 방식의 이름을 사용해야 런타임 링킹 공유 오브젝트와 다른 파일을 구분하기 쉽고, 명령행에서 파일이름을 지정하기 쉽다.


런타임 링킹을 사용하면 개발자는 모듈간 상관성에 관한 사항이나 반입/반출 목록에 신경 쓰지 않아도 된다. -bexpall 링커 옵션을 사용하면 모든 공유 오브젝트는 모든 심볼을 반출하고, 모듈간의 상관관계를 해석하기 위해 런타임 링커를 사용하게 된다.


반응형

+ Recent posts