티스토리 뷰

System/Linux

gcc 컴파일러

Tribal 2015. 11. 28. 15:08

GCC(GNU Compiler Collection) : GNU 컴파일러 묶음

gcc(GNU C Compiler) : GNU C 컴파일러



gcc의 장점 : 여러 옵션과 기능이 풍부, 수 많은 CPU 아키텍처를 지원



gcc는 실제 컴파일 과정을 담당하지 않고 전처리기와 C 컴파일러, 어셈블러, 링커를 각각 호출하는 역할을 수행하는 컴파일러 드라이버이다.

gcc 컴파일 과정 : 전처리기 -> 컴파일러 -> 어셈블러 -> 링커

 - 전처리기는 프로그램이 돌아가는 과정을 가져오는 단계(헤더 파일 합치고 #ifdef같은거 정리)

 - 컴파일러는 프로그램을 만들 준비를 함(어휘, 구문, 의미 분석 및 중간언어 생성, 최적화)
   즉, 소스코드 확인(잘못된 점이나 소스 코드 효율성 등) 

 - 어셈블러는 어셈블리어로 변환하기 위한 단계

 - 링커는 라이브러리 함수를 가져다 사용하면 연결해야 하므로 라이브러리와 링크하는 역할


gcc 컴파일 순서

 1. 전처리기인 cpp0 또는 cc1 -E 명령을 호출                                       -> .i 파일 생성

 2. 진짜 C 컴파일러인 cc1 호출                                                    -> .s 파일 생성

 3. 어셈블러 as 호출                                                              -> .o 파일 생성

 4. collect2를 호출하고 collect2가 내부적으로 링커인 ld를 호출해 라이브러리와 링크    -> 실행 파일 생성


위의 모든 단계를 gcc -v -save -temps -o 프로그램명 c소스코드 를 통해 확인할 수 있다.

(옵션 -v는 컴파일되는 과정을 화면으로 출력, -save-temps는 컴파일 과정 중 생성되는 파일 유지)


오류의 원인을 알 수 없는 에러가 발생할 경우 .i 파일을 통해 문제를 찾을 수 있다.

운영체제같은 시스템 아키텍처의 특징을 많이 타는 프로그램 제작 시 매번 컴파일할 때마다 어셈블리 파일(.s)을 확인할 필요가 있다.


전처리 과정

 - 헤더 파일의 삽입

 - 매크로 치환 및 적용

정리 : 특정 심볼을 만날 경우 이것과 매칭되어 있는 내용의 소스코드 또는 문자열 등을 전부 치환함, 선택적 구문의 경우는 선택에 맞춰서 내용에 추가한다.


어셈블리 소스 파일로 컴파일 과정

 1. 올바른 소스 코드 작성인지 분석(어휘, 구문, 의미), GIMPLE 트리 생성

    GIMPLE 트리 : 소스 코드를 트리 형태로 표현한 자료구조

 2. GIMPLE 트리를 이용한 아키텍처와 비종속적인 최적화 후 RTL을 생성

    RTL(Register Transfer Laguage) : 고급 언어와 어셈블리의 언어의 중간 형태, Return to Libc 와 전혀 다르다.

 3. 아키텍처와 종속적인 최적화 후 어셈블리 코드 생성

    이 때, 아키텍처와 종속적인 최적화라는 것은 레지스터 할당이나 인스트럭션 스케줄링 같은 아키텍처에 의존하는 작업을 말한다.
    반대로 비종속적인 최적화는 아키텍처에 구애 받지 않고 할 수 있는 기능은 동일하지만 코드는 더 효율적인 최적화를 말한다,


기계어 코드 생성

 바이너리 파일의 구조를 알아야 커널이 실행 파일을 메모리에 올리고 실행할 수 있기 때문에 각 부분을 나눠서 저장해야한다. 그래서 윈도우에서 PE 구조를 사용하는 것과 같이 유닉스/리눅스 계열 시스템은 ELF 구조를 사용한다. 이 ELF 구조는 어셈블러인 as에 의해 가지게 되기 때문에 .o 파일 부터는 ELF 구조를 확인할 수 있다.

 - readelf 명령어로 ELF 구조에 맞춰 프로그램의 정보를 확인할 수 있다.


링킹

 직적 프로그래밍을 해본 사람이라면 당연히 C로 코딩을 한다고 하였을 때, #include <stdio.h> 같은 구문을 사용하여 표준 입출력 함수(printf나 scanf 등)같은 직접 작성한 적이 없는 함수를 불러와서 사용한 적이 당연히 있을 것이다. 직접 작성을 하지 않았는데 불러와서 사용할 수 있다는 것에 의문점을 가져본 적이 있는지가 매우 중요하다. 이런 것을 가능하게 하는 것이 링킹이라는 작업이다.

 

 링킹은 코드에 사용되지만 직접 작성되지 않은 함수를 불러오기 위해서 하는 작업이다. 이 작업을 위해서는 라이브러리라는 것이 필요하다. 


 라이브러리란 프로그래밍에 사용할 수 있게 미리 만들어진 함수나 변수들의 묶음을 말하는데 이것은 미리 컴파일 되어 있어서 링크로 불러올 경우 바로 사용할 수 있다. 라이브러리는 3가지로 동적 라이브러리, 정적 라이브러리와 공유 라이브러리로 나눠진다,


- 동적 라이브러리

  프로그램이 필요할 때 메모리에 프로그램과 함께 적재하여 필요한 경우 사용할 수 있는 라이브러리이다.

- 정적 라이브러리

  프로그램에 라이브러리 자체를 적재하여 사용하는 라이브러리이다. 프로그램 내부에 라이브러리가 전부 들어가기 때문에 무겁다.

- 공유 라이브러리

  프로그램이 필요할 때 바로바로 사용할 수 있는 공유된 라이브러리이다. 동적 라이브러리와 비슷하지만 불러오는 시기의 차이가 다르다. 공유 라이브러리는 응용프로그램 시작시 자동으로 불러와서 참조하지만 동적은 응용프로그램에서 요청 시 불러온다.


실행파일 완성

 링킹까지 끝나면 실행 프로그램에 대한 모든 준비가 끝나서 하나의 실행파일이 완성되게 된다. 실행할 경우 .o 에서 만들어진 ELF 구조를 바탕으로 메모리에 로드되고 Code 영역에 적재된 실행가능한 인스트럭션을 통해 호출 또는 참조를 하여 동작하게 된다.



gcc 에 대한 자세한 내용 : http://idkwim.tistory.com/79

라이브러리에 대한 자세한 내용 : http://blog.naver.com/PostView.nhn?blogId=xogml_blog&logNo=130138049704

'System > Linux' 카테고리의 다른 글

ropgadget find  (0) 2016.01.06
32bit와 64bit의 차이  (0) 2015.12.29
포장 함수 gets, fgets의 임시 버퍼  (0) 2015.12.28
쉘 스크립트  (0) 2015.11.30
Xwindow 화면 전환 방법  (0) 2015.11.28
댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31