- 浏览: 44624 次
- 来自: 杭州
文章分类
最新评论
本文详细解释一个 ld.script 文件
可以通过以下命令查看系统默认使用的链接脚本:
$ ld -verbose #输出如下
GNU ld (GNU Binutils for Ubuntu) 2.20.1-system.20100303
Supported emulations:
elf_i386
i386linux
elf_x86_64
elf_l1om
using internal linker script:
==================================================
/* Script for -z combreloc: combine and sort reloc sections */
OUTPUT_FORMAT("elf32-i386", "elf32-i386",
"elf32-i386")
OUTPUT_ARCH(i386)
ENTRY(_start)
SEARCH_DIR("/usr/i486-linux-gnu/lib32"); SEARCH_DIR("/usr/local/lib32");
SEARCH_DIR("/lib32"); SEARCH_DIR("/usr/lib32"); SEARCH_DIR("/usr/i486-linux-gnu/lib");
SEARCH_DIR("/usr/local/lib"); SEARCH_DIR("/lib"); SEARCH_DIR("/usr/lib");
SECTIONS
{
/* Read-only sections, merged into text segment: */
PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x08048000));
. = SEGMENT_START("text-segment", 0x08048000) + 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.*)
*(.rel.ifunc)
}
.rel.plt :
{
*(.rel.plt)
PROVIDE_HIDDEN (__rel_iplt_start = .);
*(.rel.iplt)
PROVIDE_HIDDEN (__rel_iplt_end = .);
}
.init :
{
KEEP (*(.init))
} =0x90909090
.plt : { *(.plt) *(.iplt) }
.text :
{
*(.text.unlikely .text.*_unlikely)
*(.text .stub .text.* .gnu.linkonce.t.*)
/* .gnu.warning sections are handled specially by elf32.em. */
*(.gnu.warning)
} =0x90909090
.fini :
{
KEEP (*(.fini))
} =0x90909090
PROVIDE (__etext = .);
PROVIDE (_etext = .);
PROVIDE (etext = .);
.rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
.rodata1 : { *(.rodata1) }
.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) }
.got : { *(.got) *(.igot) }
. = DATA_SEGMENT_RELRO_END (12, .);
.got.plt : { *(.got.plt) *(.igot.plt) }
.data :
{
*(.data .data.* .gnu.linkonce.d.*)
SORT(CONSTRUCTORS)
}
.data1 : { *(.data1) }
_edata = .; PROVIDE (edata = .);
__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);
}
. = ALIGN(32 / 8);
. = ALIGN(32 / 8);
_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)) }
/DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) }
}
==================================================
下面逐句解释。
OUTPUT_FORMAT("elf32-i386", "elf32-i386",
"elf32-i386")
OUTPUT_ARCH(i386)
OUTPUT_FORMAT 和 OUTPUT_ARCH 都是 ld 脚本的保留字命令。OUTPUT_FORMAT 说明输出二进制文件的格式。OUTPUT_ARCH 说明输出文件系统平台。
ENTRY(_start)
ld 有多种方法设置进程入口地址,通常它按以下顺序:(编号越前, 优先级越高)
1, ld 命令行的-e选项
2, 连接脚本的 ENTRY(SYMBOL) 命令
3, 如果定义了 start 符号, 使用 start 符号值
4, 如果存在 .text section, 使用 .text section 的第一字节的位置值
5, 使用值 0
SEARCH_DIR("/usr/i486-linux-gnu/lib32");
设置链接时搜寻库文件目录.
SECTIONS
{
然后,接下来是一大段的 SECTIONS,对应的右大括号直到脚本的末尾。
SECTIONS 命令告诉 ld 如何把输入文件的 sections 映射到输出文件的各个 section:即是如何将输入 section 合为输出 section;如何把输出 section 放入程序地址空间 (VMA) 和进程地址空间 (LMA) 。该命令格式如下:
SECTIONS
{
….
}
SECTIONS 命令告诉 ld 如何把输入文件的 sections 映射到输出文件的各个 section:即是如何将输入 section 合为输出 section;如何把输出 section 放入程序地址空间 (VMA) 和进程地址空间 (LMA) 。该命令格式如下:
SECTIONS
{
….
}
/* Read-only sections, merged into text segment: */
PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x08048000));
PROVIDE 定义的变量 如果源文件中已经定义值 那么用源文件中的,如果没有定义则用脚本中定义的。
并设定该变量的值为0x08048000
. = SEGMENT_START("text-segment", 0x08048000) + SIZEOF_HEADERS;
这句把定位器符号置为 0x08048000 + SIZE_HEADERS(若不指定,则该符号的初始值为 0)。 SIZE_HEADERS为输出文件的文件头.
. 是一个特殊的符号,它是定位器,一个位置指针,指向程序地址空间内的某位置(或某section内的偏移,如果它在SECTIONS命令内的某section描述内),该符号只能在SECTIONS命令内使用。
. 是一个特殊的符号,它是定位器,一个位置指针,指向程序地址空间内的某位置(或某section内的偏移,如果它在SECTIONS命令内的某section描述内),该符号只能在SECTIONS命令内使用。
.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.*)
*(.rel.ifunc)
}
.rel.plt :
{
*(.rel.plt)
PROVIDE_HIDDEN (__rel_iplt_start = .);
*(.rel.iplt)
PROVIDE_HIDDEN (__rel_iplt_end = .);
}
以上这些段主要用于重定位.具体的内容还需进一步学习.先不做介绍.
{
*(.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.*)
*(.rel.ifunc)
}
.rel.plt :
{
*(.rel.plt)
PROVIDE_HIDDEN (__rel_iplt_start = .);
*(.rel.iplt)
PROVIDE_HIDDEN (__rel_iplt_end = .);
}
以上这些段主要用于重定位.具体的内容还需进一步学习.先不做介绍.
.init :
{
KEEP (*(.init))
} =0x90909090
.plt : { *(.plt) *(.iplt) }
.text :
{
*(.text.unlikely .text.*_unlikely)
*(.text .stub .text.* .gnu.linkonce.t.*)
/* .gnu.warning sections are handled specially by elf32.em. */
*(.gnu.warning)
} =0x90909090
.init将在下文与.fini一起介绍.
.text : 表示text段开始.
*(.text) 将所有(*符号代表任意输入文件)输入文件的.text section合并成一个.text section, 该section的地址由定位器符号的值指定, 即0x08048000.
*(.text) 将所有(*符号代表任意输入文件)输入文件的.text section合并成一个.text section, 该section的地址由定位器符号的值指定, 即0x08048000.
*(.text.unlikely .text.*_unlikely)
*(.text .stub .text.* .gnu.linkonce.t.*)
/* .gnu.warning sections are handled specially by elf32.em. */
*(.gnu.warning)
.fini : { KEEP (*(.fini)) #
KEEP()强制连接器保留一些特定的section} =0x90909090
ELF文件中定义了 .init 和 .fini 两个特殊的段,其中 .init 段中的代码会在main之前被执行,.fini 段中的代码会在main退出之后被执行.默认用NOP(0x90)字段进行填充.
PROVIDE (__etext = .);
PROVIDE (_etext = .);
PROVIDE (etext = .);
这里就定义了一个 etext 符号,当目标文件内引用了 etext 符号,却没有定义它时,etext 符号对应的地址被定义为 .text section 之后的第一个字节的地址。
.rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
.rodata1 : { *(.rodata1) }
数据段终于来到了,意思很容易理解的了(用于只读数据段)。
/* 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));
如注释所述用于地址调整,为数据段作相应的地址对齐.
/* Thread Local Storage sections */ .tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) } .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
正如注释所描述,这两个段用于线程的局部数据段(包括data及bss)
CONSTRUCTORS 是一个保留字命令。与 c++ 内的(全局对象的)构造函数和(全局对像的)析构函数相关。
.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))
}
当连接器生成的目标文件格式不支持任意section名字时,比如说ECOFF、XCOFF格式,连接器将通过名字来识别全局构造和全局析构,对于这些文件格式,连接器把与全局构造和全局析构的相关信息放入出现 CONSTRUCTORS 关键字的输出section内。
一般来说,GNU C++在函数__main内安排全局构造代码的运行,而__main函数被初始化代码(在main函数调用之前执行)调用。
.got : { *(.got) *(.igot) }
. = DATA_SEGMENT_RELRO_END (12, .);
.got.plt : { *(.got.plt) *(.igot.plt) }
_edata = .; PROVIDE (edata = .);
__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);
}
. = ALIGN(32 / 8);
. = ALIGN(32 / 8);
_end = .; PROVIDE (end = .);
. = DATA_SEGMENT_END (.);
COMMON 这个保留字的意义:
通用符号(common symbol)的输入section:
在许多目标文件格式中,通用符号并没有占用一个section。连接器认为:输入文件的所有通用符号在名为COMMON的section内。
上例中将所有输入文件的所有通用符号放入输出.bss section内。
这里,定义了几个重要的符号
__bss_start = .;
_end = .;
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)) }
这里主要包含代码的调试信息,用于调试时使用.包括一些行号信息及符号表等.
/DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) }
DISCARD关键字用于将指定段舍弃,不出现在输出文件中.
余下的这几个意义也类似,看英文注释应该能明白。
对初步编译出来的一个二进制文件进行 nm 解析(nm -n a.out),得到如下内容:
U _IO_getc@@GLIBC_2.0
w _Jv_RegisterClasses
U __ctype_b_loc@@GLIBC_2.3
U __errno_location@@GLIBC_2.0
w __gmon_start__
U __isoc99_sscanf@@GLIBC_2.7
U __libc_start_main@@GLIBC_2.0
U __stack_chk_fail@@GLIBC_2.4
U exit@@GLIBC_2.0
U fclose@@GLIBC_2.1
U feof@@GLIBC_2.0
U fgets@@GLIBC_2.0
U fopen@@GLIBC_2.1
U printf@@GLIBC_2.0
U sprintf@@GLIBC_2.0
U strerror@@GLIBC_2.0
U strtol@@GLIBC_2.0
U vfprintf@@GLIBC_2.0
080485a8 T _init
08048700 T _start
08048730 t __do_global_dtors_aux
08048790 t frame_dummy
080487b4 T die
080487e7 T print_name_pid
08048904 T print_maps
08048be3 T parse_pid
08048c3a T main
08048cc0 T __libc_csu_fini
08048cd0 T __libc_csu_init
08048d2a T __i686.get_pc_thunk.bx
08048d30 t __do_global_ctors_aux
08048d5c T _fini
08048d78 R _fp_hw
08048d7c R _IO_stdin_used
08048e80 r __FRAME_END__
08049f0c d __CTOR_LIST__
08049f0c d __init_array_end
08049f0c d __init_array_start
08049f10 d __CTOR_END__
08049f14 d __DTOR_LIST__
08049f18 D __DTOR_END__
08049f1c d __JCR_END__
08049f1c d __JCR_LIST__
08049f20 d _DYNAMIC
08049ff4 d _GLOBAL_OFFSET_TABLE_
0804a044 D __data_start
0804a044 W data_start
0804a048 D __dso_handle
0804a04c A __bss_start
0804a04c A _edata
0804a04c B stderr@@GLIBC_2.0
0804a050 b completed.7021
0804a054 b dtor_idx.7023
0804a058 A _end
可以看到,所有的地址全是从 0x08048000 开始的。
U表示该符号未定义,主要是动态链接库中的符号,在目标文件加载入内存的时候再进行解析.
w表示该符号为弱符号
U _IO_getc@@GLIBC_2.0 w _Jv_RegisterClasses U __ctype_b_loc@@GLIBC_2.3 U __errno_location@@GLIBC_2.0
w __gmon_start__
U __isoc99_sscanf@@GLIBC_2.7 U __libc_start_main@@GLIBC_2.0 U __stack_chk_fail@@GLIBC_2.4 U exit@@GLIBC_2.0 U fclose@@GLIBC_2.1 U feof@@GLIBC_2.0 U fgets@@GLIBC_2.0 U fopen@@GLIBC_2.1 U printf@@GLIBC_2.0 U sprintf@@GLIBC_2.0 U strerror@@GLIBC_2.0 U strtol@@GLIBC_2.0 U vfprintf@@GLIBC_2.0
两个个起始符号(T表示在text段中)
080485a8 T _init
08048700 T _start
几个函数都在代码段内
08048730 t __do_global_dtors_aux
08048790 t frame_dummy
080487b4 T die
080487e7 T print_name_pid
08048904 T print_maps
08048be3 T parse_pid
08048c3a T main
栈底地址果然是在 start 下方 0x4000 处
800fc000 T stack
(A 表示绝对不变)
0804a04c A __bss_start 0804a04c A _edata
0804a058 A _end
全局构造和析构变量段(D表示在已初始化过的数据段中)
08049f0c d __CTOR_LIST__
08049f0c d __init_array_end
08049f0c d __init_array_start
08049f10 d __CTOR_END__
08049f14 d __DTOR_LIST__
08049f18 D __DTOR_END__
08049f1c d __JCR_END__
08049f1c d __JCR_LIST__
08049f20 d _DYNAMIC
08049ff4 d _GLOBAL_OFFSET_TABLE_
0804a044 D __data_start
0804a044 W data_start
0804a048 D __dso_handle
还有一个是用 B 标记的,表示在未初始化的数据段中
0804a04c B stderr@@GLIBC_2.0
0804a050 b completed.7021
0804a054 b dtor_idx.7023
上面符号类型可以查看nm命令的man帮助文档.
结合这些数据,去理解前面的 ld.script 的讲解,会有一个清晰的印象。发表评论
-
fedora系统删除多余内核
2013-01-22 21:32 1759查看本地系统安装的内核版本: $rpm -q ... -
Ubuntu change GNOME to XFCE problem
2012-12-14 16:10 850I'm now experiencing this probl ... -
Signal信号
2012-10-07 12:55 01) SIGHUP 本信号在用户终端连接(正常或非正常)结 ... -
Nginx
2012-09-20 23:38 0nginx (pronounced "engine ... -
Linux 灾难恢复
2012-09-19 21:57 0简介: Linux 发行版本 ... -
close_on_exec标志位
2012-09-06 21:33 2575close_on_exec是一个进程所有文件描述 ... -
Linux进程地址空间的探究解析
2012-08-08 23:35 0我们知道,在32位机器上 linux操作系统中的进程的地址空 ... -
git使用
2012-08-08 23:23 0我认为每个学过Git的人都应该做过类似这种笔记,因为Git命令 ... -
select, poll和epoll的区别
2012-07-31 21:34 0随着2.6内核对epoll的完全支持,网络上很多的文章和 ... -
linux多线程编程
2012-07-28 23:09 0本篇总结POSIX线程。可以用多个线程在单进程环境中执行多个任 ... -
select 和 epoll区别
2012-07-27 23:16 0最近有朋友在面试的时候被问了select 和epoll效率差的 ... -
echo显示变色
2012-07-24 17:07 0先来熟悉一下echo,如下: 名称 ... -
How to create and apply a patch with Git
2012-07-24 13:55 0Git is quite common now ... -
Facebook Folly源代码分析
2012-07-23 21:33 0Folly 是 Facebook 的一个开源C++11组件库, ... -
浅谈GCC预编译头技术
2012-07-23 09:51 918——谨以此文,悼念我 ... -
MySQL索引背后的数据结构及算法原理
2012-07-21 22:37 0转自 http://blog.jobbole.com/2400 ... -
patch文件的制作与使用
2012-07-01 18:43 2182创建补丁文件: 比如一个工程目录为project-o ... -
动态链接库版本管理
2012-06-28 20:24 0一、Linux的动态共享库版本控制实现 li ... -
ulimit命令使用
2012-06-22 03:56 830ulimit: usage: ulimit [-SHacdef ... -
负载均衡工具haproxy安装配置使用
2012-06-18 20:10 930一,什么是haproxy HAProxy提供高可用性、负 ...
相关推荐
在Linux系统中,`ld`是一个至关重要的工具,它作为链接器,负责将编译产生的目标文件(.o文件)整合为一个可执行程序或者库文件。`ld`联接器的工作原理对于深入理解Linux程序的构建过程至关重要。 **一、LD的作用**...
ARM LD链接文件是嵌入式系统开发中一个关键的组成部分,尤其在基于ARM架构的处理器上,它在程序编译和执行过程中起着至关重要的作用。本文将深入探讨ARM LD链接器的工作原理、配置选项以及如何理解和使用链接脚本。 ...
createimage 是生成的内核镜像的 Linux 工具,用来将 bootblock 生成一个 image 文件。在本任务中,需要使用 createimage 工具生成 image 文件。 Makefile Makefile 是编译配置文件,在本任务中,需要使用 ...
在嵌入式系统开发中,链接脚本(Linker Script)是至关重要的一个环节,它用于控制编译器如何将编译后的对象文件组织成最终的可执行文件。在HighTec EDV-Systeme GmbH提供的TriCore Development Platform中,`File....
arm-linux-ld 命令是 arm-linux 的链接器,用于将多个目标文件(.o 文件)链接成一个可执行文件。下面是 arm-linux-ld 命令的详细说明: 首先,我们需要了解的是,在编译和链接过程中,arm-linux-ld 命令扮演着非常...
GCC LD中文手册 GCC LD中文手册是一本详细的连接器...GCC LD中文手册是一本详细的连接器手册,提供了 LD 命令行选项、连接脚本基本语法、架构类型、输入文件格式、输出文件格式、符号处理和库文件搜索等方面的知识点。
-T选项是ld的一个关键参数,它可以设定代码段、数据段和BSS段的地址。例如,`-Ttext addr`指定了代码段的起始地址。如果没有明确指定数据段和BSS段,它们会自动按照默认顺序放置。 ld的另一个核心概念是section。...
GNU链接器ld是Linux环境下用于链接编译后的目标文件的工具,它在编译程序的最后一步运行,将目标文件(.o文件)和静态库(.a文件)等链接成一个可执行文件。ld使用命令行接口,提供大量的选项以控制链接过程。 首先,ld...
GNU连接器`ld`是一个重要的工具,在软件开发领域中用于将编译后的多个目标文件(.o文件)与库文件进行链接,最终形成可执行文件或其他类型的目标文件。该工具不仅支持多种目标文件格式,如COFF或'a.out',还能在遇到...
`ld` 是 Linux 下一个重要的工具,用于将编译后的目标文件(通常以 .o 结尾)和库文件链接成可执行文件。它通过识别和解析由链接命令语言(Linker Command Language)编写的链接描述文件(Linker Script)来控制链接...
连接脚本是一个包含连接指令的文本文件,可以使用`-T`选项指定。连接脚本允许用户更加细致地控制连接过程,例如指定段的放置位置、定义全局变量等。连接脚本使用一种类似于AT&T汇编语言的超集编写。 #### 八、特殊...
- 开发者通常使用如Keil MDK或GCC等工具链进行编译和链接,启动文件需要与链接脚本(Linker Script)配合工作,确保代码正确地定位到内存中。 6. **优化和裁剪启动文件**: - 对于资源有限的项目,开发者可能会...
GNU链接器(ld)是GNU工具链中的一个核心组件,用于将多个目标文件和库文件连接成可执行程序或库。它支持多种处理器架构,并且能够处理各种复杂的链接任务。ld在Linux和其他类Unix系统开发中扮演着重要的角色,对于...
`libfreetype.so.6`是FreeType库的一个动态链接版本,FreeType是一个开源的字体渲染引擎,广泛用于各种图形软件、游戏和网页浏览器中。它的主要功能是解析并渲染各种字体格式,包括TrueType、OpenType、PostScript、...
`-T`选项是`ld`的关键,它允许我们指定链接脚本(linker script),这是一个自定义配置文件,用于详细定义内存布局和section映射。例如,`-Tdata`和`-Tbss`可以分别设定数据段和BSS段的地址。 连接器还会处理...
链接脚本(linker script)是GNU链接器(GNU Linker, gnu-ld)中的一个重要组成部分,用于指导链接器如何处理输入文件(通常是目标文件)以及如何构建最终的输出文件(如可执行文件或共享库)。通过链接脚本,开发者...