4/07/2018

Linker script

1. Linker Script 란? 

Linker Script or Linker Command 라고 불리우며, Compiler에 따라 이 이름은 달라질 수 있다.
기능은 Linker에게 Command/Script로 명령을 주어 관련 Program을 환경설정을 변경하는 것이 주목적이다

일반 애플리케이션 사용자는 사용할 일은 거의 없을 것이라고 생각되며, Kernel / Uboot 같은 소스를 수정하는 사람들이 주로 사용하지만 
요즘은 거의 사용할 일은 없다.
예를 들면, 주로 수정하여 고친다면, Interrupt Service Routine 을 새로 만들어서 SRAM에 올리거나, 
특정 Program 안에, 특정 Address 에 특정 Section을 만들어 할당하거나, 
나만의 Section을 만들어 특정 Address 할당 하는데 주로 사용한다. (ROM/RAM/SRAM)


1.1. Linker Script 기본 Manual

일반적으로 컴파일러는 자동으로 Linker Script를 생성해서 만들어주므로, 크게 신경을 쓰지 않아도 된다. 
하지만, 일반적으로 OS / Boot Loader 및 MCU 기반의 Compiler들은 ELF format을 직접이용하기보다는 특정영역에 확보하고 사용해야하는 프로그램은
관련 세부설정 부분들을 직접정의하고 각 소스에 이를 설정하여 연결한다.

주로확장자는 .ld or lds로 되어있지만, 컴파일러마다 조금씩 다를수 있으며, 이 파일들을 찾아 아래 메뉴얼들과 같이 보며 
기본동작 방식을 이해하면 될 것 같다.
항상 세부적인것은 현재 사용중인 컴파일러 메뉴얼기반으로 봐야함



SECTIONS
{
  . = 0x10000;
  .text : { *(.text) }
  . = 0x8000000;
  .data : { *(.data) }
  .bss : { *(.bss) }
}

  • 상위 SECTIONS 구성
  1. .text: code의 영역
  2. .data: 초기화된 전역변수 or static 변수
  3. .bss : 비초기화된 전역변수 or static 변수
  4. heap: bss 다음으로 연결되어지며, malloc/calloc과 연결 
  5. .noinit: init를 하지 않는 전역변수   
heap은 동적 Memory 저장하며, 일반적으로 stack은 함수호출 및 일반변수의 저장을 담당하며, heap기반으로 구성 
  https://en.wikipedia.org/wiki/Data_segment
  

1.2  GCC의 MACRO attribute

GCC Compiler의 MACRO인 attribute 설정과 Linker Script 밀접한 관계에 있으며, 이는 곳 특정영역에 넣는 것이 가능하다. 
예를드면,  C/C++ Code에 MACRO인 attribute를 설정하여 Linker script와 연결하여 동작가능하다.
이는 GCC 뿐만 아니라 다른 Compiler라도 사용방법은 얼추 비슷하며, Linker Script의 이름은 변경될 수가 있다. (TI Compiler는 Linker Command)
이외 Compiler 전용 MACRO가 있으므로, 그 부분은 각 Complier Manual 참조 

Attribute는 Function 과 Variable 에 사용될 수 있으며, 각각 설정방법은 Manual을 보자.

  • GCC Manual Index
GCC 전체 Manual 에 Index 기능으로 쉽게 보고자하는 파트를 찾을수 있다.
  https://gcc.gnu.org/onlinedocs/gcc-4.8.5/gcc/

  • MACRO  __attribute__
__attribute__로 Section을 설정하여, 함수(Function)과 변수(variable)를 Linker Script의 특정 Section에 할당가능하다. 
Function에 속성을 설정하여 Linker Script와 같이 연동하여 특정영역에 놓을 수도 있으며, 할당크기 및 다양한 설정이 가능하다
Variable , 즉 변수도 Linker Script와 같이 연동하여 특정영역뿐만 아니라 다양한 속성설정이 가능하다.
e.g. __atrribute__(section)
  https://gcc.gnu.org/onlinedocs/gcc-4.8.5/gcc/Function-Attributes.html#Function-Attributes
  https://gcc.gnu.org/onlinedocs/gcc-4.8.5/gcc/Variable-Attributes.html#Variable-Attributes


  • MACRO #pragmas
  https://gcc.gnu.org/onlinedocs/gcc-4.8.5/gcc/Pragmas.html#Pragmas
  https://gcc.gnu.org/onlinedocs/gcc-4.8.5/gcc/ARM-Pragmas.html#ARM-Pragmas



1.3 Kernel Linker Script 분석예제

Uboot/Kernel의 Linker Script를 분석들을 기본적으로 보고 이해를 하면될 것이고 더나아가 ISR 수정하거나, 변경한다면, 관련부분 전용 Linker Script 수정하면 될거 같다.

$ find . -name *.lds
$ vi kernel/arch/arm/boot/compressed/vmlinux.lds
$ vi kernel/arch/arm/kernel/vmlinux.lds

Linker Script 및 Kernel에 자료는 인터넷에 풍부하므로, 정리하지 않고, Link만 연결


  • Makefile 과 Kernel Linker Script 분석
Makefile 과 Linker Script는 밀접하게 연관되어 있으며, 우선적으로 Makefile부터 분석해야, 현재 사용중인 Linker Script도 확인가능 
  https://wiki.kldp.org/KoreanDoc/html/EmbeddedKernel-KLDP/arm.makefile.html

  • Kernel Linker Script 분석한 Blog들
Kernel Linker Script를 보면, 다양한 Section들이 나오며, 각 Section 의미를 알아보고 분석하자.
본인이 원하는 새로운 Section에 새로운 이름으로 만들어 추가도 가능하다. 
아래보기전에, Linker Script Manual부터 확인 
LDFLAGS 와 각 CONFIG기반의 Address 와 소스확인 
  https://elixir.bootlin.com/linux/v3.19.7/source/arch/arm/Makefile

ARM Kernel Image
build 후에는 System.map기반으로 확인하고, boot 관련부분을 확인 
반드시 Kernel CONFIG와 같이 확인
  https://elixir.bootlin.com/linux/v3.19.7/source/arch/arm/boot
  https://elixir.bootlin.com/linux/v3.19.7/source/arch/arm/boot/compressed/Makefile
MMU/SMP/XIP/TCM 등 각 설정기반의 설정확인 
  http://elixir.free-electrons.com/linux/v3.19.7/source/arch/arm/boot/compressed/vmlinux.lds.S

ARM Kernel Vmlinux
  https://elixir.bootlin.com/linux/v3.19.7/source/arch/arm/kernel
  https://elixir.bootlin.com/linux/v3.19.7/source/arch/arm/kernel/Makefile
  http://elixir.free-electrons.com/linux/v3.19.7/source/arch/arm/kernel/vmlinux.lds.S

빌드후, 아래와 같이 arch/arm/kernel/vmlinux.lds 생성

ARM IRQ 관련 부분
  http://elixir.free-electrons.com/linux/v3.19.7/source/arch/arm/kernel/irq.c
  https://elixir.bootlin.com/linux/v3.19.7/source/arch/arm/include/asm/exception.h#L14
  http://elixir.free-electrons.com/linux/v3.19.7/source/arch/arm/kernel/fiq.c

IRQENTRY_TEST
  https://elixir.bootlin.com/linux/v3.19.7/source/arch/arm/kernel/vmlinux.lds.S
  https://elixir.bootlin.com/linux/v3.19.7/source/include/asm-generic/vmlinux.lds.h#L440


  • ARM의 IRQ 부분 
관련된 소스를 간단하게 역추적하면서 어떻게 사용하는 지 분석해보자.

---------------------------arch/arm/kernel/irq.c
asmlinkage void __exception_irq_entry       // Exception 영역으로 
asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{
 handle_IRQ(irq, regs);
}
---------------------------arch/arm/include/asm/exception.h
#define __exception __attribute__((section(".exception.text")))  // GCC attribute 관련부분 참조 
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
#define __exception_irq_entry __irq_entry
#else
#define __exception_irq_entry __exception
#endif
 
---------------------------include/asm-generic/vmlinux.lds.h

#ifdef CONFIG_FUNCTION_GRAPH_TRACER   // CONFIG가 존재하면 사용함 
#define IRQENTRY_TEXT       \
  ALIGN_FUNCTION();     \
  VMLINUX_SYMBOL(__irqentry_text_start) = .;  \
  *(.irqentry.text)     \
  VMLINUX_SYMBOL(__irqentry_text_end) = .;
#else
#define IRQENTRY_TEXT
#endif

---------------------------arch/arm/kernel/vmlinux.lds.S

.text : {   /* Real text segment  */
  _stext = .;  /* Text and read-only data */
   __exception_text_start = .;
   *(.exception.text)         // 상위 exception 영역 
   __exception_text_end = .;
   IRQENTRY_TEXT      // header file define 되어있음 
   TEXT_TEXT
   SCHED_TEXT
   LOCK_TEXT
   KPROBES_TEXT
   IDMAP_TEXT
#ifdef CONFIG_MMU
   *(.fixup)
#endif
   *(.gnu.warning)
   *(.glue_7)
   *(.glue_7t)
  . = ALIGN(4);
  *(.got)   /* Global offset table  */
   ARM_CPU_KEEP(PROC_INFO)
 }


asmlikage 의 의미 
  http://egloos.zum.com/studyfoss/v/4951809

  • ARM9에서 많이 사용했던 TCM 관련부분 
ARM9에서 TCM(Tightly Coupled Memory)는 주로 SRAM용도로 사용하며, 아래와 같이 ITCM(Instruction)/DTCM(Data)으로 분리하여 사용한다.
ARM9뿐만아니라, Cortex에서도 사용되며, 예를 들면, Deep Sleep용 ISR 기능 뿐만아니라, 다양한 빠른 Latency 목적으로 사용가능하다고 한다.
 
#ifdef CONFIG_HAVE_TCM
 /*
  * We align everything to a page boundary so we can
  * free it after init has commenced and TCM contents have
  * been copied to its destination.
  */
 .tcm_start : {  //.tcm_start는 Instruction으로 시작
  . = ALIGN(PAGE_SIZE);              //PAGE SIZE 할당
  __tcm_start = .;            //__tcm_start  : . 현재 Address 할당 
  __itcm_start = .;                  //__itcm_start : . 현재 Address 할당 (아래참고)   
 }

 /*
  * Link these to the ITCM RAM
  * Put VMA to the TCM address and LMA to the common RAM
  * and we'll upload the contents from RAM to TCM and free
  * the used RAM after that.
  */
 .text_itcm ITCM_OFFSET : AT(__itcm_start)   // AT(ADDRESS)이므로, .text_itcm을 __itcm_start로 연결
 {
  __sitcm_text = .;    // Start ITCM TEXT 
  *(.tcm.text)         // ITCM TEXT로 Instruction  
  *(.tcm.rodata)
  . = ALIGN(4);        // ALIGN 2의 지수로만 할당(공백할당) 
  __eitcm_text = .;    // End ITCM TEXT 
 }

 /*
  * Reset the dot pointer, this is needed to create the
  * relative __dtcm_start below (to be used as extern in code).
  */
 . = ADDR(.tcm_start) + SIZEOF(.tcm_start) + SIZEOF(.text_itcm);  // 현재 Address를 변경 사이즈기반 

 .dtcm_start : {   // .tcm_start 뒤에 .dtcm_start 구성 (현재 Address 할당) 
  __dtcm_start = .;
 }

 /* TODO: add remainder of ITCM as well, that can be used for data! */
 .data_dtcm DTCM_OFFSET : AT(__dtcm_start)  // AT(ADDRESS)이므로, .data_dtcm을 __dtcm_start 연결
 {
  . = ALIGN(4);       // 공백할당 2의 지수로만 가능 
  __sdtcm_data = .;   // Start DTCM Data 
  *(.tcm.data)       // TCM Data 
  . = ALIGN(4);
  __edtcm_data = .;  // End DTCM Data  
 }

 /* Reset the dot pointer or the linker gets confused */
 . = ADDR(.dtcm_start) + SIZEOF(.data_dtcm);  // 사이즈 측정하여 현재 Address 변경 

 /* End marker for freeing TCM copy in linked object */
 .tcm_end : AT(ADDR(.dtcm_start) + SIZEOF(.data_dtcm)){  // .tcm_end에 itcm 과 dtcm 끝나는 곳으로 설정 
  . = ALIGN(PAGE_SIZE);
  __tcm_end = .;
 }
#endif



  • Davinci Deep Sleep Source (TCM이용)
오래전에, 이것때문에 TI에서 교육을 받은 적이 있는데, 이렇게 오픈되어있다니 좋다 
소스의 내용은 Sleep할때는 DRAM을 Self Refresh로 변경하여 Deep Sleep으로 가며, 
깨어날때는 DRAM을 사용할 수 없으니, TCM기반의 ISR로 SDRAM 설정변경해주는고 원래대로 돌아가는 방식 
  https://www.mail-archive.com/davinci-linux-open-source@linux.davincidsp.com/msg04407.html

  • setup_irq
  http://elixir.free-electrons.com/linux/v3.19.7/source/kernel/irq
  http://elixir.free-electrons.com/linux/v3.19.7/source/kernel/irq/manage.c
  http://elixir.free-electrons.com/linux/v3.19.7/ident/setup_irq

  • request_irq
  http://elixir.free-electrons.com/linux/v3.19.7/ident/request_irq
  http://elixir.free-electrons.com/linux/v3.19.7/source/include/linux/interrupt.h#L128
  https://free-electrons.com/docs/

  • IRQ Proc 정보 등록
  http://elixir.free-electrons.com/linux/v3.19.7/source/kernel/irq/proc.c



2. 특정 Address에 원하는 Data 넣기 

variable attribute 와 Linker script를 이용하여, 본인이 원하는 위치에 데이타를 저장이 가능하다.


  • 저장할 Data를 Code에 Variable Attribute를 이용하여 Section 선언
 unsigned char data[48]  __attribute__((section(".mySection")))  = {0}; 


  • Variable Attribute 와 같이 Linker Script 수정 
Linker Script에서 관련 Section에 0x80200000 에 위치하도록 설정하고 KEEP를 사용하자.

SECTIONS    //SECTIONS Command로 나만의 Region 만들고 이를 Memory 내에 구성된 이름에 할당 
{
  .mySegment 0x80200000 : 
  {
     KEEP(*(.mySection))
  } 
}

SECTIONS Command 
  http://korea.gnu.org/manual/release/ld/ld-mahajjh/ld_3.html#SEC18

  • MPU의 예제구성 
MPU의 경우는 아래와 같이 MEMORY Layout이 정의되어있으므로, 이 부분 확인 

MEMORY           //MEMORY Command기반으로 각 Segment/Name 정의 (주소,길이,RWX) 
{
RAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 64K           // RAM Segment (Read and Write and Excute) 
FLASH (rx)      : ORIGIN = 0x08000000, LENGTH = 64K        // FLASH Segment (Read and Excute) 
}

SECTIONS
{
  .... // 다른 Segment로 구성하고 나의 영역만들고, 이를 FLASH Segment/Name 할당해서 연결 
  .mySegment 0x80200000 : 
  {
     KEEP(*(.mySection))                // 상위소스에서  __attribute__를 이용하여 이곳에 할당 
  } >FLASH 
} 

MEMORY Command

상위내용
  https://stackoverflow.com/questions/4067811/how-to-place-a-variable-at-a-given-absolute-address-in-memory-with-gcc