105 – GCC 실행하기

gcc의 실행 원리를 이해하기 위해 우리는 아주 간단한 예제를 다시 불러오기로 하였다. 바로 “Hello World”를 출력하는 예제이다. file.c 파일을 만들고 hello world를 출력하는 프로그램을 다시 짜보겠다.

20170606_222213

이 파일을 저장한 다음에 gcc를 이용하여 파일을 컴파일하여 보겠다.

[gcc “소스파일이름”] 과 같은 구조를 이용하여 gcc를 이용하여 컴파일을 진행하였고, 진행 후 프롬프트($)가 그대로 나오면 컴파일이 성공한 것이다. 그럼 이제 컴파일 후에 어떤 파일이 생성되었는지를 확인하자.

a.out이라는 파일이 추가로 생성된 것을 볼 수 있는데, a.out이 바로 실행 가능한 링킹까지 전부 완료된 파일이다. 컴파일 도중에 만들어지는 파일은 디스크에 저장되지 않는 파일도 있고, 않을 수도 있다고도 이전 글에서 작성하였는데, 아무 옵션이 없어서 그냥 바로 실행 파일까지 만든 것이다.

이 파일을 실행하기 위해 a.out을 입력하여 보자.

우리가 전에 실행하던 방식(./a.out)과는 다르게 실행하였는데, 적고 싶은 내용이 있어서 좀 정리했다.

위와 같이 명령을 찾을 수 없다는 오류가 발생하는 것을 볼 수 있다. 이는 a.out이 저장된 디렉토리를 path로 설정하지 않았기 때문이다. 터미널에서 실행되는 명령어들은 해당 명령어로 된 실행 파일을 실행하는 것인데, 그 실행 파일들이 path라는 터미널의 명령어 실행 쉘의 path변수에 지정되어 있다. 그렇기 때문에 우리가 직접 만든 a.out은 터미널 명령 쉘에 등록되지 않은 명령어로 인식되었기 때문에 명령을 실행할 수 없습니다라는 오류를 나타내는 것이다.

그럼 우리가 전에 쓰던 “./a.out”이라는 것은 어떤 것인가? 바로 현재 디렉토리 위치 정보인 “./”명령어와 같이 실행된 것이다. 그래서 현재 디렉토리임을 확인하는 명령과 함께 현재 디렉토리에 있는 a.out을 실행한 것이다.

지금까지 우리가 C언어를 배우면서 예제 코드를 컴파일하기 위한 gcc의 실행 법과 실행을 위해 항상 적어왔던 ./a.out이 어떤 것인지를 알 수 있었을 것이다.

104 – GCC의 파일 확장자에 따른 처리법

gcc는 파일 확장자에 따라 처리 방법을 달리 한다. 소스코드 파일을 식별하기도 하고, 단계별로 처리된 파일임을 식별할 때 이용하기도 한다. 그래서 이전글에서 각각의 과정에 따라 파일명을 적은 것도 각 과정에 따라 확장자가 다르게 나오는 것과 그에 따라 과정이 나뉘는 걸 보여주기 위해 설명하였다. C 프로그래밍을 위해서 알아둬야 할 것들만 간단하게 정리를 해보겠다.

  • 확장자 | 종류 | 처리 방법
  • .c | C 소스 파일 | gcc로 전처리, 컴파일, 어셈블, 링크
  • .C .CC | C++ 소스 파일 | g++로 전처리, 컴파일, 어셈블, 링크
  • .i | 전처리된 C 파일 | gcc로 컴파일, 어셈블, 링크
  • .ii | 전처리된 C++ 파일 | g++로 컴파일, 어셈블, 링크
  • .s | 어셈블리어로 된 파일 | 어셈블, 링크
  • .S | 어벨블리어로 된 파일 | 전처리, 어셈블, 링크
  • .o | 오브젝트 파일 | 링크
  • .a .so | 컴파일된 라이브러리 파일 | 링크

103 – GCC의 동작 과정

gcc는 C, C++, objective-c, fortran 등 여러 언어를 컴파일 할 수 있다. 1999년부터 gcc는 자유 소프트웨어 재단에서 지원하는 컴파일러의 집합을 의미하기 때문이다. (그 전까지는 GNU C Compiler라고 지정하였었다.) 그 와중에도 우리는 C언어를 컴파일하기 위해 이용한다. 그래서 이 글도 C언어를 기준으로 설명하겠다.

gcc를 컴파일러라고 일반적으로 이야기한다. 그런데 정확히 말하면 gcc는 소스 파일을 이용해 실행 파일을 만들 때까지 필요한 다음과 같은 프로그램을 차례로 실행하는 일종의 툴이다.

cpp | 전처리기
cc1 | 컴파일러
as | 어셈블러
ld |링커

그리고 GCC는 이러한 프로그램을 다음과 같은 순서로 동작시킨다.

gcc는 소스 파일을 cpp로 전처리하고, cc1으로 컴파일하고, as로 어셈블링, ld로 링크해서 실행 파일인 a.out을 만들어낸다. 이 각 단계가 어떤 식인지를 file.c라는 소스코드를 처리한다고 가정하고 진행해 보겠다.

  • 전처리 단계

우선 소스파일에 gcc를 동작 시키면 가장 먼저 전처리기인 cpp가 동작한다.

cpp는 소스 파일의 #include와 #define과 같은 전처리기 부분을 처리한다. 즉, 필요한 헤더 파일을 삽입하고 실행 문장의 매크로를 상수로 반환하여둔다. 소스 파일이 전처리기를 거치면 file.i라는 이름의 파일이 생성되지만 디스크에는 저장되지 않는다.

  • 컴파일 단계

컴파일러가 전처리된 파일로부터 어셈블리어로 된 파일을 생성한다.

생성된 파일은 file.s이다. 이 파일도 보통은 디스크에 저장되진 않는다.

  • 어셈블 단계

어셈블리어로 된 파일을 기계가 직접 이해할 수 있는 오브젝트 파일로 변환한다.

오브젝트 파일(file.o)은 디스크에 생기기도 한다.

  • 링크 단계

오브젝트 파일은 기본적으로 라이브러리 함수(printf, scanf 등)에 해당하는 코드가 없다. 그래서 실행을 할수 없다. 또한 여러 파일로 이루어진 프로그램의 경우에도 파일 간의 연결 과정이 이루어져 있지 않기 때문에 실행을 할 수 없다. 이러한 라이브러리 함수나 다른 오브젝트 파일들을 연결해주는 작업이 링크 단계이다.

링크 단계가 진행되어야 실행 가능한 파일이 생성된다.

컴파일을 하는 각각의 단계에 대해서 간단한 설명을 진행하였는데, 각 단계별 옵션을 사용해서 얼마나 진행되었는지 그 상화을 볼 수 있게 수동으로도 제어가 가능하다. 이를 위해서는 gcc의 옵션들을 알아야 하는데, 나중 글에서 다루기로 하겠다.

102 – GCC로 컴파일하기

우리는 리눅스에서 실습을 할 때, C언어를 컴파일 할 때 쓰는 도구로 gcc를 이용하였다. 그러나, 이 gcc에 대해서 글을 자세히 적은 적은 거의 없다. 아마 대부분의 내 글을 본 사람들은 내가 따라할 수 있도록 만든 예제만을 보고 그대로 따라했을 것이다.

프로그램이 점점 더 커지고 더더욱 복잡한 개발 환경들이 들어갈 때, 우리는 이 컴파일러에 대해서 이해를 하고 있어야 한다. 컴퓨터는 사용자가 C언어와 같이 고급 언어로 작성한 많은 소스 프로그램을 이해하지 못한다. 이를 컴퓨터가 이해할 수 있는 기계어 형태로 변환해 주는 작업을 하는 툴이 gcc이기 때문이다. 우리가 영어를 쓰는 사람들과 대화하기 위해 영어를 공부하는 데, 어느 정도의 문법적인 요소를 한 번 정도는 상세하게 살펴보고 가는 것과 같은 이치이다.

리눅스에서 C언어로 개발할 때에는 주로 gcc를 이용한다. c언어를 개발할 때 쓰는 컴파일러는 여럿 존재하지만 gcc가 제일 압도적으로 많이 이용된다. 그러므로 일단 gcc가 어떤 것인지, 컴파일러가 어떤 것인지, 어떠한 실행과 옵션들이 있는지를 알아보도록 하겠다.