링커 스크립터에 관한 문서 ( http://korea.gnu.org/manual/release/ld/ld-mahajjh/ld_3.html#SEC18 )
링커는 입력파일을 합쳐서 하나의 출력파일을 만든다. 링크 스크립터는 링킹 과정에서 링커의 동작을 제어 한다.
커널을 컴파일 하여 arch/arm/boot/compressed/vmlinux 이미지를 생성 할때, 다음과 같은 링킹 과정을 거친다.
arm-linux-ld의 옵션 -T arch/arm/boot/compressed/vmlinux.lds 에 의해서 링커 스크립터를 지정 하였다.
링커는 입력파일을 합쳐서 하나의 출력파일을 만든다. 링크 스크립터는 링킹 과정에서 링커의 동작을 제어 한다.
커널을 컴파일 하여 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
'리눅스커널' 카테고리의 다른 글
[다시 보는 ARM 3.x 커널] 조건 코드 접미사 (0) | 2011.10.04 |
---|---|
[다시 보는 ARM 3.x 커널] ARM Infocenter ( http://infocenter.arm.com ) (0) | 2011.10.04 |
[다시 보는 ARM 3.x 커널] 커널 분석 어디서 부터 시작 하는가? (0) | 2011.09.22 |
[다시 보는 ARM 3.x 커널] make 로그로 분석하는 컴파일 과정 (0) | 2011.09.22 |
[다시 보는 ARM 3.x 커널] 툴체인 설치, 커널 컴파일, 소스 분석 준비 (0) | 2011.09.22 |