GCC 컴파일러가 C 소스 파일을 실행 파일로 변환하는 과정

2024. 9. 9. 18:07Linux*security

반응형

 


1. 전처리 (Preprocessing)
   - 소스 파일(.c)과 헤더 파일(.h)이 입력된다.
   - 으로 시작하는 전처리 지시어들(예: include, define)이 처리된다.
   - 옵션: -I, -D 등이 전처리 과정에서 사용된다.   (Dname 옵션)
     - -I: 추가 헤더 파일 경로를 지정
     - -D: 매크로 정의

 2. 컴파일 (Compilation)
   - 전처리된 파일(.i)을 컴파일하여 중간 파일(.s, 어셈블리어 코드)로 변환한다.
   - 문법 검사와 코드 최적화가 이루어진다.
   - 옵션: -O로 최적화 수준을 설정할 수 있다.
     - -O0(최적화 없음), -O1, -O2, -O3(최적화 단계)

 3. 어셈블리 (Assembly)
   - 중간 파일(.s)을 오브젝트 파일(.o)로 변환한다.

 4. 링킹 (Linking)
   - 오브젝트 파일(.o)을 라이브러리 파일(.a 또는 .so) 및 스타트업 루틴과 결합하여 실행 파일을 생성한다.
   - 옵션: -l 및 -L 옵션이 사용된다.
     - -l: 특정 라이브러리와 링크
     - -L: 라이브러리 파일 경로 지정

 5. 결과
   - 최종적으로 실행 가능한 파일이 생성된다.
   - 옵션: -o 옵션을 사용하여 출력 파일 이름을 지정할 수 있다.


 

 

 

 

■ Static Library

 

 

정적 라이브러리(Static Library)는 프로그램이 컴파일될 때 라이브러리의 코드를 실행 파일에 포함시켜 실행 시 라이브러리를 별도로 로드할 필요가 없고, 독립적으로 실행된다.

1. 정적 라이브러리 개념

  • 정적 라이브러리는 미리 생성된 오브젝트 코드(.o 파일)로 구성된 라이브러리이다. 보통 .a 확장자를 가진 파일로 생성된다.
  • 프로그램이 컴파일될 때, 정적 라이브러리의 함수가 프로그램에 포함되므로 실행 파일은 자체적으로 독립된 상태가 된다. 즉, 실행 파일이 라이브러리 함수를 가지고 있는 형태이다.
  • 링킹 과정에서 정적 라이브러리의 함수 코드가 실행 파일에 복사된다.

2. 정적 라이브러리의 사용 예시

  • hello.o, world.o와 같은 오브젝트 파일에서 각각 printf.o라는 라이브러리 함수를 사용한다고 가정하면, 정적 라이브러리를 사용했을 경우 hello.o와 world.o 모두 printf 함수 코드를 자체적으로 포함하게 된다.

 

  • hello 실행 파일: printf 함수 코드가 포함됨.
  • world 실행 파일: 동일한 printf 함수 코드가 포함됨.

  hello와 world 파일을 동시에 실행할 때 각각의 실행 파일이 자신의 printf 함수 코드를 메모리에 가지고 있게 된다.

3. 문제점

  • 동일한 정적 라이브러리 함수가 여러 실행 파일에 중복으로 포함된다.
  • 이로 인해 동일한 코드가 메모리 및 디스크에서 중복 저장되어 비효율이 발생할 수 있다.
  • , 10개의 실행 파일이 각각 printf 함수를 정적 라이브러리로 링크한 경우, 메모리 내에 동일한 printf 함수 코드가 10번 존재하게 되어 메모리 낭비가 발생한다.

4. 정적 라이브러리의 장점

  • 독립성: 프로그램이 라이브러리를 자체적으로 포함하고 있어 실행 시 별도의 라이브러리 파일을 필요로 하지 않음.
  • 배포의 용이성: 실행 파일이 라이브러리와 함께 패키징되므로 실행 환경에 따라 라이브러리 버전 문제 등이 발생하지 않음.

5. 정적 라이브러리의 단점

  • 메모리 낭비: 동일한 라이브러리 코드가 여러 실행 파일에 중복 포함되므로 메모리 사용이 비효율적.
  • 디스크 공간 낭비: 실행 파일에 포함된 라이브러리 코드가 중복될 경우, 디스크 공간 역시 비효율적으로 사용됨.
  • 업데이트의 어려움: 라이브러리 코드가 업데이트되면, 이를 사용하는 모든 실행 파일을 다시 컴파일해야 함.

6. 대안: 동적 라이브러리 (Shared Library)

  • 동적 라이브러리(Dynamic Library)는 정적 라이브러리와 달리, 프로그램이 실행될 때 필요한 라이브러리 코드를 메모리에 로드하는 방식이다.
  • 여러 실행 파일이 동일한 동적 라이브러리를 사용할 경우, 메모리에 한 번만 로드되므로 메모리와 디스크 자원을 효율적으로 사용할 수 있다.
  • 라이브러리 업데이트 시 실행 파일을 다시 컴파일할 필요가 없으며, 시스템의 동적 라이브러리만 교체하면 된다.

 

 

 

 

 

 

 

 

 

■ Shared Library

 

 

공유 라이브러리(Shared Library)는 여러 프로그램이 동일한 라이브러리 코드를 공유하여 메모리 사용을 최적화하는 방식으로 동일한 라이브러리 함수가 여러 실행 파일에서 사용되더라도 메모리 내에서 중복 없이 한 번만 로드된다.

1. 공유 라이브러리

  • 공유 라이브러리(Shared Library)는 프로그램이 실행될 때 동적으로 로드되어 여러 프로그램이 동시에 사용할 수 있는 라이브러리이다. 일반적으로 리눅스에서는 .so(Shared Object) 확장자를 사용하며, 윈도우에서는 .dll(Dynamic Link Library)로 불린다.
  • 정적 라이브러리와 달리, 프로그램에 라이브러리 코드가 포함되지 않고, 실행 시점에 필요한 라이브러리가 메모리에 로드된다.
  • 여러 프로그램이 동일한 공유 라이브러리 파일을 참조할 수 있기 때문에 메모리와 디스크 사용을 효율적으로 관리할 수 있다.

2. 공유 라이브러리의 구조

  • , hello.o와 world.o라는 두 개의 오브젝트 파일이 각각 printf 함수를 사용하는 프로그램이라고 가정하면보겠다.
  • 공유 라이브러리를 사용할 경우, printf.o가 실행 파일에 직접 포함되지 않고, 공유 라이브러리 파일에 존재한다. 프로그램이 실행될 때, 시스템은 필요한 공유 라이브러리를 메모리에 로드하고, 그 라이브러리를 참조하여 printf 함수가 호출된다.
  • world 실행 파일: printf 함수가 포함된 공유 라이브러리를 참조
  • hello 실행 파일: 동일한 공유 라이브러리를 참조

두 실행 파일(hello, world)이 동일한 공유 라이브러리(printf.o)를 참조하고, 메모리에 이미 로드된 라이브러리를 함께 사용하게 된다.

3. 공유 라이브러리의 동작 방식

  • 프로그램을 실행하면, 실행 파일에 포함된 코드가 실행되고, 이때 필요한 공유 라이브러리들이 동적으로 메모리에 로드된다.
  • 라이브러리가 메모리에 이미 로드되어 있다면, 그 메모리를 그대로 참조한다. 즉, 여러 프로그램이 동일한 라이브러리 함수를 사용하더라도 메모리에는 한 번만 로드된다.
  • 운영체제는 메모리에서 동일한 라이브러리를 참조할 수 있도록 효율적인 메모리 관리를 수행한다.

4. 공유 라이브러리의 장점

  • 메모리 절약: 동일한 라이브러리 함수가 여러 프로그램에서 사용되더라도, 메모리에는 한 번만 로드되므로 메모리 낭비가 줄어든다.
  • 디스크 공간 절약: 실행 파일에 라이브러리 코드를 포함하지 않으므로, 디스크 공간이 절약된다.
  • 라이브러리 업데이트 용이: 라이브러리를 업데이트할 때 실행 파일을 다시 컴파일할 필요 없이, 라이브러리 파일만 교체하면 된다.

5. 공유 라이브러리의 단점

  • 의존성 문제: 실행 파일이 특정 버전의 공유 라이브러리에 의존할 수 있다. 시스템에 적절한 버전의 라이브러리가 없거나 변경되면 프로그램 실행 시 문제가 발생할 수 있다.
  • 실행 시점에 라이브러리 로드: 정적 라이브러리와 달리, 실행 시점에 라이브러리를 로드해야 하므로 실행 속도가 조금 느릴 수 있다.

 

6. 정적 라이브러리와 공유 라이브러리의 비교

항목정적 라이브러리공유 라이브러리

링킹 방식 컴파일 시 실행 파일에 포함 실행 시점에 동적으로 로드
메모리 사용 각 실행 파일에 중복 포함, 메모리 낭비 한 번만 로드, 여러 프로그램이 공유
디스크 사용 실행 파일 크기가 커짐 실행 파일 크기가 작음
업데이트 라이브러리 업데이트 시 재컴파일 필요 라이브러리만 교체하면 됨

 

 

 


 1. 소스 코드 (`ex01.c`)


#include <stdio.h>

void get_attr(char* attr, char flag) {
    *attr = (*attr) & flag;
}


get_attr 함수는 attr 변수에 flag와의 비트 AND 연산을 수행하는 함수



2. 오브젝트 파일 생성 (-fPIC 옵션 사용)

공유 라이브러리를 만들기 위한 첫 단계는 위치 독립 코드 (Position Independent Code, PIC)를
생성하는 것이다. 공유 라이브러리가 메모리의 어느 위치에서도 실행될 수 있도록 하기 위해 필요하다. 


$ gcc -c -fPIC ex01.c



3. 공유 라이브러리 생성 (-shared 옵션 사용)

오브젝트 파일이 생성된 후, `ld` 명령어를 사용하여 공유 라이브러리 파일(.so)을 생성
여기서 `-shared` 옵션을 사용하여 공유 라이브러리를 생성하고, `-o` 옵션으로 출력 파일 이름을 
지정


$ ld -shared -o libattr.so ex01.o

ex01.o 오브젝트 파일을 기반으로 libattr.so 라는 공유 라이브러리 파일을 생성



- fPIC : 오브젝트 파일을 생성할 때 위치 독립 코드를 생성
- shared : 공유 라이브러리 파일을 생성

 

 
 

 

 

반응형