
컴퓨터 공학을 전공하며 build process를 잘 알아야하는 것은 필수입니다. build 과정에 대해서 알고 있긴 했지만 dynamic linking에 대해서 공부하기 위해서 다시 build를 공부했습니다. 잘 들어주시고 틀린게 있으면 지적해주세요. 아 그리고 Compiler의 build process입니다.
Build Flow는 preprocess->compile->link->load입니다.(assemble은 compile내의 범주이니 생략하겠습니다.)
먼저 Preprocess는 Text to Text의 작업입니다. Preprocessor는 코드에서 #같은 코드를 먼저 읽어서 처리해주는 역할을 합니다. 예를 들어 #include <stdio.h>라는 코드가 있으면 Preprocessor는 stdio.h라는 파일의 내용을 본 코드에 Copy&Paste합니다. 그리고 Define같은 것도 읽어서 미리 처리해둡니다. source code에다가 내용을 미리 변경해놓는 것들이라서 Text to Text라고 한 것입니다. 참고로 Preprocessing code를 보고 싶으면 gcc -E test.c하면됩니다. 그러면 source code와 유사하지만 내용이 #관련된 코드가 내용이 바껴있는 것을 볼 수 있을 것입니다. 따라서, #은 결국 replacement하기 위함입니다.
Compile로 넘어가기전 하고 싶은 말이 있는데, 이렇게 build가 단계별로 나눠져있는 이유는 portability 때문입니다. 이어서 설명하겠지만 Compile단계로 넘어가면 Machine Dependency가 주입됩니다. 그렇다면 portability는 Text level인 Preprocess단에서의 이야기가 될 것입니다. 왜 Portabilityt를 위한 것이라고 할까요? 바로 #ifdef같은 것들로 machine을 설정해줄 수 있기 때문입니다. 간단하게 예를 들면 #ifdef ARM, #ifdef INTEL 등등 이렇게 설정해주면 preprocess를 이용해서 컴파일을 하는 부분을 조절할 수 있기 때문입니다. (#ifdef에 대해서 모르겠다면 따로 공부하세요!)
Compile에서는 위에서 언급했듯이 해당 machine이 사용하는 기계어로 코드가 변형됩니다. Machine dependency가 발생하는 것이죠. 이렇게 Compile해서 나온 파일을 Relocatable Object File이라고 부릅니다. 왜냐하면 이 파일이 linking 후 Executable Object File로 만들어질 때 메모리 주소가 재배치되기 때문입니다.
그 다음 Linking이 발생합니다. Relocatable Object file들을 Executable로 만들기 위해서는 Linking을 하여서 하나의 executable file로 만들어줘야합니다. Linking은 중요한 과정입니다. 이 과정에서 library를 linking할수도 있고 main문과 나머지 함수를 구현해놓은 파일을 linking할수도 있습니다. 만약 libtest.a라는 라이브러리를 linking하고 싶다면 gcc test.c -L./ -ltest이렇게 linking하면 됩니다. -L로 라이브러리 위치를 설정하고 -l로 파일을 설정한 것입니다.
그리고 하나 더 알려드리고 싶은 것은 extern과 static입니다. source file 2개를 linking해서 사용하려고할 때, 서로의 전역 변수를 사용할 수 있습니다. 그렇기 때문에 전역 변수를 가볍게 사용해서는 안됩니다. 만약 해당 source file내에서만 사용하는 전역 변수라면 static을 붙여서 다른 파일에서 접근하지 못하게 해야합니다. 그런데 만약 서로 다른 파일끼리 같이 사용하는 전역 변수를 만들고 싶다면 한쪽 source file에서 전역변수를 선언하고, 나머지 source file에서는 extern으로 해당 변수를 선언해서 가짜 변수를 만들어줍니다. 만약 extern으로 가짜 변수를 만들어두지 않으면 해당 source file은 compile이 되지 않기 때문에 compile하기 위해서 가짜 변수를 두는 것입니다. 이렇게 compile해서 linking한다면 서로 같이 사용할 수 있는 전역변수가 되는 것입니다.
Linking해서 Executable Object File을 만들고 이를 실행시키면 해당 파일이 메모리에 올라갑니다. 이를 Load라고 부릅니다. Dynamic linking은 Load 후에 이뤄집니다. 기본적으로 컴파일러는 라이브러리를 static으로 linking하거나 dynamic으로 linkning하는데, Default는 Dynamic linking입니다. 최근 추세는 Dynamic을 사용하는 것인데 이에 대한 장점은 따로 찾아보시기를 바랍니다. static은 executable file이 해당 라이브러리에 대한 코드를 가지고 있는 상태를 말하고, dynamic은 run-time에서 해당 함수를 실행시킬 때 shared memory에서 찾아서 linking 시킨 것입니다. 이를 run-time linking이라고 부릅니다. 이러한 점을 이용해서 Interpositioning도 가능합니다. Interposition이란 Dynamic linking을 통해서 해당 함수를 shared memory에서 찾을 때, 본인이 만든 해당 함수를 먼저 위치하게 해서 이 함수를 먼저 찾게한 것 입니다. 이를 통해서 여러 작업을 할 수 있습니다. 저는 Dynamic linking을 통해서 Deadlock detector를 만들었습니다. 흥미로운 주제인 거 같습니다.
이렇게 Build process와 더불어 Dynamic linking에 대해서 전해보았습니다. Build는 여러모로 중요한 개념이니 저도 앞으로 계속 공부할 예정입니다. 다들 열공!