본문 바로가기

C&C++/C

[C/C++] 헤더 파일은 어디서 소스를 가져올까?

0. 개요

 

Linux 환경에서 커널을 살펴보거나 C 프로그래밍을 하다 보면 의문점이 생긴다.

#include <stdio.h>
#include "myheader.h"

위와 같이 소스코드 맨 위에서 #include를 통한 헤더파일 삽입 전처리기를 사용하는데,

 

<> 는 컴파일러가 정해놓은 루트(리눅스에선 일반적으로 /usr/include/)에서 찾고,

""는 현재 폴더에서 찾는다는 사실!

 

...까지는 많은 사람이 알고있다.

 

그런데 막상 그 헤더파일을 까보면

1) 각종 매크로 2) 구조체 3) 함수 선언부 4) 인라인 함수 5) 주석만 잔뜩 존재하고

3) 에서 선언된 함수의 소스는 찾을 수가 없다.

 

컴파일 구조를 생각했을 때,

우리가 작성한 main.c 파일 등에서 include한 것은 .o(오브젝트) 파일일 때에는 링크할 공간만 남겨두고

이후에 링킹 과정을 거치며 실제 코드공간으로 jump 연산을 하도록 기계어를 짜는데

 

그 "실제 코드공간",  stdio.c라는 소스코드 또는 이를 컴파일한 바이너리 파일은 대체 어디있는 걸까?

 

 

 

1. 헤더파일 찾기

 

다음 명령어를 이용해서 컴파일러의 configuration을 확인할 수 있다.

echo | gcc -x c -E -Wp,-v - >/dev/null
# cpp 헤더의 경우 아래 입력
echo | gcc -x c++ -E -Wp,-v - >/dev/null

 

 

나타난 주소에 들어가보면 다음과 같은 파일들이 있다.

/usr/local/include
/usr/include
/usr/include/x86_64-linux-gnu

 

/usr/lib/gcc/x86_64-linux-gnu/9/include

 

하지만 온통 헤더파일 뿐, 어디에서도 소스코드를 찾아볼 수 없다.

 

 

 

2. 라이브러리 바이너리 찾기

 

라이브러리는 두 가지로 나뉘는데

정적(static) : .a 파일

동적(dynamic) : .so(리눅스), .dll(윈도우), .dynlib 파일

이다.

 

 

다음 코드를 입력해서 링킹 과정에서 서치에 사용하는 폴더 목록을 볼 수 있다.

ld 는 라이브러리 디렉토리 그 자체고, gcc 명령어는 ld를 wrapping하며 몇 가지 환경설정을 해 이와 조금 다른 결과를 나타낸다.

ld --verbose | grep SEARCH_DIR | tr -s ' ;' \\012
gcc -print-search-dirs | sed '/^lib/b 1;d;:1;s,/[^/.][^/]*/\.\./,/,;t 1;s,:[^=]*=,:;,;s,;,;  ,g' | tr \; \\012 | tr : \\012

 

 

찾아보면 다음과 같이 .so 파일과 .a 파일을 확인할 수 있다.

 

/usr/local/lib
/usr/lib
/usr/lib/x86_64-linux-gnu
objdump로 기계어로 바꿔본 결과

 

아무래도 컴파일 완료된 라이브러리 파일은 cpu 벤더에 종속적이다보니,

모두가 사용할 수 있는 /usr/lib 같은 공간보다는

cpu 형식를 명시한 /lib/x86_64-linux 과 같은 장소에 저장하는 경향이 있다.

 

 

3. 디렉토리 추가

 

(1) 컴파일시 헤더파일을 찾아보는 디렉토리 추가

 

1) PATH 변수를 이용하는 방법

 

CPATH(C/C++), C_INCLUDE_PATH(C), CPLUS_INCLUDE_PATH(C++) 를 이용할 수 있다.

CPATH=/home/user/my_libs_headers
gcc -c foo.c -o foo.o

 

따로 export 등을 하지 않으면 해당 쉘이 동작하는 동안만 변수가 존재한다.

 

 

2) 옵션을 추가하는 방법

 

-I 옵션을 사용한다. 띄어쓰기는 필요 없다.

gcc -I/home/user/my_libs_headers -c foo.c -o foo.o

 

해당 컴파일에서만 인스턴트하게 동작하게 하는 방법이다.

 

 

(2) 링킹시 라이브러리를 찾아보는 디렉토리 추가

 

위와 마찬가지로 변수 이용, 옵션 이용 방법이 있다.

 

1) 변수를 이용하는 방법

 

LD_LIBRARY_PATH에 해당 PATH를 저장한다.

LD_LIBRARY_PATH=/home/user/my_libs
gcc foo.o bar.o -o foo

 

2) 옵션을 추가하는 방법

 

-L 옵션을 사용한다. 띄어쓰기는 필요 없다.

gcc -L/home/user/my_libs foo.o bar.o -o foo

 

 

4. 소스코드?

 

라이브러리를 설치할 때에는 이미 컴파일 된 바이너리 형태로 다운로드 받게 된다.

소스코드는 이를 공개한 곳에서 확인할 수 있다.

잘 모르겠으면 구글에 "libmysqlclient github" 이런식으로 쳐보면 된다.

 

 

이상.