Table of Contents

ADS 의 소개와 예제를 통한 사용법에 대해서 설명한다.
발표자료는 ads.ppt 에서 구할 수 있다.

왜 ADS 인가?

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' 를 적어준다.

__main 으로 branch 하는 이유

위의 소스 코드를 보면 조금 이상한 점이 있다. 바로 아래 부분이다.

b __main    ; main() function branch

왜 main 이 아니라 __main 인가? 실제로 main 으로 해도 컴파일하는데 아무런 문제가 없다. 하지만 printf 나 scanf 와 같은 stdio 관련 라이브러리를 사용하는 함수는 수행되지 않는다. 바로 c 프로그램이 main 함수까지 가기전에 보이지 않게 수행되는 것들(스택 초기화라든지, 라이브러리를 사용하기 위한 설정) 이 수행되지 않기 때문이다. ADS 에서는 __main 함수가 그런 역할을 한다.
이런 과정을 거쳐야 하기 때문에 __main 함수로 branch 를 해준다. 실제로 AXD 로 디버깅하면, __main 에서 어셈코드들이 수행되는데 바로 이것이 보이지 않는 초기화 과정들이다.