Sunday, July 4, 2010

essential utility (gcc)

컴파일의 의미와 과정, 그리고 binutils 패키지 활용 방법에 대해 알아본다.


gcc


-컴파일의 의미-

컴파일이란? 형식언어(C, C++) 를 기계어로 번역하는 것.
그럼 기계어(machine instruction)란? 이진수로 되어 있는 숫자로써 CPU의 종류마다 고유하며, 특정 행동을 취하게 하기 위한 코드 (예로 01010101은 i386 CPU에서 'ebp레지스트를 현재의 스택에 push 해라'). 어셈블리 언어와 1대 1 매치.
그럼 어셈블리 언어란? 기계어를 사람이 일상 생활에서 사용하는 자연어에 가깝게 1~6개의 문자로 기호화해서 나타낸 것.

C 소스 파일 컴파일시 -g 옵션을 넣어주고 컴파일 한뒤에 실행 파일을 objdump 명령어로 보면 디버깅 심벌과 기계어, 그리고 어셈블리 코드를 볼수 있다. 기계어는 사람이 보기 좋게 16진수로 되어 있다 (gcc -g -o like like.c --> objdump -S like).
책에 CPU의 0과 1값을 받는 것에 대한 설명이 그림과 함께 잘 나타나 있으나 생략한다.


-C 소스 컴파일 과정-


gcc 는 GNU에서 만든 C 컴파일러이다 (사실 C, Fortran, ada 등도 컴파일 가능하다). 실제로 gcc는 실제 컴파일을 하는 것이 아니라 전처리기(cpp0), C 컴파일러(cc1), 어셈블러(as), 링커(ld 혹은 collect2)를 호출하는 역할을 한다 (*오른쪽 그림 참조*).

컴파일 과정을 gcc -v --save-temps 으로 확인할 수 있다 (-v 는 컴파일 과정을 화면에 출력하라, --save-temps는 컴파일 과정의 파일을 지우지 마라 라는 의미). 위 결과 전처리 후 생기는 *.i 파일 등을 이용하여 에러 수정에 도움이 된다.

1.cpp0에 의한 전처리 과정: /usr/lib/gcc/x86_64-redhat-linux/3.4.6/specs에는 gcc가 호출하는 프로그램의 기본적인 옵션들이 들어 있다. 전처리 과정은 크게 헤더 파일 삽입매트로의 치환및 적용으로 이루어진다. 헤더파일 삽입은 include 한 자리에 파일의 내용이 붙게 된다. 매크로 치환은 define으로 된 내용을 찾아 치환이 일어난다.


2.cc1에 의한 어셈블리 소스 파일로 컴파일 :
1.어휘분석 : 스캐너가 소스를 토큰(문법적으로 의미있는 최소 단위)으로 나눔
2.구문분석 : 파서가 토큰을 받아서 문법적 오류가 있는지 확인
3.의미분석 : 의미상 오류 검사(변수 선언검사, 자료형 불일치 검사등등).
4.중간언어생성 : 이해 안됨
5.최적화 : 중간 코드 최적화(공통부분식 제거, 연산 강도 경감, 상수 계산. 예, var=5+3을 var=8로바꿈)와 목적 코드 최적화(최대한 레지스터를 사용)로 나뉨.
6.목적코드생성 : 목적코드란 어셈블리 언어를 뜻한다.

3.as에 의한 기계어 코드 생성 : as에 의해서, cc1에 의해 생긴 *.s 파일의 기계어와 데이터가 들어있는*.o 파일을 생성한다. *.o 파일은 ELF 바이너리 포멧(바이너리 파일의 구조를 결정짓는 규약)의 바이너리 파일이다. readelf -a *.o 를 통해 ELF 포멧 바이너리의 정보를 알 수 있다. xxd like.o 명령으로도 전체를 볼 수 있다.

4.collect2에 의한 링킹 과정 : printf와 같은 함수를 사용하기 위해 정적 라이브러리 내지는 공유 라이브러리를 ELF 포멧의 바이너리 파일과 링크 시키다.
정적라이브러리란? 미리 만들어진 함수나 변수들의 묶음으로 미리 컴파일 된 *.o 파일이 하나의 파일 lib[라이브러리 이름].a 형식으로 묶여있는 형태를 뜻한다. 이렇게 묶인 오브젝트들은 컴파일 과정에서 각 바이너리와 링크되어 삽입된다. 그러나 이렇게 되면 링크가 중복되고 파일크기도 커져 공유라이브러리를 사용한다.
공유라이브러리(동적라이브러리)란? 설명이 굉장히 복잡하기에 대략적으로 설명하기로 한다. 공유 라이브러리가 존재하면 링크 과정에서 정적라이브러리 링크 하듯이 하지 않고 단순히 오브젝트 파일에 공유라이브러리를 사용하겠다고 표시만 해놓게 된다. 컴파일이 마쳐져서 프로그램이 실행되어지면 공유라이브러리를 사용한다는 표시를 보고 프로그램과 함께 동적 링크도 같이 로드하고 동적 링크는 로드된 동적 라이브러라와 프로그램 상의 라이브러리를 매핑한다.

책에는 gcc에서 호출하는 프로그램들(cpp0, cc1, as, collect2)의 옵션설명이 자세히 나와있으며 바이너리를 조작하거나 정보를 보기 위한 바이너리 유틸리티들에 대한 설명이 있으나 이는 생략한다.

No comments:

Post a Comment