gcc를 이용한 컴파일 옵션

다음의 gcc 컴파일 명령어를 통해 사용되는 옵션에 대해 간략히 알아보자.

gcc -v -I/usr/local/include -DDEBUG -Wall -W -O2 -L/usr/local/lib -o like like.c -lm
  • v: gcc C컴파일러의 옵션으로 컴파일 과정을 출력한다. (verbose)
  • I/usr/local/include -DDEBUG: 전처리기(cc1 -E)의 옵션
  • Wall -W -O2: cc1의 옵션
  • L/usr/local/lib -o like like.c -lm: collect2의 옵션. 이는 ld를 호출 시 전달한다.

man gcc 명령을 통해 기능별 옵션 리스트를 볼 수 있다.

gcc 옵션 규칙

  1. 각각의 옵션에는 그에 상응하는 gcc 내부 변수가 존재한다.

    • gcc/options.c, gcc/options.h에는 gcc 옵션에 상응하는 변수들을 볼 수 있다.

      extern int warn_unused_variable;
      extern int flag_branch_target_load_optimize;
      ...
      
    • 예를 들어, int warn_unused_variable 변수는 Wunused_variable 옵션을 주면 값이 설정되는 변수이다. 아무런 옵션도 주지 않으면 기본 값이 0으로 설정된다.

  2. [f|W|m][옵션]은 설정이고, -[f|W|m]no-[옵션]은 해제이다.

    • Wno-unused_function을 이용해 0으로 해제한다.
  3. 동일 종류의 옵션은 최종 옵션만 옵션 변수에 설정된다.

    • gcc ... -W-unused_function -Wno-unused_function 은 마지막 설정인 -Wno-unused_function이 적용된다.
  4. 그룹 옵션이 존재한다.

    • fdump-tree-all 은 -fdump-tree-original, -fdump-tree-optimize ... 의 옵션을 동시에 준 것과 동일하다.
    • O1, -Wall이 그러하다.
  5. f는 플래그, -W는 경고, -m은 아키텍처 종속, 기타 나머지들이다.

gcc 필수 옵션 및 중요 옵션

  1. gcc C 컴파일러 드라이버 옵션
    • -E: 전처리 과정의 결과를 화면에 보인다.
    • -S: 어셈블리 파일(*.s)만 생성하고 컴파일을 멈춘다.
    • -c: 오브젝트 파일(*.o)만 생성하고 컴파일을 멈춘다.
    • -v: 컴파일 과정을 화면에 자세히 출력한다.
    • -save-temps: 컴파일 과정의 중간파일인 전처리 파일(*.i), 어셈블리 파일(*.s), 오브젝트 파일(*.o)를 지우지 않고 디렉토리에 저장한다.
  2. cc1 -E 또는 cpp0 전처리기 옵션
    • -I: 전처리 과정 중 헤더파일을 탐색하는 기본 디렉토리를 추가한다. 기본적으로 #include <file> 형식은 시스템 헤더 디렉토리로 설정된 /usr/local/include, /usr/lib/gcc/i386-redhat-linux/4.1.2/inclue, /usr/include 순으로 찾고, #include "file"형식은 현재 디렉토리에서 먼저 찾고 위 순서로 찾는다. 따라서 -I옵션으로 디렉토리를 지정하면 시스템 헤더 디렉토리보다 우선해서 찾는다.
    • -include {file}: 헤더파일을 소스 내에 추가할 때 사용한다. 이는 #include "file"한 것과 동일한 결과를 나타낸다.
    • -D{macro_name}: 매크로를 외부에서 define할 때 사용한다. 이는 디버깅에서 많이 쓰인다.
    • -D{macro_name}={value}: 이는 #define macro value한 것과 동일한 결과를 나타낸다.
    • -U{macro_name}: 이는 -D와 반대의 결과를 나타내고, #undef macro와 동일한 결과를 나타낸다.
    • -M, -MM: (Makefile작성 시 유용한 )종속 항목(컴파일되기 위해 필요한 항목) 리스트를 출력한다. -M은 make를 위한 소스 파일의 모든 종속 항목을 출력하고, -MM은 기본 include 디렉토리에 있는 헤더 파일은 빼고 종속 항목을 출력한다.
    • -nostdinc: 디폴트 include 디렉토리(/usr/include 등)에서 헤더 파일을 탐색하지 않고, -I 옵션으로 추가한 디렉토리에서만 헤더 파일을 탐색한다.
    • -C: 전처리 과정에서 주석을 제거하지 않는다.
    • -Wp,{options}: 전처리기의 옵션으로 전달하고자 할 때 사용한다. 이는 전처리기(cc1 -E)와 C 컴파일러 드라이버(gcc)의 옵션이 서로 동일하다면 해당 옵션은 C 컴파일러 드라이버의 옵션으로 처리되므로 gcc의 해석을 거치지 않고 전처리기로 옵션을 전달하고자 할 때 사용된다.
  3. cc1 C 컴파일러 옵션
    • -ansi: ANSI C 표준에 부합하는 소스를 작성하려 할 때 사용된다.
    • std={C std}: C 표준 버전을 지정하고자 할 때 사용된다. 여기에는 c89, c99 등이 있다.
    • -traditional: Traditional C 문법으로 문법을 검사한다.
    • -W, -Wall: -Wall은 모든 모호한 문법에 대한 경고 메시지를 출력한다. -W은 합법적이지만 모호한 코딩에 대해 부가적인 정보를 제공한다. 따라서 두 옵션을 모두 사용하는 것이 좋다. gcc -W -Wall -o main main.c
    • -w: 모든 경고 메시지를 제거한다. 이는 -Wall, -W{options}보다 우선시된다.
    • -O0: 어떤 최적화도 수행하지 않는다. 아무런 최적화 옵션을 주지 않으면 -O0를 준 것과 동일하다.
    • -O1, -O2, -O3: 해당 옵션 적용시 세부적인 사항은 다음의 링크를 통해 확인할 수 있다.
    • -Os: 사이즈 최적화를 수행한다. 이는 임베디드 시스템 같이 메모리 공간이 협소한 곳에서 자주 사용된다.
    • -g: 디버거에 제공하는 디버깅 정보(변수 타입, 전역 심볼명, 주소, 소스 라인정보 등)를 바이너리에 삽입한다. 이는 readelf -a file 명령으로 읽을 수 있으며, .debug_*로 시작되는 섹션들이 이에 해당한다. 이 또한 -O옵션처럼 레벨이 있는데 -g0는 아무런 디버깅 정보를 삽입하지 않고, -g-g2와 동일하다.
  4. as의 옵션
    • -Wa,{as options}: 여기서 {as options}에 들어가는 옵션들은 다음과 같다.
      -al: 어셈블 된 인스트럭션을 보여준다.
      -as: 정의된 심볼을 보여준다.
      -I{path}: include 디렉토리를 지정한다.
      -W, --no-warn: 경고 메시지를 출력하지 않는다.
      -march={architectur}: 해당 어셈블리로 번역한다.
  5. collect2, ld의 옵션
    • -L{library_path}: 라이브러리를 찾을 디렉토리를 지정한다. 기본적으로는 /usr/i386-redhat-linux/lib, /usr/local/lib, /lib, /usr/lib 순으로 찾는다.
    • -l{name_library}: 같이 링크할 라이브러리를 지정한다. 이 옵션은 main()함수가 있는 소스 파일의 뒤에 와야한다. 만약 그렇지 않다면 undefined 참조 오류가 발생할 수 있다. 이는 링커가 아카이브를 찾는 데 있어 항상 main()함수 뒤에서 탐색하기 때문이다.
    • -shared: 공유 라이브러리와 정적 라이브러리가 같이 있을 경우 공유 라이브러리를 우선해서 링크한다. 이는 디폴트 옵션이기도 하다.
    • -static: 정적 라이브러리를 우선하여 링크한다. 이 경우, 파일의 사이즈가 커짐에 유의하자.
    • -nostdlib: 링크 시 표준 C 라이브러리를 사용하지 않는다.
    • nostartfiles: crt1.o, crti.o, ctrbegin.o, crtend.o, crtn.o와 같은 crt 오브젝트 파일들을 링크하지 않는다. 이는 OS, bootloader 같은 프로그램을 컴파일 시 사용된다.
    • -Wl{liker_options}, -Xliner f{liker_option}:
      gcc -o main main.c -Wl,-M,-s: collect2를 거쳐 ld에 -M -s 옵션이 전달된다.
      gcc -o main main.c -Xlinker -M -Xlinker -s: 이 또한 위와 같다.
    • 다음은 사용 가능한 유용한 링커 옵션들이다.
      -s: 실행 파일에서 심볼 테이블 제거
      -x: 출력 파일에서 로컬 심볼 제거
      -e{name}: 시작 심볼을 name 심볼로 사용한다. 기본적으로 시작 심볼은 _start 심볼이다.
      -M: 링커 스크립트와 메모리 맵을 자세히 출력한다.

gcc를 이용한 최적화 컴파일

  1. machine dependent 옵션을 이용한 최적화
    : -march={cpu_type} -mutne={cpu-type} -mcpu={cpu-type}:
    • -march 옵션은 cpu-type으로 지정하는 프로세서의 instructio set으로 코드를 생성한다. 이는 아래에서 설명할 -mtune을 내부적으로 포함하므로 -mtune을 굳이 주지 않아도 무방하다.
    • -mtune 옵션은 인스트럭션을 스케쥴링하거나 정렬할 때 해당 CPU에 최적화되게 스케쥴링 및 정렬한다. 이 때에는 CPU 파이프라인 단계, 각 인스트럭션을 수행하는 데 걸리는 사이클, 버스, 캐시 크기 등을 고려한다.
    • -mcpu 옵션은 -mtune옵션과 거의 동일하다.
    • cpu-type으로 generic을 사용할 수 있는데, 이때 -march에서는 사용할 수 없다. 이는 CPU 종류마다 인스트럭션 셋이 다르기 때문이다.
  2. 프로파일 결과를 이용한 최적화
    : 컴파일한 프로그램을 한 번 더 수행해 프로그램의 동작 특성을 파악하여, 이 정보를 바탕으로 다시 컴파일하며 최적화하는 방법이다.
    1. gcc -O2 -o main main.c -fprofile-generate를 통해 main 바이너리에 프로파일 정보를 출력하는 코드가 추가하여 컴파일한다.
    2. $ ./main을 통해 프로그램을 수행한다.
    3. gcc -O2 -o main main.c -fprofile-use를 통해 프로그램을 수행하며 수집한 정보를 바탕으로 좀 더 최적화하여 바이너리를 생성한다.
  3. 레지스터 활용을 통한 최적화
    • -fforce-mem: 메모리에 있는 값을 레지스터에 로드해 연산을 수행한다. (GCC 4.2 below)
    • -fforce-addr: 메모리 주소 값을 레지스터에 로드 후 연산을 수행한다.
  4. gcc 컴파일 속도 최적화
    • -pipe: 컴파일 과정에서 임시파일을 생성해 전달하지 않고 파이프로 전달해 컴파일 속도를 더 빠르게 한다.

기타

  1. 환경변수를 사용한 gcc 설정
    • COMPILER_PATH: gcc는 COMPILER_PATH에 등록된 전처리기(cc1 -E), 컴파일러(cc1), 어셈블러(as), 링커(collect2, ld)를 찾는다. 따라서 export COMPILER_PATH={directory}를 통해 원하는 전처리기, 컴파일러, 어셈블러, 링커를 선택케 할 수 있다.
    • C_INCLUDE_PATH, CPLUS_INCLUDE_PATH, OBJC_INCLUDE_PATH: 소스 파일을 전처리 할 때 헤더파일을 찾을 디렉토리를 지정하는 환경변수이다.
    • LIBRARY_PATH: 라이브러리를 찾을 디렉토리를 지정하는 환경 변수이다.
  2. specs 파일을 이용한 gcc 설정

+ Recent posts