<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8">
<meta name="generator" content="pdf2htmlEX">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<link rel="stylesheet" href="https://static.pudn.com/base/css/base.min.css">
<link rel="stylesheet" href="https://static.pudn.com/base/css/fancy.min.css">
<link rel="stylesheet" href="https://static.pudn.com/prod/directory_preview_static/626077c3090cbf2c4ed0ced8/raw.css">
<script src="https://static.pudn.com/base/js/compatibility.min.js"></script>
<script src="https://static.pudn.com/base/js/pdf2htmlEX.min.js"></script>
<script>
try{
pdf2htmlEX.defaultViewer = new pdf2htmlEX.Viewer({});
}catch(e){}
</script>
<title></title>
</head>
<body>
<div id="sidebar" style="display: none">
<div id="outline">
</div>
</div>
<div id="pf1" class="pf w0 h0" data-page-no="1"><div class="pc pc1 w0 h0"><img class="bi x0 y0 w1 h1" alt="" src="https://static.pudn.com/prod/directory_preview_static/626077c3090cbf2c4ed0ced8/bg1.jpg"><div class="c x0 y1 w0 h2"><div class="t m0 x1 h3 y2 ff1 fs0 fc0 sc0 ls0 ws0">第<span class="_ _0"> </span>3<span class="_ _0"> </span>章 目标文件</div><div class="t m0 x2 h4 y3 ff1 fs1 fc0 sc1 ls0 ws0">$Re<span class="_ _1"></span>vi<span class="_ _1"></span>sio<span class="_ _1"></span>n: <span class="_ _1"></span>2.<span class="_ _1"></span>6 $</div><div class="t m0 x2 h4 y4 ff1 fs1 fc0 sc1 ls0 ws0">$Da<span class="_ _1"></span>te<span class="_ _1"></span>: 1<span class="_ _1"></span>999<span class="_ _1"></span>/0<span class="_ _1"></span>6/2<span class="_ _1"></span>9 <span class="_ _1"></span>04:<span class="_ _1"></span>21<span class="_ _1"></span>:48<span class="_ _1"></span> $</div><div class="t m0 x3 h4 y5 ff1 fs1 fc0 sc1 ls0 ws0">编译器和汇编器创建了目标文件(包含由源程序生成的二进制代码和数据)。链接器</div><div class="t m0 x2 h4 y6 ff1 fs1 fc0 sc1 ls0 ws0">将多个目标文件合并成一个,加载器读取这些目标文件并将它们加载到内存中(在一个集成</div><div class="t m0 x2 h4 y7 ff1 fs1 fc0 sc1 ls0 ws0">编程环境中,当用户告诉它建立一个程序时,编译器、汇编器、链接器会在后台运行,但是</div><div class="t m0 x2 h4 y8 ff1 fs1 fc0 sc1 ls0 ws0">它们确实是存在于“盖子”<span class="_ _1"></span>下面的)。在本章中,我们将深入到目标文件格式和内容的细节</div><div class="t m0 x2 h4 y9 ff1 fs1 fc0 sc1 ls0 ws0">之中。</div><div class="t m0 x2 h5 ya ff1 fs2 fc0 sc0 ls0 ws0">目标文件中都有什么?</div><div class="t m0 x3 h4 yb ff1 fs1 fc0 sc1 ls0 ws0">一个目标文件包含五类信息。</div><div class="t m0 x3 h4 yc ff2 fs1 fc0 sc1 ls0 ws0"><span class="_ _2"> </span><span class="ff1">头信息:关于文件的整体信息,诸如代码大小,翻译成该目标文件的源文件名称,</span></div><div class="t m0 x4 h4 yd ff1 fs1 fc0 sc1 ls0 ws0">和创建日期等。</div><div class="t m0 x3 h4 ye ff2 fs1 fc0 sc1 ls0 ws0"><span class="_ _2"> </span><span class="ff1">目标代码:由编译器或汇编器产生的二进制指令和数据。</span></div><div class="t m0 x3 h4 yf ff2 fs1 fc0 sc1 ls0 ws0"><span class="_ _2"> </span><span class="ff1">重定位信息:目标代码中的一个位置列表,链接器在修改目标代码的地址时会对它</span></div><div class="t m0 x4 h4 y10 ff1 fs1 fc0 sc1 ls0 ws0">进行调整。</div><div class="t m0 x3 h4 y11 ff2 fs1 fc0 sc1 ls0 ws0"><span class="_ _2"> </span><span class="ff1">符号:该模块中定义的全局符号,以及从其它模块导入的或者由链接器定义的符号。</span></div><div class="t m0 x3 h4 y12 ff2 fs1 fc0 sc1 ls0 ws0"><span class="_ _2"> </span><span class="ff1">调试信息:目标代码中与链接无关但会被调试器使用到的其它信息。包括源代码文</span></div><div class="t m0 x4 h4 y13 ff1 fs1 fc0 sc1 ls0 ws0">件和行号信息,本地符号,被目标代码使用的数据结构描述信息(如<span class="_ _3"> </span>C<span class="_ _3"> </span>语言数据结</div><div class="t m0 x4 h4 y14 ff1 fs1 fc0 sc1 ls0 ws0">构定义)。(某些目标文件甚至包含比这更多的信息,但上面这些对于我们在本章</div><div class="t m0 x4 h4 y15 ff1 fs1 fc0 sc1 ls0 ws0">所需关注的已经足够了)</div><div class="t m0 x3 h4 y16 ff1 fs1 fc0 sc1 ls0 ws0">并不是所有的目标文件格式都包含这几类信息,一个很有用的目标文件格式很少或不</div><div class="t m0 x2 h4 y17 ff1 fs1 fc0 sc1 ls0 ws0">包含以上任何信息,都是可能的。</div><div class="t m0 x2 h6 y18 ff1 fs3 fc0 sc0 ls0 ws0">设计一个目标文件格式</div><div class="t m0 x3 h4 y19 ff1 fs1 fc0 sc1 ls0 ws0">对一个目标文件格式的设计实际上是对目标文件所处的各种用途导致的折衷方案。一</div><div class="t m0 x2 h4 y1a ff1 fs1 fc0 sc1 ls0 ws0">个文件可能是可链接的,能够作为链接编辑器或链接加载器的输入;它也可能是可执行的,</div><div class="t m0 x2 h4 y1b ff1 fs1 fc0 sc1 ls0 ws0">可以加载到内存中作为一个程序运行;或者是可加载的,作为库同程序一起被加载到内存中;</div><div class="t m0 x2 h4 y1c ff1 fs1 fc0 sc1 ls0 ws0">或者它是以上几种情况的组合。某些格式只支持上面的一到两种用法,而另一些格式则支持</div><div class="t m0 x2 h4 y1d ff1 fs1 fc0 sc1 ls0 ws0">所有的用法。</div><div class="t m0 x3 h4 y1e ff1 fs1 fc0 sc1 ls0 ws0">一个可链接文件还包含链接器处理目标代码时所需的扩展符号和重定位信息。目标代</div></div></div><div class="pi" data-data='{"ctm":[1.611639,0.000000,0.000000,1.611639,0.000000,0.000000]}'></div></div>
</body>
</html>
<div id="pf2" class="pf w0 h0" data-page-no="2"><div class="pc pc2 w0 h0"><img class="bi x0 y0 w1 h1" alt="" src="https://static.pudn.com/prod/directory_preview_static/626077c3090cbf2c4ed0ced8/bg2.jpg"><div class="c x0 y1 w0 h2"><div class="t m0 x2 h4 y1f ff1 fs1 fc0 sc1 ls0 ws0">码经常被划分为多个会被链接器区别对待的小逻辑段。一个可执行程序中会包含目标代码</div><div class="t m0 x2 h7 y20 ff1 fs1 fc0 sc1 ls0 ws0">(为了能让文件被映射到地址空间中它通常是页对<span class="ff3">齐</span>的),但是可以不需<span class="ff3">要</span>任何符号(<span class="ff3">除非</span></div><div class="t m0 x2 h7 y21 ff1 fs1 fc0 sc1 ls0 ws0">它<span class="ff3">要</span>进行运行时<span class="ff3">动态</span>链接)以及重定位信息。目标代码可以是一个<span class="ff3">单独</span>的大段,或<span class="ff3">反</span>映了<span class="ff3">硬</span></div><div class="t m0 x2 h7 y22 ff1 fs1 fc0 sc1 ls0 ws0">件执行环境的一组小段(多数是只读或可读<span class="ff3">写</span>的页)。<span class="ff3">根</span>据<span class="ff3">系统</span>运行时环境细节的不同,一</div><div class="t m0 x2 h7 y23 ff1 fs1 fc0 sc1 ls0 ws0">个可加载文件可以<span class="ff3">仅</span>包含目标代码,或为了进行运行时链接还包含了<span class="ff3">完</span>整的符号和重定位信</div><div class="t m0 x2 h4 y24 ff1 fs1 fc0 sc1 ls0 ws0">息。</div><div class="t m0 x3 h7 y25 ff1 fs1 fc0 sc1 ls0 ws0">在<span class="ff3">应</span>用中会存在某些<span class="ff3">冲突</span>。面<span class="ff3">向</span>逻辑的可链接段分组<span class="ff3">策略</span>很少能够与面<span class="ff3">向硬</span>件的可执</div><div class="t m0 x2 h7 y26 ff1 fs1 fc0 sc1 ls0 ws0">行段分组<span class="ff3">策略相匹配</span>。<span class="ff3">尤</span>其是在一些<span class="ff3">较</span>小的计<span class="ff3">算机</span>上,链接器<span class="ff3">每次</span>只会对可链接文件的一小</div><div class="t m0 x2 h7 y27 ff3 fs1 fc0 sc1 ls0 ws0">片<span class="ff1">进行读</span>写<span class="ff1">,但可执行程序会被整体的加载到内存中。这种区别在<span class="_ _3"> </span></span>MS-<span class="_ _1"></span><span class="ff1">D<span class="ff3">O<span class="_ _1"></span>S<span class="_ _3"> </span><span class="ff1">中</span>尤<span class="ff1">为</span>明显<span class="ff1">,</span>因</span></span></div><div class="t m0 x2 h7 y28 ff1 fs1 fc0 sc1 ls0 ws0">为它的可链接<span class="_ _3"> </span><span class="ff3">OMF<span class="_ _4"> </span></span>格式与可执行<span class="_ _3"> </span><span class="ff3">EX<span class="_ _1"></span>E<span class="_ _4"> </span><span class="ff1">格式是</span>完<span class="ff1">全不同的。</span></span></div><div class="t m0 x3 h7 y29 ff1 fs1 fc0 sc1 ls0 ws0">这<span class="ff3">里</span>我们将会<span class="ff3">涉</span>及到一<span class="ff3">系</span>列常用的格式,从<span class="ff3">最简单</span>的<span class="ff3">开始</span>,一<span class="ff3">直</span>到<span class="ff3">最复杂</span>的。</div><div class="t m0 x2 h8 y2a ff1 fs2 fc0 sc0 ls0 ws0">空目标文件格式: <span class="_ _1"></span><span class="ff3">MS<span class="_ _1"></span>-<span class="ff1">D<span class="_ _1"></span><span class="ff3">OS<span class="_ _5"> </span><span class="ff1">的.C<span class="_ _1"></span><span class="ff3">OM<span class="_ _5"> </span><span class="ff1">文件</span></span></span></span></span></span></div><div class="t m0 x3 h7 y2b ff3 fs1 fc0 sc1 ls0 ws0">碰<span class="ff1">到一个</span>仅<span class="ff1">有可运行二进制代码而</span>没<span class="ff1">有其它信息的能够使用的目标代码文件是可能的。</span></div><div class="t m0 x2 h7 y2c ff3 fs1 fc0 sc1 ls0 ws0">MS-<span class="_ _1"></span><span class="ff1">D<span class="ff3">O<span class="_ _1"></span>S<span class="_ _4"> </span><span class="ff1">的.C</span>OM<span class="_ _4"> </span>就<span class="ff1">是</span>最<span class="ff1">有名的</span>例<span class="ff1">子。一个.C</span>O<span class="_ _1"></span>M<span class="_ _4"> </span><span class="ff1">文件中</span>除<span class="ff1">了二进制代码</span>外没<span class="ff1">有别的。当</span>操<span class="ff1">作</span>系</span></span></div><div class="t m0 x2 h7 y2d ff3 fs1 fc0 sc1 ls0 ws0">统<span class="ff1">运行一个.C</span>O<span class="_ _1"></span>M<span class="_ _4"> </span><span class="ff1">文件时,它只需将文件的内容加载到一块空</span>闲<span class="ff1">内存中,从</span>偏移量<span class="_ _3"> </span><span class="ff1">0</span>x<span class="ff1">10<span class="_ _1"></span>0<span class="_ _4"> </span>处<span class="ff3">开</span></span></div><div class="t m0 x2 h7 y2e ff3 fs1 fc0 sc1 ls0 ws0">始<span class="ff1">执行(0</span>-<span class="ff1">0<span class="_ _1"></span><span class="ff3">xF<span class="_ _1"></span>F<span class="_ _4"> </span><span class="ff1">存</span>放<span class="ff1">的是程序的</span>命<span class="ff1">令行</span>参<span class="ff1">数和其它</span>参<span class="ff1">数,称为程序段</span>前缀<span class="_ _3"> </span>PSP<span class="ff1">),将所有的</span></span></span></div><div class="t m0 x2 h7 y2f ff3 fs1 fc0 sc1 ls0 ws0">x<span class="ff1">86<span class="_ _4"> </span>段</span>寄<span class="ff1">存器设置为指</span>向<span class="_ _3"> </span>PS<span class="_ _1"></span>P<span class="ff1">,将<span class="_ _4"> </span></span>SP<span class="ff1">(</span>栈<span class="ff1">指</span>针<span class="ff1">)</span>寄<span class="ff1">存器指</span>向<span class="ff1">该段的</span>末尾<span class="ff1">(由于</span>栈<span class="ff1">是</span>向<span class="ff1">下生</span>长</div><div class="t m0 x2 h7 y30 ff1 fs1 fc0 sc1 ls0 ws0">的),<span class="ff3">然</span>后<span class="ff3">跳转</span>到被加载程序的入<span class="ff3">口</span>处。</div><div class="t m0 x3 h7 y31 ff3 fs1 fc0 sc1 ls0 ws0">x<span class="ff1">86<span class="_ _4"> </span>的分段</span>架<span class="ff1">构使</span>得<span class="ff1">这种文件格式可以</span>工<span class="ff1">作。</span>因<span class="ff1">为所有的<span class="_ _3"> </span></span>x<span class="ff1">8<span class="_ _1"></span>6<span class="_ _4"> </span>程序地址都被<span class="ff3">解释</span>为是<span class="ff3">相</span></span></div><div class="t m0 x2 h7 y32 ff1 fs1 fc0 sc1 ls0 ws0">对于当<span class="ff3">前</span>段<span class="ff3">基</span>地址的,所有的段<span class="ff3">寄</span>存器都指<span class="ff3">向</span>该段的<span class="ff3">基</span>址,而程序<span class="ff3">总</span>是以<span class="ff3">相</span>对段位置为<span class="_ _3"> </span>0<span class="ff3">x</span>1</div><div class="t m0 x2 h7 y33 ff1 fs1 fc0 sc1 ls0 ws0">00<span class="_ _4"> </span>的方式被加载。<span class="ff3">因此</span>,对于可以<span class="ff3">放</span>入<span class="ff3">单</span>个段的程序而言,由于段<span class="ff3">相</span>对地址可以在链接时</div><div class="t m0 x2 h7 y34 ff1 fs1 fc0 sc1 ls0 ws0">确定而不需<span class="ff3">要再</span>进行调整。</div><div class="t m0 x3 h7 y35 ff1 fs1 fc0 sc1 ls0 ws0">对于<span class="ff3">那</span>些不能<span class="ff3">放</span>入<span class="ff3">单</span>一段的程序<span class="ff3">来说</span>,对地址的调整<span class="ff3">工</span>作是程序<span class="ff3">员</span>的<span class="ff3">事</span>情。而<span class="ff3">且</span>确实</div><div class="t m0 x2 h7 y36 ff1 fs1 fc0 sc1 ls0 ws0">存在一些程序是在<span class="ff3">启动</span>时读取某个段<span class="ff3">寄</span>存器<span class="ff3">然</span>后将它的<span class="ff3">值</span>与<span class="ff3">保</span>存在程序中的某个地方的段<span class="ff3">值</span></div><div class="t m0 x2 h7 y37 ff3 fs1 fc0 sc1 ls0 ws0">相<span class="ff1">加。当</span>然<span class="ff1">这类</span>繁琐<span class="ff1">的</span>工<span class="ff1">作</span>趋向<span class="ff1">于由链接器和加载器</span>来自动完<span class="ff1">成,</span>MS-<span class="_ _1"></span><span class="ff1">D<span class="ff3">O<span class="_ _1"></span>S<span class="_ _4"> </span><span class="ff1">通</span>过<span class="ff1">.</span>EXT<span class="_ _4"> </span><span class="ff1">文件</span>来</span></span></div><div class="t m0 x2 h7 y38 ff3 fs1 fc0 sc1 ls0 ws0">完<span class="ff1">成这些(在本章</span>稍<span class="ff1">后</span>部<span class="ff1">分会</span>讲<span class="ff1">述到)。</span></div><div class="t m0 x2 h8 y39 ff1 fs2 fc0 sc0 ls0 ws0">代码区段: <span class="_ _1"></span><span class="ff3">U<span class="ff1">n<span class="_ _1"></span>i<span class="ff3">x<span class="_ _1"></span><span class="ff1"> a<span class="_ _1"></span>.o<span class="_ _1"></span><span class="ff3">u<span class="ff1">t<span class="_ _5"> </span>文件</span></span></span></span></span></span></div><div class="t m0 x3 h7 y3a ff3 fs1 fc0 sc1 ls0 ws0">具<span class="ff1">有</span>硬<span class="ff1">件内存重定位</span>部<span class="ff1">件的计</span>算机系统<span class="ff1">(</span>今天<span class="ff1">几</span>乎<span class="ff1">所有的计</span>算机<span class="ff1">都有)通常都会为</span>新</div><div class="t m0 x2 h7 y3b ff1 fs1 fc0 sc1 ls0 ws0">运行的程序创建一个<span class="ff3">具</span>有空地址空间的<span class="ff3">新</span>进程,这种情况下程序<span class="ff3">就</span>可以<span class="ff3">按照</span>从某个<span class="ff3">固</span>定地址</div><div class="t m0 x2 h7 y3c ff3 fs1 fc0 sc1 ls0 ws0">开始<span class="ff1">的方式被链接,而不需</span>要<span class="ff1">加载时的重定位。</span>UNI<span class="_ _1"></span>X<span class="_ _4"> </span><span class="ff1">的<span class="_ _3"> </span>a.o</span>u<span class="_ _1"></span><span class="ff1">t<span class="_ _4"> </span>目标文件格式<span class="ff3">就</span>是<span class="ff3">针</span>对这种情</span></div><div class="t m0 x2 h4 y3d ff1 fs1 fc0 sc1 ls0 ws0">况的。</div><div class="t m0 x3 h7 y3e ff3 fs1 fc0 sc1 ls0 ws0">最简单<span class="ff1">的情况下,一个<span class="_ _3"> </span>a.o<span class="_ _1"></span><span class="ff3">u<span class="ff1">t<span class="_ _4"> </span>文件包含一个小文件头,后面接</span>着<span class="ff1">是可执行代码(由于</span>历</span></span></div><div class="t m0 x2 h7 y3f ff3 fs1 fc0 sc1 ls0 ws0">史<span class="ff1">的</span>原因<span class="ff1">被称为文本段),</span>然<span class="ff1">后是</span>静态<span class="ff1">数据的</span>初始值<span class="ff1">,如</span>图<span class="_ _3"> </span><span class="ff1">1<span class="_ _4"> </span>所</span>示<span class="ff1">。</span>P<span class="ff1">D</span>P-<span class="ff1">1<span class="_ _1"></span>1<span class="_ _4"> </span>只有<span class="_ _3"> </span>16<span class="_ _4"> </span>位<span class="ff3">寻</span>址,</span></div></div></div><div class="pi" data-data='{"ctm":[1.611639,0.000000,0.000000,1.611639,0.000000,0.000000]}'></div></div>
<div id="pf3" class="pf w0 h0" data-page-no="3"><div class="pc pc3 w0 h0"><img class="bi x0 y0 w1 h1" alt="" src="https://static.pudn.com/prod/directory_preview_static/626077c3090cbf2c4ed0ced8/bg3.jpg"><div class="c x0 y1 w0 h2"><div class="t m0 x2 h7 y1f ff1 fs1 fc0 sc1 ls0 ws0">将程序的地址空间<span class="ff3">限</span>制为<span class="_ _3"> </span>64<span class="ff3">K<span class="_ _1"></span><span class="ff1">。这个<span class="ff3">限</span>制很<span class="ff3">快就变得太</span>小了,所以<span class="_ _3"> </span><span class="ff3">P</span>D<span class="_ _1"></span><span class="ff3">P-<span class="ff1">1<span class="_ _1"></span>1<span class="_ _4"> </span>产<span class="ff3">品线</span>的后<span class="ff3">续型</span>号</span></span></span></span></div><div class="t m0 x2 h7 y20 ff1 fs1 fc0 sc1 ls0 ws0">为代码(称为指令空间<span class="_ _3"> </span><span class="ff3">I</span>)和数据(称为数据空间<span class="_ _4"> </span>D)<span class="ff3">提供</span>了<span class="ff3">独</span>立的地址空间,这<span class="ff3">样</span>一个程</div><div class="t m0 x2 h7 y21 ff1 fs1 fc0 sc1 ls0 ws0">序可以<span class="ff3">拥</span>有<span class="_ _3"> </span>64<span class="ff3">K<span class="_ _4"> </span></span>的代码空间和<span class="_ _3"> </span>64<span class="_ _1"></span><span class="ff3">K<span class="_ _4"> </span><span class="ff1">的数据空间。为了支持这个</span>特性<span class="ff1">,编译器、汇编器、链接</span></span></div><div class="t m0 x2 h7 y22 ff1 fs1 fc0 sc1 ls0 ws0">器都被修改为可以创建两个段的目标文件(代码<span class="ff3">放</span>入第一个段中,数据<span class="ff3">放</span>入第二个段中,程</div><div class="t m0 x2 h7 y23 ff1 fs1 fc0 sc1 ls0 ws0">序加载时<span class="ff3">先</span>将第一个段载入进程的<span class="_ _3"> </span><span class="ff3">I<span class="_ _4"> </span></span>空间,<span class="ff3">再</span>将第二个段载入进程的<span class="_ _3"> </span>D<span class="_ _3"> </span>空间)。</div><div class="t m0 x5 h9 y40 ff3 fs4 fc0 sc1 ls0 ws0">---------------------------------------------------------------------------------------------</div><div class="t m0 x3 h7 y41 ff3 fs1 fc0 sc1 ls0 ws0">图<span class="_ _3"> </span><span class="ff1">3</span>-<span class="ff1">1<span class="_ _1"></span>: <span class="_ _1"></span><span class="ff3">简化<span class="ff1">的<span class="_ _3"> </span>a.o<span class="_ _1"></span><span class="ff3">u<span class="ff1">t</span></span></span></span></span></div><div class="t m0 x3 h7 y42 ff1 fs1 fc0 sc1 ls0 ws0">a.o<span class="_ _1"></span><span class="ff3">u<span class="ff1">t<span class="_ _4"> </span>头</span>部<span class="ff1">,文本段,数据段,和其它段</span></span></div><div class="t m0 x5 h9 y43 ff3 fs4 fc0 sc1 ls0 ws0">---------------------------------------------------------------------------------------------</div><div class="t m0 x3 h7 y44 ff3 fs1 fc0 sc1 ls0 ws0">独<span class="ff1">立的<span class="_ _3"> </span></span>I<span class="_ _4"> </span><span class="ff1">和<span class="_ _3"> </span>D<span class="_ _3"> </span>空间还有另一个</span>性<span class="ff1">能上的</span>优势<span class="ff1">:由于一个程序不能修改</span>自己<span class="ff1">的<span class="_ _3"> </span></span>I<span class="_ _3"> </span><span class="ff1">空间,</span>因</div><div class="t m0 x2 h7 y45 ff3 fs1 fc0 sc1 ls0 ws0">此<span class="ff1">一个程序的多个实体可以</span>共享<span class="ff1">一</span>份<span class="ff1">程序代码的</span>副<span class="ff1">本。在诸如<span class="_ _3"> </span></span>UNI<span class="_ _1"></span>X<span class="_ _4"> </span><span class="ff1">这</span>样<span class="ff1">的分时</span>系统<span class="ff1">上,s</span>h<span class="ff1">e</span></div><div class="t m0 x2 h7 y46 ff3 fs1 fc0 sc1 ls0 ws0">ll<span class="ff1">(</span>命<span class="ff1">令</span>解释<span class="ff1">器)和</span>网络服务<span class="ff1">进程</span>具<span class="ff1">有多个</span>副<span class="ff1">本是很</span>普遍<span class="ff1">的,</span>共享<span class="ff1">程序代码可以节</span>省相<span class="ff1">当可</span></div><div class="t m0 x2 h7 y47 ff3 fs1 fc0 sc1 ls0 ws0">观<span class="ff1">的内存空间。</span></div><div class="t m0 x3 h7 y48 ff3 fs1 fc0 sc1 ls0 ws0">现<span class="ff1">在</span>唯<span class="ff1">一通用的</span>仍然<span class="ff1">为代码和数据进行</span>单独寻<span class="ff1">址的计</span>算机就<span class="ff1">是<span class="_ _3"> </span>286<span class="_ _1"></span>(或处于<span class="_ _3"> </span>16<span class="_ _4"> </span>位<span class="ff3">保护</span></span></div><div class="t m0 x2 h7 y49 ff1 fs1 fc0 sc1 ls0 ws0">模式的<span class="_ _3"> </span>386<span class="_ _1"></span>)。<span class="ff3">即</span>使在地址空间<span class="ff3">巨</span>大的<span class="ff3">现</span>代计<span class="ff3">算机</span>上,<span class="ff3">操</span>作<span class="ff3">系统</span>也可以通<span class="ff3">过虚拟</span>内存<span class="ff3">来</span>更有</div><div class="t m0 x2 h7 y4a ff3 fs1 fc0 sc1 ls0 ws0">效<span class="ff1">的(</span>相<span class="ff1">比于可读/</span>写<span class="ff1">页的方式)处理只读代码页的</span>共享<span class="ff1">,</span>因此<span class="ff1">所有的</span>现<span class="ff1">代加载器都支持它</span></div></div></div><div class="pi" data-data='{"ctm":[1.611639,0.000000,0.000000,1.611639,0.000000,0.000000]}'></div></div>
<div id="pf4" class="pf w0 h0" data-page-no="4"><div class="pc pc4 w0 h0"><img class="bi x0 y0 w1 h1" alt="" src="https://static.pudn.com/prod/directory_preview_static/626077c3090cbf2c4ed0ced8/bg4.jpg"><div class="c x0 y1 w0 h2"><div class="t m0 x2 h7 y1f ff1 fs1 fc0 sc1 ls0 ws0">们。这<span class="ff3">意味着</span>链接器创建的格式中至少<span class="ff3">要</span>标<span class="ff3">识出</span>只读和可读<span class="ff3">写</span>的段<span class="ff3">来</span>。实际中,多数链接器</div><div class="t m0 x2 h7 y20 ff1 fs1 fc0 sc1 ls0 ws0">支持的格式中都<span class="ff3">具</span>有多种类<span class="ff3">型</span>的段,诸如只读数据,<span class="ff3">供</span>后<span class="ff3">继</span>链接<span class="ff3">操</span>作使用的符号和重定位信</div><div class="t m0 x2 h7 y21 ff1 fs1 fc0 sc1 ls0 ws0">息段,调试符号,和<span class="ff3">共享</span>库信息(<span class="ff3">UNI<span class="_ _1"></span>X<span class="_ _4"> </span><span class="ff1">的</span>惯例<span class="ff1">令</span>人混淆<span class="ff1">的将文件区段</span>`<span class="ff1">se</span>c<span class="_ _1"></span><span class="ff1">tio<span class="_ _1"></span>n<span class="ff3">'<span class="_ _1"></span><span class="ff1">称为段<span class="ff3">`</span>se<span class="_ _1"></span><span class="ff3">gm</span></span></span></span></span></div><div class="t m0 x2 h7 y22 ff1 fs1 fc0 sc1 ls0 ws0">ent<span class="_ _1"></span><span class="ff3">'<span class="ff1">,所以我们在</span>讨论<span class="_ _4"> </span>UNIX<span class="_ _4"> </span><span class="ff1">的文件格式时也使用这个</span>术<span class="ff1">语)。</span></span></div><div class="t m0 x2 ha y4b ff1 fs3 fc0 sc0 ls0 ws0">a.o<span class="ff3">u</span>t<span class="_ _4"> </span>头<span class="ff3">部</span></div><div class="t m0 x3 h7 y4c ff1 fs1 fc0 sc1 ls0 ws0">a.o<span class="_ _1"></span><span class="ff3">u<span class="ff1">t<span class="_ _4"> </span>的头</span>部根<span class="ff1">据<span class="_ _3"> </span></span>UNI<span class="_ _1"></span>X<span class="_ _4"> </span>版<span class="ff1">本的不同而</span>略<span class="ff1">有</span>变化<span class="ff1">,但</span>最典型<span class="ff1">的是<span class="_ _3"> </span></span>BS<span class="ff1">D <span class="_ _1"></span><span class="ff3">UNI<span class="_ _1"></span>X<span class="_ _4"> </span><span class="ff1">的</span>版<span class="ff1">本,如</span>图</span></span></span></div><div class="t m0 x2 h7 y4d ff1 fs1 fc0 sc1 ls0 ws0">2<span class="_ _4"> </span>所<span class="ff3">示</span>(在本章的<span class="ff3">示例</span>中,int<span class="_ _4"> </span>类<span class="ff3">型</span>为<span class="_ _3"> </span>32<span class="_ _4"> </span>位,s<span class="ff3">h</span>o<span class="ff3">r</span>t<span class="_ _4"> </span>类<span class="ff3">型</span>为<span class="_ _4"> </span>16<span class="_ _3"> </span>位)。</div><div class="t m0 x5 h9 y4e ff3 fs4 fc0 sc1 ls0 ws0">---------------------------------------------------------------------------------------------</div><div class="t m0 x3 h7 y4f ff3 fs1 fc0 sc1 ls0 ws0">图<span class="_ _3"> </span><span class="ff1">3</span>-<span class="ff1">2<span class="_ _1"></span>: <span class="_ _1"></span>a.o<span class="_ _1"></span><span class="ff3">u<span class="ff1">t<span class="_ _4"> </span>头</span>部</span></span></div><div class="t m0 x3 h7 y50 ff1 fs1 fc0 sc1 ls0 ws0">int<span class="_ _1"></span> a<span class="_ _1"></span><span class="ff3">_m<span class="ff1">a<span class="_ _1"></span><span class="ff3">g<span class="ff1">i</span>c<span class="_ _1"></span>;<span class="ff1"> <span class="_ _1"></span> //<span class="_ _1"></span> <span class="ff3">幻</span>数</span></span></span></span></div><div class="t m0 x3 h7 y51 ff1 fs1 fc0 sc1 ls0 ws0">int<span class="_ _1"></span> a<span class="_ _1"></span><span class="ff3">_<span class="ff1">te<span class="_ _1"></span><span class="ff3">x<span class="ff1">t</span>;<span class="_ _1"></span><span class="ff1"> <span class="_ _1"></span> //<span class="_ _1"></span> 文本段大小</span></span></span></span></div><div class="t m0 x3 h7 y52 ff1 fs1 fc0 sc1 ls0 ws0">int<span class="_ _1"></span> a<span class="_ _1"></span><span class="ff3">_d<span class="ff1">a<span class="_ _1"></span>ta<span class="ff3">;<span class="_ _1"></span><span class="ff1"> <span class="_ _1"></span> //<span class="_ _1"></span> <span class="ff3">初始化</span>的数据段大小</span></span></span></span></div><div class="t m0 x3 h7 y53 ff1 fs1 fc0 sc1 ls0 ws0">int<span class="_ _1"></span> a<span class="_ _1"></span><span class="ff3">_b<span class="ff1">s<span class="_ _1"></span>s<span class="ff3">;</span> <span class="_ _1"></span> <span class="_ _1"></span> //<span class="_ _1"></span> <span class="ff3">未初始化</span>的数据段大小</span></span></div><div class="t m0 x3 h7 y54 ff1 fs1 fc0 sc1 ls0 ws0">int<span class="_ _1"></span> a<span class="_ _1"></span><span class="ff3">_<span class="ff1">s</span>y<span class="_ _1"></span>m<span class="ff1">s</span>;<span class="_ _1"></span><span class="ff1"> <span class="_ _1"></span> //<span class="_ _1"></span> 符号表大小</span></span></div><div class="t m0 x3 h7 y55 ff1 fs1 fc0 sc1 ls0 ws0">int<span class="_ _1"></span> a<span class="_ _1"></span><span class="ff3">_<span class="ff1">en<span class="_ _1"></span>t<span class="ff3">ry<span class="_ _1"></span>;<span class="ff1"> <span class="_ _1"></span> //<span class="_ _1"></span> 入<span class="ff3">口点</span></span></span></span></span></div><div class="t m0 x3 h7 y56 ff1 fs1 fc0 sc1 ls0 ws0">int<span class="_ _1"></span> a<span class="_ _1"></span><span class="ff3">_<span class="ff1">t</span>r<span class="_ _1"></span><span class="ff1">si<span class="ff3">z<span class="_ _1"></span><span class="ff1">e<span class="ff3">;<span class="_ _1"></span><span class="ff1"> //<span class="_ _1"></span> 文本重定位段大小</span></span></span></span></span></span></div><div class="t m0 x3 h7 y57 ff1 fs1 fc0 sc1 ls0 ws0">int<span class="_ _1"></span> a<span class="_ _1"></span><span class="ff3">_dr<span class="_ _1"></span><span class="ff1">si<span class="ff3">z<span class="_ _1"></span><span class="ff1">e<span class="ff3">;<span class="_ _1"></span><span class="ff1"> //<span class="_ _1"></span> 数据重定位段大小</span></span></span></span></span></span></div><div class="t m0 x5 h9 y58 ff3 fs4 fc0 sc1 ls0 ws0">---------------------------------------------------------------------------------------------</div><div class="t m0 x3 h7 y59 ff3 fs1 fc0 sc1 ls0 ws0">幻<span class="ff1">数<span class="_ _3"> </span>a</span>_m<span class="_ _1"></span><span class="ff1">a<span class="ff3">g<span class="_ _1"></span><span class="ff1">i<span class="ff3">c<span class="_ _4"> </span>说明</span>了当<span class="ff3">前</span>可执行文件的类<span class="ff3">型</span></span></span></span></div><div class="t m0 x6 hb y5a ff1 fs5 fc0 sc1 ls0 ws0">1</div><div class="t m0 x7 h7 y59 ff1 fs1 fc0 sc1 ls0 ws0">。不同的<span class="ff3">幻</span>数告诉<span class="ff3">操</span>作<span class="ff3">系统</span>的程序加载器</div><div class="t m0 x2 h7 y5b ff1 fs1 fc0 sc1 ls0 ws0">以不同的方式将文件加载到内存中;我们将在下面<span class="ff3">讨论</span>这些区别。文本和数据段大小<span class="_ _3"> </span>a<span class="ff3">_</span>t<span class="_ _1"></span>e<span class="ff3">x<span class="_ _1"></span><span class="ff1">t</span></span></div><div class="t m0 x2 h7 y5c ff1 fs1 fc0 sc1 ls0 ws0">和<span class="_ _3"> </span>a<span class="ff3">_d<span class="_ _1"></span><span class="ff1">at<span class="_ _1"></span>a<span class="_ _4"> </span>以<span class="ff3">字</span>节为<span class="ff3">单</span>位标<span class="ff3">识</span>了头<span class="ff3">部</span>后面的只读代码段和可读<span class="ff3">写</span>数据段的大小。由于<span class="_ _3"> </span><span class="ff3">UNIX<span class="_ _4"> </span></span>会</span></span></div><div class="t m0 x2 h7 y5d ff3 fs1 fc0 sc1 ls0 ws0">自动<span class="ff1">将</span>新<span class="ff1">分</span>配<span class="ff1">的内存</span>清零<span class="ff1">,</span>因此初值<span class="ff1">无关</span>紧要<span class="ff1">或者为<span class="_ _3"> </span>0<span class="_ _4"> </span>的数据不</span>必<span class="ff1">在<span class="_ _3"> </span>a.o</span>u<span class="ff1">t<span class="_ _4"> </span>文件中存</span>储<span class="ff1">。</span>未</div><div class="t m0 x2 h7 y5e ff3 fs1 fc0 sc1 ls0 ws0">初始化<span class="ff1">数据大小<span class="_ _3"> </span>a</span>_b<span class="_ _1"></span><span class="ff1">ss<span class="_ _4"> </span><span class="ff3">说明</span>了在<span class="_ _3"> </span>a.o<span class="_ _1"></span><span class="ff3">u<span class="ff1">t<span class="_ _4"> </span>文件中的可读</span>写<span class="ff1">数据段后面逻辑上存在多少</span>未初始化</span></span></div><div class="t m0 x2 h7 y5f ff1 fs1 fc0 sc1 ls0 ws0">的数据(实际上是被<span class="ff3">初始化</span>为<span class="_ _3"> </span>0)。</div><div class="t m0 x2 h7 y60 ff1 fs1 fc0 sc1 ls0 ws0">a<span class="ff3">_</span>e<span class="_ _1"></span>nt<span class="_ _1"></span><span class="ff3">ry<span class="_ _4"> </span>域<span class="ff1">指</span>明<span class="ff1">了程序的起</span>始<span class="ff1">地址,同时<span class="_ _3"> </span>a</span>_<span class="ff1">s</span>y<span class="_ _1"></span>m<span class="ff1">s<span class="_ _1"></span>,a<span class="ff3">_</span>t<span class="_ _1"></span><span class="ff3">r<span class="ff1">s<span class="_ _1"></span>i<span class="ff3">z</span>e<span class="_ _4"> </span>和<span class="_ _3"> </span>a<span class="ff3">_<span class="_ _1"></span>dr<span class="ff1">s<span class="_ _1"></span>i<span class="ff3">z</span>e<span class="_ _4"> </span><span class="ff3">说明</span>了在文件数据</span></span></span></span></span></span></div><div class="t m0 x2 h7 y61 ff1 fs1 fc0 sc1 ls0 ws0">段后的符号表与重定位信息的大小。已经被链接<span class="ff3">好</span>可以运行的程序中<span class="ff3">既</span>不需<span class="ff3">要</span>符号表也不需</div><div class="t m0 x2 h7 y62 ff3 fs1 fc0 sc1 ls0 ws0">要<span class="ff1">重定位信息,所以</span>除非<span class="ff1">链接器为了调试器加入符号信息,</span>否<span class="ff1">则在可运行文件中这些</span>域<span class="ff1">都是</span></div><div class="t m0 x2 h4 y63 ff1 fs1 fc0 sc1 ls0 ws0">0。</div><div class="t m0 x2 ha y64 ff1 fs3 fc0 sc0 ls0 ws0">与<span class="ff3">虚拟</span>内存的<span class="ff3">交互</span></div><div class="t m0 x3 h7 y65 ff3 fs1 fc0 sc1 ls0 ws0">操<span class="ff1">作</span>系统<span class="ff1">加载和</span>启动<span class="ff1">一个</span>简单<span class="ff1">的</span>双<span class="ff1">段文件的</span>过<span class="ff1">程</span>非<span class="ff1">常</span>简单<span class="ff1">,如</span>图<span class="_ _3"> </span><span class="ff1">3<span class="_ _4"> </span>所</span>示<span class="ff1">:</span></div><div class="t m0 x2 h9 y66 ff4 fs4 fc0 sc1 ls0 ws0">1<span class="_ _2"> </span><span class="ff3">历史<span class="ff1">上</span>最初<span class="ff1">的<span class="_ _4"> </span></span></span>PDP-11<span class="_ _4"> </span><span class="ff1">上的<span class="ff3">幻</span>数是<span class="ff3">八</span>进制的<span class="_ _4"> </span></span>407<span class="ff1">,这是一个分支指令,可以<span class="ff3">越过</span>它后面<span class="ff3">属</span>于头<span class="ff3">部</span>的<span class="_ _4"> </span></span>7<span class="_ _6"> </span><span class="ff1">个<span class="ff3">字</span></span></div><div class="t m0 x8 h9 y67 ff1 fs4 fc0 sc1 ls0 ws0">(<span class="ff4">word</span>)而<span class="ff3">跳转</span>到文本段的起<span class="ff3">始</span>位置。这<span class="ff3">就</span>构成了位置无关代码的<span class="ff3">最初形</span>式。一个<span class="ff3">引</span>导加载器可以将包括</div><div class="t m0 x8 h9 y68 ff1 fs4 fc0 sc1 ls0 ws0">头<span class="ff3">部</span>在内的整个可执行程序加载到内存中(通常在位置<span class="_ _6"> </span><span class="ff4">0</span>),<span class="ff3">然</span>后<span class="ff3">跳转</span>到被加载文件的入<span class="ff3">口</span>位置运行程序。</div><div class="t m0 x8 h9 y69 ff3 fs4 fc0 sc1 ls0 ws0">尽管<span class="ff1">只有少</span>量<span class="ff1">程序用到了这个</span>特性<span class="ff1">,但</span>幻<span class="ff1">数<span class="_ _6"> </span><span class="ff4">407<span class="_ _4"> </span></span>在<span class="_ _6"> </span><span class="ff4">25<span class="_ _4"> </span></span></span>年<span class="ff1">后</span>仍然<span class="ff1">与我们同在。</span></div></div><a class="l" rel='nofollow' onclick='return false;'><div class="d m1"></div></a></div><div class="pi" data-data='{"ctm":[1.611639,0.000000,0.000000,1.611639,0.000000,0.000000]}'></div></div>
<div id="pf5" class="pf w0 h0" data-page-no="5"><div class="pc pc5 w0 h0"><img class="bi x0 y0 w1 h1" alt="" src="https://static.pudn.com/prod/directory_preview_static/626077c3090cbf2c4ed0ced8/bg5.jpg"><div class="c x0 y1 w0 h2"><div class="t m0 x5 h9 y6a ff3 fs4 fc0 sc1 ls0 ws0">---------------------------------------------------------------------------------------------</div><div class="t m0 x3 h7 y6b ff3 fs1 fc0 sc1 ls0 ws0">图<span class="_ _3"> </span><span class="ff1">3</span>-<span class="ff1">3<span class="_ _1"></span>:加载一个<span class="_ _3"> </span>a.<span class="_ _1"></span>o<span class="ff3">u</span>t<span class="_ _4"> </span>文件到一个进程中</span></div><div class="t m0 x3 h7 y6c ff1 fs1 fc0 sc1 ls0 ws0">文件和段构成的<span class="ff3">图</span>,<span class="ff3">箭</span>头表<span class="ff3">明</span>数据<span class="ff3">流向</span></div><div class="t m0 x5 h9 y6d ff3 fs4 fc0 sc1 ls0 ws0">---------------------------------------------------------------------------------------------</div><div class="t m0 x3 h7 y6e ff2 fs1 fc0 sc1 ls0 ws0"><span class="_ _2"> </span><span class="ff1">读取<span class="_ _3"> </span>a.o<span class="_ _1"></span><span class="ff3">u<span class="ff1">t<span class="_ _4"> </span>的头</span>部获<span class="ff1">取段的大小。</span></span></span></div><div class="t m0 x3 h7 y6f ff2 fs1 fc0 sc1 ls0 ws0"><span class="_ _2"> </span><span class="ff3">检查<span class="ff1">是</span>否<span class="ff1">已存在该文件的可</span>共享<span class="ff1">代码段。如</span>果<span class="ff1">是的</span>话<span class="ff1">,将</span>那<span class="ff1">个段映射到该进程的地</span></span></div><div class="t m0 x4 h7 y70 ff1 fs1 fc0 sc1 ls0 ws0">址空间。如<span class="ff3">果</span>不是,创建一个并将它映射到地址空间中,<span class="ff3">然</span>后从文件中读取文本段</div><div class="t m0 x4 h7 y71 ff3 fs1 fc0 sc1 ls0 ws0">放<span class="ff1">入这个</span>新<span class="ff1">的内存区</span>域<span class="ff1">。</span></div><div class="t m0 x3 h7 y72 ff2 fs1 fc0 sc1 ls0 ws0"><span class="_ _2"> </span><span class="ff1">创建一个足够容<span class="ff3">纳</span>数据段和<span class="_ _3"> </span><span class="ff3">BSS<span class="_ _4"> </span></span>的<span class="ff3">私</span>有数据段,将它映射到进程的地址空间中,<span class="ff3">然</span></span></div><div class="t m0 x4 h7 y73 ff1 fs1 fc0 sc1 ls0 ws0">后从文件中读取数据段<span class="ff3">放</span>入内存中的数据段并将<span class="_ _3"> </span><span class="ff3">BSS<span class="_ _4"> </span></span>段对<span class="ff3">应</span>的内存空间<span class="ff3">清零</span>。</div><div class="t m0 x3 h7 y74 ff2 fs1 fc0 sc1 ls0 ws0"><span class="_ _2"> </span><span class="ff1">创建一个<span class="ff3">栈</span>的段并将其映射到进程的地址空间(由于数据<span class="ff3">堆</span>和<span class="ff3">栈</span>的<span class="ff3">增长</span>方<span class="ff3">向</span>不同,</span></div><div class="t m0 x4 h7 y75 ff3 fs1 fc0 sc1 ls0 ws0">因此栈<span class="ff1">段通常是</span>独<span class="ff1">立于数据段的)。将</span>命<span class="ff1">令行或者调用程序</span>传递<span class="ff1">的</span>参<span class="ff1">数</span>放<span class="ff1">入</span>栈<span class="ff1">中。</span></div><div class="t m0 x3 h7 y76 ff2 fs1 fc0 sc1 ls0 ws0"><span class="_ _2"> </span><span class="ff3">适<span class="ff1">当的设置各种</span>寄<span class="ff1">存器并</span>跳转<span class="ff1">到起</span>始<span class="ff1">地址。</span></span></div></div></div><div class="pi" data-data='{"ctm":[1.611639,0.000000,0.000000,1.611639,0.000000,0.000000]}'></div></div>