Linux内核完全注释 阅读笔记:3.2、GNU as汇编
 2018.09.14    |      Linux内核完全注释    |     AilsonJack    |     暂无评论    |     1178 views
By: Ailson Jack
Date: 2018-09-10
个人博客: http://www.only2fire.com/
<p style="text-indent: 2em;">上节介绍的as86汇编器仅用于编译内核中的boot/bootsect.s引导扇区程序和实模式下的设置程序boot/setup.s。内核中其余所有汇编语言程序(包括C语言产生的汇编程序)均使用as来编译,并与C语言程序编译产生的模块进行链接。<br/></p><p style="text-indent: 2em;">实际上as汇编器最初是专门用于汇编gcc产生的中间汇编语言程序的,而非作为一个独立的汇编器使用。因此,as汇编器也支持很多C语言的特性,这包括字符、数字和常数表示方法以及表达式形式等方面。<br/></p><p class="artical_littlestyle1">1、编译as汇编语言程序</p><p style="text-indent: 0em;">使用as汇编器编译一个as汇编语言程序的基本命令行格式如下所示:<br/></p><p style="text-indent: 2em;"><span style="color: rgb(255, 0, 0);">as [选项] [-o objfile] [srcfile.s …]</span><br/></p><p style="text-indent: 2em;">其中<span style="color: rgb(0, 112, 192);">objfile</span>是as编译输出的目标文件名,<span style="color: rgb(0, 112, 192);">srcfile.s</span>是as的输入汇编语言程序名。如果没有指定输出文件名,那么as会编译输出名称为a.out的默认目标文件。<br/></p><p style="text-indent: 2em;">一个程序的源代码可以被放置在一个或多个文件中,程序的源代码是如何分割放置在几个文件中并不会改变程序的语义。程序的源代码是所有这些文件按次序的组合结果。每次运行as编译器,它只编译一个源程序,但一个源程序可以由多个文本文件组成(终端的标准输入也是一个文件)。<br/></p><p style="text-indent: 2em;">我们可以在as命令行上给出零个或多个输入文件名。as将会按从左到右的顺序读取这些输入文件的内容。在命令行上任何位置处的参数若没有特定含义的话,将会被作为一个输入文件名看待。<br/></p><p style="text-indent: 2em;">as的输出文件是输入的汇编语言程序编译生成的二进制数据文件,即目标文件。该目标文件主要用于作为链接器ld的输入文件。</p><p class="artical_littlestyle2">2、as汇编语法</p><p style="text-indent: 2em;">为了维持与gcc输出汇编程序的兼容性,as汇编器使用AT&amp;T系统V的汇编语法(下面简称为AT&amp;T语法)。这种语法与Intel汇编程序使用的语法(简称Intel语法)很不一样,它们之间的主要区别有以下几点:<br/></p><p style="text-indent: 2em;">a、AT&amp;T语法中立即操作数前面要加一个字符’$’;寄存器操作数名前要加一个字符百分号’%’;绝对跳转/调用(相对于与程序计数器有关的跳转/调用)操作数前面要加星号’*’。而Intel汇编语法均没有这些限制;<br/></p><p style="text-indent: 2em;">b、AT&amp;T语法与Intel语法使用的源和目的操作数次序正好相反。AT&amp;T的源和目的操作数是从左到右 ‘源, 目的’。例如Intel的语句’add eax, 4’,对应AT&amp;T的语句是’addl $4, %eax’;<br/></p><p style="text-indent: 2em;">c、AT&amp;T语法中内存操作数的长度(宽度)由操作码最后一个字符来确定。操作码后缀’b’、’w’、’l’分别指示内存引用宽度为8位字节(byte)、16位字(word)、32位长字(long)。Intel语法则通过在内存操作数前使用前缀’byte ptr’、’word ptr’、’dword ptr’来达到同样的目的。因此,Intel的语句’mov al,byte ptr foo’对应于AT&amp;T的语句’movb $foo,%al’;<br/></p><p style="text-indent: 2em;">d、AT&amp;T语法中立即形式的远跳转和远调用为’ljmp/lcall $section,$offset’,而Intel的是’jmp/call far section:offset’。同样,AT&amp;T语法中远返回指令’lret $stack-adjust’对应Intel的’ret far stack-adjust’;<br/></p><p style="text-indent: 2em;">e、AT&amp;T汇编器不提供对多代码段程序的支持,UNIX类操作系统要求所有代码在一个段中。<br/><span style="background-color: rgb(118, 146, 60);">(1)、</span>汇编程序预处理<br/></p><p style="text-indent: 2em;">as汇编器具有对汇编语言程序内置的简单预处理功能。该预处理功能会调整并删除多余的空格字符和制表符;删除所有注释语句并且使用单个空格或一些换行符替换他们;把字符常数转换成对应的数值。但是该预处理功能不会对宏定义进行处理,也没有处理包含文件的功能。如果需要这方面的功能,那么可以让汇编语言程序使用大写的后缀’.S’,让as使用gcc的CPP预处理功能。<br/></p><p style="text-indent: 2em;"><span style="color: rgb(255, 0, 0);">as汇编语言程序的注释方法除了使用C语言注释语句(即’/*’和’*/’)以外,还使用井号’#’作为单行注释开始字符。</span><br/><span style="background-color: rgb(118, 146, 60);">(2)、</span>符号、语句和常数<br/></p><p style="text-indent: 2em;">符号(Symbol)是由字符组成的标识符,组成符号的有效字符取自于大小写字符集、数字和三个字符‘_.$’。符号不允许使用数字字符开始,并且大小写含义不同。<br/></p><p style="text-indent: 2em;">语句(Statement)以换行符或者行分隔符(’;’)作为结束。<span style="color: rgb(255, 0, 0);">文件最后语句必须以换行符作为结束</span>。<br/></p><p style="text-indent: 2em;">语句由零个或多个标号(Label)开始,后面可以跟随一个确定语句类型的关键符号。标号由符号后面跟随一个冒号(’:’)构成。如果关键符号以一个’.’开始,那么当前语句就是一个汇编命令(或称为伪指令、指示符)。如果关键符号以一个字母开始,那么当前语句就是一条汇编语言指令语句。<br/></p><p style="text-indent: 2em;">常数是一个数字,可以分为字符常数和数字常数两类。字符常数还可分为字符串和单个字符;而数字常数可分为整数、大数和浮点数。<br/></p><p style="text-indent: 2em;">字符串必须用双引号括住,并且其中可以使用反斜杠’\’来转义包含特殊字符。例如,’\\’表示一个反斜杠字符。<br/></p><p style="text-indent: 2em;">汇编程序中使用单个字符常数时,可以写成在该字符前加一个单引号,例如”’A”表示值65,”’C”表示值67。表3-1中的转义码也同样可以用于单个字符常数。例如“’\\”表示一个普通反斜杠字符常数。<br/></p><p style="text-align:center"><img src="/uploads/AilsonJack/2018.09.14/1536922196151140.png" onclick="preview_image(&#39;/uploads/AilsonJack/2018.09.14/1536922196151140.png&#39;)"/></p><p style="text-indent: 2em;">整数数字常数有4种表示方法,即使用’0b’或’0B’开始的二进制数(’0-1’);以’0’开始的八进制数(’0-7’);以非’0’数字开始的十进制数(’0-9’)和使用’0x’或’0X’开头的十六进制数(’0-9a-FA-F’)。若要表示负数,只需在前面添加负号’-’。<br/></p><p style="text-indent: 2em;">大数(Bignum)是位数超过32位二进制位的数。其表示方法与整数相同。<br/></p><p class="artical_littlestyle3">3、指令语句、操作数和寻址</p><p style="text-indent: 2em;">指令(Instructions)是CPU执行的操作,通常指令也称为操作码(Opcode);操作数(Operand)是指令操作的对象;而地址(Address)是指定数据在内存中的位置。指令语句是程序运行时刻执行的一条语句,它通常可包含4个组成部分:标号(可选)、操作码(指令助记符)、操作数(由具体指令指定)、注释。<br/></p><p style="text-indent: 2em;">一条指令语句可以含有0个或最多3个用逗号分开的操作数。对于具有两个操作数的指令语句,第1个是源操作数,第2个是目的操作数,即指令操作结果保存在第2个操作数中。<br/></p><p style="text-indent: 2em;">操作数可以是立即数(即值是常数值的表达式)、寄存器(值在CPU的寄存器中)或内存(值在内存中)。一个间接操作数(Indirect operand)含有实际操作数值的地址值。AT&amp;T语法通过在操作数前加一个’*’字符来指定一个间接操作数。<span style="color: rgb(255, 0, 0);">只有跳转/调用指令才能使用间接操作数</span>。立即操作数前需要加一个’$’字符前缀,寄存器名前需要加一个’%’字符前缀。<br/><span style="background-color: rgb(118, 146, 60);">(1)、</span>指令操作码的命名<br/></p><p style="text-indent: 2em;">AT&amp;T语法中指令操作码名称(即指令助记符)最后一个字符用了指明操作数的宽度。字符’b’、’w’和’l’分别指定byte、word和long类型的操作数。如果指令名称没有带这样的字符后缀,并且指令语句中不含内存操作数,那么as就会根据目的寄存器操作数来尝试确定操作数的宽度。<br/></p><p style="text-indent: 2em;">AT&amp;T与Intel语法中,几乎所有指令操作码的名称都相同,但仍有几个例外。符号扩展和零扩展指令都需要2个宽度来指明,即需要为源和目的操作数指明宽度。AT&amp;T语法中是通过使用两个操作码后缀来做到的。AT&amp;T语法中符号扩展和零扩展的基本操作码名称分别是’movs…’和’movz…’,Intel中分别是’movsx’和’movzx’。例如“使用符号扩展从al%移动到edx%”的AT&amp;T语句是’movsbl %al,%edx’,即从byte到long是bl、从byte到word是bw、从word到long是wl。AT&amp;T语法与Intel语法中转换指令的对应关系如下表所示:</p><p style="text-align:center"><img src="/uploads/AilsonJack/2018.09.14/1536922196931228.png" onclick="preview_image(&#39;/uploads/AilsonJack/2018.09.14/1536922196931228.png&#39;)"/></p><p style="text-indent: 0em;"><span style="background-color: rgb(118, 146, 60);">(2)、</span>指令操作码前缀<br/></p><p style="text-indent: 2em;">操作码前缀用于修饰随后的操作码。它们用于重复字符串指令、提供区覆盖、执行总线锁定操作、或指定操作数和地址宽度。通常操作码前缀可作为一条没有操作数的指令独占一行并且必须直接位于所影响指令之前,但是最好与它修饰的指令放在同一行。操作码前缀及说明如下表所示:</p><p style="text-align:center"><img src="/uploads/AilsonJack/2018.09.14/1536922196638587.png" onclick="preview_image(&#39;/uploads/AilsonJack/2018.09.14/1536922196638587.png&#39;)"/></p><p style="text-indent: 0em;"><span style="background-color: rgb(118, 146, 60);">(3)、</span>内存引用<br/></p><p style="text-indent: 2em;">Intel语法的间接内存引用形式:<span style="color: rgb(0, 112, 192);">section:[base + index*scale + disp]</span>。<br/></p><p style="text-indent: 2em;">对应的AT&amp;T语法形式:<span style="color: rgb(0, 112, 192);">section:disp(base, index, scale)</span>。<br/></p><p style="text-indent: 2em;">其中base和index是可选的32位基址寄存器和索引寄存器,disp是可选的偏移值。scale是比例因子,取值范围是1、2、4和8。scale乘上索引index用来计算操作数地址。如果没有指定scale,则scale取默认值1。section为内存操作数指定可选的段寄存器,并且会覆盖操作数使用的当前默认段寄存器。请注意,如果指定的段覆盖寄存器与默认操作的段寄存器相同,则as就不会为汇编的指令再输出相同的段前缀。以下是几个AT&amp;T和Intel语法形式的内存引用例子:</p><p style="text-align:center"><img src="/uploads/AilsonJack/2018.09.14/1536922196459751.png" onclick="preview_image(&#39;/uploads/AilsonJack/2018.09.14/1536922196459751.png&#39;)"/></p><p style="text-indent: 0em;"><span style="background-color: rgb(118, 146, 60);">(4)、</span>跳转指令<br/></p><p style="text-indent: 2em;">跳转指令可分为无条件跳转和条件跳转两大类。条件跳转指令将依赖于执行指令时标志寄存器中某个相关标志的状态来确定是否进行跳转,而无条件跳转指令则不依赖于这些标志。<br/></p><p style="text-indent: 2em;"><span style="color: rgb(255, 0, 0);">jmp是无条件跳转指令,并可分为直接(direct)跳转和间接(indirect)跳转两类,而条件跳转指令只有直接跳转形式</span>。对于直接跳转指令,跳转到的目标指令的地址是作为跳转指令的一部分直接编码进跳转指令中;对于间接跳转指令,跳转的目的位置取自于某个寄存器或者某个内存位置中。<span style="color: rgb(0, 112, 192);">直接跳转语句的写法是给出跳转目标处的标号;间接跳转语句的写法是必须使用一个星字符’*’作为操作指示符的前缀字符,并且该操作指示符使用movl指令相同的语法</span>。下面是直接跳转和间接跳转的例子:<br/><span style="color: rgb(255, 0, 0);">jmp NewLoc&nbsp;</span> &nbsp; <span style="color: rgb(0, 112, 192);">#直接跳转,无条件直接跳转到标号NewLoc处继续执行</span></p><p style="text-indent: 0em;"><span style="color: rgb(255, 0, 0);">jmp *%eax</span>&nbsp;&nbsp; &nbsp;<span style="color: rgb(0, 112, 192);">#间接跳转,寄存器%eax的值是跳转的目标位置</span><br/><span style="color: rgb(255, 0, 0);">jmp *(%eax)</span>&nbsp;&nbsp; <span style="color: rgb(0, 112, 192);">#间接跳转,从%eax指明的地址处读取跳转的目标位置</span><br/></p><p style="text-indent: 2em;">同样,与指令计数器PC(程序指令计数器:Program Counter)无关的间接调用的操作数也必须有一个’*’作为前缀字符。若没有使用’*’字符,那么as汇编器就会选择与指令计数器PC相关的跳转标号。还有,其他任何具有内存操作数的指令都必须使用操作码后缀(’b’、’w’、’l’)指明操作数的大小(byte、word或long)。<br/></p><p class="artical_littlestyle4">4、区与重定位</p><p style="text-indent: 2em;">链接器ld会把输入的目标文件中的内容按照一定规律组合生成一个可执行程序。当as汇编器输出一个目标文件时,该目标文件中的代码被默认设置成从从地址0开始。此后ld将会在链接过程中为不同目标文件中的各个部分分配不同最终地址位置。ld会把程序中的字节块移动到程序运行时的地址处。这些块是作为固定单元进行移动的,它们的长度以及字节次序都不会改变。这样的固定单元被称作是区(或段、部分)。而<span style="color: rgb(0, 112, 192);">为区分配运行时刻的地址操作就被称为重定位(Relocation)操作</span>,其中包括调整目标文件中记录的地址,从而让它们对应到恰当的运行时刻地址上。<br/></p><p style="text-indent: 2em;">as汇编器输出产生的目标文件中至少具有3个区,分别被称为正文(text)、数据(data)和bss区。每个区都可能是空的。如果没有使用汇编命令把输出放置在’.text’或’.data’区中,这些区仍然存在,但内容是空的。在一个目标文件中,text区从地址0开始,随后是data区,再后面是bss区。<br/></p><p style="text-indent: 2em;">as使用的所有地址都可表示为:(区) + (区中偏移)。在下面说明中,我们使用记号”{secname N}”来表示区secname中偏移N。除了text、data、bss区,我们还需要了解绝对地址区(absolute区)。当链接器把各个目标文件组合在一起时,absolute区的地址始终不变。目标文件中的absolute区必会重叠而覆盖。另外还有一种名为”未定义的”区(Undefined section)。<br/></p><p style="text-indent: 2em;">链接器ld会把程序所有目标文件中的text区放在相邻的地址处。我们习惯上所说的程序的text区实际上是指其所有目标文件text区组合构成的整个地址区域。对程序中data和bss区的理解也是同样如此。</p><p style="text-indent: 0em;"><span style="background-color: rgb(118, 146, 60);">(1)、</span>链接器涉及的区<br/>链接器ld只涉及如下4类区:<br/></p><p style="text-indent: 2em;"><span style="color: rgb(0, 112, 192);">text区、data区</span>:这两个区用于保存程序。当程序运行时,则通常text区是不会改变的。当程序运行时,data区的内容通常是会变化的,例如,C变量一般就存放在data区中。<br/></p><p style="text-indent: 2em;"><span style="color: rgb(0, 112, 192);">bss区</span>:在程序开始运行时,这个区中含有0值字节。<br/></p><p style="text-indent: 2em;"><span style="color: rgb(0, 112, 192);">absolute区</span>:该区的地址0总是”重定位”到运行时刻地址0处。<br/></p><p style="text-indent: 2em;"><span style="color: rgb(0, 112, 192);">undefined区</span>:对不在先前所述各个区中对象的地址引用都属于本区。<br/></p><p style="text-indent: 2em;">图3-2是3个理想化的可重定位区的例子,其中水平轴表示内存地址:<br/></p><p style="text-align:center"><img src="/uploads/AilsonJack/2018.09.14/1536922197256624.png" onclick="preview_image(&#39;/uploads/AilsonJack/2018.09.14/1536922197256624.png&#39;)"/></p><p style="text-indent: 0em;"><span style="background-color: rgb(118, 146, 60);">(2)、</span>子区<br/></p><p style="text-indent: 2em;">汇编取得的字节数据通常位于text或data区中。有时候在汇编程序某个区中可能分布着一些不相邻的数据组,但是你可能想让它们在汇编后聚集在一起存放。as汇编器允许你利用子区(subsection)来达到这个目的。在每个区中,可以有编号为0-8192的子区存在。编制在同一个子区中的对象会在目标文件中与该子区中的其他对象放在一起。例如,编译器可能想把常数存放在text区中,但是不想让这些常数散布在被汇编的整个程序中。在这种情况下,编译器就可以在每个会输出的代码区之前使用’.text 0’子区,并且在每组会输出的常数之前使用’.text 1’子区。<br/></p><p style="text-indent: 2em;">使用子区是可选的,如果没有使用子区,那么所有对象都会被放在子区0中。子区会以其从小到大的编号顺序出现在目标文件中,但是目标文件中并不包含表示子区的任何信息。处理目标文件的ld及其他程序并不会看到子区的踪迹。如果只指定了’.text’,那么就会默认使用’.text 0’;同样的,’.data’表示使用’.data 0’。<br/></p><p style="text-indent: 2em;"><span style="color: rgb(0, 112, 192);">每个区都有一个位置计数器(Location Counter),它会对每个汇编进该区的字节进行计数。由于子区仅供as汇编器使用方便而设置的,因此并不存在子区计数器</span>。<br/><span style="background-color: rgb(118, 146, 60);">(3)、</span>bss区<br/></p><p style="text-indent: 2em;">bss区用于存储局部公共变量。你可以在bss区中分配空间,但是在程序运行之前不能在其中放置数据,因为当程序刚开始执行时,bss区中所有字节内容都将被清零。<span style="color: rgb(255, 0, 0);">’.lcomm’汇编命令用于在bss区中定义一个符号;’.comm’可用于在bss区中声明一个公共符号</span>。<br/></p><p class="artical_littlestyle1">5、符号</p><p style="text-indent: 2em;">在程序编译和链接过程中,符号(Symbol)是一个比较重要的概念。程序员使用符号来命名对象,链接器使用符号来进行链接操作,而调试器使用符号来进行调试。标号(Label)是后面紧跟随一个冒号的符号。<br/></p><p style="text-indent: 2em;">符号名以一个字母或’._’字符之一开始。局部符号用于协助编译器和程序员临时使用的名称。在一个程序中共有10个局部符号名(’0’…’9’)可供重复使用。为了定义一个局部符号,只要写出形如’N:’的标号(其中N代表任何数字)。若是引用前面最近定义的这个符号,需要写成’Nb’;若需要引用下一个定义的局部符号,则需要写成’Nf’。其中’b’意思是向后(backwards),’f’表示向前(forwards)。局部标号在使用方面没有限制,但是在任何时候,我们只能向前/向后引用最远10个局部标号。<br/><span style="background-color: rgb(118, 146, 60);">(1)、</span>特殊点符号<br/></p><p style="text-indent: 2em;">特殊符号’.’表示as汇编的当前地址。因此表达式’mylab: . long .’就会把mylab定义为包含它自己所处的地址值。给’.’赋值就如同汇编命令’.org’的作用。因此表达式’.=.+4’与’.space 4’完全相同。<br/><span style="background-color: rgb(118, 146, 60);">(2)、</span>符号属性<br/></p><p style="text-indent: 2em;">除了名字以外,每个符号都有”值”和”类型”属性。根据输出的格式不同,符号也可以具有辅助属性。如果不定义就使用一个符号,as就会假设其所有属性均为0,这表示该符号是一个外部定义的符号。<br/></p><p style="text-indent: 2em;">符号的值通常是32位的。对于标出text、data、bss和absolute区中一个位置的符号,其值是从区开始到标号处的地址值。对于text、data和bss区,一个符号的值通常会在链接过程中由于ld改变区的基地址而变化;absolute区中符号的值不会改变,这也是为何称它们是绝对符号的原因。<br/></p><p style="text-indent: 2em;">ld会对未定义符号的值进行特殊处理。<span style="color: rgb(255, 0, 0);">如果未定义的符号的值是0,则表示该符号在本汇编程序中没有定义</span>,ld会尝试根据其他链接的文件来确定它的值。在程序中使用了一个符号但没有对符号进行定义,就会产生这样的符号。<span style="color: rgb(255, 0, 0);">若未定义符号的值不为0,那么该符号值就表示是.comm公共声明的需要保留的公共存储空间字节长度,符号指向该存储空间的第一个地址处</span>。<br/></p><p style="text-indent: 2em;">符号的类型属性含有用于链接器和调试器的重定位信息、指示符号是外部的标志以及一些其他可选信息。对于a.out格式的目标文件,符号的类型属性存放在一个8位字段中(n_type字节),其含义请参见有关include/a.out.h文件的说明。<br/></p><p class="artical_littlestyle2">6、汇编命令</p><p style="text-indent: 2em;">汇编命令是指示汇编器操作方式的伪指令。所有汇编命令的名称都以’.’开始,其余是字符,并且大小写无关。下面列出一些常用汇编命令:<br/><span style="background-color: rgb(118, 146, 60);">(1)、</span>.align abs-expr1, abs-expr2, abs-expr3<br/></p><p style="text-indent: 2em;">.align是存储对齐汇编命令,用于在当前子区中把位置计数器值设置(增加)到下一个指定存储边界处。第1个绝对值表达式abs-expr1(absolute expression)指定要求的边界对齐值。对于使用a.out格式目标文件的80x86系统,该表达式值是位置计数器值增加后其二进制最右面0值位的个数,即是2的次方值。例如,’.align 3’表示把位置计数器值增加到8的倍数上,如果位置计数器值本身就是8的倍数,那么就无需改变。但是对于使用ELF格式的80x86系统,该表达式值直接就是要求对齐的字节数。例如,’.align 8’就是把位置计数器值增加到8的倍数上。<br/></p><p style="text-indent: 2em;">第2个表达式给出用于对齐而填充的字节值。该表达式与其前面的逗号可以省略。若省略,则填充字节值是0。第3个可选表达式abs-expr3用于指示对齐操作允许填充跳过的最大字节数。如果对齐操作要求跳过的字节数大于这个最大值,那么该对齐操作就被取消。若想省略第2个参数,可以在第1和第3个参数之间使用两个逗号。<br/><span style="background-color: rgb(118, 146, 60);">(2)、</span>.ascii &quot;string&quot;...<br/></p><p style="text-indent: 2em;">从位置计数器所指当前位置为字符串分配空间并存储字符串。可使用逗号分开写出多个字符串。例如,’.ascii “Hello world!”, “My assembler”’。该汇编命令会让as把这些字符串汇编在连续的地址位置处,每个字符串后面不会自动添加0(NULL)字节。<br/><span style="background-color: rgb(118, 146, 60);">(3)、</span>.asciz &quot;string&quot;...<br/></p><p style="text-indent: 2em;">该汇编命令与’.ascii’类似,但是每个字符串后面会自动添加NULL字符。<br/><span style="background-color: rgb(118, 146, 60);">(4)、</span>.byte expressions<br/></p><p style="text-indent: 2em;">该汇编命令定义0个或多个用逗号分开的字节值。每个表达式的值是1字节。<br/><span style="background-color: rgb(118, 146, 60);">(5)、</span>.comm symbol, length<br/></p><p style="text-indent: 2em;">在bss区中声明一个命名的公共区域。在ld链接过程中,某个目标文件中的一个公共符号会与其他目标文件中同名的公共符号合并。如果ld没有找到一个符号的定义,而只是一个或多个公共符号,那么ld就会分配指定长度length字节的未初始化内存。length必须是一个绝对值表达式,如果ld找到多个长度不同但同名的公共符号,ld就会分配长度最大的空间。<br/><span style="background-color: rgb(118, 146, 60);">(6)、</span>.data subsection<br/></p><p style="text-indent: 2em;">该汇编命令通知as把随后的语句汇编到编号为subsection的data子区中。如果省略编号,则默认使用编号0。编号必须是绝对值表达式。<br/><span style="background-color: rgb(118, 146, 60);">(7)、</span>.desc symbol, abs-expr<br/></p><p style="text-indent: 2em;">用绝对表达式的值设置符号symbol的描述符字段n_desc的16位值。仅用于a.out格式的目标文件。参见有关include/a.out.h文件的说明。<br/><span style="background-color: rgb(118, 146, 60);">(8)、</span>.fill repeat, size, value<br/></p><p style="text-indent: 2em;">该汇编命令会产生数个(repeat个)大小为size字节的重复拷贝。大小值size可以为0或某个值,但是若size大于8,则限定为8。每个重复字节内容取自一个8字节数。高4字节为0,低4字节是数值value。这3个参数值都是绝对值,size和value是可选的。如果第2个逗号和value省略,value默认为0值;如果后两个参数都省略的话,则size默认为1。<br/><span style="background-color: rgb(118, 146, 60);">(9)、</span>.global symbol(或者.globl symbol)<br/></p><p style="text-indent: 2em;">该汇编命令会使得链接器ld能看见符号symbol。如果在我们的目标文件中定义了符号symbol,那么它的值将能被链接过程中的其他目标文件使用。若目标文件中没有定义该符号,那么它的属性将从链接过程中其他目标文件的同名符号中获得。这是通过设置符号symbol类型字段中的外部位N_EXT来做到的。参见有关include/a.out.h文件的说明。<br/><span style="background-color: rgb(118, 146, 60);">(10)、</span>.int expressions<br/></p><p style="text-indent: 2em;">该汇编命令在某个区中设置0个或多个整数值(80386系统为4字节,同.long)。每个用逗号分开的表达式的值就是运行时刻的值。例如,’.int 1234, 567, 0x89AB‘。<br/><span style="background-color: rgb(118, 146, 60);">(11)、</span>.lcomm symbol, length<br/></p><p style="text-indent: 2em;">为符号symbol指定的局部公共区域保留长度为length字节的空间。所在的区和符号symbol的值是新的局部公共块的值。分配的地址在bss区中,因此在运行时刻这些字节值被清零。由于符号symbol没有被声明为全局的,因此链接器ld看不见。<br/><span style="background-color: rgb(118, 146, 60);">(12)、</span>.long expressions<br/></p><p style="text-indent: 2em;">含义与.int相同。<br/><span style="background-color: rgb(118, 146, 60);">(13)、</span>.octa bignums<br/></p><p style="text-indent: 2em;">这个汇编命令指定0个或多个用逗号分开的16字节大数(.byte,.word,.long,.quad,.octa分别对应1、2、4、8和16字节数)。<br/><span style="background-color: rgb(118, 146, 60);">(14)、</span>.org new_lc, fill<br/></p><p style="text-indent: 2em;">这个汇编命令会把当前区的位置计数器设置为new_lc。new_lc是一个绝对值(表达式),或者是具有相同区作为子区的表达式,也即<span style="color: rgb(255, 0, 0);">不能用.org跨越各区</span>。如果new_lc的区不对,那么.org就不会起作用。请注意,位置计数器是基于区的,即以每个区作为计数起点。<br/>当位置计数器值增长时,所跳跃过的字节将被填入值fill,该值必须是绝对值。如果省略了逗号和fill,则fill默认为0值。<br/><span style="background-color: rgb(118, 146, 60);">(15)、</span>.quad bignums<br/></p><p style="text-indent: 2em;">这个汇编命令指定0个或多个用逗号分开的8字节大数bignum。如果大数放不进8个字节中,则取低8个字节。<br/><span style="background-color: rgb(118, 146, 60);">(16)、</span>.short expressions<br/></p><p style="text-indent: 2em;">这个汇编命令指定某个区中0个或多个用逗号分开的2字节数。对于每个表达式,在运行时刻都会产生一个16位的值。<br/><span style="background-color: rgb(118, 146, 60);">(17)、</span>.space size, fill<br/></p><p style="text-indent: 2em;">该汇编命令产生size个字节,每个字节填值fill。这个参数均为绝对值,如果省略了逗号和fill,那么fill的默认值就是0。<br/><span style="background-color: rgb(118, 146, 60);">(18)、</span>.string &quot;string&quot;<br/></p><p style="text-indent: 2em;">定义一个或多个用逗号分开的字符串。在字符串中可以使用转义字符。每个字符串都自动附加一个NULL字符结尾。例如,‘.string “\n\nStarting”, “other strings”’。<br/><span style="background-color: rgb(118, 146, 60);">(19)、</span>.text subsection<br/></p><p style="text-indent: 2em;">通知as把随后的语句汇编进编号为subsection的子区中。如果省略了编号subsection,则使用默认编号值0。<br/><span style="background-color: rgb(118, 146, 60);">(20)、</span>.word expressions<br/></p><p style="text-indent: 2em;">对于32位机器,该汇编命令含义与.short相同。<br/></p><p class="artical_littlestyle3">7、编写16位代码</p><p style="text-indent: 2em;">虽然as通常用来编写纯32位的80x86代码,但是1995年后它对编写运行于实模式或16位保护模式的代码也提供有限的支持。为了让as汇编时产生16位代码,需要在运行于16位模式的指令语句之前添加汇编命令’.code16’,并且使用汇编命令’.code32’让as汇编器切换回32位代码汇编方式。<br/></p><p style="text-indent: 2em;">16位模式下,因为as为所有指令添加了额外的地址和操作数宽度前缀,所以汇编产生的代码长度和性能上将会受到影响。<br/></p><p class="artical_littlestyle4">8、as汇编器命令行选项</p><p style="text-indent: 0em;">-a 开启程序列表<br/>-f 快速操作<br/>-o 指定输出的目标文件名<br/>-R 组合数据区和代码区<br/>-W 取消警告信息<br/></p>
欢迎关注博主的公众号呀,精彩内容随时掌握:
热情邀请仔细浏览下博客中的广告,万一有对自己有用或感兴趣的呢。◕ᴗ◕。。
如果这篇文章对你有帮助,记得点赞和关注博主就行了^_^,当然了能够赞赏博主,那就非常感谢啦!
注: 转载请注明出处,谢谢!^_^
暂无评论,要不要来个沙发
发表评论

 
Copyright © 2015~2023  说好一起走   保留所有权利   |  百度统计  蜀ICP备15004292号