본문 바로가기
크로스 컴파일러(Cross Compiler)가 뭐죠?

뭘 알아야 이해를 하지

임베디드 시스템 개발을 하기 위해서는 반드시 크로스 컴파일러가 있어야 해요. 크로스 컴파일러는 씨 소스를 개발하고자 하는 시피유에 맞게 컴파일해서 바이너리로 만들어 주는 역할을 해요.
호스트 피시에서 사용되는 씨 언어 컴파일러는 크로스 컴파일러라고 표현하지는 않아요. 호스트 피시 같은 경우, 개발자가 씨 프로그램을 만들어서 컴파일을 하고 나면 프로그램 실행 결과를 바로 확인 할 수가 있죠. 그 이유는 호스트 피시에 설치된 씨 컴파일러는 호스트 피시의 시피유에 맞게 실행 바이너리가 만들어지기 때문입니다. 그에 반해 임베디드용 시피유는 호스트 피시의 시피유와 다르기 때문에 실행 바이너리도 당연히 다르겠죠? 즉, 내가 만든 씨 프로그램은 같지만 호스트 피시의 시피유에서 실행되는 바이너리와 임베디드 시피유에서 실행되는 바이너리는 다르다는 거죠. 그래서 임베디드 시피유용 컴파일러를 설치해야 하는데, 일반적으로 이러한 컴파일러를 크로스 컴파일러라고 부른답니다.

그럼 호스트 피시용 씨 컴파일러와 크로스 씨 컴파일러의 차이점은 무엇일까요?
우리가 사용하는 호스트 피시 시피유는 32/64비트인데, 임베디드 시피유는 8, 16, 32, 64비트 등 이렇게 다양한 비트를 가지고 있어요. 여기서 비트가 다르다는 이야기는 시피유가 덧셈 명령 하나를 처리할 때 해당 시피유가 가지는 비트 수만큼 메모리에 있는 바이러리를 읽어와서 처리하도록 되어 있어요. 그러므로 컴파일 시 각 시피유의 비트에 맞게 바이너리를 만들어 주게 되어 있고 그 비트 만큼 읽어와서 하나의 명령어를 처리하니 시피유의 성능에 따라 많은 차이가 있답니다.

[ARM Register]
N _ R0 4 R8 0
Z _ R1 30020008 R9 0
C C R2 4 R10 0
V _ R3 1 R11 3003D7FC
I _ R4 0 R12 3003D7E0
F _ R5 0 R13 3003D7E0
T _ R6 0 R14 049C
R7 0 PC 0490
svc SPSR 10 CPSR 20000013

[ARM DisAssemble]
___addr/line__|code_____|label____|mnemonic________________|comment
77| for(i=0; i<5; i++) func2();
SR:00000484|E3A03000 mov r3,#0x0
SR:00000488|E50B3010 str r3,[r11,#-0x10]
SR:0000048C|E51B3010 ldr r3,[r11,#-0x10]
SR:00000490|E3530004 cmp r3,#0x4
SR:00000494|CA000004 bgt 0x4AC


[암 프로세서에서 노어 플래시에 있는 바이너리를 읽어오는 과정]

또한 같은 32비트지만 시피유의 구조가 어떤가에 따라 성능 차이가 많이 난답니다. 하지만 아무리 시피유 구조가 좋다고 해도 그에 맞는 컴파일러 성능도 역시 좋아야 해요.
예를 들면, 암 시피유로 전자액자를 개발한다면 크로스 컴파일러로 암 시피유 컴파일러를 설치해야 해요. 컴파일러는 아무거나 사용해도 되지만 이왕이면 시피유를 만든 회사가 가장 잘 만드는 편이죠. 또한 컴파일러를 전문적으로 만드는 회사들에게 컴파일러 개발을 맡겨 의뢰를 하기도 하죠. 이러한 컴파일러는 모두 상용이기 때문에 개발자는 컴파일러 구매하는데 비용이 들어가지만 시피유의 성능을 최적으로 낼 수가 있기 때문에 많이 선호한답니다. 상용 컴파일러가 아닌 프리웨어 컴파일러도 있는데 바로 GCC 컴파일러예요. 호스트 피시용 GCC, 암GCC, 파워피시GCC, 밉스GCC 등 웹에서 쉽게 구할 수가 있어요. 하지만 상용 컴파일러보다 성능이 다소 떨어지죠.
GCC 컴파일러는 임베디드 시피유에 따라 버전이 있는데, 만약 암GCC가 필요해서 웹에서 구하신다면 암 코어가 ARM7, ARM9, ARM11, ARM Cortex에 맞게 다운로더를 받아서 크로스 컴파일러를 설치해야 하지요.
상용 크로스 컴파일러도 마찬가지인데, ARM 7 시피유 경우에는 주로 SDT라는 컴파일러를 사용했고, ARM9 시피유 경우에는 ADS, 암11 이후는 RVCT라는 컴파일러를 사용한답니다. 모두 암 컴파일러로 시피유에 맞게 버전이 따로 있다는 것을 잊지 마세요.

일반적으로 크로스 컴파일러를 설치하고 나면 어셈블리 컴파일러, 씨 언어 컴파일러, 씨 플러스플러스 컴파일러가 기본적으로 설치가 된답니다.
설치가 완료가 됐다면 이제 간단한 프로그램을 하나를 만들어서 컴파일을 해 보아요.

--------------
#include
static int extern_static_one;
int main()
{
int i, value=0;
static int inter_static_star;
while(1)
{
value++;
if(value>=19)value=0;

extern_static_one++;
inter_static_star++;
}
return 0;
}

[컴파일 방법]
CMD>CD C:\EXAM1
C:\EXAM1> arm-elf-gcc main.c -g -gdwarf-2 -o0 -c -o main.o

[컴파일된 실행 파일 확인]
C:\EXAM1> arm-elf-readelf -h main.o
ELF Header:
Magic: 7f 45 4c 46 01 01 01 61 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: ARM
ABI Version: 0
Type: REL (Relocatable file)
Machine: ARM
Version: 0x1
Entry point address: 0x0
Start of program headers: 0 (bytes into file)
Start of section headers: 996 (bytes into file)
Flags: 0x200, GNU EABI, software FP
Size of this header: 52 (bytes)
Size of program headers: 0 (bytes)
Number of program headers: 0
Size of section headers: 40 (bytes)
Number of section headers: 21
Section header string table index: 18

컴파일 과정을 살펴 볼게요.
가장 기본적인 컴파일러인 씨 컴파일러와 어셈블리 컴파일러가 있어요. 개발자가 프로그램 소스를 만들고 나면 씨 컴파일러와 어셈블러 컴파일러에 의해 오브젝트 파일(*.o)이 만들어 지죠. 이렇게 만들어진 오브젝트 파일은 링커에 의해 실행 가능한 *.elf(Executable and Linkable Format) 파일로 만들어진답니다. elf 파일을 가지고 오브젝트카피라는 컴파일러 유틸리티를 통해 바이너리(*.bin)가 만들어 진답답니다.

위에서 이야기한 elf 파일에 대해 조금 더 자세히 살펴 볼게요. elf 파일에는 여러 가지 정보가 들어 있는데 디버깅을 위한 타겟의 메모리 주소와 개발자의 프로그램 소스와의 연관 정보를 갖는 주소 Format이랍니다. 헤더, 바이너리, 심볼 3개의 파트로 나누어져 있으며, 헤더에는 파일의 구성을 나타내는 로더맵(Load Map)과 같은 역할을 하지요. 바이너리는 순수하게 실행할 수 있는 코드만으로 구성되어 있으며 실제 타겟 플래시 메모리에 올라가게 되죠. 마지막으로 심볼 정보는 디버깅을 위한 정보랍니다. 개발자가 만든 프로그램 소스가 컴파일러를 통해 바이너리로 만들어지면, 개발자가 정의한 함수나 변수들이 실제 메모리의 어느 주소에 있는지를 심볼 정보에 의해 알 수가 있답니다.
하드웨어 디버거로 심볼을 보면 다음과 같아요.

symbol___________________|type________|address_________________|
CLKSLOW | | C:4C000010
Clock_Management | | P:00000044
CLR_IF | | P:00000150
extern_static_one |(static int)| D:30020004--30020007
FIQMODE | | C:00000011
_Lzero_bytes | | P:00000118
_Lzero_words | | P:00000108
main |(int ()) | R:00000314--0000037F

여기서 main 함수에 대해 조금 더 자세히 분석 해 보면 다음과 같아요.

컴파일 했을 당시 소스 위치가 어디에 있고 몇 번째 라인 알려 주며, 실제 main 함수가 플래시 메모리 어느 주소에 있는지 알려 준답니다.

하드웨어 디버거로 심볼 정보를 올려서 디버깅을 위한 화면 구성은 다음과 같아요.

add/line은 플래시 메모리 주소이고, 빨간색 부분은 arm-elf-gcc 컴파일러가 만들어낸 바이너리이며, mnemonic라고 되어 있는 부분은 DisAssemble 랍니다.


우후훗! Season 1의 여러 Section에 걸쳐 있는 얘기를 간추려서 이야기해 주셨네요. Season 1의 "3) Software 데꾸바쮸 (Decoupage) - Software의 정체와 만들기" 부분을 참고하시면 좋을 것 같아요~

Linked at 친절한 임베디드 시스템 개발자.. at 2010/05/09 23:24

... p; 209 엘시디가 뭐죠? 3장 임베디드 시스템 Software 구성 요소 300 크로스컴파일러 (Cross Compiler)가 뭐죠? 301 스타트업(Startup.s) 파일이 뭐죠? 30 ... more

Commented by ruring at 2010/05/10 15:38
우왕굿!! <<- 한번해보고싶었..;

ㅎ_ㅎ 잘읽고갑니다~
Commented by soto at 2010/05/10 22:54
괜찮나염...ㅋㅋ
Commented by at 2010/05/18 12:12
오우!!! 잘 보고 있습니다.
공부해야 하는데... 날씨가 널뛰게 하네요.
Commented by soto at 2010/05/29 05:34
후후...^^
Commented by ㅎㅎㅎ at 2010/05/19 04:06
깔끔하게 정리하시네요.
앞으로도 잘 부탁드립니다.
Commented by soto at 2010/05/29 05:34
우와..감사감사...ㅋㅋ
Commented by khan at 2010/05/20 00:09
반드시 있어야 하는 것이라...
이 홈피도 반드시 있어야 해요. ㅋㅋㅋ
Commented by soto at 2010/05/29 05:35
넵..감사요...
Commented by 한얼아빠 at 2010/05/21 01:04
잘 보았습니다.. 열공해야 겠어요..^^
Commented by soto at 2010/05/29 05:35
열공을 할 수 있도록 잘 써 보겠음다..ㅋㅋ
※ 이 포스트는 더 이상 덧글을 남길 수 없습니다.
친절한 임베디드 시스템 개발자 되기 강좌 글 전체 리스트 (링크) -



댓글





친절한 임베디드 개발자 되기 강좌 글 전체 리스트 (링크) -