gcc란?


GNU Compiler Collection(gcc)는 전처리기(cpp0), C컴파일러(cc1), 어셈블러(as), 링커(ld)를 각각 호출하는 역할을 한다.


컴파일과정

sample.c > 전처리기(cpp0) > sample.i > C컴파일러(cc1) > sample.S > 어셈블러(as) > sample.o > 링커(ld) > a.out



소스코드를 컴파일 해 바이너리를 만들어내는 과정은 자동차를 제작하는 원리와 비슷하다. 자동차는 수많은 부품들이 있고 완성 후에도 여러가지 옵션으로 부품을 추가하거나 업그레이드 할 수 있는데 이것으로 인해 성능 또한 달라질 수 있다.


gcc도 정말 많은 옵션들을 제공하고 있다. 컴파일 시에 이러한 옵션들로 인한 성능 향상으로 속도적 효율이 증대되거나 메모리 또는 저장공간의 공간적 효율을 가져올 수 있다.




gcc 옵션


-E

-E 옵션은 전처리 과정의 결과를 화면에 보이는 옵션이다.

하지만 –save-temps 옵션을 사용해 [파일명].i 파일을 읽는 것이 더 좋은 방법이다.


-S

cc1으로 전처리된 파일을 어셈블리 파일까지만 컴파일하고 멈춘다.

최종결과물: 어셈블리 파일(.S)


-c

-c 옵션은 as에 의한 어셈블까지만 수행하고 링크는 수행하지 않는다.

최종결과물: 오브젝트 파일(.o)


-v

이전에 많이 사용하던 옵션으로 gcc가 컴파일을 어떤 식으로 수행하는지를 화면에 출력해서 보여준다.


--save-temps

컴파일 과정에서 생성되는 중간파일(전처리 파일과 어셈블리 파일)을 지우지 않고 현재 디렉토리에 저장한다.

컴파일 과정에서 발생되는 오류를 분석할 때 이용한다.


-o

컴파일된 파일명을 지정한다.




전처리기(cpp0) 옵션


-I

전처리 과정에서 헤더 파일을 탐색하는 기본 디렉토리를 추가할 때 사용하는 옵션이다.

“그런 파일이나 디렉토리가 없음” 이라는 에러가 뜨면 헤더를 찾아 –I[디렉토리명] 옵션으로 추가한다. 그러면 추가한 디렉토리에서 우선적으로 찾게 된다.

기본적으로는 다음의 디렉토리에 위치한 헤더 파일만을 참고한다.

  • /usr/local/include
  • /usr/lib/gcc-lib/리눅스머신/리눅스버전/include
  • /usr/include


-include [헤더파일경로]

헤더 파일을 소스 내에 추가할 때 사용한다. 소스에서 #include “헤더파일경로” 한 것과 동일하다.


-D[매크로]

매크로를 외부에서 define 할 때 사용한다.

만약 –DDEBUG 옵션으로 컴파일을 했다면 소스의 제일 앞에 #define DEBUG 옵션을 추가한 것과 같다.

주로 디버깅 메시지 활성&비활성화 시에 사용한다.


-D[매크로]=[매크로 값]

코드 내에서 #define [매크로] [매크로값] 을 추가한 것과 같다.


-U

–D와 반대로 소스 파일(.c) 내에 #undef [매크로] 옵션을 추가한 것과 같다.


–M 과 -MM

Makefile을 작성할 때 유용한 종속 항목 리스트를 출력한다. 종속항목이란 타겟이 컴파일 되기 위해 필요한 항목을 의미한다.

-M: make를 위한 소스파일의 모든 종속 항목을 출력한다.

-MM: make를 위한 소스파일에서 기본 include 디렉토리에 있는 헤더 파일은 빼고 종속 항목을 출력한다.


-nostdinc

디폴트 include 디렉토리(/usr/include)에서 헤더파일을 찾지 않고 –I 옵션으로 추가한 디렉토리에서만 헤더를 찾는다.

리눅스 커널과 같이 표준 C함수를 사용하지 않는 프로그램을 컴파일할 때 잘못된 include로 인한 오류가 발생하는 것을 방지한다.


-C

-E 옵션과 함께 사용하며 전처리 과정에서 주석을 제거하지 않는다.


-Wp, [옵션]

만약 cpp0와 gcc의 옵션이 중복되면 gcc의 옵션으로 해석된다. gcc가 모든 컴파일 도구들을 컨트롤하기 때문이다. gcc의 해석을 거치지 않고 바로 cpp0의 옵션으로 전달하고 싶을 때 –Wp 옵션을 사용한다.

-Wp, -DDEBUG, -I/usr/include, -M 이렇게 사용하면 cpp0로 -DDEBUG, -I/usr/include, -M 옵션이 바로 전달된다.




C컴파일러(cc1) 옵션


gcc의 수많은 옵션 중 거의 대부분은 cc1의 옵션이다. cc1의 옵션은 크게 4가지 종류로 나뉘는데

C언어 옵션, 경고메시지 옵션, 최적화 옵션, 디버깅 옵션이다.



C언어 옵션

주로 c언어 종류와 표준에 관한 옵션으로 K&R C, Traditional C, ANSI C 그리고 GNU C 같은 각 종류와 표준에 부합하는 C 소스를 작성하고자 할 때 사용한다. 기본적으로는 GNU C89 문법을 가지고 체크한다.


-ansi

ANSI C 표준에 부합하는 소스작성시 사용하는 옵션으로 GNU C의 확장 문법은 사용이 불가하다.

이식성이 높다는게 장점이고 이 옵션을 사용할 때는 –pedantic 경고 옵션도 사용하면 좋다. 이는 ANSI C89 문법을 가지고 문법체크를 한다.


-std=[C 표준들]

기타 다른 표준을 지정할 때 사용한다. c89, c99, gnc89, gnu99 가 있다.


-traditional

오래된 Traditional C(K&R C)문법으로 문법을 검사한다.


-fno –asm

gnu89 문법에서 지원하는 asm, inline, typeof 키워드를 사용하지 않는다. asm은 C 소스내의 어셈블리 코드를 삽입할 때, inline은 인라인 함수를 지정할 때, typeof 키워드는 변수의 타입을 알아볼 때 사용하는데 이러한 키워드는 ANSI C89 표준에서는 지원하지 않기 때문에 ANSI C89에서 지원하는 __asm__, __inline__, __typeof__ 키워드를 사용해야한다.



경고메시지 옵션

cc1의 옵션을 조정하여 경고수위를 조정할 수 있다. 모든 경고를 활성화 하는 –Wall –W 옵션을 사용하면 지나칠 정도로 사소한 것까지 경고하기 때문에 이 옵션을 활성화 시켰을 때 어떻게 수위 조절을 할 것인지가 중요하다.


-Wall 와 –W

-Wall: 모든 모호한 문법에 대한 경고 메시지를 출력한다.

–W: 합법적이지만 모호한 코딩에 대해서 부가적인 정보를 제공한다.

소스의 엄격성을 제공하기 위해서는 두 옵션은 반드시 사용해야 한다.


-w

모든 경고 메시지 제거


-Werror

모든 경고를 컴파일을 중단하는 오류로 취급하여 경고가 하나만 나와도 컴파일은 중단된다.


-pedantic

ANSI C89 표준에서 요구하는 모든 경고 메시지를 표시


-pedantic –errors

ANSI C89 표준에서 요구하는 모든 오류 메시지를 표시


-Wtraditional

소스가 ANSI C와 K&R C 간에 서로 다른 결과를 가져올 수 있는 부분이 있다면 경고



최적화 옵션

최적화는 실행파일의 크기를 줄여 메모리와 하드디스크의 공간을 절약하는 것과 실행 속도를 향상시키는 것을 의미한다. 물론 프로그램의 원래 기능은 유지되어야 할 것이다. 최적화가 중요한 이유는 컴파일 한 프로그램이 언제까지나 계속 사용될 수 있다는 것이다.


공간 최적화는 임베디드 시스템에서 더욱 의미가 있다. 1바이트라도 절약해야 하는 작은 시스템에서의 공간 최적화는 그 의미가 더 크다.


최적화 기법

  1. 불필요한 함수 호출 제거
  2. 상수의 전달과 변수 복제 전달의 최적화
  3. 불필요한 루프 제거
  4. 상수의 곱셈 최적화
  5. 함수 호출 인수의 지연된 pop 동작 방지(스택 최소화)
  6. 재귀 호출이 힘수의 최종부에서 일어나지 않도록 최적화
  7. 단순 시간 지연 구간에 기능 삽입
  8. 자동 레지스터 할당
  9. 유추에 의한 변수 최적화
  10. 인라인 함수의 최적화와 스택 프레임 포인터의 제거
  11. 각 명령의 실행 사이클을 고려한 스케쥴링
  12. 반복되는 코드의 제거
  13. 루프에서의 동작 불변
  14. 인스트럭션에 대해 속성 부여 기능
  15. 각 하드웨어에서 지원하는 단축명령 기능에 의한 지역 최적화


gcc는 최적화에 대해서도 굉장한 유연성을 제공한다. 최적화 기법을 일일이 선택할 수도 있고 –O[최적화 레벨] 옵션을 통해 미리 여러 최적화 기법의 묶음으로 묶여있는 최적화 방법을 사용할 수도 있다.


-O0

최적화를 수행하지 않는다. 컴파일 시 최적화 옵션을 붙이지 않으면 이 옵션과 같다. 또한 인라인 함수가 사용돼도 확장되지 않는다. 리눅스 커널같이 인라인 함수가 많은 소스를 컴파일 할 경우엔 최적화를 하지 않을 시 문제가 발생한다.


-O1

최적화를 하지 않을 때보다 속도향상과 사이즈감소가 있다. 인라인 함수도 확장된다.


-O2

가장 많이 사용하는 최적화 옵션이고 일반 응용 프로그램이나 커널 컴파일시 사용된다. 대부분의 최적화를 수행한다.


-O3

모든 함수를 인라인 함수와 같이 취급한다. (함수를 호출할 때 call 인스트럭션을 사용하지 않고 호출) 가장 높은 레벨의 최적화라고 해서 가장 빠른 것도 아닐뿐더러 너무 많은 소스의 변경이 생기기 때문에 왜곡이 발생할 수 있다. (사용하지 않는 것이 좋다.)


-Os

사이즈 최적화를 수행한다. 임베디드 시스템 같은 곳에서 사용한다.



디버깅 옵션


-g

gdb에게 제공하는 정보를 바이너리에 삽입한다. 디버깅 심벌이라 하여 C 소스를 바이너리 안에 삽입한다. 실제 소스내용은 아니고 어떤 인스트럭션이 어떤 소스파일의 몇 번째 행이라는 정보만 삽입된다.


–g 옵션도 –O 옵션과 같이 레벨이 있는데 –g0은 디버깅 정보를 삽입하지 않고 –g3은 디버깅 정보를 가장 많이 제공한다. –g 는 –g2와 동일하다. 하지만 –g 옵션으로 컴파일 한 바이너리는 용량이 매우 크기 때문에 개발 초기에만 사용하고 개발이 끝나면 제거한다.


-pg

프로파일을 위한 코드 삽입. 이 옵션을 사용한 프로그램이 종료되면 프로파일 정보가 gmon.out 파일에 저장된다. 이후 gprof에 의해 gmon.out 파일의 내용을 분석하여 어떤 함수가 얼마나 호출되었고 시간은 얼마나 걸렸는지 확인할 수 있다. 그래서 병목 현상이 일어나는 부분과 어떤 부분을 최적화 해야 하는지 알 수 있다.




collect2 또는 ld 옵션


링크 옵션은 매우 중요하다. 이유는 자신이 작성한 프로그램은 링크 오류가 잘 안나지만 다른 사람이 작성한 프로그램을 컴파일하려고 하면 링크 오류가 자주 나기 때문이다. 원하는 바이너리를 만들기 위해서는 링크의 개념과 사용되는 옵션을 명확하게 알아야 시행착오를 격지 않을 것이다.


-L[라이브러리 디렉토리]

라이브러리를 찾을 디렉토리를 지정한다. 라이브러리 위치가 다를 때 사용한다.

기본적으로는 다음의 디렉토리에 위치한 라이브러리 파일만을 참고한다.

  • /usr/lib
  • /usr/lib/gcc-lib/리눅스종류/리눅스버전


-l [라이브러리 이름]

같이 링크할 라이브러리를 지정한다.

–l 옵션은 꼭 소스파일의 뒤에 와야 한다.


-shared

공유 라이브러리와 정적 라이브러리가 같이 있을 경우 공유 라이브러리를 우선하여 링크한다.

기본적으로 공유 라이브러리를 우선시한다.


-static

공유 라이브러리와 정적 라이브러리가 같이 있을 경우 정적 라이브러리를 우선하여 링크한다.

정적라이브러리는 크기가 크지만 속도가 빠르고 어디서나 실행 가능하다. 반면 공유 라이브러리는 사이즈가 작지만 속도가 느리고 다른 컴퓨터에서 실행할 때 컴파일 당시의 공유 라이브러리와 메이저 버전이 다를 경우 실행되지 않는다.


-nostdlib

링크시에 표준 C 라이브러리를 사용하지 않는다.

운영체제나 부트로더, 디바이스 드라이브와 같이 표준 C 라이브러리를 사용하지 않는 프로그램을 컴파일 할 때 사용한다.


-nostartfiles

crt1.o 등과 같이 start up 파일을 링크하지 않는다.

역시 운영체제, 부트로더와 같은 프로그램을 컴파일 할 때 사용한다.


-Wl, [링크옵션들]

gcc를 거치지 않고 바로 링커에게 옵션을 정해주고자 할 때 사용한다. 사용법은 –Wa와 동일하다. 옵션들은 다음과 같다.

  • -s: 실행 파일에서 심볼 테이블 제거
  • -x: 출력 파일에 로컬 심볼 제거
  • -n: 텍스트 영역을 읽기 전용으로 만듦
  • -r: 추후 링크가 가능하게 오브젝트를 만듦
  • -e [name]: 시작 심볼을 name 심볼로 사용(기본적으로 _start 심볼이 시작 심볼)
  • -M: 심볼들의 정보를 자세하게 출력
  • -oformat [format]: 주어진 형식의 오브젝트 파일을 생성