📄 038.htm
字号:
ELF (Executable and Linking Format) 最初是由USL(UNIX System Laboratories)所发展的二进位格式(binary
format),而目前正应用於Solaris与System V Release 4上面.由於ELF所增涨的弹性(flexibility)远远超过Linux过去所用的a.out格式,因此GCC与C程式库的发展人士於去年(1995)决定改用ELF为Linux标准的二进位格式.
<br>
<br>
6.4.2.1. 怎麽又来了? <br>
这一节是来自於'/news-archives/comp.sys.sun.misc'的文件. <br>
<br>
<br>
ELF("Executable Linking Format")是於SVR4所引进的新式改良目的档格式.ELF比起COFF可是多出了不少的功能.以ELF而言,它*是*可由使用者自行延伸的(user-extensible).ELF视一目的档为节区(sections)如串列般的组合,而且此串列可为任意的(arbitrarily)长度(而不是一固定大小的阵列).这些节区与COFF的不一样,并不需要固定在某个地方,也不需要以某种顺序排列.如果users希望能补捉到新的资料,他们便可以加入新的节区到目的档内.ELF也有一个更强而有力的除错格式,称为DWARF(Debugging
With Attribute Record Format)-目前Linux并不完全支援.DWARF DIEs(Debugging
Information Entries)的连结串列会在ELF内形成.debug的节区.DWARF DIEs的每一个.debug节区并非一些少量的(small)且固定大小的(fixed-size)资讯记录(information
records)的集合(collection),而是一任意长度的串列,拥有复杂的属性,而且程式的资料会以□围为根据的树状资料结构(scope-based
tree)写出来.DIEs所能补捉到的大量资讯是COFF的.debug节区无法望其项背的.(像是C++的继承图).
<br>
<br>
<br>
ELF档案是从SVR4(Solaris 2.0 ?)ELF存取程式库(ELF access library)内存取的.此程式库可提供一简便快速的介面予ELF.使用ELF存取程式库最主要的恩惠之一便是,你不再需要去察看一个ELF档的qua了.就UNIX的档案而言,它是以Elf*的型式来存取;呼叫elf_open()之後,从此时开始,你只需呼叫elf_foobar()来处理档案的某一部份(components)即可,并不需要把档案实际在磁碟上的image搞得一团乱.
<br>
<br>
ELF的优缺点与升级至ELF等级所需经历的种种痛苦(contortions),已在ELF-HOWTO内论及;我并不打算在这儿涂浆糊.ELF
HOWTO应该与这份文件相同之处有同样的主题才是. <br>
<br>
6.4.2.2. ELF共享程式库 <br>
若想建立libfoo.so成为共享程式库,基本的步骤会像下面这样: <br>
<br>
<br>
$ gcc -fPIC -c *.c <br>
$ gcc -shared -Wl,-soname,libfoo.so.1 -o libfoo.so.1.0 *.o <br>
$ ln -s libfoo.so.1.0 libfoo.so.1 <br>
$ ln -s libfoo.so.1 libfoo.so <br>
$ LD_LIBRARY_PATH=`pwd`:$LD_LIBRARY_PATH ; export LD_LIBRARY_PATH <br>
<br>
<br>
这会产生一个名为libfoo.so.1.0的共享程式库,以及给予ld适当的连结(libfoo.so)还有使得动态载入程式(dynamic
loader)能找到它(libfoo.so.1).为了进行测试,我们将目前的目录加到LD_LIBRARY_PATH里.
<br>
<br>
<br>
当你津津乐道於程式库制做成功之时, 别忘了把它移到如/usr/local/lib的目录底下,并且重新建立正确的连结路径.
libfoo.so.1与libfoo.so.1.0的连结会由ldconfig依日期不断的更新;就大部份的系统来说,ldconfig会在开机程序中执行.
libfoo.so的连结必须由手动方式更新.如果你对程式库所有组成份子(如标头档等)的升级,总是抱持著一丝不□的态度(scrupulous),那麽最简单的方法就是让libfoo.so
-> libfoo.so.1;如此一来,ldconfig便会替你同时保留最新的连结.要是你没有这麽做,你自行设定的东东就会在数日後造成千奇百怪的花样出现.到时候,可别说我没提醒你啊!
<br>
<br>
<br>
$ su <br>
# cp libfoo.so.1.0 /usr/local/lib <br>
# /sbin/ldconfig <br>
# ( cd /usr/local/lib ; ln -s libfoo.so.1 libfoo.so ) <br>
<br>
<br>
6.4.2.3. 版本编号, soname与符号连结 <br>
每一个程式库都有一个soname.当连结器发现它正在搜寻的程式库中有这样的一个名称,连结器便会将soname箝入(embed)连结中的二进位档内,
而不是它正在运作的实际的档名.在程式执行期间,动态载入程式会搜寻拥有soname这样的档名的档案,而不是程式库的档名.因此,一个名为libfoo.so的程式库,就可以有一个libbar.so的soname了.而且所有连结到libbar.so的程式,当程式开始执行时,会寻找的便是libbar.so了.
<br>
<br>
这听起来好像一点意义也没有,但是这一点,对於了解数个不同版本的同一个程式库是如何在单一系统上共存(coexist)的原因,却是关键之钥.Linux程式库标准的命名方式,比如说是libfoo.so.1.2,而且给这个程式库一个libfoo.so.1的soname.如果此程式库是加到标准程式库的目录底下(e.g.
/usr/lib),ldconfig会建立符号连结libfoo.so.1 -> libfoo.so.1.2,使其正确的image能於执行期间(run-time)找到.你也需要连结libfoo.so
-> libfoo.so.1,使ld能於连结期间(link-time)找到正确的soname. <br>
<br>
所以罗,当你修正程式库内的bugs,或是添加了新的函数进去(任何不会对现存的程式造成不利的(adversely)影响的改变),你会重建此程式库,保留原本已有的soname,然後更改程式库档名.而当你对程式库的变更会使得现有的程式(binaries)中断(break),那麽你只需增加soname中的编号---此例中,称新版本为libfoo.so.2.0,而soname变成libfoo.so.2.紧接著,再将libfoo.so的连结转向新的版本;至此,世界又再度恢复了和平!
<br>
<br>
其实你不须要以此种方式来替程式库命名,不过这的确是个好的传统(convention).ELF所赋予你在程式库命名上的弹性,会使得人气喘呼呼的搞不清楚状况;有这样的弹性在,也并不表示你就得去用它.
<br>
<br>
ELF总结:假设经由你睿智的观察发现有个惯例说:程式库主要的升级会破坏(break)相容性(compatibility);而次要的升级则可能不会;那麽以下面的方式来连结,所有的一切就都会相安无事了.
<br>
<br>
<br>
gcc -shared -Wl,-soname,libfoo.so.major -o libfoo.so.major.minor <br>
<br>
<br>
6.4.3. a.out---旧旧的格式~ <br>
建立共享程式库的便利性(ease)是升级至ELF的主要原因之一.那也是说,a.out可能还是有用处在的.上ftp站去抓ftp://tsx-11.mit.edu/pub/linux/packages/GCC/src/tools-2.17.tar.gz;解压缩後你会发现有20页的文件可以慢慢的读哩.我很不喜欢自己党派的偏见(partisan)表现得那麽的淋璃尽致,可是从上下文间,应该也可以很清楚的嗅出我从来不拿石头砸自己的脚的脾气吧!:-)
<br>
<br>
6.4.3.1. ZMAGIC vs QMAGIC <br>
QMAGIC是一种类似旧格式的a.out(亦称为ZMAGIC)的可执行档格式,这种格式会使得第一个分页无法map.当0-4096的□围内没有mapping存在时,则可允许NULL
dereference trapping更加的容易.所产生的边界效应(side effect)是你的执行档会比较小(大约是1K左右).
<br>
<br>
只有即将作废的连结器有支援ZMAGIC,一半已埋入棺材的连结器有支援这两种格式;而目前的版本仅支援QMAGIC而已.事实上,这并没有多大的影响,那是因为目前的核心两种格式都能执行.
<br>
<br>
*file*命令应该可以确认程式是不是QMAGIC的格式的. <br>
<br>
6.4.3.2. 档案配置(File Placement) <br>
一a.out(DLL)的共享程式库包含两个真实的档案与一个符号连结.就*foo*这个用於整份文件做为□例的程式库而言,这些档案会是libfoo.sa与libfoo.so.1.2;符号连结会是libfoo.so.1,而且会指向libfoo.so.1.2.这些是做什麽用的?
<br>
<br>
在编译时, ld会寻找libfoo.sa.这是程式库的*stub*档案,而且含有所有执行期间连结所需的exported的资料与指向函数的指标.
<br>
<br>
执行期间,动态载入程式会寻找libfoo.so.1.这仅仅是一个符号连结,而不是真实的档案,故程式库可更新成较新的且已修正错误的版本,而不会损毁任何此时正在使用此程式库的应用程式.在新版---比如说libfoo.so.1.3---已完整呈现时,ldconfig会以一极微小的操作,将连结指向新的版本,使得任何原本使用旧版的程式不会感到丝毫的不悦.
<br>
<br>
DLL程式库(我知道这是无谓的反覆(tautology)---所以对我提出告诉吧!)通常会比它们的静态副本(static
counterparts)要来得大多了.它们是以*洞(holes)*的形式来保留空间以便日後的扩充.这种*洞*可以不占用任何的磁碟空间.一个简单的cp呼叫,或是使用makehole程式,就可以达到这样效果(achieve).因为它们的位址是固定在同一位置上,所以在建立程式库後,你可以把它们拿掉.千万不要试著夺走ELF的程式库.
<br>
<br>
6.4.3.3. ``libc-lite''? <br>
libc-lite是轻量级(light-weight)的libc版本.可用来存放在磁碟片上,与替大部份低微的(menial)UNIX任务收尾(suffice).它没有包含curses,
dbm, termcap等等的程式码.如果你的/lib/libc.so.4是连结到一个lite的libc,那麽建议你以完整的版本取代它.
<br>
</p>
<BR>
<hr color="#EE9B73" size="1" width="94%">
</TD>
<TD CLASS="tt3" VALIGN="bottom" width="8%" bgcolor="#e0e0e0"><strong><A HREF="039.htm">后一页</A><BR>
<A HREF="037.htm">前一页</A><BR>
<A HREF="index.html">回目录</A><BR>
<A HREF="../../../../index.htm">回首页</A><BR>
</strong>
</TD>
</TR>
</table>
</BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -