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 에서 어셈코드들이 수행되는데 바로 이것이 보이지 않는 초기화 과정들이다.