Linker Configuration File (*.icf)

이번 호에서는 IAR Embedded Workbench for ARM(이하 EWARM)에서 링크 작업시 메모리 정보를 가지고 있는 Linker Configuration File(이하 icf)에 대해 알아보도록 하겠다.  

다음 [그림 1]과 같이 각종 라이브러리와 컴파일돼 생성된 오브젝트 코드들을 ILINK링커 프로그램에서 icf 파일을 참조해 코드 및 변수에 어드레스를 할당해 ROM 또는 RAM에 위치시킵니다. 생성되는 실행 코드는 ELF또는 DWARF 형식의 파일로 .out확장자를 가지며 함수나 변수들이 할당된 정보들은 .map 파일에서 확인 할 수 있다. ROM/RAM의 사용 정보를 알고 싶으면 map파일에서 확인 할 수 있다.

[그림 1] ILINK Linker

icf파일은 [그림 2]처럼 ROM/RAM의 시작 번지와 마지막 번지에 대한 정보를 가지고 있다. 링커는 이러한 정보를 토대로 코드 및 변수에 대해 어드레스를 지정하게 된다. 

프로젝트 옵션에서 디바이스를 선택하면 해당 디바이스에 해당하는 icf파일이 자동으로 연계 동작한다. EWARM 툴이 설치 될 때 하나의 icf파일이 설치되며, 이 파일을 수정해 사용한다면 다음에 사용 시 동일한 내용이 아니면 문제가 발생한다. 

그래서 icf파일을 프로젝트 내에 다른 이름으로 저장해 사용하고, 이런 경우 프로젝트 내에 있는 icf파일을 지정해 사용해야 한다. Override default를 선택하고 프로젝트 안에 있는 icf파일을 지정한다. 그러면 절대 패스 구조로 파일이 지정되는데 $PROJ_DIR$와 같은 환경 변수를 이용해 절대 패스가 아닌 상대 패스 구조로 변경해야 프로젝트 파일을 다른 PC에 복사해도 오류 없이 사용할 수 있다. 

[그림 2] Linker Configuration File

먼저 icf 파일에서 사용되는 용어와 구문에 대해 간략히 살펴보도록 하겠다.

Memory 

번지 할당이 가능한 최대 메모리의 양을 지정한다. 실제적으로 사용할 수 있는 메모리를 의미하는 것은 아니다.
define memory [ name ] with size = expr (expression)

Region

코드/데이터 섹션이 위치 할 수 있는 사용 가능한 실제 메모리의 범주다.
define region name = [ memory-name: ] [ from expr { to expr | size expr } ];

Block 

섹션이나 다른 블록들이 포함돼 있는 인접한 메모리 영역이다.
define block name [ with size = expr ] [ with alignment = expr ] [ fixed order ] 
{ extended-selectors }

Overlay 

오버레이드된 섹션이나 블록들이 포함돼 있는 인접한 메모리 영역이다.
define overlay name [ with size = expr ] [ with alignment = expr ] [ fixed order ] 
{ extended-selectors }

Section 

코드나 데이터가 포함돼 있는 논리적 개체이다.
Place in 명령어는 메모리 영역의 범주를 지정한다. (from...to...의 시작과 끝의 영역을 명시한다). 또한 Place at 명령어는 메모리의 시작 주소를 명시한다. (from...의 주소이다.)

place in region { section-selector }
place at start of region { section-selector }
place at end of region { section-selector }
place at address memory:address { section-selector }
Section selector: [attribute] [section section_name] [object file_name]
 Section name: .intvec, .text, .rodata, .data, .bss, …
                place at address MEM:0x0 { section .intvec };
 Section attribute: [ ro [ code | data ] | rw [ code | data ] | zi ]
                place in ROM { readonly };// ro
                place in RAM { readwrite };// rw
File name: the name of an object file (*.o) or library file (*.a)
         place at address MEM:0x8000 { section .text object myfile.o };
         place in MYREGION { ro object mylib.a };

다음은 미리 내부적으로 정의된 시스템 정의 섹션들이다. .intvec 섹션은 벡터 테이블의 위치를 지정하기 위한 섹션이며 .text 섹션은 실행 코드들이 위치할 메모리 영역이다. .data 섹션은 변수가 할당되는 메모리 영역이며 .data_init 섹션은 변수 초기값이 있는 영역이다.

[그림 3] System-defined sections

[그림 4]에서 오브젝트 파일에서 각 섹션들이 ROM(Region Text) 영역과 RAM(Region Data)로 분류돼 메모리에 할당된 상태를 도식화한 것이다.

[그림 4] Placing sections into the memory

애플리케이션이 시작하면서 섹션이 초기화되는 과정을 살펴보면 [그림 5]와 같이 icf파일에서 initialize by copy { readwrite }; 명령어는 변수 초기화 작업에 대해 자동으로 초기화 코드를 삽입한다. int c 변수는 .bss 영역에 할당되고 0으로 초기화되며, b 변수는 .data_init 섹션에 저장돼 있는 초기화 값(1)을 가져와 .data 섹션에 할당되는 b 변수에 값을 1로설정한다. 

const int a = 1; 의 a 변수는 .rodata의 ROM(Flash)영역에 위치하게 된다. __no_init로 지정된 변수는 초기화 작업을 하지 않으므로 변수 d에는 RAM의 .noinit섹션에 할당만 되고 값은 지정하지 않는다. 또한 __ramfunc 지시어로 정의된 함수는 .textrw_init 섹션에 실행 코드가 저장돼 있으며, 초기화 작업시 코드를 RAM에 위치한 .textrw 섹션에 복사해 함수를 RAM에서 실행하도록 한다.

[그림 5] Sections initialization at the startup

수동 초기화(Manual initialization) 

initialize by copy 

링커는 프로그램 시작 시 읽기쓰기(readwrite) 섹션에 대해 내용을 복사함으로써 초기화 작업을 처리한다. 초기 값을 생성하고 초기화 테이블을 관리하며 복사하는 코드들을 생성해 삽입한다.

initialize manually 

사용자가 복사되는 요소에 대해 내용, 시기, 처리 방식에 대해 수동으로 제어할 수 있다. 컴파일러 내장 함수로 섹션의 시작/끝 주소 및 크기에 대한 정보를 가져올 수 있다.
__section_begin(), __section_end(), __section_size();

icf 파일에 입력: 
initialize manually { section MYSECTION };

C 소스 파일에 입력: 
#pragma section = “MYSECTION“// the section itself
#pragma section = “MYSECTION_init“// the initializer created by linker
void MyInit()
{
char * from = __section_begin(“MYSECTION_init”);
char * to     = __section_begin(“MYSECTION”);
memcpy( to, from, __section_size(“MYSECTION”) );
}

블록(Block) 

Block without content 
메모리 공간을 할당하기에 유용하다. 예를 들어 stack이나 heap을 설정하는 경우다.
define block CSTACK with size = 0x800, alignment = 8 { };
define block HEAP with size = 0x400, alignment = 8 { };

Block with content 
연이어 함께 위치해야 하는 섹션들을 그룹화하는데 유용하다.
// put the functions and constants from lib.a contiguously
define block MYBLOCK { section .text object lib.a, section .rodata object lib.a };

with fixed order가 명시되지 않으면, 순서는 임의적으로 배치된다.
define block MYBLOCK with fixed order { section AA, section BB, section CC };
섹션과 같은 방법으로 블럭의 위치와 크기 정보를 가져올 수 있다.
#pragma section = “MYBLOCK”
char * mylocation = __section_begin(“MYBLOCK”);
int mysize = __section_size(“MYBLOCK”);

오버레이(Overlay) 

여러 번 정의될 수 있는 메모리 블럭이다.
각 정의된 내용은 다른 섹션에 위치할 수 있으나 같은 메모리 블럭이다.
여러 보조 애플리케이션이 같은 메모리 공간에서 실행되는 경우에 유용하다.

Example 
initialize manually { section OverlaidSection1, section OverlaidSection2 };
define overlay MYOVERLAY { section OverlaidSection1 };
define overlay MYOVERLAY { section OverlaidSection2 };
place in RAM { MYOVERLAY };
void SwitchToOverlay1 (void)
{// initializer of OverlaidSection1
        char * from = __section_begin(“OverlaidSection1_init”); 
     char * to = __section_begin(“MYOVERLAY”);
      memcpy( to, from, __section_size(“OverlaidSection1_init”) );
}
void SwitchToOverlay2 (void) 
{// initializer of OverlaidSection2
      char * from = __section_begin(“OverlaidSection2_init”);
       char * to = __section_begin(“MYOVERLAY”);
      memcpy( to, from, __section_size(“OverlaidSection2_init”) );
}

함수의 위치(메모리 번지) 제어 

사용자 정의 섹션 

사용자 정의 섹션에 함수 위치를 지정한다.
void MyFunc (void) @ “.mycode” {…}
or
#pragma location=“.mycode”
 void MyFunc (void) {…}

icf파일에서 섹션 위치를 정의한다.
place … { section .mycode };// place in Flash 
(참고로 place … 은 place in 또는 at 명령어를 간략히 표시한다.)

오브젝트 파일 또는 라이브러리 파일 

오브젝트 파일 또는 라이브러리 파일의 “.text” 섹션을 선택한다.
place … { section .text object MyFile.o };// place in Flash
place … { section .text object MyLib.a };// place in Flash

const의 위치 제어 

절대 번지 
const int MyConst @ 0x1000 = 0x12345678;

사용자 정의 섹션 
const int MyConst @ “.myconst” = 0x12345678;

또는
#pragma location=“.myconst”
const int MyConst = 0x12345678;
place ... { section .myconst };// place in Flash

오브젝트 파일 또는 라이브러리 파일 
place ... { section .rodata object MyFile.o };// place in Flash
place ... { section .rodata object MyLib.a };// place in Flash

변수의 위치 제어(Controlling the address of variables) 

절대 번지 
int MyVar1 @ 0x20001000 = 0x12345678;
int MyVar2 @ 0x20002000;

사용자 정의 섹션 

int MyVar1 @ “.mydata” = 0x12345678;

또는
#pragma location=“.mydata”
int MyVar1 = 0x12345678;
place … { section .mydata };// place in RAM

오브젝트 파일 또는 라이브러리 파일 

place … { section .data object MyFile.o };// place in RAM
place … { section .bss object MyLib.a };      // place in RAM

RAM에서 함수 실행 

“__ramfunc” 키워드 
__ramfunc void MyRamFunc (void) {…}

.textrw_init (Flash) 섹션에서 .textrw (RAM) 섹션으로 복사돼 RAM상에서 실행된다.

사용자 정의 섹션 
void MyRamFunc (void) @ “.myramcode” {…}

또는 r
#pragma location=“.myramcode”
void MyRamFunc (void) {…}
initialize by copy { readwrite, section .myramcode };// copy to RAM
place … { section .myramcode };// place in RAM

오브젝트 파일 또는 라이브러리 파일 
initialize by copy { readwrite, section .text object MyFile.o }; // copy to RAM
place … { section .text object MyFile.o}; // place in RAM

RAM에서 모든 코드 실행 

애플리케이션 시작시에 전체 프로그램이 ROM에서 RAM으로 복사 후에 실행하려면, 다음과 같이 initilize by copy 명령어를 사용한다.
initialize by copy { readonly, readwrite };

초기화 작업을 위한 코드 및 데이터를 제외하고 모든 코드와 데이터가 RAM으로 이동해 동작하게 된다.

맺음말 

IC 파일에서 사용하는 용어 및 명령어들에 대해 살펴뵀다. 특정 번지에 코드를 위치시키거나 변수를 위치시키는 방법 등을 잘 활용한다면 위치시키기 위한 별도의 코드를 작성하지 않아도 편리하게 애플리케이션을 완성할 수 있고 코드의 관리도 용이해  진다.  

글 고성용 IAR 시스템즈 이사 Sung-Yong.Ko@iar.com

이 기사를 공유합니다
저작권자 © 테크월드뉴스 무단전재 및 재배포 금지