기본 'include' 경로 확인
$ gcc -v -E -x c /dev/null
...
/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
에 값을 설정해 특정 전처리기, 컴파일러, 어셈블러, 링커등을 사용해 컴파일 할 수 있다.
$ 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"
/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. 그룹 옵션 존재
gcc 옵션
-v
- gcc가 컴파일 과정을 어떻게 수행하는지 화면에 출력
-E
$ gcc -E like.c
like.c
를 전처리한 후, 결과가 화면에 출력된다.
- 하지만,
-save-temps
옵션을 사용하여 like.i
파일을 읽어보는 것이 더 좋은 방법
-S
- 어셈블리 파일만 생성하고 컴파일 과정을 멈춘다.
$ gcc -S like.c
-c
- 오브젝트 파일까지만 생성하고 컴파일 과정을 멈춘다.
-save-temps
- 컴파일 과정에서 생성되는 중간 파일인 전처리 파일(
*.i
), 어셈블리 파일(*.s
), 오브젝트 파일(*.o
)을 지우지 않고 현재 디렉토리에 저장한다.
전처리기(cc1 -E
) 옵션
-I<위치>
$ 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.c
는 stdio.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
옵션을 넣으면, 아주 사소한 모호성에 대해서도 경고가 발생한다.
-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
-g3
-pg
- 프로파일을 위한 코드를 삽입한다.
중단부의 SSA 최적화 이전에 인터프로시저 분석(IPA) 과정에서 삽입된다.
-pg
옵션을 사용해 컴파일한 프로그램이 종료하면, 프로파일 정보가 gmon.out
파일에 저장된다.
- 이후
gprof
로 gmon.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
-Wl,-s
-Wl,-x
-Wl,-e<name>
$ gcc -Wl,-emain -o like like.c
- 시작 심볼로
main
심볼을 사용한다.
- 기본적으로 시작 심볼은
_start
심볼이다.
-Wl,-M
- 링커 스크립트와 메모리 맵을 자세하게 출력한다.
- 때문에, 프로그램의 메모리 구조를 자세하게 알 수 있다.
-Xlinker option
- gcc가 인식하지 못하는 특정 링커에 대해 옵션을 지정할 때 사용한다.
$ gcc -o like like.c -Xlinker -M -Xlinker -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