'linker script'에 해당되는 글 1건

  1. 2011.09.26 [다시 보는 ARM 3.x 커널] 링커 스크립터 ( Linker Script )
링커 스크립터에 관한 문서 ( http://korea.gnu.org/manual/release/ld/ld-mahajjh/ld_3.html#SEC18 )
 

링커는 입력파일을 합쳐서 하나의 출력파일을 만든다. 링크 스크립터는 링킹 과정에서 링커의 동작을 제어 한다.

커널을 컴파일 하여  arch/arm/boot/compressed/vmlinux 이미지를 생성 할때, 다음과 같은 링킹 과정을 거친다.

/opt/codesourcery/bin/arm-linux-ld -EL --defsym zreladdr=0x40008000 \
  -p --no-undefined -X -T arch/arm/boot/compressed/vmlinux.lds \
  arch/arm/boot/compressed/head.o \
  arch/arm/boot/compressed/piggy.gzip.o \
  arch/arm/boot/compressed/misc.o \
  arch/arm/boot/compressed/decompress.o \
  arch/arm/boot/compressed/lib1funcs.o \
  -o arch/arm/boot/compressed/vmlinux


arm-linux-ld의 옵션 -T arch/arm/boot/compressed/vmlinux.lds 에 의해서 링커 스크립터를 지정 하였다.

-T FILE, --script FILE      Read linker script 


특별히 지정 하지 않았을 경우에는 default linker script를 사용하게 되는데, 이를 보는 옵션은 다음과 같다.
$ arm-linux-ld --verbose
GNU ld (Sourcery G++ Lite 2008q3-41) 2.18.50.20080215
  Supported emulations:
   armelf_linux_eabi
   armelfb_linux_eabi
using internal linker script:
==================================================
/* Script for -z combreloc: combine and sort reloc sections */
OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm",
              "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SEARCH_DIR("=/usr/local/lib"); SEARCH_DIR("=/lib"); SEARCH_DIR("=/usr/lib");
SECTIONS
{
  /* Read-only sections, merged into text segment: */
  PROVIDE (__executable_start = 0x00008000); . = 0x00008000 + SIZEOF_HEADERS;
  .interp         : { *(.interp) }
  .note.gnu.build-id : { *(.note.gnu.build-id) }
  .hash           : { *(.hash) }
  .gnu.hash       : { *(.gnu.hash) }
  .dynsym         : { *(.dynsym) }
  .dynstr         : { *(.dynstr) }
  .gnu.version    : { *(.gnu.version) }
  .gnu.version_d  : { *(.gnu.version_d) }
  .gnu.version_r  : { *(.gnu.version_r) }
  .rel.dyn        :
    {
      *(.rel.init)
      *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*)
      *(.rel.fini)
      *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*)
      *(.rel.data.rel.ro* .rel.gnu.linkonce.d.rel.ro.*)
      *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*)
      *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*)
      *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*)
      *(.rel.ctors)
      *(.rel.dtors)
      *(.rel.got)
      *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*)
    }
  .rela.dyn       :
    {
      *(.rela.init)
      *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
      *(.rela.fini)
      *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
      *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*)
      *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*)
      *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*)
      *(.rela.ctors)
      *(.rela.dtors)
      *(.rela.got)
      *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
    }
  .rel.plt        : { *(.rel.plt) }
  .rela.plt       : { *(.rela.plt) }
  .init           :
  {
    KEEP (*(.init))
  } =0
  .plt            : { *(.plt) }
  .text           :
  {
    *(.text .stub .text.* .gnu.linkonce.t.*)
    /* .gnu.warning sections are handled specially by elf32.em.  */
    *(.gnu.warning)
    *(.glue_7t) *(.glue_7) *(.vfp11_veneer) *(.v4_bx)
  } =0
  .fini           :
  {
    KEEP (*(.fini))
  } =0
  PROVIDE (__etext = .);
  PROVIDE (_etext = .);
  PROVIDE (etext = .);
  .rodata         : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
  .rodata1        : { *(.rodata1) }
  .ARM.extab   : { *(.ARM.extab* .gnu.linkonce.armextab.*) }
   __exidx_start = .;
  .ARM.exidx   : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) }
   __exidx_end = .;
  .eh_frame_hdr : { *(.eh_frame_hdr) }
  .eh_frame       : ONLY_IF_RO { KEEP (*(.eh_frame)) }
  .gcc_except_table   : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) }
  /* Adjust the address for the data segment.  We want to adjust up to
     the same address within the page on the next page up.  */
  . = ALIGN (CONSTANT (MAXPAGESIZE)) - ((CONSTANT (MAXPAGESIZE) - .) & (CONSTANT (MAXPAGESIZE) - 1)); . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE));
  /* Exception handling  */
  .eh_frame       : ONLY_IF_RW { KEEP (*(.eh_frame)) }
  .gcc_except_table   : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }
  /* Thread Local Storage sections  */
  .tdata          : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
  .tbss           : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
  .preinit_array     :
  {
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array))
    PROVIDE_HIDDEN (__preinit_array_end = .);
  }
  .init_array     :
  {
     PROVIDE_HIDDEN (__init_array_start = .);
     KEEP (*(SORT(.init_array.*)))
     KEEP (*(.init_array))
     PROVIDE_HIDDEN (__init_array_end = .);
  }
  .fini_array     :
  {
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(.fini_array))
    KEEP (*(SORT(.fini_array.*)))
    PROVIDE_HIDDEN (__fini_array_end = .);
  }
  .ctors          :
  {
    /* gcc uses crtbegin.o to find the start of
       the constructors, so we make sure it is
       first.  Because this is a wildcard, it
       doesn't matter if the user does not
       actually link against crtbegin.o; the
       linker won't look for a file to match a
       wildcard.  The wildcard also means that it
       doesn't matter which directory crtbegin.o
       is in.  */
    KEEP (*crtbegin.o(.ctors))
    KEEP (*crtbegin?.o(.ctors))
    /* We don't want to include the .ctor section from
       the crtend.o file until after the sorted ctors.
       The .ctor section from the crtend file contains the
       end of ctors marker and it must be last */
    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
    KEEP (*(SORT(.ctors.*)))
    KEEP (*(.ctors))
  }
  .dtors          :
  {
    KEEP (*crtbegin.o(.dtors))
    KEEP (*crtbegin?.o(.dtors))
    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
    KEEP (*(SORT(.dtors.*)))
    KEEP (*(.dtors))
  }
  .jcr            : { KEEP (*(.jcr)) }
  .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro* .gnu.linkonce.d.rel.ro.*) }
  .dynamic        : { *(.dynamic) }
  . = DATA_SEGMENT_RELRO_END (0, .);
  .got            : { *(.got.plt) *(.got) }
  .data           :
  {
    __data_start = . ;
    *(.data .data.* .gnu.linkonce.d.*)
    SORT(CONSTRUCTORS)
  }
  .data1          : { *(.data1) }
  _edata = .; PROVIDE (edata = .);
  __bss_start = .;
  __bss_start__ = .;
  .bss            :
  {
   *(.dynbss)
   *(.bss .bss.* .gnu.linkonce.b.*)
   *(COMMON)
   /* Align here to ensure that the .bss section occupies space up to
      _end.  Align after .bss to ensure correct alignment even if the
      .bss section disappears because there are no input sections.
      FIXME: Why do we need it? When there is no .bss section, we don't
      pad the .data section.  */
   . = ALIGN(. != 0 ? 32 / 8 : 1);
  }
  _bss_end__ = . ; __bss_end__ = . ;
  . = ALIGN(32 / 8);
  . = ALIGN(32 / 8);
  __end__ = . ;
  _end = .; PROVIDE (end = .);
  . = DATA_SEGMENT_END (.);
  /* Stabs debugging sections.  */
  .stab          0 : { *(.stab) }
  .stabstr       0 : { *(.stabstr) }
  .stab.excl     0 : { *(.stab.excl) }
  .stab.exclstr  0 : { *(.stab.exclstr) }
  .stab.index    0 : { *(.stab.index) }
  .stab.indexstr 0 : { *(.stab.indexstr) }
  .comment       0 : { *(.comment) }
  /* DWARF debug sections.
     Symbols in the DWARF debugging sections are relative to the beginning
     of the section so we begin them at 0.  */
  /* DWARF 1 */
  .debug          0 : { *(.debug) }
  .line           0 : { *(.line) }
  /* GNU DWARF 1 extensions */
  .debug_srcinfo  0 : { *(.debug_srcinfo) }
  .debug_sfnames  0 : { *(.debug_sfnames) }
  /* DWARF 1.1 and DWARF 2 */
  .debug_aranges  0 : { *(.debug_aranges) }
  .debug_pubnames 0 : { *(.debug_pubnames) }
  /* DWARF 2 */
  .debug_info     0 : { *(.debug_info .gnu.linkonce.wi.*) }
  .debug_abbrev   0 : { *(.debug_abbrev) }
  .debug_line     0 : { *(.debug_line) }
  .debug_frame    0 : { *(.debug_frame) }
  .debug_str      0 : { *(.debug_str) }
  .debug_loc      0 : { *(.debug_loc) }
  .debug_macinfo  0 : { *(.debug_macinfo) }
  /* SGI/MIPS DWARF 2 extensions */
  .debug_weaknames 0 : { *(.debug_weaknames) }
  .debug_funcnames 0 : { *(.debug_funcnames) }
  .debug_typenames 0 : { *(.debug_typenames) }
  .debug_varnames  0 : { *(.debug_varnames) }
  /* DWARF 3 */
  .debug_pubtypes 0 : { *(.debug_pubtypes) }
  .debug_ranges   0 : { *(.debug_ranges) }
  .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }
  .note.gnu.arm.ident 0 : { KEEP (*(.note.gnu.arm.ident)) }
  /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) }
}


==================================================


실제로 arch/arm/boot/compressed/vmlinux 이미지를 생성 할때 사용되는 링커 스크립터를 보자.
아래는 arch/arm/boot/compressed/vmlinux.lds 파일의 내용이다.
 

/*
 *  linux/arch/arm/boot/compressed/vmlinux.lds.in
 *
 *  Copyright (C) 2000 Russell King
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
  /DISCARD/ : {
    *(.ARM.exidx*)
    *(.ARM.extab*)
    /*
     * Discard any r/w data - this produces a link error if we have any,
     * which is required for PIC decompression.  Local data generates
     * GOTOFF relocations, which prevents it being relocated independently
     * of the text/got segments.
     */
    *(.data)
  }

  . = 0; /* 주소를 0x0 으로 지정한다. */
  _text = .; /* 이 곳에 심볼 _text 를 정의한다. 코드 영역의 시작 지정 */

  .text : { /* 섹션이름 .text 영역 정의 */
    _start = .; /* 이 곳에 심볼 _start 를 정의한다. */
    *(.start) /* .start 섹션으로 정의된 코드 부터 차례로 배열 된다. */
    *(.text)
    *(.text.*)
    *(.fixup)
    *(.gnu.warning)
    *(.glue_7t)
    *(.glue_7)
  }
  .rodata : { /* 섹션이름 .rodata 영역 정의 */
    *(.rodata)
    *(.rodata.*)
  }
  .piggydata : { /* 섹션이름 .piggydata 영역 정의 */
    *(.piggydata)
  }

  . = ALIGN(4);
  _etext = .; /* 이 곳에 심볼 _etext 를 정의한다. 코드 영역의 끝 지정 */

  .got.plt : { *(.got.plt) }
  _got_start = .;
  .got : { *(.got) } /* got 영역 */
  _got_end = .;
  _edata = .;

  . = ALIGN(8);
  __bss_start = .;
  .bss : { *(.bss) } /* bss 영역 */
  _end = .;

  . = ALIGN(8); /* the stack must be 64-bit aligned */
  .stack : { *(.stack) }

  .stab 0 : { *(.stab) }
  .stabstr 0 : { *(.stabstr) }
  .stab.excl 0 : { *(.stab.excl) }
  .stab.exclstr 0 : { *(.stab.exclstr) }
  .stab.index 0 : { *(.stab.index) }
  .stab.indexstr 0 : { *(.stab.indexstr) }
  .comment 0 : { *(.comment) }

} 


스크립터에 사용 된 명령을 살펴 보자.
OUT_ARCH(arm) : 출력 아키텍쳐를 지정한다.
ENTRY(_start) : 프로그램에서 처음 실행되는 진입점이다. 심볼명을 지정한다.
SECTION  { } : 링커가 어떻게 입력 섹션을 출력 섹션으로 대응하고, 출력 섹션을 메모리에 배치하는지를 지정한다.

'/DISCARD/' : 특별한 출력 세션 이름으로 입력 섹션을 버리는 용도로 사용한다. 이 섹션으로 지정된 입력은 실제로 출력으로 배치되지 않는다.

objdump -h 명령으로 어떠한 섹션이 있는지 살펴 보자.

$ arm-linux-objdump -h arch/arm/boot/compressed/vmlinux

arch/arm/boot/compressed/vmlinux:     file format elf32-littlearm

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .text         00003b50  00000000  00000000  00008000  2**5
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .rodata       00000c94  00003b50  00003b50  0000bb50  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  2 .piggydata    0012bf8c  000047e4  000047e4  0000c7e4  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  3 .got.plt      0000000c  00130770  00130770  00138770  2**2
                  CONTENTS, ALLOC, LOAD, DATA
  4 .got          0000002c  0013077c  0013077c  0013877c  2**2
                  CONTENTS, ALLOC, LOAD, DATA
  5 .bss          00000020  001307a8  001307a8  001387a8  2**2
                  ALLOC
  6 .stack        00001000  001307c8  001307c8  001387a8  2**0
                  ALLOC
  7 .comment      00000054  00000000  00000000  001387a8  2**0
                  CONTENTS, READONLY
  8 .ARM.attributes 00000029  00000000  00000000  001387fc  2**0
                  CONTENTS, READONLY
  9 .debug_line   0000107d  00000000  00000000  00138825  2**0
                  CONTENTS, READONLY, DEBUGGING
 10 .debug_info   000018bc  00000000  00000000  001398a2  2**0
                  CONTENTS, READONLY, DEBUGGING
 11 .debug_abbrev 00000633  00000000  00000000  0013b15e  2**0
                  CONTENTS, READONLY, DEBUGGING
 12 .debug_aranges 00000088  00000000  00000000  0013b798  2**3
                  CONTENTS, READONLY, DEBUGGING
 13 .debug_ranges 00000548  00000000  00000000  0013b820  2**3
                  CONTENTS, READONLY, DEBUGGING
 14 .debug_frame  000001f8  00000000  00000000  0013bd68  2**2
                  CONTENTS, READONLY, DEBUGGING
 15 .debug_loc    00002791  00000000  00000000  0013bf60  2**0
                  CONTENTS, READONLY, DEBUGGING
 16 .debug_pubnames 000001a4  00000000  00000000  0013e6f1  2**0
                  CONTENTS, READONLY, DEBUGGING
 17 .debug_str    00000688  00000000  00000000  0013e895  2**0
                  CONTENTS, READONLY, DEBUGGING


출력되는 섹션의 배치를 간략하게 그림으로 표현했다.



코드상에서 정의된 심볼의 값

        .word   0x016f2818                                 |      24:   016f2818    .word   0x016f2818
        .word   start                                         |      28:   00000000    .word   0x00000000
        .word   _edata                                      |      2c:   001307a8    .word   0x001307a8


        .type   LC0, #object                               |0000017c <LC0>:
LC0:        .word   LC0                                    |     17c:   0000017c    .word   0x0000017c
        .word   __bss_start                                |     180:   001307a8    .word   0x001307a8
        .word   _end                                         |     184:   001307c8    .word   0x001307c8
        .word   _edata                                      |     188:   001307a8    .word   0x001307a8
        .word   input_data_end - 4                     |     18c:   0013076c    .word   0x0013076c
        .word   _got_start                                  |     190:   0013077c    .word   0x0013077c
        .word   _got_end                                   |     194:   001307a8    .word   0x001307a8
        .word   .L_user_stack_end                     |     198:   001317c8    .word   0x001317c8
        .size   LC0, . - LC0                               |     19c:   e1a00000    .word   0xe1a00000


Posted by blee
,