ADS 의 소개와 예제를 통한 사용법에 대해서 설명한다.
발표자료는 ads.ppt 에서 구할 수 있다.
ARM 사에서 만든 개발툴로서 현재는 더이상 버전업 하고 있지 않다. ADS 를 대체할 RealView 가 나왔기 때문이다.
하지만, 내가 이제와서 새삼스럽게(?) ADS 를 소개하는 것은 ADS 의 메리트 때문이다.
바로 크랙이 손쉽다는 것이다. RealView 부터 하드웨어 맥(MAC) 인증 방식을 도입했기 때문에, 같은 IP 대역이 아니면 라이센스를 받아올 수 없다. 이것은 엄청난 제약으로 네트웍으로 연결되지 않으면 개발 조차 할 수 없다.
하지만 ADS 는 크랙 한번으로 언제 어디서나 사용 가능하다.
하나의 어셈블리 파일(boot.s)과 하나의 C 파일(month.c) 로 구성된다. 다음은 boot.s 파일이다.
; C function import IMPORT __main IMPORT undefined_instruction_handler IMPORT software_interrupt_handler IMPORT prefetch_abort_handler IMPORT data_abort_handler IMPORT not_used_handler IMPORT irq_handler IMPORT fiq_handler AREA INIT,CODE,READONLY ENTRY ; vector table start b reset b undefined_instruction b software_interrupt b prefetch_abort b data_abort b not_used b irq b fiq reset MOV r0, #12 b __main ; main() function branch undefined_instruction b undefined_instruction_handler software_interrupt b software_interrupt_handler prefetch_abort b prefetch_abort_handler data_abort b data_abort_handler not_used b not_used_handler irq b irq_handler fiq b fiq_handler END
다음은 month.c 파일이다.
/* month.c simple program to output a calendar month */ #include <stdio.h> void nextday(void); /* Function prototype */ int swi(void); /* Inline-assembly function prototype */ struct Date_Format /* Variable declaration */ { int day; int month; int year; }date; int main() { int dflag, mflag; printf("\n\nThis program will read the date in the form yyyy mm dd\n"); printf("\ne.g. 1972 02 17\n\n"); printf("then display the dates of the following calendar month."); swi(); printf("\n\nPlease type the date (yyyy mm dd) -> "); scanf("%d%d%d", &date.year, &date.month, &date.day); printf("\n\nThe month following %4d %2d %2d is:\n\n", date.year, date.month, date.day); dflag = date.day; mflag = date.month; nextday(); while(date.day != dflag && date.month - mflag < 2) { printf("%4d %2d %2d\n", date.year, date.month, date.day); nextday(); } printf("\n\n"); } void nextday(void) /* Function definition */ { static int daysInMonth; switch(date.month) { case 2: if(date.year % 400 == 0) daysInMonth = 29; else if(date.year % 4 == 0) daysInMonth = 29; else daysInMonth = 28; break; case 4: case 6: case 9: case 11: daysInMonth = 30; break; default: daysInMonth = 31; } if(date.day == daysInMonth) { date.day = 1; if(date.month == 12) { date.month = 1; date.year++; } else date.month++; } else date.day++; } __inline int swi() { __asm { SWI 0x0 /* Software interrupt call */ } } /* Exception handler */ void undefined_instruction_handler(void) { printf("\nthis is undefined_instruction handler!!\n"); } void software_interrupt_handler(void) { printf("\nthis is software_interrupt handler!!\n"); } void prefetch_abort_handler(void) { printf("\nthis is prefetch_abort handler!!\n"); } void data_abort_handler(void) { printf("\nthis is data_abort handler!!\n"); } void not_used_handler(void) { printf("\nthis is not_used handler!!\n"); } void irq_handler(void) { printf("\nthis is irq handler!!\n"); } void fiq_handler(void) { printf("\nthis is fiq handler!!\n"); }
몇 가지 유의할 사항이 있다. 내가 원하는 것은 위의 두 개의 파일을 컴파일해서 하나의 오브젝트 파일(.axf) 로 생성하고, 0x0 번지에서 부터 boot.s 파일이 수행되고, month.c 파일에서 소프트웨어 인터럽트가 발생했을 때 0x8 번지로 점프를 하게 끔하는 것이다.
코드워리어의 'Edit - DebugRel Settings' 에서 'Language Settings' 항목의 컴파일러에 대한 타겟 코어를 선택한다.
'Linker - ARM Linker' 에서 'Output' 항목에서 'RO_Base' 주소를 '0x0' 로 설정한다.
'Options' 항목에서 'Image entry point' 주소를 '0x0' 설정한다.
'Layout' 항목에서 'Place at beginning of Image' 에 바이너리 이미지의 처음에 올 섹션을 적어준다.
여기서는 boot.s 파일의 INIT 섹션이 가장 처음에 와야 함으로 'boot.o' 와 'INIT' 를 적어준다.
위의 소스 코드를 보면 조금 이상한 점이 있다. 바로 아래 부분이다.
b __main ; main() function branch
왜 main 이 아니라 __main 인가? 실제로 main 으로 해도 컴파일하는데 아무런 문제가 없다. 하지만 printf 나 scanf 와 같은 stdio 관련 라이브러리를 사용하는 함수는 수행되지 않는다. 바로 c 프로그램이 main 함수까지 가기전에 보이지 않게 수행되는 것들(스택 초기화라든지, 라이브러리를 사용하기 위한 설정) 이 수행되지 않기 때문이다. ADS 에서는 __main 함수가 그런 역할을 한다.
이런 과정을 거쳐야 하기 때문에 __main 함수로 branch 를 해준다. 실제로 AXD 로 디버깅하면, __main 에서 어셈코드들이 수행되는데 바로 이것이 보이지 않는 초기화 과정들이다.