컴파일 경로, 옵션

markyang92·2024년 11월 14일
0

compile_guide

목록 보기
1/1
post-thumbnail

기본 'include' 경로 확인

$ gcc -v -E -x c /dev/null
...
#include "..." search starts here:
#include <...> search starts here:
 /usr/lib/gcc/x86_64-linux-gnu/11/include
 /usr/local/include
 /usr/include/x86_64-linux-gnu
 /usr/include
... 

기본 '라이브러리' 경로 확인

$ gcc -print-search-dirs

$ ld --verbose | grep SEARCH_DIR

$ ld --verbose -L${PWD} | grep SEARCH_DIR | tr -s ' ;' \\n
SEARCH_DIR("=/usr/local/lib/x86_64-linux-gnu")
SEARCH_DIR("=/lib/x86_64-linux-gnu")
SEARCH_DIR("=/usr/lib/x86_64-linux-gnu")
SEARCH_DIR("=/usr/lib/x86_64-linux-gnu64")
SEARCH_DIR("=/usr/local/lib64")
SEARCH_DIR("=/lib64")
SEARCH_DIR("=/usr/lib64")
SEARCH_DIR("=/usr/local/lib")
SEARCH_DIR("=/lib")
SEARCH_DIR("=/usr/lib")
SEARCH_DIR("=/usr/x86_64-linux-gnu/lib64")
SEARCH_DIR("=/usr/x86_64-linux-gnu/lib")

환경변수

COMPILER_PATH

  • gcc는 내부적으로 전처리기(cc1 -E), 컴파일러(cc1), 어셈블러(as), 링커(collect2 or ld)를 호출할 때,
    COMPILER_PATH에 등록된 디렉토리에서 먼저 찾는다.
    그래서, COMPILER_PATH에 값을 설정해 특정 전처리기, 컴파일러, 어셈블러, 링커등을 사용해 컴파일 할 수 있다.
# cc1을 찾을 디렉토리로 /tmp, /root를 설정
$ export COMPILER_PATH=/tmp:/root

C_INCLUDE_PATH, CPLUS_INCLUDE_PATH, OBJC_INCLUDE_PATH

  • 소스 파일을 전처리할 때, 헤더 파일을 찾을 디렉토리를 지정하는 환경 변수
$ export C_INCLUDE_PATH=/tmp:/root

LIBRARY_PATH

  • 라이브러리를 찾을 디렉토리를 지정하는 환경변수
    collect2 또는 ld에게 LIBRARY_PATH에 등록된 디렉토리들을 -L 옵션으로 넘긴다.
$ export LIBRARY_PATH=/tmp:/root
  • 현재 ~/test 디렉토리에서 간단한 빌드 수행 해본다.
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/11/include-fixed"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/11/../../../../x86_64-linux-gnu/include"
#include "..." search starts here:
#include <...> search starts here:
 /home/dhyang/test       <---------- 인클루드 경로 확인
 /usr/lib/gcc/x86_64-linux-gnu/11/include
 /usr/local/include
 /usr/include/x86_64-linux-gnu
 /usr/include
End of search list.
GNU C17 (Ubuntu 11.4.0-1ubuntu1~22.04) version 11.4.0 (x86_64-linux-gnu)
        compiled by GNU C version 11.4.0, GMP version 6.2.1, MPFR version 4.1.0, MPC version 1.2.1, isl version isl-0.24-GMP

GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: 50eaa2331df977b8016186198deb2d18
COLLECT_GCC_OPTIONS='-v' '-I' '/home/dhyang/test' '-L/home/dhyang/test' '-o' 'like' '-mtune=generic' '-march=x86-64'
 as -v -I /home/dhyang/test --64 -o /tmp/cczrOCzD.o /tmp/cc3XQHc0.s
GNU assembler version 2.38 (x86_64-linux-gnu) using BFD version (GNU Binutils for Ubuntu) 2.38
COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/11/:/usr/lib/gcc/x86_64-linux-gnu/11/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/11/:/usr/lib/gcc/x86_64-linux-gnu/


<--- LIBRARY_PATH에는 -L로 준 경로가 뜨지 않는다. --->
LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/11/:\  <--- LIBRARY_PATH에는 -L로 준 경로가 뜨지 않는다.
/usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/:\
/usr/lib/gcc/x86_64-linux-gnu/11/../../../../lib/:\
/lib/x86_64-linux-gnu/:\
/lib/../lib/:\
/usr/lib/x86_64-linux-gnu/:\
/usr/lib/../lib/:\
/usr/lib/gcc/x86_64-linux-gnu/11/../../../:/lib/:/usr/lib/



COLLECT_GCC_OPTIONS='-v' '-I' '/home/dhyang/test' '-L/home/dhyang/test' '-o' 'like' '-mtune=generic' '-march=x86-64' '-dumpdir' 'like.'
 /usr/lib/gcc/x86_64-linux-gnu/11/collect2 \
 -plugin /usr/lib/gcc/x86_64-linux-gnu/11/liblto_plugin.so \
 -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/11/lto-wrapper \
 -plugin-opt=-fresolution=/tmp/cclVXe1m.res \
 -plugin-opt=-pass-through=-lgcc \
 -plugin-opt=-pass-through=-lgcc_s \
 -plugin-opt=-pass-through=-lc \
 -plugin-opt=-pass-through=-lgcc \
 -plugin-opt=-pass-through=-lgcc_s \
 --build-id --eh-frame-hdr \
 -m elf_x86_64 --hash-style=gnu --as-needed \
 -dynamic-linker /lib64/ld-linux-x86-64.so.2 \
 -pie -z now -z relro \
 -o like \
 /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/Scrt1.o \
 /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/crti.o \
 /usr/lib/gcc/x86_64-linux-gnu/11/crtbeginS.o \
 -L/home/dhyang/test \  <ㅡㅡㅡㅡ 여기에서 -L 옵션 넣은걸 확인할 수 있다. ㅡㅡㅡㅡㅡㅡ>
 -L/usr/lib/gcc/x86_64-linux-gnu/11 \
 -L/usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu \
 -L/usr/lib/gcc/x86_64-linux-gnu/11/../../../../lib \
 -L/lib/x86_64-linux-gnu \
 -L/lib/../lib -L/usr/lib/x86_64-linux-gnu \
 -L/usr/lib/../lib \
 -L/usr/lib/gcc/x86_64-linux-gnu/11/../../.. /tmp/cczrOCzD.o \
 -lprint -lgcc \
 --push-state --as-needed \
 -lgcc_s --pop-state -lc -lgcc --push-state \
 --as-needed -lgcc_s \
 --pop-state /usr/lib/gcc/x86_64-linux-gnu/11/crtendS.o \
 /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/crtn.o
COLLECT_GCC_OPTIONS='-v' \
'-I' '/home/dhyang/test' \
'-L/home/dhyang/test' '-o' 'like' '-mtune=generic' '-march=x86-64' '-dumpdir' 'like.'


컴파일 옵션

$ gcc -v -I/usr/local/include -DDEBUG -Wall -W -O2 -L/usr/local/lib -o like like.c -m

규칙 1. -[f|W|m][옵션] 설정. -[f|W|m]no-[옵션]은 해제

  • -fcse-skip-blocks: 옵션 설정
  • -fno-cse-skip-blocks: 옵션 끔
  • -Wunused-function
  • -Wno-unused-function

규칙 2. 동일 종류 옵션은 최종 옵션만 옵션 변수에 설정 된다.


규칙 3. 그룹 옵션 존재

  • 그룹 옵션들은 그룹 내 포함된 서브 옵션을 지정할 때, 순서 영향을 받는다.
    • -O1 옵션을 주면 -fdefer-pop, -fdelayed-branch.. 등 옵션 변수 값이 모두 1로 설정된다.
    $ gcc -o like like.c -O1 -fno-defer-pop
    시, -O1 옵션에 포함되는 모든 옵션 변수 값은 1로 설정되고, int flag_defer_pop 변수의 값만 0으로 설정된다.
    $ gcc -o like like.c -fno-defer-pop -O1
    시, 마지막 -O1의 영향을 받기 때문에, 모든 그룹 옵션 변수 값이 1로 설정된다.

gcc 옵션

-v

  • gcc가 컴파일 과정을 어떻게 수행하는지 화면에 출력

-E

  • 전처리 과정의 결과를 화면에 보이는 옵션
$ gcc -E like.c
  • like.c를 전처리한 후, 결과가 화면에 출력된다.
  • 하지만, -save-temps 옵션을 사용하여 like.i 파일을 읽어보는 것이 더 좋은 방법

-S

  • 어셈블리 파일만 생성하고 컴파일 과정을 멈춘다.
$ gcc -S like.c
  • like.s 어셈블리 파일을 얻을 수 있다.

-c

  • 오브젝트 파일까지만 생성하고 컴파일 과정을 멈춘다.
    • 따라서 링킹 과정은 수행되지 않는다.

-save-temps

  • 컴파일 과정에서 생성되는 중간 파일인 전처리 파일(*.i), 어셈블리 파일(*.s), 오브젝트 파일(*.o)을 지우지 않고 현재 디렉토리에 저장한다.

전처리기(cc1 -E) 옵션

-I<위치>

  • -I<위치>: include경로를 추가한다.
$ gcc -I/root/include -o like like.c
  • 먼저 -I<위치>로 찾고 그다음 시스템 헤더 디렉토리를 찾는다.
  • #include <헤더파일>
    • /usr/local/include
    • /usr/lib/gcc/x86_64-linux-gnu/11/include
    • /usr/include
      순으로 찾는다.
  • #include "헤더파일"
    • 현재디렉토리
    • /usr/local/include
    • /usr/lib/gcc/x86_64-linux-gnu/11/include
    • /usr/include
      순으로 찾는다.

-include <헤더파일 경로>

  • 헤더파일을 소스 내에 추가할 때 사용한다.
  • 소스 제일 처음에 #include "헤더파일경로"로 한 것과 동일하다.
  • 많이 사용되지 않는다.

-D<매크로>

  • -D<매크로>: #define <매크로> 한 것과 동일
$ gcc -D__ELF__ -o like like.c



#define __ELF__ 와 동일 역할

-D<매크로>=<매크로값>

  • #define <매크로> <매크로 값> 추가한 것과 동일
#include <stdio.h>

int main()
{
	printf("package : %s\n", PACKAGE);
    return 0;
}
$ gcc -DPACKAGE=\"likeU\" -o like like.c
$ ./like
package : likeU

-U<매크로>

  • -U-D와 반대로, #undef <매크로> 옵션을 추가한 것과 동일하다.

-M, -MM

  • Makefile을 작성할 때, 유용한 종속 항목 리스트를 출력한다.
    • 종속 항목: 타겟이 컴파일 되기 위해 필요한 항목을 의미
      • like 실행 파일은 like.c 소스 파일이 필요하고, like.cstdio.h 파일이 필요하다.
      • stdio.h가 include하고 있는 여러 헤더 파일도 필요하다.
    • 이런 종속 관계를 make의 기술 파일에서 사용하는 형식으로 정리해 출력한다.
  • -M: make를 위한 소스 파일의 모든 종속 항목 출력
user3 at ~/test ❯ gcc -M like.c
like.o: like.c /usr/include/stdc-predef.h /usr/include/stdio.h \
 /usr/include/x86_64-linux-gnu/bits/libc-header-start.h \
 /usr/include/features.h /usr/include/features-time64.h \
 /usr/include/x86_64-linux-gnu/bits/wordsize.h \
 /usr/include/x86_64-linux-gnu/bits/timesize.h \
 /usr/include/x86_64-linux-gnu/sys/cdefs.h \
 /usr/include/x86_64-linux-gnu/bits/long-double.h \
 /usr/include/x86_64-linux-gnu/gnu/stubs.h \
 /usr/include/x86_64-linux-gnu/gnu/stubs-64.h \
 /usr/lib/gcc/x86_64-linux-gnu/11/include/stddef.h \
 /usr/lib/gcc/x86_64-linux-gnu/11/include/stdarg.h \
 /usr/include/x86_64-linux-gnu/bits/types.h \
 /usr/include/x86_64-linux-gnu/bits/typesizes.h \
 /usr/include/x86_64-linux-gnu/bits/time64.h \
 /usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h \
 /usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h \
 /usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h \
 /usr/include/x86_64-linux-gnu/bits/types/__FILE.h \
 /usr/include/x86_64-linux-gnu/bits/types/FILE.h \
 /usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h \
 /usr/include/x86_64-linux-gnu/bits/stdio_lim.h \
 /usr/include/x86_64-linux-gnu/bits/floatn.h \
 /usr/include/x86_64-linux-gnu/bits/floatn-common.h
  • -MM: 기본 include 디렉토리에 있는 헤더 파일은 빼고 종속 항목을 출력한다.
user3 at ~/test ❯ gcc -MM like.c
like.o: like.c

-nostdinc

  • 디폴트 include 디렉토리에서 헤더 파일을 탐색하지 않고, -I 옵션으로 추가한 디렉토리에서만 헤더파일을 찾는다.

-C

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

-Wp,<옵션들>

  • 전처리기(cc1 -E), C 컴파일러 드라이버(gcc)의 옵션이 서로 동일하다면, 해당 옵션은 C 컴파일러 드라이버의 옵션으로 처리된다.
  • gcc가 옵션을 해석해 cc1 -E, cc1, as, collect2에 옵션을 전달하기 때문이다.
  • gcc의 해석을 거치지 않고 바로 전처리기의 옵션으로 전달하고 싶을 때, -Wp 옵션을 사용한다.
  • -Wp,-DDEBUG,-I/usr/include,-M으로 사용하면 전처리기로 -DDEBUG -I/usr/include -M 옵션이 바로 전달된다.

컴파일러(cc1) 옵션

-W


-W

  • 합법적이지만 모호한 코딩에 대해 경고

-Wall

  • 모든 모호한 코딩에 대해 경고
  • 따라서, -W -Wall 옵션을 넣으면, 아주 사소한 모호성에 대해서도 경고가 발생한다.
  • -Wno-<제거할 경고>
    • -Wno-unused-function

-Werror

  • 모든 경고는 컴파일을 중단하는 오류로 취급한다.

-w

  • 모든 경고 메시지를 제거한다.
  • -w 옵션은 -Wall, -W<옵션명> 등 경고 옵션들보다 우선시 되므로, 다른 경고 옵션을 붙이더라도 경고가 출력되지 않는다.

-pedantic

  • ANSI C89 표준에서 요구하는 모든 경고 메시지를 표시한다.
  • ANSI 표준 문법으로 소스코드를 작성할 때는 -ansi 옵션과 함께 반드시 -pedantic 옵션도 같이 사용

-pedantic-erros

  • ANSI C89 표준에서 요구하는 모든 경고를 오류로 취급한다.

-O<레벨>


-O0

  • 어떤 최적화도 수행하지 않는다.
  • 소스 내에 함수 선언시, inline 키워드를 사용해 인라인 함수를 명시하더라도, 인라인 함수가 사용된 데 대해 인라인 확장X
  • 컴파일 시, 아무 최적화 옵션을 붙이지 않으면 -O0 옵션을 준것과 동일하다.

-Os

  • 사이즈 최적화를 수행한다.
  • -O2 옵션 중에서 사이즈를 늘리는 최적화만 제거하고 나머지 최적화는 다 수한다.

-g

  • 디버깅 정보를 바이너리에 삽입한다.
  • 디버깅을 위해 -g 옵션을 주고 컴파일할때는 최적화를 하지 말자.

-g0

  • 디버깅 정보를 삽입하지 않음

-g2

  • -g와 동일

-g3

  • 디버깅 정보를 가장 많이 삽입한다.

-pg

  • 프로파일을 위한 코드를 삽입한다.
    중단부의 SSA 최적화 이전에 인터프로시저 분석(IPA) 과정에서 삽입된다.
  • -pg 옵션을 사용해 컴파일한 프로그램이 종료하면, 프로파일 정보가 gmon.out 파일에 저장된다.
  • 이후 gprofgmon.out 파일을 분서갷 어떤 함수가 얼마나 호출되었고, 시간은 얼마나 걸렸는지 확인할 수 있다.

-ansi

  • 기본적으로 gcc는 GNU C89 문법으로 문법을 체크한다.
  • -ansi는 ANSI C 표준(ISO C89) 소스를 작성하려할 때, 사용하는 옵션이다.

-std=[C표준]

  • -std=c89
  • -std=c99
  • -std=gnu89
  • -std=gnu99

-fno-asm

  • gnu89 문법에서 지원하는 asm, inline, typeof 키워드를 사용하지 않는다.
  • gcc는 기본적으로 gnu89 문법으로 체크하는데
    • asm은 C 소스내에 어셈블리 코드를 삽입
    • inline, 인라인 함수 지정
    • typeof, 변수의 타입을 알아볼 때 사용
    • 이러한 키워드들은 ANSI C89 표준에서는 지원하지 않기 때문에, ANSI C89에서 지원하는 __asm__, __inline__, __typeof__ 키워드를 사용해야한다.
  • gnu89 문법을 바탕으로 asm, inline, typeof 키워드를 사용하지 않으려 할 때, -fno-asm 옵션 사용

링커 옵션

  • 링커인(ld)에게 넘겨 링킹 과정을 수행한다.

-L<위치>

  • 라이브러리를 찾을 디렉토리를 지정한다.
    기본적으로 찾는 디렉토리가 아니라면, -L옵션으로 따로 지정해야한다.

-o <파일>


-l<라이브러리>

  • 링크할 라이브러리 지정
  • 다른 컴파일 옵션들은 컴파일 명령에서 소스파일 앞에오나 뒤에오나 상관 없지만,
    -l 옵션은 소스 파일 뒤에 와야한다.
    특히, main() 함수가 있는 파일보다는 무조건 뒤에 와야한다.

-Wl,<option>

  • -Wl,<옵션>은 gcc를 거치지 않고 바로 링커에 옵션을 전하려 할 때 사용한다.
$ gcc -o like like.c -Wl,-M,-s
  • 링커에 -M -s옵션이 전달된다.

-Wl,-s

  • 실행 파일에서 심볼테이블 제거

-Wl,-x

  • 출력 파일에 로컬 심볼 제거

-Wl,-e<name>

  • 시작 심볼을 name 심볼로 사용한다.
$ gcc -Wl,-emain -o like like.c
  • 시작 심볼로 main 심볼을 사용한다.
  • 기본적으로 시작 심볼은 _start 심볼이다.

-Wl,-M

  • 링커 스크립트와 메모리 맵을 자세하게 출력한다.
  • 때문에, 프로그램의 메모리 구조를 자세하게 알 수 있다.

-Xlinker option

  • gcc가 인식하지 못하는 특정 링커에 대해 옵션을 지정할 때 사용한다.
$ gcc -o like like.c -Xlinker -M -Xlinker -s
  • 링커에 -M -s 옵션이 전달된다.

-rdynamic


-shared

  • 공유 라이브러리와 정적 라이브러리가 같이 있을 경우, 공유 라이브러리를 우선 링크
  • 기본적으로 아무 옵션을 주지 않아도 공유 라이브러리를 우선 링크한다.

-static

  • 정적라이브러리를 우선 링크한다.

-nostdlib

  • 링크 시, 표준 C라이브러리를 사용하지 안흔ㄴ다.
  • OS, 부트로더, 디바이스 드라이버와 같이 표준 C라이브러리를 사용하지 않는 프로그램 컴파일 때, 사용한다.

-nostartfiles

  • crt1.o, crti.o, crtbegin.o, crtend.o, crtn.o등과 같은 crt 오브젝트 파일들을 링크하지 않는다.
  • OS, 부트로더와 같은 프로그램을 컴파일할 때 사용한다.

어셈블러 옵션 (as)

  • as는 아키텍처마다 각기 다른 어셈블러가 사용되기 때문에, gcc에서 지원하는 옵션은 거의 없고
    해당 어셈블러에게 옵션을 바로 넘겨주기 위한 옵션들이 주로 있다.

-Wa,<option>


-march=cpu-type -mtune=cpu-type -mcpu=cpy-type

  • -march: cpu-type으로 지정하는 프로세서의 인스트럭션 셋으로 구성된 코드를 생성
    • -mtune, -mcpu는 cpu-type에 좀 더 최적화 된 코드를 생성한다.
  • -march=pentium4옵션을 주면, 펜티엄4에서 지원하는 인스트럭션 셋으로 구성된 코드를 생성한다.
    • 그래서 MMX, SSE, SSE2의 인스트럭션들을 사용할 수 있다.
      때문에 MMx, SSE, SSE2 인스트럭션을 지원하지 않는 오래된 펜티엄에서는 -march=pentium4 옵션을 주고 컴파일한 코드가 동작하지 않을 수 있다.
  • -mtune: 인스트럭션을 스케줄링하거나 정렬할 때, -mtune 옵션으로 지정해 준 CPU에 최적화되게 스케줄링하고 정렬한다.
    • 이때 고려되는 것에는 각 CPU의 파이프라인 단계와 각 인스트럭션을 수행하는데 걸리는 사이클, 버스와 캐스 크기등이 있다.
    • 쉽게 예를 들어, 펜티엄3는 파이프라인 단계가 10단계이고, 펜티엄4는 파이프라인 단계가 20단계인데, CPU 내부 멈춤(stall)을 줄이기 위해 인스트럭션의 순서를 섞을 때, -mtune 옵션으로 지정한 CPU에 최적화되게 인스트럭션을 섞는다.
      • 인스트럭션 스케줄링: CPU 내부의 특정 자원이 사용 가능하지 않아, CPU가 멈추는 stall을 줄이기 위해 인스트럭션 수행 순서를 바꾸는 것을 말한다.
      • 인스트럭션 정렬: 함수나 레이블이 시작하는 주소를 해당 CPU의 버스 너비와 캐시의 크기에 최적화되게 맞추는 것
    • -mtune 옵션은 이런 CPU의 특성을 반영해 최적화된 코드를 생성하는 역할만 담당한다.
      따라서, -mtune 옵션이 인스트럭션 셋에 영향을 주는 것은 아니기 때문에, -mtune=pentium4 옵션으로 컴파일했더라도, i386 인스트럭션 셋을 사용했다면 오래된 펜티엄에서도 동작하는 코드를 생성할 수 있다.
      마찬가지로, -mtune=athlon-xp 옵션으로 컴파일했다고 해서, 펜티엄에서 동작하지 않는 것도 아니다. 단지 펜티엄에서 좀 비효율 적이게 될 뿐이다.
  • -mcpu: -mtune과 거의 동일
  • -march 옵션은 -mtune 옵션을 내부적으로 포함
    • -march=pentium4 줬다면, 굳이 -mtune=pentium4를 명시하지 않아도 동일한 결과를 얻을 수 있다.
  • -march-mtune의 cpu-type을 다르게 지정하면
    • 생성된 코드의 인스트럭션 셋: -march의 cpu-type
    • 스케줄링, 코드 정렬: -mtune의 cpu-type
  • -march설정 X, -mtune만 설정
    • 인스트럭션 셋: 모든 x86 아키텍처에 공통 된 것
    • 스케줄링, 코드 정렬: -mtune의 cpu-type
profile
pllpokko@alumni.kaist.ac.kr

0개의 댓글