解释一个ldscript文件.docx
《解释一个ldscript文件.docx》由会员分享,可在线阅读,更多相关《解释一个ldscript文件.docx(15页珍藏版)》请在冰豆网上搜索。
解释一个ldscript文件
解释一个ld.script文件
本文详细解释一个ld.script文件
OUTPUT_FORMAT("elf32-tradlittlemips")
OUTPUT_ARCH(mips)
ENTRY(_start)
SECTIONS
{
/*Read-onlysections,mergedintotextsegment:
*/
.=0x80100000;
.text:
{
_ftext=.;
*(.text)
*(.rodata)
*(.rodata1)
*(.reginfo)
*(.init)
*(.stub)
/*.gnu.warningsectionsarehandledspeciallybyelf32.em.*/
*(.gnu.warning)
}=0
_etext=.;
PROVIDE(etext=.);
.fini:
{*(.fini)}=0
.data:
{
_fdata=.;
*(.data)
CONSTRUCTORS
}
.data1:
{*(.data1)}
.ctors:
{
__CTOR_LIST__=.;
LONG((__CTOR_END__-__CTOR_LIST__)/4-2)
*(.ctors)
LONG(0)
__CTOR_END__=.;
}
.dtors:
{
__DTOR_LIST__=.;
LONG((__DTOR_END__-__DTOR_LIST__)/4-2)
*(.dtors)
LONG(0)
__DTOR_END__=.;
}
_gp=ALIGN(16)+0x7ff0;
.got:
{
*(.got.plt)*(.got)
}
/*Wewantthesmalldatasectionstogether,sosingle-instructionoffsets
canaccessthemall,andinitializeddataallbeforeuninitialized,so
wecanshortentheon-disksegmentsize.*/
.sdata:
{*(.sdata)}
.lit8:
{*(.lit8)}
.lit4:
{*(.lit4)}
_edata=.;
PROVIDE(edata=.);
__bss_start=.;
_fbss=.;
.sbss:
{*(.sbss)*(.scommon)}
.bss:
{
*(.dynbss)
*(.bss)
*(COMMON)
}
.=ALIGN(16);
__bss_end=.;
_end=.;__end=.;end=.;
PROVIDE(end=.);
/*TheseareneededforELFbackendswhichhavenotyetbeen
convertedtothenewstylelinker.*/
.stab0:
{*(.stab)}
.stabstr0:
{*(.stabstr)}
/*DWARFdebugsections.
Symbolsinthe.debugDWARFsectionarerelativetothebeginningofthe
sectionsowebegin.debugat0.It'snotclearyetwhatneedstohappen
fortheothers.*/
.debug0:
{*(.debug)}
.debug_srcinfo0:
{*(.debug_srcinfo)}
.debug_aranges0:
{*(.debug_aranges)}
.debug_pubnames0:
{*(.debug_pubnames)}
.debug_sfnames0:
{*(.debug_sfnames)}
.line0:
{*(.line)}
/*Thesemustappearregardlessof.*/
.gptab.sdata:
{*(.gptab.data)*(.gptab.sdata)}
.gptab.sbss:
{*(.gptab.bss)*(.gptab.sbss)}
}
下面逐句解释。
OUTPUT_FORMAT("elf32-tradlittlemips")
OUTPUT_ARCH(mips)OUTPUT_FORMAT和OUTPUT_ARCH都是ld脚本的保留字命令。
OUTPUT_FORMAT
说明输出二进制文件的格式。
OUTPUT_ARCH说明输出文件在平台。
ENTRY(_start)ENTRY命令的作用是,将后面括号中的符号值设置成入口地址。
入口地址(entry
point)的定义是这样的──进程执行的第一条用户空间的指令在进程地址空间中的地址。
ld有多种方法设置进程入口地址,通常它按以下顺序:
(编号越前,优先级越高)
1,ld命令行的-e选项
2,连接脚本的
ENTRY(SYMBOL)命令
3,如果定义了start符号,使用start符号值
4,如果存在.textsection,使用
.textsection的第一字节的位置值
5,使用值0
SECTIONS
{然后,接下来是一大段的SECTIONS,对应的右大括号直到脚本的末尾。
SECTIONS命令告诉ld
如何把输入文件的sections映射到输出文件的各个section:
即是如何将输入section合为输出section;如何把输出section
放入程序地址空间(VMA)和进程地址空间(LMA)。
该命令格式如下:
SECTIONS
{
….
}
/*Read-onlysections,mergedintotextsegment:
*/
.=0x80100000;这句把定位器符号置为0x80100000(若不指定,则该符号的初始值为0)。
.
是一个特殊的符号,它是定位器,一个位置指针,指向程序地址空间内的某位置(或某section内的偏移,如果它在SECTIONS命令内的某section描述内),该符号只能在SECTIONS命令内使用。
.text:
{
_ftext=.;
*(.text)
*(.rodata)
*(.rodata1)
*(.reginfo)
*(.init)
*(.stub)
/*.gnu.warningsectionsarehandledspeciallybyelf32.em.*/
*(.gnu.warning)
}=0.text:
表示text段开始。
_ftext
*(.text)
将所有(*符号代表任意输入文件)输入文件的.textsection合并成一个.textsection,该section的地址由定位器符号的值指定,
即0x80100000.
*(.rodata)
*(.rodata1)
*(.reginfo)
*(.init)
*(.stub)
*(.gnu.warning)
}
=0表示合并时留下的空隙用0填充;
_etext=.;
PROVIDE(etext=.);_etext=.;我们看到,很多变量都定义成等于这个.
符,实际上这个符号所代表的值是在变化的,随着越往后走,值越增加,根据前面填充的多少自动往后加。
PROVIDE关键字用于定义这类符号:
在目标文件内被引用,但没有在任何目标文件内被定义的符号。
这里就定义了一个
etext符号,当目标文件内引用了etext符号,却没有定义它时,etext符号对应的地址被定义为.textsection之后的第一个字节的地址。
.fini:
{*(.fini)}=0意思与前面一样,但fini这名字是哪个段,我还搞不太清楚(?
?
?
)。
.data:
{
_fdata=.;
*(.data)
CONSTRUCTORS
}
.data1:
{*(.data1)}数据段终于来到了,意思很容易理解的了。
CONSTRUCTORS是一个保留字命令。
与c++
内的(全局对象的)构造函数和(全局对像的)析构函数相关。
.ctors:
{
__CTOR_LIST__=.;
LONG((__CTOR_END__-__CTOR_LIST__)/4-2)
*(.ctors)
LONG(0)
__CTOR_END__=.;
}
.dtors:
{
__DTOR_LIST__=.;
LONG((__DTOR_END__-__DTOR_LIST__)/4-2)
*(.dtors)
LONG(0)
__DTOR_END__=.;
}对于支持任意section名的目标文件格式,比如COFF、ELF格式,GNUC++将全局构造和全局析构信息分别放入
.ctorssection和.dtorssection
内
当连接器生成的目标文件格式不支持任意section名字时,比如说ECOFF、XCOFF格式,连接器将通过名字来识别全局构造和全局析构,对于这些文件格式,连接器把与全局构造和全局析构的相关信息放入出现
CONSTRUCTORS关键字的输出section内。
符号__CTORS_LIST__表示全局构造信息的的开始处,__CTORS_END__表示全局构造信息的结束处。
符号__DTORS_LIST__表示全局构造信息的的开始处,__DTORS_END__表示全局构造信息的结束处。
这两块信息的开始处是一字长的信息,表示该块信息有多少项数据,然后以值为零的一字长数据结束。
一般来说,GNU
C++在函数__main内安排全局构造代码的运行,而__main函数被初始化代码(在main函数调用之前执行)调用。
_gp=ALIGN(16)+0x7ff0;_gp是一个重要的全局变量,好像是用作全局引用的一个指针。
.got:
{
*(.got.plt)*(.got)
}
/*Wewantthesmalldatasectionstogether,sosingle-instructionoffsets
canaccessthemall,andinitializeddataallbeforeuninitialized,so
wecanshortentheon-disksegmentsize.*/
.sdata:
{*(.sdata)}
.lit8:
{*(.lit8)}
.lit4:
{*(.lit4)}意义类似。
_edata=.;
PROVIDE(edata=.);意义与前面的etext类似。
edata符号也较为重要。
__bss_start=.;
_fbss=.;
.sbss:
{*(.sbss)*(.scommon)}
.bss:
{
*(.dynbss)
*(.bss)
*(COMMON)
}
.=ALIGN(16);
__bss_end=.;
_end=.;__end=.;end=.;
PROVIDE(end=.);BSS段开始了。
COMMON这个保留字的意义:
通用符号(common
symbol)的输入section:
在许多目标文件格式中,通用符号并没有占用一个section。
连接器认为:
输入文件的所有通用符号在名为COMMON的section内。
上例中将所有输入文件的所有通用符号放入输出.bss
section内。
这里,定义了几个重要的符号
__bss_start=.;
__bss_end=.;
_end=.;
__end=
.;
end=.;
在代码中可能会用到的。
/*TheseareneededforELFbackendswhichhavenotyetbeen
convertedtothenewstylelinker.*/
.stab0:
{*(.stab)}
.stabstr0:
{*(.stabstr)}
/*DWARFdebugsections.
Symbolsinthe.debugDWARFsectionarerelativetothebeginningofthe
sectionsowebegin.debugat0.It'snotclearyetwhatneedstohappen
fortheothers.*/
.debug0:
{*(.debug)}
.debug_srcinfo0:
{*(.debug_srcinfo)}
.debug_aranges0:
{*(.debug_aranges)}
.debug_pubnames0:
{*(.debug_pubnames)}
.debug_sfnames0:
{*(.debug_sfnames)}
.line0:
{*(.line)}
/*Thesemustappearregardlessof.*/
.gptab.sdata:
{*(.gptab.data)*(.gptab.sdata)}
.gptab.sbss:
{*(.gptab.bss)*(.gptab.sbss)}
}余下的这几个意义也类似,看英文注释应该能明白。
对初步编译出来的一个二进制文件进行nm解析,得到如下内容
80100200D__CTOR_END__
801001f8D__CTOR_LIST__
80100208D__DTOR_END__
80100200D__DTOR_LIST__
80100220A__bss_end
80100208A__bss_start
80100220A__end
80100208A_edata
80100220A_end
801001f8A_etext
80100208A_fbss
80100200A_fdata
80100000T_ftext
80108200A_gp
80100000T_start
80100038tcleanpipe
80100220Aend
80100210bflag_initialized.1263
80100158TinbFrCom
801000b4TinitBss
80100060TinitConstructor
80100100TinitMips
801001a4ToutbToCom
80100140TreadComReg
801000f8TshowVersion
800fc000Tstack
80100000Tstart
80100120TwriteComReg
可以看到,所有的地址全是从0x80100000开始的。
三个起始符号(T表示在text段中)
80100000T_start
80100000Tstart
80100000T
_ftext
几个函数都在代码段内
80100038tcleanpipe
80100060T
initConstructor
801000b4TinitBss
801000f8TshowVersion
80100100T
initMips
80100120TwriteComReg
80100140TreadComReg
80100158T
inbFrCom
801001a4ToutbToCom
栈底地址果然是在start下方0x4000处
800fc000Tstack
(A表示绝对不变)
801001f8A_etext
80100200A_fdata
80100208A
_edata
80100208A_fbss
80100208A__bss_start
80100220A
__bss_end
80100220A__end
80100220A_end
80100220Aend
全局构造和析构变量段(D表示在已初始化过的数据段中)
801001f8D__CTOR_LIST__
80100200D
__CTOR_END__
80100200D__DTOR_LIST__
80100208D__DTOR_END__
还有一个是用B标记的,表示在未初始化的数据段中
80100210b
flag_initialized.1263
对应的代码是
staticintflag_initialized=
0;
可以看出,这是个局部静态变量。
令其按地址排序
nm-nbamboo800fc000Tstack
80100000T_ftext
80100000T_start
80100000Tstart
80100038tcleanpipe
80100060TinitConstructor
801000b4TinitBss
801000f8TshowVersion
80100100TinitMips
80100120TwriteComReg
80100140TreadComReg
80100158TinbFrCom
801001a4ToutbToCom
801001f8D__CTOR_LIST__
801001f8A_etext
80100200D__CTOR_END__
80100200D__DTOR_LIST__
80100200A_fdata
80100208D__DTOR_END__
80100208A__bss_start
80100208A_edata
80100208A_fbss
80100210bflag_initialized.1263
80100220A__bss_end
80100220A__end
80100220A_end
80100220Aend
80108200A_gp
结合这些数据,去理解前面的ld.script的讲解,会有一个清晰的印象。