기계어(=머신 인스트럭션, 기계 명령어)
gcc
- GNU에서 만든 C 컴파일러
- 전처리기, C 컴파일러, 어셈블러, 링커 호출
/usr/bin/gcc는 내부적으로 전처리기(cpp0) 호출해 전처리 과정 수행 → C컴파일러(cc1)호출해 컴파일 → 어셈블러(as) 호출해 오브젝트 코드 만듦 → 링커(ld, collect2) 호출해 오브젝트 코드 링크 → 실행파일
--save-temps => 중간에 생성되는 파일을 저장
- 예제 코드(main.c)
=> 다음 백준 문제 답 코드 https://www.acmicpc.net/problem/10818#include<stdio.h> int main() { int n = 0; int min = 1000000; int max = -1000000; scanf("%d", &n); int arr[n]; for(int i = 0; i < n; i++) { scanf("%d", &arr[i]); } for(int i = 0; i < n; i++) { if(arr[i] < min) { min = arr[i]; } if(arr[i] > max) { max = arr[i]; } } printf("%d %d", min, max); return 0; }
헤더 파일의 삽입
=> C 소스 내의 헤더 파일을 include함
ex) #include <stdio.h>를 만나게 되면 /usr/include 에서 stdio.h 파일이 있는지 찾음(Unix 계열의 경우) → 찾으면 현재 소스 파일의 #include <stdio.h>라고 적힌 부분에 stdio.h 파일의 내용을 읽어서 붙여넣음
매크로 치환 및 적용
=> #define된 부분은 cpp0의 심볼 테이블에 저장 → 심볼 테이블에 들어 있는 문자열과 같은 문자열을 만나게 되면 #define된 내용으로 치환이 일어남
다음 명령어 실행 시 /usr/include에서 헤더파일을 찾는 과정을 볼 수 있음
gcc -E main.c -v -o main.i
컴파일 과정: 어휘 분석 → 구문 분석 → 의미 분석 → 중간 언어 생성 → 최적화 → 목적 코드 생성
어휘 분석
.i 소스가 읽히며 소스를 문법적으로 의미있는 가장 최소 단위(Token)로 나눔 →파서에게 전달
구문 분석
파서가 문법적 오류가 있는지 검사 → 파서 트리(GIMPLE 트리) 만듦
의미 분석
문법상 오류는 없지만 의미상 오류가 있는 것 검사
ex) 선언되지 않은 변수의 사용, 자료형 불일치 등
중간 언어 생성
RTL(Register Transfer Language, 고급언어와 어셈블리 언어의 중간 형태) 생성
최적화
프로그램의 기능은 같지만 크기가 작고 빠르며, 메모리를 작게 사용하게 코드를 변경
중간 코드 최적화: 지역 최적화, 전역 최적화, 루프 최적화
목적 코드 최적화: 메모리보다 레지스트를 사용하게 함, 효율적인 인스트럭션 선택
목적 코드 생성
되도록 재배치가 가능한 어셈블리 언어 생성
컴파일러 실행하기 위해 다음 명령어 실행
gcc -S main.i -v -o main.s
vim main.s
ELF 헤더(엔디안, 운영체제, CPU의 정보,바이너리 내부의 각 구역의 시작 위치와 크기 정보) + .s를 어셈블하여 만들어진 인스트럭션 + .s의 데이터들 + .c를 컴파일한 gcc 컴파일러의 버전 문자열 + 기타 섹션들
Relocation 섹션(재배치 섹션): 재배치 해야하는 심볼의 정보가 들어 있는 위치
gcc -c main.s -v -o main.o
vim main.o
main.o 파일의 ELF 헤더 정보, 심볼테이블, Relocation 섹션 정보는 다음 명령어를 통해 확인할 수 있음
readelf -a main.o
ELF 헤더 정보
심볼테이블
표준 C 라이브러리와 링크
<gcc 옵션>
-static : libc.a와 링크
gcc main.o -o main.exe
./main.exe
옵션 | 기능 |
---|---|
-v | gcc가 컴파일을 어떻게 수행하는지 보임 |
-E / --save-temps | 전처리 과정의 결과를 화면에 보임 / .i과 .s 파일을 현재 디렉토리에 저장 |
-S | cc1으로 전처리된 파일을 어셈블리 파일로 컴파일까지만 수행하고 멈춤 |
-c | 어셈블까지만 수행하고 멈춤(.o 파일 만들어짐) |
옵션 | 기능 |
---|---|
-I | 헤더 파일을 탐색하는 기본 디렉토리를 추가 |
-include [헤더파일경로] | 헤더파일을 소스 내에 추가 |
-D[매크로] / -U [매크로] | 매크로를 외부에서 define / 소스 파일 내에 #undef [매크로] 옵션 추가 |
-D[매크로]=[매크로값] | 소스 내에 #define [매크로][매크로값] 옵션 추가 |
-M / -MM | make를 위한 소스 파일의 모든 종속 항목 출력 / 기본 include 디렉토리에 있는 헤더 파일은 빼고 종속 항목 출력 |
-nostdinc | -I 옵션으로 추가한 디렉토리에서만 헤더 파일 찾음 |
-C | -E 옵션과 함께 사용하며 전처리 과정에서 주석을 제거하지 않음 |
C 언어 종류와 관련된 옵션
옵션 | 기능 |
---|---|
-std=[C 표준들] | 기타 다른 표준을 지정 |
경고 수위 조절
옵션 | 기능 |
---|---|
-W / -Wall / -W -Wall | 합법적이지만 모호한 코딩에 대해 경고 / 모든 모호한 코딩에 대해 경고 / 아주 사소한 모호성에 대해서 경고 |
-w | 모든 경고메세지 제거 |
-Werror | 모든 경고를 컴파일을 중단하는 오류로 취급 |
옵션 | 기능 |
---|---|
-O0 | 최적화 수행하지 않음 |
-O2 | 최적화 레벨2, 거의 대부분의 최적화 수행 |
-Os | 사이즈 최적화 |
옵션 | 기능 |
---|---|
-g | gdb에게 제공하는 정보를 바이너리에 삽입 => 디버깅 시에 소스를 보며 디버깅 가능 |
-pg | 프로파일을 위한 코드 삽입 |
-Wa,[as 옵션들] 형식으로 사용
옵션 | 기능 |
---|---|
-al | 어셈블된 인스트럭션 보임 |
-as | 정의된 심볼 보임 |
-I[패스] | include 디렉토리 지정 |
옵션 | 기능 |
---|---|
-L[라이브러리 디렉토리] | 라이브러리를 찾을 디렉토리 저장 |
-I [라이브러리 이름] | 같이 링크할 라이브러리 지정 |
-shared | 공유라이브러리를 우선하여 링크 |
-static | 정적라이브러리를 우선하여 링크 |