当前位置:Linux教程 - Linux - ELF文件格式(中文)(三)

ELF文件格式(中文)(三)

==================== Dynamic Linking (动态链接) =====================


一个可执行文件可能有一个 PT_INTERP 程序头元素。在 exec(BA_OS) 的
过程中,系统从 PT_INTERP 段中取回一个路径名并由解释器文件的段创建初始的
进程映像。也就是说,系统为解释器“编写”了一个内存映像,而不是使用原始
的可执行文件的段映像。此时该解释器就负责接收系统来的控制并且为应用程序
提供一个环境变量。


解释器使用两种方法中的一种来接收系统来的控制。首先,它会接收一个文件描述符
来读取该可执行文件,定位于开头。它可以使用这个文件描述符来读取 并且(或者)
映射该可执行文件的段到内存中。其次,依赖于该可执行文件的格式,系统会载入
这个可执行文件到内存中而不是给该解释器一个文件描述符。伴随着可能的文件描述符
异常的情况,解释器的初始进程声明应匹配该可执行文件应当收到的内容。解释器本身
并不需要第二个解释器。一个解释器可能是一个共享对象也可能是一个可执行文件。


* 一个共享对象(通常的情况)在被载入的时候是位置无关的,各个进程可能不同;
系统在 mmap(KE_OS) 使用的动态段域为它创建段和相关的服务。因而,一个
共享对象的解释器将不会和原始的可执行文件的原始段地址相冲突。


* 一个可执行文件被载入到固定地址;系统使用程序头表中的虚拟地址为其创建段。
因而,一个可执行文件解释器的虚拟地址可能和第一个可执行文件相冲突;这种
冲突由解释器来解决。



Dynamic Linker(动态链接器)

当使用动态链接方式建立一个可执行文件时,链接器把一个 PT_INTERP 类型
的元素加到可执行文件中,告诉系统把动态链接器做为该程序的解释器。

注意:由系统提供的动态链接器是和特定处理器相关的。

Exec(BA_OS) 和动态链接器合作为程序创建进程,必须有如下的动作:

* 将可执行文件的内存段加入进程映像中;
* 将共享对象的内存段加入进程映像中;
* 为可执行文件和它的共享对象进行重定位;
* 如果有一个用于读取可执行文件的文件描述符传递给了动态链接器,那么关闭它。
* 向程序传递控制,就象该程序已经直接从 exec(BA_OS) 接收控制一样。

链接器同时也为动态链接器构建各种可执行文件和共享对象文件的相关数据。就象
在上面“程序头”中说的那样,这些数据驻留在可载入段中,使得它们在执行过程
中有效。(再一次的,要记住精确的段内容是处理器相关的。可以参阅相应处理器
的补充说明来获得详尽的信息。)


* 一个具有 SHT_DYNAMIC 类型的 .dynamic section 包含各种数据。驻留在
section 开头的结构包含了其他动态链接信息的地址。

* SHT_HASH 类型的 .hash section 包含了一个 symbol hash table.

* SHT_PROGBITS 类型的 .got 和 .plt section 包含了两个分离的 table:
全局偏移表和过程链接表。 下面的 section 演示了动态链接器使用和改变
这些表来为 object file 创建内存映像。


由于每一个遵循 ABI 的程序从一个共享对象库中输入基本的系统服务,因此动态
链接器分享于每一个遵循 ABI 的程序的执行过程中。


就象在处理器补充说明的“程序载入”所解释的那样,共享对象也许会占用与记录在
文件的程序头表中的地址不同的虚拟内存地址。动态链接器重定位内存映像,在应用程序
获得控制之前更新绝对地址。尽管在库被载入到由程序头表指定的地址的情况下绝对地址
应当是正确的,通常的情况却不是这样。


如果进程环境 [see exec(BA_OS)] 包含了一个非零的 LD_BIND_NOW 变量,
动态链接器将在控制传递到程序之前进行所有的重定位。举例而言,所有下面的
环境入口将指定这种行为。


* LD_BIND_NOW=1
* LD_BIND_NOW=on
* LD_BIND_NOW=off

其他情况下, LD_BIND_NOW 或者不在环境中或者为空值。动态链接器可以不急于
处理过程链接表入口,因而避免了对没有调用的函数的符号解析和重定位。参阅
"Procedure Linkage Table"获取更多的信息。



Dynamic Section(动态section)


假如一个object文件参与动态的连接,它的程序头表将有一个类型为PT_DYNAMIC
的元素。该“段”包含了.dynamic section。一个_DYNAMIC特别的符号,表明了
该section包含了以下结构的一个数组。

+ Figure 2-9: Dynamic Structure

typedef struct {
Elf32_Sword d_tag;
union {
Elf32_Sword d_val;
Elf32_Addr d_ptr;
} d_un;
} Elf32_Dyn;

extern Elf32_Dyn _DYNAMIC[];

对每一个有该类型的object,d_tag控制着d_un的解释。

* d_val

那些Elf32_Word object描绘了具有不同解释的整形变量。

* d_ptr

那些Elf32_Word object描绘了程序的虚拟地址。就象以前提到的,在执行时,
文件的虚拟地址可能和内存虚拟地址不匹配。当解释包含在动态结构中的地址
时是基于原始文件的值和内存的基地址。为了一致性,文件不包含在
重定位入口来纠正在动态结构中的地址。


以下的表格总结了对可执行和共享object文件需要的tag。假如tag被标为
mandatory,ABI-conforming文件的动态连接数组必须有一个那样的入口。
同样的,“optional”意味着一个可能出现tag的入口,但是不是必须的。

+ Figure 2-10: Dynamic Array Tags, d_tag

Name Value d_un Executable Shared Object
==== ===== ==== ========== =============
DT_NULL 0 ignored mandatory mandatory
DT_NEEDED 1 d_val optional optional
DT_PLTRELSZ 2 d_val optional optional
DT_PLTGOT 3 d_ptr optional optional
DT_HASH 4 d_ptr mandatory mandatory
DT_STRTAB 5 d_ptr mandatory mandatory
DT_SYMTAB 6 d_ptr mandatory mandatory
DT_RELA 7 d_ptr mandatory optional
DT_RELASZ 8 d_val mandatory optional
DT_RELAENT 9 d_val mandatory optional
DT_STRSZ 10 d_val mandatory mandatory
DT_SYMENT 11 d_val mandatory mandatory
DT_INIT 12 d_ptr optional optional
DT_FINI 13 d_ptr optional optional
DT_SONAME 14 d_val ignored optional
DT_RPATH 15 d_val optional ignored
DT_SYMBOLIC 16 ignored ignored optional
DT_REL 17 d_ptr mandatory optional
DT_RELSZ 18 d_val mandatory optional
DT_RELENT 19 d_val mandatory optional
DT_PLTREL 20 d_val optional optional
DT_DEBUG 21 d_ptr optional ignored
DT_TEXTREL 22 ignored optional optional
DT_JMPREL 23 d_ptr optional optional
DT_LOPROC 0x70000000 unspecified unspecified unspecified
DT_HIPROC 0x7fffffff unspecified unspecified unspecified

* DT_NULL

一个DT_NULL标记的入口表示了_DYNAMIC数组的结束。

* DT_NEEDED

这个元素保存着以NULL结尾的字符串表的偏移量,那些字符串是所需库的名字。
该偏移量是以DT_STRTAB 为入口的表的索引。看“Shared Object Dependencies”
关于那些名字的更多信息。动态数组可能包含了多个这个类型的入口。那些
入口的相关顺序是重要的,虽然它们跟其他入口的关系是不重要的。

* DT_PLTRELSZ

该元素保存着跟PLT关联的重定位入口的总共字节大小。假如一个入口类型
DT_JMPREL存在,那么DT_PLTRELSZ也必须存在。

* DT_PLTGOT

该元素保存着跟PLT关联的地址和(或者)是GOT。具体细节看处理器补充
(processor supplement)部分。

* DT_HASH

该元素保存着符号哈希表的地址,在“哈希表”有描述。该哈希表指向
被DT_SYMTAB元素引用的符号表。

* DT_STRTAB

该元素保存着字符串表地址,在第一部分有描述,包括了符号名,库名,
和一些其他的在该表中的字符串。

* DT_SYMTAB

该元素保存着符号表的地址,在第一部分有描述,对32-bit类型的文件来
说,关联着一个Elf32_Sym入口。

* DT_RELA

该元素保存着重定位表的地址,在第一部分有描述。在表中的入口有明确的
加数,就象32-bit类型文件的Elf32_Rela。一个object文件可能好多个重定位
section。当为一个可执行和共享文件建立重定位表的时候,连接编辑器连接
那些section到一个单一的表。尽管在object文件中那些section是保持独立的。
动态连接器只看成是一个简单的表。当动态连接器为一个可执行文件创建一个
进程映象或者是加一个共享object到进程映象中,它读重定位表和执行相关的
动作。假如该元素存在,动态结构必须也要有DT_RELASZ和DT_RELAENT元素。
当文件的重定位是mandatory,DT_RELA 或者 DT_REL可能出现(同时出现是
允许的,但是不必要的)。


* DT_RELASZ

该元素保存着DT_RELA重定位表总的字节大小。

* DT_RELAENT

该元素保存着DT_RELA重定位入口的字节大小。


* DT_STRSZ

该元素保存着字符串表的字节大小。

* DT_SYMENT

该元素保存着符号表入口的字节大小。

* DT_INIT

该元素保存着初始化函数的地址,在下面“初始化和终止函数”中讨论。

* DT_FINI

该元素保存着终止函数的地址,在下面“初始化和终止函数”中讨论。

* DT_SONAME

该元素保存着以NULL结尾的字符串的字符串表偏移量,那些名字是共享
object的名字。偏移量是在DT_STRTAB入口记录的表的索引。关于那些名字看
Shared Object Dependencies 部分获得更多的信息。

* DT_RPATH

该元素保存着以NULL结尾的搜索库的搜索目录字符串的字符串表偏移量。
在共享object依赖关系(Shared Object Dependencies)中有讨论

* DT_SYMBOLIC

在共享object库中出现的该元素为在库中的引用改变动态连接器符号解析的算法。
替代在可执行文件中的符号搜索,动态连接器从它自己的共享object开始。假如
一个共享的object提供引用参考失败,那么动态连接器再照常的搜索可执行文件
和其他的共享object。


* DT_REL

该元素相似于DT_RELA,除了它的表有潜在的加数,正如32-bit文件类型的
Elf32_Rel一样。假如这个元素存在,它的动态结构必须也同时要有DT_RELSZ
和DT_RELENT的元素。

* DT_RELSZ

该元素保存着DT_REL重定位表的总字节大小。

* DT_RELENT

该元素保存着DT_RELENT重定为入口的字节大小。

* DT_PLTREL

该成员指明了PLT指向的重定位入口的类型。适当地, d_val成员保存着
DT_REL或DT_RELA。在一个PLT中的所有重定位必须使用相同的转换。

* DT_DEBUG

该成员被调试使用。它的内容没有被ABI指定;访问该入口的程序不是
ABI-conforming的。

* DT_TEXTREL

如在程序头表中段许可所指出的那样,这个成员的缺乏代表没有重置入
口会引起非写段的修改。假如该成员存在,一个或多个重定位入口可能
请求修改一个非写段,并且动态连接器能因此有准备。


* DT_JMPREL

假如存在,它的入口d_ptr成员保存着重定位入口(该入口单独关联着
PLT)的地址。假如lazy方式打开,那么分离它们的重定位入口让动态连接
器在进程初始化时忽略它们。假如该入口存在,相关联的类型入口DT_PLTRELSZ
和DT_PLTREL一定要存在。

* DT_LOPROC through DT_HIPROC

在该范围内的变量为特殊的处理器语义保留。除了在数组末尾的DT_NULL元素,
和DT_NEEDED元素相关的次序,入口可能出现在任何次序中。在表中不出
现的Tag值是保留的。


Shared Object Dependencies(共享Object的依赖关系)

当连接器处理一个文档库时,它取出库中成员并且把它们拷贝到一个输出的
object文件中。当运行时没有包括一个动态连接器的时候,那些静态的连接服
务是可用的。共享object也提供服务,动态连接器必须把正确的共享object
文件连接到要实行的进程映象中。因此,可执行文件和共享的object文件之间
存在着明确的依赖性。

当动态连接器为一个object文件创建内存段时,依赖关系(在动态结构的
DT_NEEDED入口中记录)表明需要哪些object来为程序提供服务。通过
重复的连接参考的共享object和他们的依赖关系,动态连接器可以建造一个
完全的进程映象。当解决一个符号引用的时候,动态连接器以宽度优先搜索
(breadth-first)来检查符号表,换句话说,它先查看自己的可实行程序
中的符号表,然后是顶端DT_NEEDED入口(按顺序)的符号表,再接下来是
第二级的DT_NEEDED入口,依次类推。共享object文件必须对进程是可读的;
其他权限是不需要的。

注意:即使当一个共享object被引用多次(在依赖列关系表中),动态连接器
只把它连接到进程中一次。

在依赖关系列表中的名字既被DT_SONAME字符串拷贝,又被建立object文件
时的路径名拷贝。例如,动态连接器建立一个可执行文件(使用带DT_SONAME
入口的lib1共享文件)和一个路径名为/usr/lib/lib2的共享object库,
那么可执行文件将在它自己的依赖关系列表中包含lib1和/usr/bin/lib2。

假如一个共享object名字有一个或更多的反斜杠字符(/)在这名字的如何地方,
例如上面的/usr/lib/lib2文件或目录,动态连接器把那个字符串自己做为路径名。
假如名字没有反斜杠字符(/),例如上面的lib1,三种方法指定共享文件的
搜索路径,如下:

* 第一,动态数组标记DT_RPATH保存着目录列表的字符串(用冒号(:)分隔)。
例如,字符串/home/dir/lib:/home/dir2/lib:告诉动态连接器先搜索
/home/dir/lib,再搜索/home/dir2/lib,再是当前目录。

* 第二,在进程环境中(see exec(BA_OS)),有一个变量称为LD_LIBRARY_PATH
可以保存象上面一样的目录列表(随意跟一个分号(;)和其他目录列表)。
以下变量等于前面的例子:
LD_LIBRARY_PATH=/home/dir/lib:/home/dir2/lib:
LD_LIBRARY_PATH=/home/dir/lib;/home/dir2/lib:
LD_LIBRARY_PATH=/home/dir/lib:/home/dir2/lib:;
所以的LD_LIBRARY_PATH目录在DT_RPATH指向的目录之后被搜索。尽管一些
程序(例如连接编辑器)不同的处理分号前和分号后的目录,但是动态连接
不会。不过,动态连接器接受分号符号,具体语意在如上面描述。

* 最后,如果上面的两个目录查找想要得到的库失败,那么动态连接器搜索
/usr/lib.

注意:出于安全考虑,动态连接器忽略set-user和set-group的程序的
LD_LIBRARY_PATH所指定的搜索目录。但它会搜索DT_RPATH指明的目录和
/usr/lib。


Global Offset Table(GOT全局偏移量表)

一般情况下,位置无关的代码不包含绝对的虚拟地址。全局偏移量表在私有数据
中保存着绝对地址,所以应该使地址可用的,而不是和位置无关性和程序代码段
共享能力妥协。一个程序引用它的GOT(全局偏移量表)来使用位置无关的地址并且
提取绝对的变量,所以重定位位置无关的参考到绝对的位置。

初始时,GOT(全局偏移量表)保存着它重定位入口所需要的信息 [看第一部分的
“Relocation”]。在系统为一个可装载的object文件创建内存段以后,动态
连接器处理重定位入口,那些类型为R_386_GLOB_DAT的指明了GOT(全局偏移量表)。
动态连接器决定了相关的标号变量,计算他们的绝对地址,并且设置适当的内存
表入口到正确的变量。虽然当连接编辑器建造object文件的时候,绝对地址
是不知道,连接器知道所以内存段的地址并且能够因此计算出包含在那里的
标号地址。

假如程序需要直接访问符号的绝对地址,那么这个符号将有一个GOT(全局偏移量表)
入口。因为可执行文件和共享文件有独立的GOT(全局偏移量表),一个符号地址
可能出现在不同的几个表中。在交给进程映象的代码控制权以前,动态连接器处
理所有的重定位的GOT(全局偏移量表),所以在执行时,确认绝对地址是可用的。

该表的入口0是为保存动态结构地址保留的(参考_DYNAMIC标号)。这允许
象动态连接程序那样来找出他们自己的动态结构(还没有处理他们的重
定向入口)。这些对于动态连接器是重要的,因为它必要初始化自己而不
能依赖于其他程序来重定位他们的内存映象。在32位Interl系统结构中,在
GOT中的人口1和2也是保留的,具体看以下的过程连接表(Procedure Linkage
Table)。

系统可以为在不同的程序中相同的共享object选择不同的内存段;它甚至可以
为相同的程序不同的进程选择不同的库地址。虽然如此,一旦进程映象被建立
以后,内存段不改变地址。只要一个进程存在,它的内存段驻留在固定的虚拟
地址。

GOT表的格式和解释是处理器相关的。在32位Intel体系结构下,标号
_GLOBAL_OFFSET_TABLE_可能被用来访问该表。

+ Figure 2-11: Global Offset Table

extern Elf32_Addr _GLOBAL_OFFSET_TABLE_[];

标号_GLOBAL_OFFSET_TABLE_可能驻留在.got section的中间,允许负的和非负
的下标索引这个数组。


Procedure Linkage Table(PLT过程连接表)

就象GOT重定位把位置无关的地址计算成绝对地址一样,PLT过程连接表重定位
位置无关的函数调用到绝对的地址。从一个可执行或者共享的object文件到另外的,
连接编辑器不解析执行的传输(例如函数的调用)。因此,连接编辑器安排程序
的传递控制到PLT中的入口。在SYSTEM V体系下,PLT存在共享文本中,但是它们
使用的地址是在私有的GOT中。符号连接器决定了目标的绝对地址并且修改GOT的
内存映象。因此,在没有危及到位置无关、程序文本的共享能力的情况下。动态
连接器能重定位人口。


+ Figure 2-12: Absolute Procedure Linkage Table {*}
绝对的过程连接表

.PLT0:pushl got_plus_4
jmp *got_plus_8
nop; nop
nop; nop
.PLT1:jmp *name1_in_GOT
pushl $offset
jmp .PLT0@PC
.PLT2:jmp *name2_in_GOT
pushl $offset
jmp .PLT0@PC
...

+ Figure 2-13: Position-Independent Procedure Linkage Table
位置无关(或者说位置独立)的过程连接表
.PLT0:pushl 4(%ebx)
jmp *8(%ebx)
nop; nop
nop; nop
.PLT1:jmp *name1@GOT(%ebx)
pushl $offset
jmp .PLT0@PC
.PLT2:jmp *name2@GOT(%ebx)
pushl $offset
jmp .PLT0@PC
...

注意:如图所示,PLT的指令使用了不同的操作数地址方式,对绝对代码和
对位置无关的代码。但是,他们的界面对于动态连接器是相同的。

以下的步骤,动态连接器和程序协作(cooperate)通过PLT和GOT来解析符号
引用。

1. 当第一次创建程序的内存映象时,动态连接器为在GOT中特别的变量设置
第二次和第三次的入口。下面关于那些变量有更多的解释。

2. 假如PLT是位置无关的,那么GOT的地址一定是保留在%ebx中的。每个在进程
映象中共享的object文件有它自己的PLT,并且仅仅在同一个object文件中,
控制传输到PLT入口。从而,要调用的函数有责任在调用PLT入口前,设置PLT
地址到寄存器中。

3. 举例说明,假如程序调用函数name1,它的传输控制到标号.PLT1.

4. 第一个指令跳到在GOT入口的name1地址。初始话时,GOT保存着紧跟着的push1
指令的地址,而不是真实的name1的地址。

5. 因此,程序在堆栈中压入(push)一个重定位的偏移量。重定位的偏移量是
一个32位,非负的字节偏移量(从定位表算起)。指派的重定位入口将是
一个R_386_JMP_SLOT类型,它的偏移量指明了GOT入口(在前面的jmp指令中
被使用)。该重定位入口也包含一个符号表的索引,因此告诉动态连接器
哪个符号要被引用,在这里是name1。

6. 在压入(push)一个重定位的偏移量后,程序跳到.PLT0,在PLT中的第一个入口。
push1指令在堆栈中放置第二个GOT入口(got_plus_4 or 4(%ebx))的值,
因此,给动态连接器一个word的鉴别信息。然后程序跳到第三个GOT入口
(got_plus_8 or 8(%ebx)),它传输控制到动态连接器。

7. 当动态连接器接到控制权,它展开堆栈,查看指派的重定位入口,寻找符号的
值,在GOT入口中存储真实的name1地址,然后传输控制想要目的地。

8. PLT入口的并发执行将直接传输控制到name1,而不用第二次调用动态连接器
了。所以,在.PLT1中的jmp指令将转到name1,代替“falling through”
转到pushl指令。

LD_BIND_NOW环境变量能改变动态连接器的行为。假如这个变量为非空,动态
连接器在传输控制到程序前计算PLT入口。换句话说,动态连接器处理重定位
类型为R_386_JMP_SLOT的入口在进程初始化时。否则,动态连接器计算PLT入口
懒惰的,推迟到符号解析和重定位直到一个表入口的第一次执行。


注意:一般来说,以懒惰(Lazy)方式绑定是对全应用程序执行的改进。
因为不使用的符号就不会招致动态连接器做无用功。然而,对一些应用程序,
两种情况使用懒惰(Lazy)方式是不受欢迎的。

第一 初始的引用一个共享object函数比后来的调用要花的时间长,因为动
态连接器截取调用来解析符号。一些应用程序是不能容忍这样的。
第二 假如这个错误发生并且动态连接器不能解析该符号,动态连接器将终止
程序。在懒惰(Lazy)方式下,这可能发生在任意的时候。一再的,一
些应用程序是不能容忍这样的。通过关掉懒惰(Lazy)方式,在应用程
序接到控制前,当在处理初始话时发生错误,动态连接器强迫程序,使
之失败。

Hash Table(哈希表)

Elf32_Word object的哈希表支持符号表的访问。
标号出现在下面帮助解释哈希表的组织,但是它们不是规范的一部分。

+ Figure 2-14: Symbol Hash Table

nbucket
nchain
bucket[0]
...
bucket[nbucket - 1]
chain[0]
...
chain[nchain - 1]


bucket数组包含了nbucket入口,并且chain数组包含了nchain个入口;索引从0开始。
bucket和chain保存着符号表的索引。Chain表入口类似于符号表。符号表入口的
数目应该等于nchain;所以符号表的索引也选择chain表的入口。
一个哈希函数(如下的)接受一个符号名并且返回一个可以被计算机使用的bucket索引
的值。因此,假如一个哈希函数返回一些名字的值为X,那么bucket[x%nbucket]
将给出一个索引y(既是符号表和chain表的索引)。假如符号表入口不是期望的,
chain[y]给出下一个符号表的入口(使用相同的哈希变量)。可以沿着chain
链直到选择到了期望名字的符号表入口或者是碰到了STN_UNDEF的入口。

+ Figure 2-15: Hashing Function

unsigned long
elf_hash(const unsigned char *name)
{
unsigned long h = 0, g;

while (*name) {
h = (h << 4) + *name++;
if (g = h & 0xf0000000)
h ^= g >> 24;
h &= ~g;
}
return h;
}


Initialization and Termination Functions
初始化和终止函数


在动态连接妻建立进程映象和执行重定位以后,每一个共享object得到适当
的机会来执行一些初始话代码。初始化函数不按特别的顺序被调用,但是
所有的共享object初始化发生在执行程序获得控制之前。

类似地,共享的object可能包含终止函数,它们在进程本身开始它的终止之后
被执行(以atexit(BA_OS)的机制)。

共享object通过设置在动态结构中的DT_INIT和DT_FINI入口来指派它们的初始化
和终止函数,如上动态section(Dynamic Section)部分描述。典型的,那些函数
代码存在.init和.fini section中,第一部分的“section”已经提到过。

注意:尽管atexit(BA_OS)的终止处理一般可可正常完成,但是不保证在死进程上
被执行。特别的,假如_exit被调用(看exit(BA_OS))或者假如进程死掉,那么
进程是不执行终止处理的。因为它收到一个信号,该信号可捕获或忽略。

________________________________________________________________


3. C LIBRARY

________________________________________________________________


========================== C Library ===========================

C库,libc,包含了所有的符号(包含在libsys),另外,包含在在下面两个
表中列出的运行函数。第一个表中的运行函数是ANSI C标准的。

+ Figure 3-1: libc Contents, Names without Synonyms

abort fputc isprint putc strncmp
abs fputs ispunct putchar strncpy
asctime fread isspace puts strpbrk
atof freopen isupper qsort strrchr
atoi frexp isxdigit raise strspn
atol fscanf labs rand strstr
bsearch fseek ldexp rewind strtod
clearerr fsetpos ldiv scanf strtok
clock ftell localtime setbuf strtol
ctime fwrite longjmp setjmp strtoul
difftime getc mblen setvbuf tmpfile
div getchar mbstowcs sprintf tmpnam
fclose getenv mbtowc srand tolower
feof gets memchr sscanf toupper
ferror gmtime memcmp strcat ungetc
fflush isalnum memcpy strchr vfprintf
fgetc isalpha memmove strcmp vprintf
fgetpos iscntrl memset strcpy vsprintf
fgets isdigit mktime strcspn wcstombs
fopen isgraph perror strlen wctomb
fprintf islower printf strncat

再加上, libc 保存着以下的服务。

+ Figure 3-2: libc Contents, Names with Synonyms

__assert getdate lockf ** sleep tell **
cfgetispeed getopt lsearch strdup tempnam
cfgetospeed getpass memccpy swab tfind
cfsetispeed getsubopt mkfifo tcdrain toascii
cfsetospeed getw mktemp tcflow _tolower
ctermid hcreate monitor tcflush tsearch
cuserid hdestroy nftw tcgetattr _toupper
dup2 hsearch nl_langinfo tcgetpgrp twalk
fdopen isascii pclose tcgetsid tzset
__filbuf isatty popen tcsendbreak _xftw
fileno isnan putenv tcsetattr
__flsbuf isnand ** putw tcsetpgrp
fmtmsg ** lfind setlabel tdelete

** = Function is at Level 2 in the SVID Issue 3 and therefore at
Level 2 in the ABI.

包括上面同义(Synonyms)表列出的标号,对于<name> 入口已经存在的_<name>
形式(带一个下划线,上面没有列出来)优先权高于它们的名字。所以,例如,
libc同时包含了getopt和_getopt。

在常规的上列中,其他地方以下没有被定义。

int __filbuf(FILE *f);
This function returns the next input character for f, filling
its buffer as appropriate. It returns EOF if an error occurs.

int __flsbuf(int x, FILE *f);
This function flushes the output characters for f as if
putc(x, f) had been called and then appends the value of x to
the resulting output stream. It returns EOF if an error occurs
and x otherwise.

int _xftw(int, char *, int (*)(char *, struct stat *, int), int);
Calls to the ftw(BA_LIB) function are mapped to this function
when applications are compiled. This function is identical to
ftw(BA_LIB), except that _xftw() takes an interposed first
argument, which must have the value 2.


要了解更多的关于SVID,ANSI C,POSIX的知识,可看该章节其他的库section部分。
该节“System Data Interfaces”后有更多的描述。

Global Data Symbols
全局数据符号


libc库需要一些外部的全局数据符号(为了它自己的常规工作而定义的)。
所有向libsys库请求的数据符号一定要让libc提供,就象下面表中的数据符号。

正式定义的数据object被他们的符号描述,看System V接口定义,第三版本
或者第6章节的数据定义(Data Definitions)section(在适当的处理器
补充到System V ABI)。

在下面表中的入口有<name>-_<name>的形式。一对符号都代表了一些数据。
下划线的synonyms假设满足ANSI C标准。


+ Figure 3-3: libc Contents, Global External Data Symbols

getdate_err optarg
_getdate_err opterr
__iob optind
optopt