'컴퓨터 서적 정리/Binary Hacks'에 해당되는 글 21건
- 2011.07.05 [Hack #22] GCC 확장기능 (빌트인, 애트리뷰트, 레이블)
- 2011.07.05 [Hack #20] 공유 라이브러리에 PIC를 사용하는 이유 1
- 2011.07.04 [Hack #19] 링크할 때 심볼 충돌 방지하기
- 2011.07.04 [Hack #18] C와 C++ 프로그램 링크 방법
- 2011.07.04 [Hack #17] ar - 정적 라이브러리 다루기
- 2011.07.04 [Hack #16] strip - 오브젝트 파일에서 심볼 삭제
- 2011.07.03 [Hack #15] addr2line - 주소에서 파일명과 행 번호 얻기
- 2011.07.03 [Hack #14] c++filt - C++ 심볼 demangle
[ 빌트인 함수 ] | |
♧ gcc에는 표준 라이브러리에 있을 듯한 몇몇 함수가 빌트인 함수로 마련되어 있음. 최적화 방법에 따라 소스에 쓰인 것과 다른 코드를 생성할 경우 있다.
▷ 예를 들면 printf(3) 함수의 경우 다음과 같이 문자열을 출력하는 코드에서는 실행할 때 pirntf(3)의 형식 문자열을 해석할 수 없으므로 다음과 같이 puts(3)를 호출하는 코드를 생성.
▷ 기본적으로는 같은 동작을 하고 더 빠르게 실행될 듯한 코드가 생성되어 문제는 없으나, LD_PRELOAD 등으로 오버라이드(override)해서 동작을 변경하고자 할 경우에는 주의 할 필요가 있다. 최적화를 통해 어떤 코드가 생성되는지 gcc/builtins.c에 프로그램 되어 있다.
|
[ 애트리뷰트 (__attribute__) ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
♧ 함수에 애트리뷰트를 덧붙여 선언하면 함수에 특별한 의미를 부여하거나 함수호출을 최적화 할 수 있다.
▶ 애트리뷰트 선언 2가지 방법
▶ 함수에 관한 애트리뷰트에는 다음과 같은 것들이 있다.
|
[ 레이블(label) 참조 ] | |||
♧ C에서는 그다지 사용되지 않지만 goto로 점프할 위치를 지정할 때 레이블을 사용.
▷ GCC에서 레이블은 &&로 참조하고 void *형 함수에 대입할 수 있다.
이와 같이 레이블을 변수에 대입할 수 있기 때문에 변수 값에 따라 점프할 위치를 변경하도록 하는 코드는 레이블로의 참조를 요소로 갖는 배열로도 구현할 수 있다. 레이블을 &&로 참조한 값은 void *에 대입할 수 있는 포인터형이므로 뺄셈으로 오프셋을 얻을 수 있다. 따라서 다음과 같은 코드를 작성할 수 있다.
|
[ 정리 ]
GCC는 C99표준에 준거하도록 노력하는 한편, 소스코드를 기술하기 쉽도록 하기 위한
확장 기능을 제공하고 있다. GCC의 확장기능임을 이해한 후 이 기능을 사용하면 편리한
경우가 있다. 예를 들면 리눅스 커널에는 GCC확장 기능을 이용한 코드를 볼 수 있다.
이러한 코드를 파악하려 할 경우에는 GCC 확장기능에 대한 이해가 필수적이다.
'컴퓨터 서적 정리 > Binary Hacks' 카테고리의 다른 글
[Hack #20] 공유 라이브러리에 PIC를 사용하는 이유 (1) | 2011.07.05 |
---|---|
[Hack #19] 링크할 때 심볼 충돌 방지하기 (0) | 2011.07.04 |
[Hack #18] C와 C++ 프로그램 링크 방법 (0) | 2011.07.04 |
[Hack #17] ar - 정적 라이브러리 다루기 (0) | 2011.07.04 |
[Hack #16] strip - 오브젝트 파일에서 심볼 삭제 (0) | 2011.07.04 |
♧ 통상 GNU/리눅스의 공유 라이브러리를 만들 때는
각각의 .C 파일을 PIC(Position Independent Code)가 되도록 컴파일 한다.
그러나 실은 PIC로 컴파일 하지 안아도 공유 라이브러리는 만들 수 있다.
그러면 굳이 PIC로 컴파일 하는 이유가 있는 것일까?
▶ fpic.c 작성
#include <stdio.h> void func() { printf(""); printf(""); printf(""); } |
☞ PIC로 컴파일하기 위해 gcc -fpic 또는 fPIC 옵션을 지정한다.
-fpic :: 좀더 고속으로 코드를 생성할 가능성이 있지만,
CPU에 따라 -fpic로 생성할 수 있는 GOT(Glocal Offset Table)의 크기에 제한이 있다.
-fPIC :: CPU에 관계없이 사용할 수 있다. 여기서는 -fPIC를 사용한다.
(※ x86에서는 -fpic와 -fPIC가 동일)
% gcc -o fpic-no-pic.s -S fpic.c % gcc -fPIC -o fpic-pic.s -S fpic.c |
위와 같이 생성된 어셈블리어의 소스코드를 보면 PIC 버전은 printf를
PLT(Procedure Linkage Table)를 경유해서 호출하는 것을 알 수 있다.
(※ 우분투 7.04에서 테스트)
다음에는 공유 라이브러리를 만든다.
% gcc -shared -o fpic-no-pic.so fpic.c % gcc -shared -fPIC -o fpic-pic.so fpic.c |
▷ 위 공유 라이브러리의 동적 섹션(dynamic section)을 readelf 명령으로 보면,
비 PIC공유 라이브러리에서는 TEXTREL 이라는 엔트리가 있고 (텍스트 내의 재배치 필요),
RELCOUNT(재배치 수)가 5로, PIC 공유 라이브러리보다 3만큼 크다.
그 이유는 printf()를 3회 호출하기 때문이다.
▷ PIC 공유 라이브러리에서의 RELCOUNT가 0이 아닌 이유는 gcc가 기본적으로 사용하는
시작 파일에 포함된 코드 때문이다. gcc에 -nostartfiles 옵션을 지정하면 이 값은 0이 된다.
[ PIC와 비 PIC 공유 라이브러리의 성능 비교 ] | ||||||||||||
♧ 위 예에서는 비 PIC 버전은 실행 시에(동적 링크 시에) 5개의 주소가 재배치 되어야 한다고 했다. 그러면 재배치 수가 매우 커지면 어떻게 될 것인가?
공유 라이브버리를 PIC버전과 비 PIC버전printf()를 천만 번 호출 ( 셸 스크립트로 실행 비교) ▶ 다음 셸 스크립트를 실행하면 printf()를 천만 번 호출하는 공유 라이브러리를 비 PIC버전과 PIC 버전으로 만들고, 각각을 링크한 실행 파일 fpic-no-pic와 fpic-pic를 생성.
비 PIC 버전이 첫 회 2.15초, 두 번째 이후에는 약 0.55초가 걸리고,
PIC 버전은 최초 0.02초, 두 번째 이후에는 0.00초가 되었다.
☞ main()의 내용은 없으므로 비 PIC 버전은 동적 링크할 때 재배치에 2.15 ~ 0.55초를 필요로 함을 알 수 있다. 실행 환경은 Xeon 2.8GHz + Debian GNU/리눅스 sarge + GCC 3.3.5이다.
▷ 비 PIC 버전의 단점은 실행할 때 재배치에 시간이 걸린다는 점만이 아니다. 재배치가 필요한 부분의 코드를 재작성 하기 위해 『텍스트 섹션 내의 재배치가 필요한 페이지를 로드 → 재작성 → copy on write 발생 → 다른 프로세스와 텍스트를 공유할 수 없음』 이라는 사태가 발생. 즉, 여기서는 텍스트(프로그램 코드)를 다른 프로세스와 공유할 수 있는 『공유』라이브러리의 주요한 장점이 사라지고 만다.
한편, 비 PIC 버전의 fpic-no-pic.so 와 PIC 버전의 fpic-pic.so 파일 크기를 비교하면, 전자는 268MB, 후자는 134MB로 크게 차이가 난다. readelf -S로 섹션 데이터를 보면 다음과 같은 차이가 있다.
비 PIC 버전은 코드(.text) 크기는 PIC 버전보다 작지만, 재배치에 필요한 정보(.rel.dyn)가 상당한 용량을 차지.
[ 정리 ] 여기서는 공유 라이브러리를 작성할 때 PIC로 컴파일 해야 하는 필요성에 대해 알아보았다. 비 PIC 공유 라이브러리를 작성할 수는 있지만 실행할 때 재배치에 시간이 소요되고 다른 프로세스와 코드(.text)를 공유할 수 없는 커다란 단점이 있다. 따라서 공유 라이브러리를 작성할 때는 .c 파일을 PIC로 컴파일 하도록 한다. |
'컴퓨터 서적 정리 > Binary Hacks' 카테고리의 다른 글
[Hack #22] GCC 확장기능 (빌트인, 애트리뷰트, 레이블) (0) | 2011.07.05 |
---|---|
[Hack #19] 링크할 때 심볼 충돌 방지하기 (0) | 2011.07.04 |
[Hack #18] C와 C++ 프로그램 링크 방법 (0) | 2011.07.04 |
[Hack #17] ar - 정적 라이브러리 다루기 (0) | 2011.07.04 |
[Hack #16] strip - 오브젝트 파일에서 심볼 삭제 (0) | 2011.07.04 |
♧ 같은 이름을 갖는 심볼의 충돌
C 또는 C++프로그램에서 같은 이름을 갖는 전역 심볼이 둘 이상 존재하면 어떻게 될까?
[ .o 파일을 모아서 링크할 경우 ] |
|||
▷ 우선 a.c와 b.c가 동일한 이름의 func()함수 정의
▷ main.c에서 func() 호출.
▶ 위 세 파일을 각각 컴파일해서 실행.
☞ 정적으로 링크하려 하면 func() 함수가 여러 개 존재하므로 링커에서 에러를 출력. 이 에러 덕분에 예상외의 func() 함수가 호출되는 문제를 사전에 피해갈 수 있다.
|
[ 라이브러리를 생성해서 링크할 경우 ] |
||
♧ 앞에서와 동일한 소스코드 a.c와 b.c에서 ar을 사용해 정적 라이브러리를 생성하면 링크할 때 에러가 발생하지 않는다. 그 이유는 링커와 달리 오브젝트 파일로 아카이브를 생성하는 ar 명령은 심볼 충돌을 검사하지 않기 때문.
▶ 생성된 실행 파일 a.out을 실행
☞ 실행해 보면 a.c의 func()를 호출함을 알 수 있다. 이는 링크할 때 libfoo.a에 있는 두 func() 중에 a.c의 함수를 b.c의 함수보다 먼저 찾기 때문.
▶ 정적 라이브러리 libfoo.a를 ar 명령으로 생성할 때 인자의 순서를 a.o b.o에서 b.o a.o로 변경하면 b.o의 func()가 호출.
▶ 또한 a.c와 b.c를 이용해 각각 정적 라이브러리를 만든 경우에도 링크할 때는 에러가 발생하지 않는다. 이때 gcc에 넘긴 liba.a와 libb.a의 순서를 반대로 하면 b.c의 func()를 호출하게 된다.
▶ 마찬가지로 a.c와 b.c로 각각 동적 공유 오브젝트 생성해서 링크하면 에러는 발생하지 않는다. 아래 예에서는 a.so의 func()가 호출.
링크할 때 명령행 인자의 a.so와 b.so의 순서를 바꾸면 b.so의 func() 함수를 먼저 찾게 된다. 동적 링크할 때는 기본적으로 먼저 발견한 심볼 정의가 사용된다.
▷ GNU C Library의 주요 개발 구성원인 울리히 드레퍼가 작성한 『How to write Shared Libraries』 에는 다음과 같이 기술 되어 있다.
|
[ C++와 같은 이름의 클래스 ] |
||||
C++에서 동일 심볼 문제는 예상치 않은 멤버 함수를 호출하는 문제를 야기한다.
소비세를 계산하는 s.{h, cpp}라는 소스코드와 이를 이용한 main.cpp가 있다고 하자. ▷ a.h 작성
▷ a.cpp 작성
▷ main.cpp 작성
▶ 위 소스코드 컴파일, 링크해서 실행하면 apple: 105라는 메시지 출력.
여기서 별도로 개발된 b.cpp가 존재하고 같은 이름의 클래스 Tax가 구현되어 있다고 하자. b.cpp내의 Tax 클래스의 용도는 a.cpp와는 전혀 다르다. ▷ b.cpp 작성
▶ 그리고 b.cpp를 b.so로 컴파일하고 불행히도 b.so, a.so, main.so 순으로 링크된다면 성가신 일이 발생한다.
이번 실행 결과는 apple: 10이다. 이는 Tax 객체의 생성자로 b.so 내의 Tax::Tax()가 호출되고, Tax 객체의 멤버함수로서 a.so 내의 Tax::tax()가 호출되었기 때문이다. 즉, 예상외의 생성자가 호출되어 버그의 원인이 되었다.
☞ 프로그램이 거대해질수록 발생가능성이 커진다. 따라서 namespace를 사용해서 충돌을 피해갈 필요가 있다. 심볼을 하나의 파일 내에 모두 넣는다면 이름 없는 namespace를 사용해도 될 것이다. 예를 들면 b.cpp 전체를 snamespace { … }로 둘러싸면 위와 같은 문제는 발생하지 않는다.
|
[ weak 심볼 ] |
|||
♧ 첫머리에서 .o 파일을 모아서 링크할 때는 동일한 이름의 심볼 충돌이 확인 했다. 그러나 weak 심볼이 존재하면 애기는 달라진다.
다음과 같은 프로그램 main.cpp를 생각해보자.
▶ 컴파일 실행하면 256 출력.
여기에 main.cpp와는 전혀 별개로 개발된 다음과 같은 a.cpp가 있다. a.cpp 내에도 클래스 Foo가 구현되어 있다.
▶ 컴파일한 a.o가 불행히 main에 링크된다면 매우 성가신 일이 발생한다.
☞ 이번 실행 결과는 256이 아니라 제곱수인 65536이 출력 되었다. 이때는 링크 순서가 main.o, a.o 또는 a.o, main.o 둘 다 상관 없이 결과는 변하지 않는다. main.cpp 내에 정의된 생성자 Foo::Foo(int)가 아니라 a.cpp 내의 Foo::Foo(int)가 사용된 것은 분명하지만, 이런 문제가 발생하는 이유는 무엇일까?
▶ 〔weak 심볼〕이란 무엇인가? 원인은 main.o에 포함된 Foo::Foo(int)는 weak 심볼이고 a.o에 포함된 Foo::Foo(int)는 weak 심볼이 아니기 때문이다. ▷ 아래의 nm 출력 결과에서 W로 표시된 것이 weak 심볼을 나타내는 표시이다.
☞ weak 심볼에 관해서는 『Linkers & Loaders』(john R. Levine 저, Morgan Kaufmaun) 의 6장에 다음과 같이 설명되어 있다.
즉, a.o에 일반적인(비weak 인) 정의가 존재하므로 main.o에 있는 Foo::Foo(int)의 weak 정의는 무시되었다는 것이다. 일단 이 문제를 피해가려면 이름 없는 namespace를 사용해 링키지 링크를 막는 것이 효과적이다. 구체적으로 main.cppㅇ듸 클래스 Foo { … }주위에 namespace { … }로 둘러싼다.
▶ weak 정의와 중복 코드 제거 그런데 왜 main.o Foo::Foo(int) 및 Foo::Func()는 weak 정의가 되는 것일까? 그 이유는 g++는 인라인 함수를 weak 정의로서 컴파일 하기 때문이다. (클래스 정의 내의 함수정의는 표준 C++ 규격에 따라 인라인 함수로 간주) 따라서 weak 정의는 .o파일 내의 .gnu.linkonce.t.*라는 이름의 섹션에 배치된다. .gnu.linkonce.t.*는 링크할 때 중복 코드 제거에 사용되는 섹션이다. 클래스 정의는 대개의 경우 .h 파일 내에 기술되고, .h 파일은 여러 개의 .cpp 파일에 #include 된다. 이 때문에 각각의 .o 파일에 포함된 weak 정의를 링크할 때 하나로 모을 필요가 있다. 인라인 함수가 weak 정의로 되는 것은 이 때문이다.
|
'컴퓨터 서적 정리 > Binary Hacks' 카테고리의 다른 글
[Hack #22] GCC 확장기능 (빌트인, 애트리뷰트, 레이블) (0) | 2011.07.05 |
---|---|
[Hack #20] 공유 라이브러리에 PIC를 사용하는 이유 (1) | 2011.07.05 |
[Hack #18] C와 C++ 프로그램 링크 방법 (0) | 2011.07.04 |
[Hack #17] ar - 정적 라이브러리 다루기 (0) | 2011.07.04 |
[Hack #16] strip - 오브젝트 파일에서 심볼 삭제 (0) | 2011.07.04 |
♧ C로 작성된 함수를 C++에서 호출하고자 할 경우,
혹은 반대로 C++로 작성된 함수를 C프로그램에서 호출하고자 할 경우.
[ C/C++와 심볼명 ] |
|||||||
▷ 아래의 dbg 함수를 C/C++컴파일러로 각각 컴파일해 보자.
# C 컴파일 :: gcc -W -Wall -c dbg.c # C++ 컴파일 :: g++ -W -Wall -c dbg.c
▷ C컴파일 / 심볼명 확인
▷ C++컴파일 / 심볼명 확인
▷ 컴파일해서 생성된 오브젝트 파일에 포함된 심볼명.
☞ C 컴파일러로 함수를 컴파일 하면 기본적으로는 함수명이 그대로 심볼명이 된다. 시스템 환경에 따라서는 심볼명이 "dbg"가 아니라 "_dbg"가 될 경우도 있지만 크게 다르지는 않다. 한편, C++ 컴파일러로 함수를 컴파일한 경우 함수가 속한 이름공간(namespace) 정보 또는 함수 인자의 자료형 정보가 심볼명에 포함되어 나타난다.
|
[ C++에서 C함수 호출하기 ] |
|||
▶ dbg.c를 C컴파일러로 컴파일한 후 C++로 작성된 함수(sample.cpp)에서 호출해 보자. ① sample.cpp 작성
② sample.cpp 컴파일, 링크하면 정상적으로 실행 파일이 생성/실행
이상 없이 C++함수(sample.cpp)에서 C함수(dbg)를 호출 하였다. ☞ sample.cpp의 『extern "C"』가 포인트다.
▶ 『extern "C"』의 유무에 따라 sample.o의 변화. ① 『extern "C"』가 있을 경우.
② 『extern "C"』가 없을 경우.
☞ ②에서 "sample.o"는 "_z3dbgPKc"라는 심볼을 참조하지만, dbg.o에 포함된 심볼은 단지 "dbg"로 "_z3dbgPKc"는 심볼은 어디에도 없다.
▷ 따라서 『extern "C"』를 넣지 않으면 다음과 같이 링크할 때 실패하게 된다.
이와 같이 C++에서 C함수를 호출할 때 『extern "C"』가 중요한 역할을 한다.
▶ 〔주의〕 extern "C"를 붙이면 인자의 자료형 일치 여부를 검사하지 않는다. ① sample.cpp의 dbg자료형 변경.
② 컴파일, 링크, 실행.
☞ 정상적으로 링크는 되지만 생성된 실행 파일은 정상적으로 실행 되지 않는다.
▶ 〔왜! 링크는 성공한 것일까?〕 sample.o가 참조하고 있는 것은 어디까지나 자료형 정보를 포함하지 않은 심볼 "dbg"로, dbg.o에 포함되어 있는 심볼명과 일치하기 때문이다. "링크에 성공하면 잘못된 자료형으로 함수가 호출되지 않는다."라고 생각하기 쉽지만, C++라도 extern "C"를 사용한 부분인 경우에 한해서는 그렇지 않다. 이는 컴파일러에서 자료형을 검사하는 안전성 수준이 C언어 수준으로 저하되기 때문이다.
▷ 이러한 문제를 미연에 방지하려면 C언어 측의 헤더 파일을 C++에서도 이용할 수 있도록 준비해 두어야 한다. 예를 들면, 아래의 dbg.h와 같은 헤더 파일을 작성하는 것이 바람직하다.
|
[ C에서 C++함수 호출하기 ] |
|||
▷ 이번에는 반대로 C에서 C++ 함수를 호출해 보도록 하자. 『최대공약수를 구하는 함수』를 Boost C++ Library를 이용해 C++로 간단히 구현하고, 그 함수를 C에서 호출해 보자.
▶ 〔중요한 점 두가지〕 º 링크는 gcc가 아닌, g++로 수행.
▷ C++측 구현
▷ C++함수를 호출하는 C프로그램은 특별히 달라진 것이 없다.
▶ 컴파일, 실행.
|
〔 주의 사항 〕 |
||
▶ C++함수는 C함수로 예외 처리를 넘겨서는 안됨. C언어에서 호출되는 C++함수를 작성할 때는 C++의 예외가 C함수로 도달하지 않도록 주의. C++의 예외가 C언어로 작성된 함수에 도달된 경우의 함수 동작에 대해 C++표준 규격에는 명확히 정의되어 있지 않다. 예를 들면 GCC에서는 프로세스가 이상 종료되는 현상이 나타남. C++ 함수에서 명시적인 예외를 throw하지 않도록 신경 쓰는 것은 물론, ▷ 다음과 같은 점도 충분히 주의해야 한다. º new 연산자가 std::bad_alloc 예외를 throw할 가능성 º std::vector의 멤버 함수 at에서 std::out_of_range 예외를 throw할 가능성
▶ 함수 포인터를 다루는 C함수에 주의. C함수 내부에서 C++의 예외가 발생하는 것을 막을 수 없는 경우가 있다. 다음과 같이 표준 C의 qsort함수를 C++에서 사용한 경우를 생각해 보자.
☞ qsort 함수에 인자로 넘긴 비교함수 compar가 예외를 throw하기 때문에 qsort함수내부에서 C++의 예외가 발생하게 된다. GCC를 사용할 경우에 이와 같은 경우라도 프로세스가 이상 종료되지 않기 위해서는 아래와 같이 qsort 함수를 "-fexceptions"옵션을 지정해서 컴파일 해야 한다.
일반적으로 함수 포인터를 다루는 C함수는 -fexceptions옵션을 지정해서 컴파일해야 오류가 발생하지 않는다. glibc의 Makefile을 보면 이러한 함수(qsort, bsearch 등)는 이 옵션을 지정해서 컴파일 하도록 되어 있다. |
'컴퓨터 서적 정리 > Binary Hacks' 카테고리의 다른 글
[Hack #20] 공유 라이브러리에 PIC를 사용하는 이유 (1) | 2011.07.05 |
---|---|
[Hack #19] 링크할 때 심볼 충돌 방지하기 (0) | 2011.07.04 |
[Hack #17] ar - 정적 라이브러리 다루기 (0) | 2011.07.04 |
[Hack #16] strip - 오브젝트 파일에서 심볼 삭제 (0) | 2011.07.04 |
[Hack #15] addr2line - 주소에서 파일명과 행 번호 얻기 (0) | 2011.07.03 |
♧ 사실 ar 명령으로 생성한 아카이브는 정적 라이브러리에 국한되지 않고
tar(1) 등과 마찬가지로 범용적인 비압축 아카이브로 사용할 수 있다.
그러나 일반적으로는 정적 라이브러리를 다루기 위해 사용된다.
▶ 아카이브를 생성할 경우 :: ar rcus [라이브러리 이름] [오브젝트 파일들]
r 옵션 :: 새로운 오브젝트 파일이면 추가, 기존 파일이면 치환함.
c 옵션 :: libhoge.a 파일이 존재하지 않아도 경고 메시지를 출력하지 않음.
u 옵션 :: 오브젝트 파일의 타임스탬프를 비교해 새로운 파일일 경우에만 치환함.
s 옵션 :: ranlib(1)과 마찬가지로 아카이브 인덱스를 생성.
→ 아카이브 인덱스를 생성하지 않으면 링크 속도가 느려지고, 시스템 환경에 따라서는 에러가 발생.
(※ 아카이브 인덱스는 nm -s로 조회할 수 있다.)
▶ 기존 아카이브 파일에 오브젝트 제거 :: ar ds [라이브러리 이름] [오브젝트 파일들]
d 옵션 :: 아카이브 모듈을 삭제
→ 삭제할 파일이 없다면 아카이브를 건들지 않음.
▶ 아카이브의 내용을 조회 :: ar tv [라이브러리 이름]
t 옵션 :: 아카이브에 있는 파일 리스트 출력
v 옵션 :: 자세한 내용을 보여주는 verbose 모드.
→ 이 옵션을 이용하면 파일크기나 갱신시각 등의 정보도 출력.
▶ 아카이브에서 파일을 추출하려면 :: ar xv [라이브러리 이름]
x 옵션 :: 아카이브에서 오브젝트 파일 추출
→ v옵션을 지정하면 추출한 파일에 대한 파일명 출력.
▷ ar 명령은 정적 라이브러리를 생성할 때뿐만 아니라,
정적 라이브러리의 기능을 부분적으로 변경하고자 할 경우에도 사용할 수 있다.
라이브러리가 모두 오픈 소스로 구성되어 있지 않은 이유 등으로
정적 라이브러리를 처음부터 다시 작성할 수 없는 경우에 편리하게 이용될 수 있다.
그 밖에도 아카이브 냉의 이동, 제거 등 아카이브를 다루는 옵션이 더 있지만,
대부분의 경우 위에서 설명한 옵션만으로도 문제가 없을 것이다.
'컴퓨터 서적 정리 > Binary Hacks' 카테고리의 다른 글
[Hack #19] 링크할 때 심볼 충돌 방지하기 (0) | 2011.07.04 |
---|---|
[Hack #18] C와 C++ 프로그램 링크 방법 (0) | 2011.07.04 |
[Hack #16] strip - 오브젝트 파일에서 심볼 삭제 (0) | 2011.07.04 |
[Hack #15] addr2line - 주소에서 파일명과 행 번호 얻기 (0) | 2011.07.03 |
[Hack #14] c++filt - C++ 심볼 demangle (0) | 2011.07.03 |
♧ strip은 오브젝트 파일에 있는 심볼을 삭제하는 툴.
일반적으로 빌드 완료한 실행파일 또는 라이브러리에서 불필요한 심볼을 제거하는데 사용.
[ strip 사용법 ]
strip 사용법은 간단하다.
기본적으로는 심볼을 제거하려는 오브젝트 파일을 인수로 지정하면 된다.
test라는 실행 파일에서 심볼을 제거하려는 경우 다음과 같이 실행.
▷ test.c 작성.
#include <stdio.h> void test() { } int main() { return 0; } |
▷ gcc로 컴파일 하면 완성된 test에 심볼 정보가 포함된다.
이때 test는 8,327바이트 크기로 생성되었다.
▶ strip을 사용하여 심볼 제거.
심볼을 제거한 후 5,492바이트가 되었다.
C 소스코드를 보면 거의 비어 있는 프로그램인데 약 3,000바이트 정보가 줄었다.
이유는, 사실 test의 대부분이 /usr/lib/crt*.o라는 오브젝트 파일로 채워져 있기 때문.
▶ -d옵션 :: 디버그용 정보(파일명 또는 행 번호 등)만을 제거하고
함수명 등의 일반 심볼은 남게 된다.
% gcc -g test.c -o test
% objdump -h test
...(중략)...
▷ strip -d로 삭제후 확인
▶ -R 옵션 :: 지정된 섹션을 제거하는 옵션.
stript -R .text program을 실행하면 프로그램의 텍스트 섹션(코드 부분)이
모두 제거되어 전혀 작동하지 않게 된다. 또한 .o 또는 .a 파일에 strip 명령을 실행하면
사실상 다른 오브젝트 파일과 링크를 할 수 없게 된다.
이는 링커가 심볼에 의존하기 때문이다.
그러므로 .o와 .a 파일에서 심볼을 제거하지 않도록 한다.
▷ 한편, 완성 제품으로 판매한 버전의 바이너리는 strip하고
개발자는 디버그 정보가 포함된 바이너리를 남겨 둔다면,
사용자 환경에서 생성된 코어 파일을 개발 환경에서 디버깅할 수 있게 된다.
[ strip 구조 ]
strip은 BSD 라이브러리를 이용해 제작된 툴로, BFD API를 이용해서 오브젝트 파일을 조작.
GNU Binutils의 소스코드를 보면 objcopy와 동일한 코드로 이루어져 있음을 알 수 있다.
실제로 objcopy 명령에 -strip-*옵션을 사용하면 strip과 같은 기능을 수행한다.
[ 정리 ]
디스크 용량이 풍부한 PC에서는 실행 파일의 크기를 줄일 수 있다는 장점을 느끼기 어렵지만,
사용할 수 있는 디스크나 메모리 용량이 제한적인 시스템 환경에서 프로그램을 설치할 경우
또는 프로그램을 네트워크로 전송해서 실행해야 할 경우에는 상당히 유용한 툴.
'컴퓨터 서적 정리 > Binary Hacks' 카테고리의 다른 글
[Hack #18] C와 C++ 프로그램 링크 방법 (0) | 2011.07.04 |
---|---|
[Hack #17] ar - 정적 라이브러리 다루기 (0) | 2011.07.04 |
[Hack #15] addr2line - 주소에서 파일명과 행 번호 얻기 (0) | 2011.07.03 |
[Hack #14] c++filt - C++ 심볼 demangle (0) | 2011.07.03 |
[Hack #13] strings - 바이너리 파일에서 문자열 추출 (0) | 2011.07.03 |
♧ addr2line은 디버그 정보를 이용해서 주소로부터 파일명과 행 번호를 얻는다.
이 때문에 프로그램은 미리 디버그 정보를 포함(gcc -g) 하도록 컴파일 해야 한다.
▶ test.c 작성.
#include <stdio.h> void func() { } int main() { printf ("%p\n", &func); return 0; } |
이를 디버그 정보를 포함하도록 컴파일/ 실행.
▷ addr2line을 사용하여 주소에 해당하는 파일명과 행 번호를 출력.
[ -e filename ] :: 실행 파일 지정
▷ 함수명과 함께 출력.
[ -f | --functions ] :: 함수명 출력
▷ 표준입력으로부터 주소를 addr2line 으로 넘길 수도 있다.
이는 여러 주소를 모아서 처리하고자 할 때 편리하다.
'컴퓨터 서적 정리 > Binary Hacks' 카테고리의 다른 글
[Hack #17] ar - 정적 라이브러리 다루기 (0) | 2011.07.04 |
---|---|
[Hack #16] strip - 오브젝트 파일에서 심볼 삭제 (0) | 2011.07.04 |
[Hack #14] c++filt - C++ 심볼 demangle (0) | 2011.07.03 |
[Hack #13] strings - 바이너리 파일에서 문자열 추출 (0) | 2011.07.03 |
[Hack #12] nm - 오브젝트 파일에 포함된 심볼 확인 (0) | 2011.07.03 |
♧ c++ 컴파일러는 단 하나의 이름을 갖는 심볼이 되도록
name mangling이라는 과정을 수행한다.
여기서는 명령행에서 C++의 심볼을 demangle하는 방법을 소개한다.
▷ C++ 오브젝트 파일을 nm명령으로 확인하면 기본적으로 심볼명은
name mangling된 읽기 어려운 형식으로 출력 된다.
▷ c++filt를 이용해여 demangle.
▷ nm에 --demangle 옵션을 사용하여 demangle.
'컴퓨터 서적 정리 > Binary Hacks' 카테고리의 다른 글
[Hack #16] strip - 오브젝트 파일에서 심볼 삭제 (0) | 2011.07.04 |
---|---|
[Hack #15] addr2line - 주소에서 파일명과 행 번호 얻기 (0) | 2011.07.03 |
[Hack #13] strings - 바이너리 파일에서 문자열 추출 (0) | 2011.07.03 |
[Hack #12] nm - 오브젝트 파일에 포함된 심볼 확인 (0) | 2011.07.03 |
[Hack #11] objcopy - 실행 파일에 데이터 삽입하기 (0) | 2011.07.03 |