Ted's Blog

Happy coding

AT&T汇编伪指令

  最近一直在看OS方面的东西,其中要用到许多AT&T汇编,下面是在网上打到的关于它的伪指令方面的中英文对照版本。英文版本也可以输入如下命令来查看:
    info as   
    read the "Pseudo Ops" node

7 Assembler Directives
All assembler directives have names that begin with a period (‘.’). The rest
of the name is letters, usually in lower case.
This chapter discusses directives that are available regardless of the target
machine configuration for the gnu assembler. Some machine configurations provi
de additional directives. See Chapter 8 [Machine Dependencies], page 61.

7 汇编器命令
所有的汇编器命令名都由句号('.')开头。命令名的其余是字母,通常使用小写。
本章讨论可用命令,不理会gun汇编器针对目标机器配置。某些机器的配置提供附加的命令
。见第8章[机器相关性],第61页。

7.1 .abort
This directive stops the assembly immediately. It is for compatibility with ot
her assemblers. The original idea was that the assembly language source would
be piped into the assembler. If the sender of the source quit, it could use th
is directive tells as to quit also. One day .abort will not be supported.

7.1 .abort
本命令立即终止汇编过程。这是为了兼容其它的汇编器。早期的想法是汇编语言的源码会
被输送进汇编器。如果发送源码的程序要退出,它可以使用本命令通知as退出。将来可能
不再支持使用.abort


7.2 .ABORT
When producing COFF output, as accepts this directive as a synonym for ‘.abor
t’.
When producing b.out output, as accepts this directive, but ignores it.

7.2 .ABORT
当生成COFF输出时,汇编器把这条命令作为.abort接受。
当产成b.out输出时,汇编器允许使用这条命令,但忽略它。


7.3 .align abs-expr, abs-expr, abs-expr
Pad the location counter (in the current subsection) to a particular storage b
oundary. The first expression (which must be absolute) is the alignment requir
ed, as described below.
The second expression (also absolute) gives the fill value to be stored in the
padding bytes. It (and the comma) may be omitted. If it is omitted, the paddi
ng bytes are normally zero. However, on some systems, if the section is marked
as containing code and the fill value is omitted, the space is filled with no
-op instructions.
The third expression is also absolute, and is also optional. If it is present,
it is the maximum number of bytes that should be skipped by this alignment di
rective. If doing the alignment would require skipping more bytes than the spe
cified maximum, then the alignment is not done at all. You can omit the fill v
alue (the second argument) entirely by simply using two commas after the requi
red alignment; this can be useful if you want the alignment to be filled with
no-op instructions when appropriate.
The way the required alignment is specified varies from system to system. For
the a29k, hppa, m68k, m88k, w65, sparc, and Hitachi SH, and i386 using ELF for
mat, the first expression is the alignment request in bytes. For example ‘.al
ign 8’ advances the location counter until it is a multiple of 8. If the loca
tion counter is already a multiple of 8, no change is needed.
For other systems, including the i386 using a.out format, and the arm and stro
ngarm, it is the number of low-order zero bits the location counter must have
after advancement. For example ‘.align 3’ advances the location counter unti
l it a multiple of 8. If the location counter is already a multiple of 8, no c
hange is needed.
This inconsistency is due to the different behaviors of the various native ass
emblers for these systems which GAS must emulate. GAS also provides .balign an
d .p2align directives, described later, which have a consistent behavior acros
s all architectures (but are specific to GAS).


7.3 .align abs-expr, abs-expr, abs-expr
增加位置计数器(在当前的子段)使它指向规定的存储边界。第一个表达式参数(结果必须是
纯粹的数字)是必需参数:边界基准,见后面的描述。
第二个表达式参数(结果必须是纯粹的数字)给出填充字节的值,用这个值填充位置计数器
越过的地方。这个参数(和逗点)可以省略,如果省略它,填充字节的值通常是0。但在某些
系统上,如果本段标识为包含代码,而填充值被省略,则使用no-op指令填充这个空间。

第3个参数表达式的结果也必须是纯粹的数字,这个参数是可选的。如果存在第3个参数,
它代表本对齐命令允许越过字节数的最大值。如果完成这个对齐需要跳过的字节比指定的
最大值还多,则根本无法完成对齐。您可以在边界基准后简单地使用两个逗号,以省略填充
值参数(第二参数);如果您想在适当的时候,对齐操作自动使用no-op指令填充,这个方法
将非常奏效。
边界基准的定义因系统而有差异。a29k,hppa,m68k,m88k,w65,sparc,Hitachi SH,
和使用ELF格式的i386,第一个表达式是边界基准,单位是字节。例如‘.align 8’向后移
动位置计数器至8的倍数。如果地址已经是8的倍数,则无需移动。
有些其它系统,包括使用a.out格式的i386,ARM和strongarm,这代表位置计数器移动后,
计数器中连续为0的低序位数量。例如‘.align 3’向后移动位置计数器直至8的倍数(计
数器的最低的3位为0)。如果地址已经是8倍数,则无需移动。
之所以存在这样的区别,是因为GAS需要模仿各种汇编器的不同动作。GAS还提供.balign和
.p2align命令,在以后详细讲述,这两条命令在所有的机型上使用相同的动作 (但需要向
GAS明确说明机型)。


7.4 .ascii "string". . .
.ascii expects zero or more string literals (see Section 3.6.1.1 [Strings], pa
ge 19) separated by commas. It assembles each string (with no automatic traili
ng zero byte) into consecutive addresses.

7.4 .ascii "string"...
.ascii可不带参数或者带多个由逗点分开的字符串(见3.6.1.1节[Strings],第19页)。它把
汇编好的每个字符串(在字符串末不自动追加零字节)存入连续的地址。


7.5 .asciz "string". . .
.asciz is just like .ascii, but each string is followed by a zero byte. The “
z” in ‘.asciz’ stands for “zero”.

7.5 .asciz "string"...
.asciz类似与.ascii,但在每个字符串末自动追加一个零字节。‘.asciz’中的‘z’代表
“zero”。


7.6 .balign[wl] abs-expr, abs-expr, abs-expr
Pad the location counter (in the current subsection) to a particular storage b
oundary. The first expression (which must be absolute) is the alignment reques
t in bytes. For example ‘.balign 8’ advances the location counter until it i
s a multiple of 8. If the location counter is already a multiple of 8, no chan
ge is needed.
The second expression (also absolute) gives the fill value to be stored in the
padding bytes. It (and the comma) may be omitted. If it is omitted, the paddi
ng bytes are normally zero. However, on some systems, if the section is marked
as containing code and the fill value is omitted, the space is filled with no
-op instructions.
The third expression is also absolute, and is also optional. If it is present,
it is the maximum number of bytes that should be skipped by this alignment di
rective. If doing the alignment would require skipping more bytes than the spe
cified maximum, then the alignment is not done at all. You can omit the fill v
alue (the second argument) entirely by simply using two commas after the requi
red alignment; this can be useful if you want the alignment to be filled with
no-op instructions when appropriate.
The .balignw and .balignl directives are variants of the .balign directive. Th
e .balignw directive treats the fill pattern as a two byte word value. The .ba
lignl directives treats the fill pattern as a four byte longword value. For ex
ample, .balignw 4,0x368d will align to a multiple of 4. If it skips two bytes,
they will be filled in with the value 0x368d (the exact placement of the byte
s depends upon the endianness of the processor). If it skips 1 or 3 bytes, the
fill value is undefined.

7.6
.balign[wl] abs-expr, abs-expr, abs-expr
增加位置计数器(在当前子段)使它指向规定的存储边界。第一个表达式参数(结果必须是纯
粹的数字)是必需参数:边界基准,单位为字节。例如,‘.balign 8’向后移动位置计数器
直至计数器的值等于8的倍数。如果位置计数器已经是8的倍数,则无需移动。
第2个表达式参数(结果必须是纯粹的数字)给出填充字节的值,用这个值填充位置计数器越
过的地方。第2个参数(和逗点)可以省略。如果省略它,填充字节的值通常是0。但在某些
系统上,如果本段标识为包含代码,而填充值被省略,则使用no-op指令填充空白区。
第3个参数的结果也必须是纯粹的数字,这个参数是可选的。如果存在第3个参数,它代表
本对齐命令允许跳过字节数的最大值。如果完成这个对齐需要跳过的字节数比规定的最大
值还多,则根本无法完成对齐。您可以在边界基准参数后简单地使用两个逗号,以省略填充
值参数(第二参数);如果您在想在适当的时候,对齐操作自动使用no-op指令填充,本方法
将非常奏效。

.balignw和.balignl是.balign命令的变化形式。.balignw使用2个字节来填充空白区。.b
alignl使用4字节来填充。例如,.balignw 4,0x368d将地址对齐到4的倍数,如果它跳过2个
字节,GAS将使用0x368d填充这2个字节(字节的确切存放位置视处理器的存储方式而定)。
如果它跳过1或3个字节,则填充值不明确。


7.7 .byte expressions
.byte expects zero or more expressions, separated by commas. Each expression i
s assembled into the next byte.

7.7.byte expressions
.byte可不带参数或者带多个表达式参数,表达式之间由逗点分隔。每个表达式参数都被汇
编成下一个字节。


7.8 .comm symbol , length
.comm declares a common symbol named symbol. When linking, a common symbol in
one object file may be merged with a defined or common symbol of the same name
in another object file. If ld does not see a definition for the symbol–just
one or more common symbols–then it will allocate length bytes of uninitialize
d memory. length must be an absolute expression. If ld sees multiple common sy
mbols with the same name, and they do not all have the same size, it will allo
cate space using the largest size.
When using ELF, the .comm directive takes an optional third argument. This is
the desired alignment of the symbol, specified as a byte boundary (for example
, an alignment of 16 means that the least significant 4 bits of the address sh
ould be zero). The alignment must be an absolute expression, and it must be a
power of two. If ld allocates uninitialized memory for the common symbol, it w
ill use the alignment when placing the symbol. If no alignment is specified, a
s will set the alignment to the largest power of two less than or equal to the
size of the symbol, up to a maximum of 16.
The syntax for .comm differs slightly on the HPPA. The syntax is ‘symbol .com
m, length’;symbol is optional.

7.8 .comm symbol , length
.comm声明一个符号名为symbol的通用符号(common symbol)。当连接时,目标文件中的通
用符号可能被并入其它目标文件中已定义的符号,或者被并入其他目标文件中同名通用符
号。如果ld无法找到该符号的定义——只有一个或多个通用符号——则分配length个字节
的未初始化内存。Length必须是一个纯粹的表达式。如果ld发现多个同名的通用符号,并
且它们的长度不同,ld将按照它们之中最大的length值为符号分配内存。
当使用ELF格式时,.comm可以使用第3个参数。它代表符号需要对齐的边界基准(例如,边界
基准为16时意味着符号存放地址的最低4位应该是零)。第3个参数表达式结果必须是纯粹的
数字,而且一定是2的幂。当ld为通用符号分配未初始化内存时,在存放符号时要使用到这
个参数。如果没有规定边界基准,as将把边界基准设置成以2为底的该符号长度的对数,并
向下取整。最大值为16。
.comm的语法在HPPA上稍微有些不同。语法是‘symbol .comm, length’;其中参数symbol
是可选的。


7.9 .data subsection
.data tells as to assemble the following statements onto the end of the data s
ubsection numbered subsection (which is an absolute expression). If subsection
is omitted, it defaults to zero.

7.9 .data subsection
.data通知as汇编后续语句,将它们追加在编号为subsection(subsection必须是纯粹的表
达式)数据段末。如果参数subsection省略,则默认是0。


7.10 .def name
Begin defining debugging information for a symbol name; the definition extends
until the .endef directive is encountered. This directive is only observed wh
en as is configured for COFF format output; when producing b.out, ‘.def’ is
recognized, but ignored.

7.10 .def name
开始定义符号'name'的调试信息;定义区延伸至遇到.endef命令。本命令只在as被配置成C
OFF格式输出时才使用;当输出为b.out格式时,可以使用‘.def’命令,但被忽略。

7.11 .desc symbol, abs-expression
This directive sets the descriptor of the symbol (see Section 5.5 [Symbol Attr
ibutes],page 30) to the low 16 bits of an absolute expression.
The ‘.desc’ directive is not available when as is configured for COFF output
; it is only for a.out or b.out object format. For the sake of compatibility,
as accepts it, but produces no output, when configured for COFF.

7.11 .desc symbol, abs-expression
本命令用一个纯粹表达式的低16位的值设置符号symbol的描述符(见5.5[符号属性],第30页
)。当as被配置成COFF输出时,‘.desc’命令无效;它只适用于a.out或b.out目标格式。为
兼容起见,当配置为COFF时,as接受此命令,但不产生输出。


7.12 .dim
This directive is generated by compilers to include auxiliary debugging inform
ation in the symbol table. It is only permitted inside .def/.endef pairs.
‘.dim’ is only meaningful when generating COFF format output; when as is gen
erating b.out, it accepts this directive but ignores it.

7.12 .dim
这条命令由编译器生成的,以便在符号表中加入辅助调试信息。只可以在.def/.endef对之
间使用此命令。
'.dim'仅仅在生成COFF格式输出时是有意义的;当生成b.out时,as接受这条命令,但忽略它



7.13 .double flonums
.double expects zero or more flonums, separated by commas. It assembles floati
ng point numbers. The exact kind of floating point numbers emitted depends on
how as is configured. See Chapter 8 [Machine Dependencies], page 61.

7.13 .double flonums
.double后跟着零个或由逗点分开多个的浮点数。本指令汇编出浮点数字。生成的浮点数的
确切类型视as的配置而定。见第8章[机器相关性],第61页。


7.14 .eject
Force a page break at this point, when generating assembly listings.

7.14 .eject
当生成汇编清单时,强制清单页在此点中断。


7.15 .else
.else is part of the as support for conditional assembly; see Section 7.35 [.i
f], page 43. It marks the beginning of a section of code to be assembled if th
e condition for the preceding
.if was false.

7.15 .else
.else 是支持as进行的条件汇编指令之一;见7.35[.if],第43页。如果前面.if命令的条件
不成立,则表示需要汇编.else后的一段代码。


7.16 .elseif
.elseif is part of the as support for conditional assembly; see Section 7.35 [
.if],page 43. It is shorthand for beginning a new .if block that would otherwi
se fill the entire .else section.

7.16 .elseif
.elseif 是支持as进行的条件汇编指令之一。见7.35节 [.if],第43页。它可以在.esle段
中快速产生一个新的.if块。


7.17 .end
.end marks the end of the assembly file. as does not process anything in the f
ile past the .end directive.

7.17 .end
.end标记着汇编文件的结束。as不处理.end命令后的任何语句。


7.18 .endef
This directive flags the end of a symbol definition begun with .def.
‘.endef’ is only meaningful when generating COFF format output; if as is con
figured to generate b.out, it accepts this directive but ignores it.

7.18 .endef
这条命令标志着从.def开始的符号定义结束。
‘.endef’命令仅仅在生成COFF格式的输出有意义;如果as被配置为生成b.out输出,虽然a
s接受这条命令,但忽略它。


7.19 .endfunc
.endfunc marks the end of a function specified with .func.

7.19 .endfunc
.endfunc标志着一个由.func命令定义的函数的结束。


7.20 .endif
.endif is part of the as support for conditional assembly; it marks the end of
a block of code that is only assembled conditionally. See Section 7.35 [.if],
page 43.

7.20 .endif
.endif是支持as进行的条件汇编的指令之一.它标志着条件汇编代码块的结束。见7.35节[
.if],第43页。


7.21 .equ symbol, expression
This directive sets the value of symbol to expression. It is synonymous with ‘
.set’; see Section 7.68 [.set], page 53.
The syntax for equ on the HPPA is ‘symbol .equ expression’.

7.21 .equ symbol, expression
本命令把符号symbol值设置为expression。它等同与'.set'命令。见7.68[.set],第53页。

在HPPA上的equ语法是‘symbol .equ expression’。


7.22 .equiv symbol, expression
The .equiv directive is like .equ and .set, except that the assembler will sig
nal an error if symbol is already defined.
Except for the contents of the error message, this is roughly equivalent to

.ifdef SYM
.err
.endif
.equ SYM,VAL

7.22 .equiv symbol, expression
.equiv 类似与.equ & .set命令, 不同之处在于,如果符号已经定义过,as会发出错误信
号。
除了错误信息的内容之外,它大体上等价与:
.ifdef SYM
.err
.endif
.equ SYM,VAL

7.23 .err
If as assembles a .err directive, it will print an error message and, unless t
he -Z option was used, it will not generate an object file. This can be used t
o signal error an conditionally compiled code.

7.23 .err
如果as汇编一条.err命令, 将打印一条错误信息,除非使用了-Z 选项, as不会生成目标文
件。 可以在条件编译代码中使用它来发出错误信息。


7.24 .exitm
Exit early from the current macro definition. See Section 7.50 [Macro], page 4
7.

7.24 .exitm
从当前宏定义体中提前退出。见7.50 [Macro],第47页。

7.25 .extern
.extern is accepted in the source program—for compatibility with other assemb
lers—but it is ignored. as treats all undefined symbols as external.

7.25 .extern
.extern可以在源程序中使用--以便兼容其他的汇编器—但会被忽略。as将所有未定义的符
号都当作外部符号处理。


7.26 .fail expression
Generates an error or a warning. If the value of the expression is 500 or more
, as will print a warning message. If the value is less than 500, as will prin
t an error message. The message will include the value of expression. This can
occasionally be useful inside complex nested macros or conditional assembly.


7.26 .fail expression
生成一个错误(error)或警告(warning)。如果expression的值大于或等于500,as会打印一
条“警告”消息。如果expression的值小于500,as会打印一条“错误”消息。消息中包含
了expression的值。这在复杂的宏嵌套或条件汇编时偶尔用到。


7.27 .file string
.file tells as that we are about to start a new logical file. string is the ne
w file name.
In general, the filename is recognized whether or not it is surrounded by quot
es ‘"’; but if you wish to specify an empty file name, you must give the quo
tes–"". This statement may go away in future: it is only recognized to be com
patible with old as programs. In some configurations of as, .file has already
been removed to avoid conflicts with other assemblers. See Chapter 8 [Machine
Dependencies], page 61.

7.27 .file string
.file 通告as我们准备开启一个新的逻辑文件。 string 是新文件名。总的来说,文件名
是否使用引号‘"’都可以;但如果您希望规定一个空文件名时,必须使用引号-""。本语
句将来可能不再使用—允许使用它只是为了与旧版本的as程序兼容。在as的一些配置中,
已经删除了.file以避免与其它的汇编器冲突。见第8章 [Machine Dependencies], 第61页



7.28 .fill repeat , size , value
repeat, size and value are absolute expressions. This emits repeat copies of s
ize bytes. Repeat may be zero or more. Size may be zero or more, but if it is
more than 8, then it is deemed to have the value 8, compatible with other peop
le’s assemblers. The contents of each repeat bytes are taken from an 8-byte n
umber. The highest order 4 bytes are zero. The lowest order 4 bytes are value
rendered in the byte-order of an integer on the computer as is assembling for.
Each size bytes in a repetition is taken from the lowest order size bytes of
this number. Again, this bizarre behavior is compatible with other people’s a
ssemblers.
size and value are optional. If the second comma and value are absent, value i
s assumed zero. If the first comma and following tokens are absent, size is as
sumed to be 1.

7.28 .fill repeat , size , value
repeat, size 和value都必须是纯粹的表达式。本命令生成size个字节的repeat个副本。
Repeat可以是0或更大的值。Size 可以是0或更大的值, 但即使size大于8,也被视作8,以
兼容其它的汇编器。各个副本中的内容取自一个8字节长的数。最高4个字节为零,最低的
4个字节是value,它以as正在汇编的目标计算机的整数字节顺序排列。每个副本中的size
个字节都取值于这个数最低的size个字节。再次说明,这个古怪的动作只是为了兼容其他
的汇编器。
size参数和value参数是可选的。如果不存在第2个逗号和value参数,则假定value为零。
如果不存在第1个逗号和其后的参数,则假定size为1。


7.29 .float flonums
This directive assembles zero or more flonums, separated by commas. It has the
same effect as .single. The exact kind of floating point numbers emitted depe
nds on how as is configured. See Chapter 8 [Machine Dependencies], page 61.


7.29 .float flonums
本命令汇编0个或多个浮点数,浮点数之间由逗号分隔。它和.single的汇编效果相同。生
成的浮点数的确切类型视as的配置而定。见第8章 [Machine Dependencies], 61页。


7.30 .func name[,label]
.func emits debugging information to denote function name, and is ignored unle
ss the file is assembled with debugging enabled. Only ‘--gstabs’ is currentl
y supported. Label is the entry point of the function and if omitted name prep
ended with the ‘leading char’ is used. ‘leading char’ is usually _ or noth
ing, depending on the target. All functions are currently defined to have void
return type. The function must be terminated with .endfunc.

7.30 .func name[,label]
.func发出一个调试信息用以指示函数name,这个信息将被忽略,除非文件使用debugging
enabled方式的汇编。目前只支持‘--gstabs’。label是函数的入口点,如果name被省略
则使用预定的‘引导符’。‘引导符’通常可以是 _ 或者什么也没有,视目标机型而定。
所有函数现时被定义为void返回类型,函数体必须使用.endfunc来结束


7.31 .global symbol, .globl symbol
.global makes the symbol visible to ld. If you define symbol in your partial p
rogram, its value is made available to other partial programs that are linked
with it. Otherwise, symbol takes its attributes from a symbol of the same name
from another file linked into the same program.
Both spellings (‘.globl’ and ‘.global’) are accepted, for compatibility wi
th other assemblers.
On the HPPA, .global is not always enough to make it accessible to other parti
al programs. You may need the HPPA-only .EXPORT directive as well. See Section
8.8.5 [HPPA Assembler Directives], page 84.

7.31 .global symbol, .globl symbol
.global 使符号symbol对连接器ld可见。如果您在局部过程中定义符号symbol,其它和此
的局部过程都可以访问它的值。另外,symbol从连接到本过程的另一个文件中的同名符号
获取自己的属性。
两种写法都可以(‘.globl’ 和‘.global’),以便兼容多种汇编器。

在HPPA上, .global未必总能够使符号被其它局部过程访问。可能同时需要使用HPPA-only
.EXPORT命令。见8.8.5[HPPA Assembler Directives],84页。


7.32 .hidden names
This one of the ELF visibility directives. The other two are .internal (see Se
ction 7.39 [.internal], page 44) and .protected (see Section 7.58 [.protected]
, page 50).
This directive overrides the named symbols default visibility (which is set by
their binding: local, global or weak). The directive sets the visibility to h
idden which means that the symbols are not visible to other components. Such s
ymbols are always considered to be protected as well.

7.32 .hidden names
这是一条关于ELF可见度的命令。其它两条是.internal(见7.39[.internal],44页) 和 .p
rotected (见7.58 [.protected], 50页)。本命令取消指定符号的缺省可见度(可见度由其
他命令捆绑设定:local,global,weak)。本命令把可见度设置为hidden,这意味着本符号对
其他部分不可见。这最好是一些需要长期保护的符号。


7.33 .hword expressions
This expects zero or more expressions, and emits a 16 bit number for each.
This directive is a synonym for ‘.short’; depending on the target architectu
re, it may also be a synonym for ‘.word’.

7.33 .hword expressions
本命令后可以不带或带多个expressions,并且为每个参数生成一个16位数。
本命令等同与'.short'命令。在某些架构上,也可能等同与'.word'。


7.34 .ident
This directive is used by some assemblers to place tags in object files. as si
mply accepts the directive for source-file compatibility with such assemblers,
but does not actually emit anything for it.

7.34 .ident
本命令被某些汇编器用来在目标文件中加入标饰。为了使汇编源码文件兼容上述的汇编器
,as简单地接受本命令,但实际上不产生东西。


7.35 .if absolute expression
.if marks the beginning of a section of code which is only considered part of
the source program being assembled if the argument (which must be an absolute
expression) is nonzero. The end of the conditional section of code must be mar
ked by .endif (see Section 7.20 [.endif], page 40); optionally, you may includ
e code for the alternative condition, flagged by .else (see Section 7.15 [.els
e], page 40). If you have several conditions to check, .elseif may be used to
avoid nesting blocks if/else within each subsequent .else block.
The following variants of .if are also supported:
.ifdef symbol
Assembles the following section of code if the specified symbol has been defin
ed.
.ifc string1,string2
Assembles the following section of code if the two strings are the same. The s
trings may be optionally quoted with single quotes. If they are not quoted, th
e first string stops at the first comma, and the second string stops at the en
d of the line. Strings which contain whitespace should be quoted. The string c
omparison is case sensitive.
.ifeq absolute expression
Assembles the following section of code if the argument is zero.
.ifeqs string1,string2
Another form of .ifc. The strings must be quoted using double quotes.
.ifge absolute expression
Assembles the following section of code if the argument is greater than or equ
al to zero.
.ifgt absolute expression
Assembles the following section of code if the argument is greater than zero.

.ifle absolute expression
Assembles the following section of code if the argument is less than or equal
to zero.
.iflt absolute expression
Assembles the following section of code if the argument is less than zero.
.ifnc string1,string2.
Like .ifc, but the sense of the test is reversed: this assembles the following
section of code if the two strings are not the same.
.ifndef symbol
.ifnotdef symbol
Assembles the following section of code if the specified symbol has not been d
efined. Both spelling variants are equivalent.
.ifne absolute expression
Assembles the following section of code if the argument is not equal to zero (
in other words, this is equivalent to .if).
.ifnes string1,string2
Like .ifeqs, but the sense of the test is reversed: this assembles the followi
ng section of code if the two strings are not the same.

7.35 .if absolute expression
.if 标志着一段代码的开始,这段代码只有在参数absolute experession(必须是一个独立
的表达式)不为0时才进行汇编。这段条件汇编代码必须使用.endif标志结束。(见7.20[.e
ndif], 40页);另外,可以使用.esle来标记一个代码块(见7.15 [.else],40页),这个代码
块与前面那段代码只有一个会进行汇编。 如果您需要检查数个汇编条件,可以在使用.el
seif命令,以避免在.else代码块中进行if/else语句块的嵌套。
同样可以使用下面.if的变体:
.ifdef symbol
如果指定的符号symbol已经定义过,汇编下面那段代码。
.ifc string1,string2
如果两个字符串相同的话,汇编下面那段代码。 字符串可以可选地使用单引号。如果不使
用引号则第1个字符串在逗号处结束。第2个字符串在本行末结束。包含空白的字符串应该
使用引号标注。字符串比较时是区分大小写的。

.ifeq absolute expression
如果参数的值为0,汇编下面那段代码。

.ifeqs string1,string2
这是.ifc的另一种形式,字符串必须使用双引号标注。

.ifge absolute expression
如果参数的值大于等于0,汇编下面那段代码。

.ifgt absolute expression
如果参数的值大于0,汇编下面那段代码。

.ifle absolute expression
如果参数的值小于等于0,汇编下面那段代码。

.iflt absolute expression
如果参数的值小于0,汇编下面那段代码。

.ifnc string1,string2.
类似与.ifc,不过使用反向的测试: 如果两个字符串不相等的话,汇编下面那段代码。

.ifndef symbol
.ifnotdef symbol
如果指定的符号symbol不曾被定义过,汇编下面那段代码。 上面两种写法是等效的。


.ifne absolute expression
如果参数的值为不等于0,汇编下面那段代码。 (换句话说, 这是.if的另一种写法).

.ifnes string1,string2
类似与.ifeqs,不过使用反向的测试: 如果两个字符串不相等的话,汇编下面那段代码。



7.36 .incbin "file"[,skip[,count]]
The incbin directive includes file verbatim at the current location. You can c
ontrol the search paths used with the ‘-I’ command-line option (see Chapter
2 [Command-Line Options], page 11). Quotation marks are required around file.

The skip argument skips a number of bytes from the start of the file. The coun
t argument indicates the maximum number of bytes to read. Note that the data i
s not aligned in any way, so it is the user’s responsibility to make sure tha
t proper alignment is provided both before and after the incbin directive.

7.36 .incbin "file"[,skip[,count]]
这条incbin命令在当前位置逐字地引入file文件的内容。您可以使用命令行选项参数“-I
”来控制搜索路径。(见第2章[Command-Line Options], 11页)。文件名必须使用引号。

参数skip表示需要从文件头跳过的字节数目。参数count表示读入的最大字节数目。注意,
数据没有进行任何方式的对齐操作,所以用户需要在 .incbin命令的前后进行必要的边界
对齐。


7.37 .include "file"
This directive provides a way to include supporting files at specified points
in your source program. The code from file is assembled as if it followed the
point of the .include; when the end of the included file is reached, assembly
of the original file continues. You can control the search paths used with the
‘-I’ command-line option (see Chapter 2 [Command-Line Options], page 11). Q
uotation marks are required around file.

7.37 .include "file"
本命令提供在源程序中指定点引入支撑文件的手段。file中的代码如同紧跟.include后一
样被汇编。当引入文件汇编结束,继续汇编原来的文件。您可以使用命令行选项参数“-I
”来控制搜索路径(见第2章[Command-Line Options], 11页)。文件名必须使用引号来标注



7.38 .int expressions
Expect zero or more expressions, of any section, separated by commas. For each
expression, emit a number that, at run time, is the value of that expression.
The byte order and bit size of the number depends on what kind of target the
assembly is for.

7.38 .int expressions
可以不带参数或带多个expressions,参数之间由逗号分隔。每个expressions都生成一个数
字,这个数字等于表达式在目标机器运行时的值。字节顺序和数字的位数视汇编的目标机器
而定。


7.39 .internal names
This one of the ELF visibility directives. The other two are .hidden (see Sect
ion 7.32 [.hidden], page 42) and .protected (see Section 7.58 [.protected], pa
ge 50).
This directive overrides the named symbols default visibility (which is set by
their binding: local, global or weak). The directive sets the visibility to i
nternal which means that the symbols are considered to be hidden (ie not visib
le to other components), and that some extra, processor specific processing mu
st also be performed upon the symbols as well.

7.39 .internal names
这是一条与ELF可见度相关的命令。另外的两条是.hidden(见7.32[.hidden],42页) 和 .p
rotected (见7.58 [.protected],50页)。
本命令取消指定符号的缺省可见度(可见度由其他命令捆绑设定:local,global,weak)。本
命令把指定符号可见度设置为internal,这意味着此符号需要被隐藏(即对其他部分不可见
),另外,符号还必须经过处理器的特别的处理。




# 回复:linux下汇编的Directive Operands 2004-09-08 3:24 PM n9871009
定语:发现简单的把英文直译成中文有时产生会极大的混乱。向前和向后就是一例,我在
5.3节符号名发现这个问题,当时另选了两个词替代了向前和向后。看起来现在必须说明一
下。
向前(移动):向文件头的方向(移动)。
向后(移动):向文件尾的方向(移动)。
===========================================================
7.40 .irp symbol, values . . .
Evaluate a sequence of statements assigning different values to symbol. The se
quence of statements starts at the .irp directive, and is terminated by an .en
dr directive. For each value, symbol is set to value, and the sequence of stat
ements is assembled. If no value is listed, the sequence of statements is asse
mbled once, with symbol set to the null string. To refer to symbol within the
sequence of statements, use \symbol.
For example, assembling
.irp param,1,2,3
move d\param,sp@-
.endr
is equivalent to assembling
move d1,sp@-
move d2,sp@-
move d3,sp@-

7.40 .irp symbol,values . . .
加工一个需要用values替代symbol的语句序列。语句序列从.irp命令开始,在.endr命令前
结束。对于每个value都进行如下加工:用value替代Symbol,并对此语句序列进行汇编。
如果没有给出value,则用空字符串(null sting)替代symbol,并将此语句序列汇编一次。
使用\symbol, 把参数symbol提交给语句序列。
例如下列代码
.irp param,1,2,3
move d\param,sp@-
.endr
等同与
move d1,sp@-
move d2,sp@-
move d3,sp@-


7.41 .irpc symbol,values . . .
Evaluate a sequence of statements assigning different values to symbol. The se
quence of statements starts at the .irpc directive, and is terminated by an .e
ndr directive. For each character in value, symbol is set to the character, an
d the sequence of statements is assembled. If no value is listed, the sequence
of statements is assembled once, with symbol set to the null string. To refer
to symbol within the sequence of statements, use \symbol.
For example, assembling
.irpc param,123
move d\param,sp@-
.endr
is equivalent to assembling
move d1,sp@-
move d2,sp@-
move d3,sp@-

7.41 .irpc symbol,values. . .
加工一个需要用values替代symbol的语句序列。语句序列从.irpc命令开始,在.endr命令
前结束。对于value中的每个字符,都进行如下加工;用此字符替代symbol,并对此语句序
列进行汇编。如果没有给出value参数,则用空字符串(null sting)替代symbol,并将此语
句序列汇编一次。使用\symbol, 把参数symbol提交给语句序列。
例如下列代码
.irpc param,123
move d\param,sp@-
.endr
等同与
move d1,sp@-
move d2,sp@-
move d3,sp@-


7.42 .lcomm symbol , length
Reserve length (an absolute expression) bytes for a local common denoted by sy
mbol. The section and value of symbol are those of the new local common. The a
ddresses are allocated in the bss section, so that at run-time the bytes start
off zeroed. Symbol is not declared global (see Section 7.31 [.global], page 4
2), so is normally not visible to ld.
Some targets permit a third argument to be used with .lcomm. This argument spe
cifies the desired alignment of the symbol in the bss section.
The syntax for .lcomm differs slightly on the HPPA. The syntax is ‘symbol .lc
omm, length’; symbol is optional.

7.42 .lcomm symbol , length
为一个本地通用符号symbol预留length个字节的内存。symbol 的段(属性)和值(属性)被设
置为一个新的本地通用符号应有的属性:内存是在bss段中分配的,所以在运行时,这些字
节开始都是零。因为symbol没有被声明为全局性的符号,所以symbol对ld通常不可见。

某些目标格式允许在.lcomm命令中使用第3个参数。这个参数指出这个bss段中的符号对齐
操作所需要的边界基准。
.lcomm的语法在HPPA上稍有不同。表示为‘symbol .lcomm, length’; symbol 是可选的



7.43 .lflags
as accepts this directive, for compatibility with other assemblers, but ignore
s it.

7.43 .lflags
as接受本命令,以兼容其他的汇编器,但忽略之。


7.44 .line line-number
Change the logical line number. line-number must be an absolute expression. Th
e next line has that logical line number. Therefore any other statements on th
e current line (after a statement separator character) are reported as on logi
cal line number line-number - 1. One day as will no longer support this direct
ive: it is recognized only for compatibility with existing assembler programs.

Warning: In the AMD29K configuration of as, this command is not available; use
the synonym .ln in that context.
Even though this is a directive associated with the a.out or b.out object-code
formats, as still recognizes it when producing COFF output, and treats ‘.lin
e’ as though it were the COFF ‘.ln’ if it is found outside a .def/.endef pa
ir.
Inside a .def, ‘.line’ is, instead, one of the directives used by compilers
to generate auxiliary symbol information for debugging.

7.44 .line line-number
更改逻辑行号,参数line-number必须是个纯粹的表达式。本命令后的下一行将被赋予此逻
辑行号。因此在当前行之前任何其他的语句(在语句分隔符后)的逻辑行号将被视作line
-number - 1。以后 as将不在支持这条命令:只是为了兼容现存的汇编器而接受本命令。

Warning: 在为AMD29K目标机器配置的as中,不能使用本指令。在这种场合可以使用.ln命令

尽管这是与a. out或b. out目标代码格式相关的命令,在生成COFF输出时as仍然接受它,
并且如果‘.line’出现在.def/endef之外的话,就把它视为‘.ln’命令。
如果‘.line’在.def语句块中的话,.line命令则是一条编译器使用的命令,用来为调式
生成辅助符号信息。


7.45 .linkonce [type]
Mark the current section so that the linker only includes a single copy of it.
This may be used to include the same section in several different object file
s, but ensure that the linker will only include it once in the final output fi
le. The .linkonce pseudo-op must be used for each instance of the section. Dup
licate sections are detected based on the section name, so it should be unique
.
This directive is only supported by a few object file formats; as of this writ
ing, the only object file format that supports it is the Portable Executable f
ormat used on Windows NT.
The type argument is optional. If specified, it must be one of the following s
trings. For example:
.linkonce same_size
Not all types may be supported on all object file formats.
discard Silently discard duplicate sections. This is the default.
one_only Warn if there are duplicate sections, but still keep only one copy.

same_size Warn if any of the duplicates have different sizes.
same_contents
Warn if any of the duplicates do not have exactly the same contents.

7.45 .linkonce [type]
给当前段做一个标志,以便连接器只包含它的一个拷贝。这个命令可以用于几个不同的目标
文件中包含同样的段,但需要连接器在最终的输出文件中只包含一个这样的段。. linkou
ce伪操作必须在每个段的实例都中使用。对重复段的探测基于段名来进行,因此这个段将
是唯一的。
本命令只在少数目标格式文件中有效,到写本文为止,只有基于Windows NT的PE (Portab
le Executable)格式的目标文件支持本命令,
参数type是可选的,如果指定了此参数,它必须是下列字符串之一。例如
. Linkonce same_size
不是在所有的格式目标文件都可以使用所有类型的参数。
discard 静静地舍弃重复的段,这也是默认值。
one_only 如果存在重复的段则发出警告,但只保存一个拷贝。
same_size 如果重复的段有不同的大小则发出警告。
same_contents 如果重复段的内容不是精确的相符则发出警告。


7.46 .ln line-number
‘.ln’ is a synonym for ‘.line’.

7.46 .ln line-number
‘.ln’命令等同与‘.line’.


7.47 .mri val
If val is non-zero, this tells as to enter MRI mode. If val is zero, this tell
s as to exit MRI mode. This change affects code assembled until the next .mri
directive, or until the end of the file. See Section 2.8 [MRI mode], page 13.


7.47 .mri val
如果参数val是非零值,这将通知as进入MRI模式。如果参数val的值是零,这通知as退出M
RI模式。这个变化会影响汇编的结果,直到下个.mri命令,或者直到文件尾。见2.8 [MRI
mode], 13页。


7.48 .list
Control (in conjunction with the .nolist directive) whether or not assembly li
stings are generated. These two directives maintain an internal counter (which
is zero initially). .list increments the counter, and .nolist decrements it.
Assembly listings are generated whenever the counter is greater than zero.
By default, listings are disabled. When you enable them (with the ‘-a’ comma
nd line option; see Chapter 2 [Command-Line Options], page 11), the initial va
lue of the listing counter is one.

7.48 .list
控制(和.nolist命令配合)是否生成汇编清单。这两个命令维护一个内部的计数器(计数
器初始值为0).list命令增加计数器的值,.nolist减少计数器的值。当计数器的值大与0
时将汇编列表。
缺省状态汇编列表的生成是关闭的。当您打开它的时候(使用带-a选项的命令行)第2章
[Command-Line Options], 11页), 内部计数器的初始值为1。


7.49 .long expressions
.long is the same as ‘.int’, see Section 7.38 [.int], page 44.

7.49 .long expressions
.long是.int的等价命令,见7.38 [.int], 44页.

7.50 .macro
The commands .macro and .endm allow you to define macros that generate assembl
y output. For example, this definition specifies a macro sum that puts a seque
nce of numbers into memory:
.macro sum from=0, to=5
.long \from
.if \to-\from
sum "(\from+1)",\to
.endif
.endm
With that definition, ‘SUM 0,5’ is equivalent to this assembly input:
.long 0
.long 1
.long 2
.long 3
.long 4
.long 5
.macro macname
.macro macname macargs ...
Begin the definition of a macro called macname. If your macro definition requi
res arguments, specify their names after the macro name, separated by commas o
r spaces. You can supply a default value for any macro argument by following t
he name with ‘=deflt’. For example, these are all valid .macro statements:


.macro comm
Begin the definition of a macro called comm, which takes no arguments.
.macro plus1 p, p1
.macro plus1 p p1
Either statement begins the definition of a macro called plus1,which takes two
arguments; within the macro definition, write ‘\p’ or ‘\p1’ to evaluate t
he arguments.
.macro reserve_str p1=0 p2
Begin the definition of a macro called reserve_str, with two arguments. The fi
rst argument has a default value, but not the second. After the definition is
complete, you can call the macro either as ‘reserve_str a, b’ (with ‘\p1’
evaluating to a and ‘\p2’ evaluating to b), or as ‘reserve_str ,b’ (with ‘
\p1’ evaluating as the default, in this case ‘0’, and ‘\p2’ evaluating to
b).
When you call a macro, you can specify the argument values either by position,
or by keyword. For example, ‘sum 9,17’ is equivalent to ‘sum to=17, from=9
’.
.endm Mark the end of a macro definition.
.exitm Exit early from the current macro definition.
\@ as maintains a counter of how many macros it has executed in this pseudov-a
riable; you can copy that number to your output with ‘\@’, but only within a
macro definition.


7.50 .macro
本命令.macro和.endm命令允许您定义宏来生成汇编输出。例如,下面的语句定义了一个宏
sum,这个宏把一个数字序列放入内存。

.macro sum from=0, to=5
.long \from
.if \to-\from
sum "(\from+1)",\to
.endif
.endm
使用上述定义,'SUM 0,5'语句就等于输入下面的汇编语句:
.long 0
.long 1
.long 2
.long 3
.long 4
.long 5

.macro macname
.macro macname macargs ...
开始定义一个名为macname的宏。如果您的宏需要使用参数,则在宏的名字后指定他们的名
字,参数之间用逗号或空格分隔。您可以为任意的参数提供参数的缺省值,只需要在参数
后使用“=deflt”,。例如,下列都是合法的宏定义语句:
.macro comm
定义一个名为comm宏,不使用参数。
.macro plus1 p, p1
.macro plus1 p p1
两个语句都声明要定义一个名为plus1的宏,这个宏需要两个参数,在宏定义体内,使用'
\p'或'\p1'来引用参数的值。
.macro reserve_str p1=0 p2
声明要定义一个名为reserve_str的宏,使用两个参数。第一个参数有缺省值,第二个没有
缺省值。宏定义完成后,您可以通过‘reserve_str a, b’(宏体中‘\p1’引用a的值,‘
\p2’引用b值)或通过‘reserve_str ,b’(‘\p1’使用缺省值,在此为‘0’,‘\p2’引
用b的值)来调用这个宏。

当调用一个宏时,您既可以通过位置指定参数值,也可以通过关键字指定参数值。例如,
‘sum 9,17’和‘sum to=17, from=9’是等价的。
.endm 标志宏定义体的结束。
.exitm 提前从当前宏定义体中退出。
\@ 这个伪变量其实是as维护的一个计数器,用来统计执行了多少个宏。您可以通过使用\
@把这个数字复制到您的输出中,但仅限于在宏定义体中使用。


7.51 .nolist
Control (in conjunction with the .list directive) whether or not assembly list
ings are generated. These two directives maintain an internal counter (which i
s zero initially). .list increments the counter, and .nolist decrements it. As
sembly listings are generated whenever the counter is greater than zero.

7.51 .nolist
控制(和.list命令配合)是否生成汇编列表。这两个命令维护一个内部的计数器(计数器
初始值为0).list命令增加计数器的值,.nolist减少计数器的值。当计数器的值大与0时
将汇编列表。


7.52 .octa bignums
This directive expects zero or more bignums, separated by commas. For each big
num, it emits a 16-byte integer.
The term “octa” comes from contexts in which a “word” is two bytes; hence
octa-word for 16 bytes.

7.52 .octa bignums
本命令可以不带参数或多个由逗号分隔开的巨数bignum,针对每个巨数bignum,它生成一个
16个字节的整数。
术语"octa"来源:word为2个字节,故此octa-word为16个字节。


7.53 .org new-lc , fill
Advance the location counter of the current section to new-lc. new-lc is eithe
r an absolute expression or an expression with the same section as the current
subsection. That is, you can’t use .org to cross sections: if new-lc has the
wrong section, the .org directive is ignored. To be compatible with former as
semblers, if the section of new-lc is absolute, as issues a warning, then pret
ends the section of new-lc is the same as the current subsection.
.org may only increase the location counter, or leave it unchanged; you cannot
use .org to move the location counter backwards.
Because as tries to assemble programs in one pass, new-lc may not be undefined
. If you really detest this restriction we eagerly await a chance to share you
r improved assembler.
Beware that the origin is relative to the start of the section, not to the sta
rt of the subsection. This is compatible with other people’s assemblers.
When the location counter (of the current subsection) is advanced, the interve
ning bytes are filled with fill which should be an absolute expression. If the
comma and fill are omitted, fill defaults to zero.

7.53 .org new-lc , fill
向后移动当前段的位置计数器至new-lc。new-lc要么是一个纯粹的表达式,要么这个表达
式与当前子段在同一个段中。换句话说,就是您不能使用.org进行段超越。如果new-lc指
向错误的段,则忽略.org命令。为了兼容以前的汇编器,如果new-lc指向一个地址独立的
段,as发出一个警告,并假定new-lc指向当前子段。
.org 仅仅可以增大位置计数器,或者保持位置计数器不变;您不能使用.org命令把位置计
数器向回移动。
因为as尽量一次完成程序汇编,所以不能使用未定义的new-lc。如果您厌恶这个限制,我
们急切期待有机会分享经过您改进的汇编器。
注意起点相对于段的首地址,而不是子段的首地址。这与其他的汇编器相兼容。
当(当前语句块)位置计数器到达指定位置,用fill填充该字节,fill必须是纯粹的表达
式。如果没有给出逗号和fill,fill值缺省为0。


7.54 .p2align[wl] abs-expr, abs-expr, abs-expr
Pad the location counter (in the current subsection) to a particular storage b
oundary. The first expression (which must be absolute) is the number of low-or
der zero bits the location counter must have after advancement. For example ‘
.p2align 3’ advances the location counter until it a multiple of 8. If the lo
cation counter is already a multiple of 8, no change is needed.
The second expression (also absolute) gives the fill value to be stored in the
padding bytes. It (and the comma) may be omitted. If it is omitted, the paddi
ng bytes are normally zero. However, on some systems, if the section is marked
as containing code and the fill value is omitted, the space is filled with no
-op instructions.
The third expression is also absolute, and is also optional. If it is present,
it is the maximum number of bytes that should be skipped by this alignment di
rective. If doing the alignment would require skipping more bytes than the spe
cified maximum, then the alignment is not done at all. You can omit the fill v
alue (the second argument) entirely by simply using two commas after the requi
red alignment; this can be useful if you want the alignment to be filled with
no-op instructions when appropriate.
The .p2alignw and .p2alignl directives are variants of the .p2align directive.
The .p2alignw directive treats the fill pattern as a two byte word value. The
.p2alignl directives treats the fill pattern as a four byte longword value. F
or example, .p2alignw 2,0x368d will align to a multiple of 4. If it skips two
bytes, they will be filled in with the value 0x368d (the exact placement of th
e bytes depends upon the endianness of the processor). If it skips 1 or 3 byte
s, the fill value is undefined.

7.54 .p2align[wl] abs-expr, abs-expr, abs-expr
增加位置计数器(在当前的子段)使它指向规定的存储边界。第一个表达式参数(结果必须是
纯粹的数字) 代表位置计数器移动后,计数器中连续为0的低序位数量。例如‘.align 3’
向后移动位置指针直至8的倍数(指针的最低的3位为0)。如果地址已经是8倍数,则无需
移动。
第二个表达式参数(结果必须是纯粹的数字)给出填充字节的值。用这个值填充位置计数器
越过的地方。这个参数(和逗点)可以省略。如果省略它,填充字节的值通常默认为0。但在
某些系统上,如果本段标识为包含代码,而填充值被省略,则使用no-op指令填充填充区。

第3个参数表达式的结果也必须是纯粹的数字,这个参数是可选的。如果存在第3个参数,
它代表本对齐命令允许越过字节数的最大值。如果完成这个对齐需要跳过的字节比指定的
最大值还多,则根本无法完成对齐。您可以在边界基准后简单地使用两个逗号,以省略填充
值参数(第二参数);如果您想在适当的时候,对齐操作自动使用no-op指令填充,这个方法
将非常奏效。
.p2alignw和.p2alignl是.p2align命令的变化形式。.p2alignw 使用2个字节来填充填充区
。.p2alignl使用4字节来填充。例如,. .p2alignw 2,0x368d将地址对齐到4的倍数,如果
它跳过2个字节,GAS将使用0x368d填充这2个字节(字节的准确的位置视处理器的存储方式
而定)。如果它跳过1或3个字节,填充值则不明确。


7.55 .previous
This is one of the ELF section stack manipulation directives. The others are .
section (see Section 7.66 [Section], page 52), .subsection (see Section 7.79 [
SubSection], page 56), .pushsection (see Section 7.61 [PushSection], page 50),
and .popsection (see Section 7.56 [PopSection], page 50).
This directive swaps the current section (and subsection) with most recently r
eferenced section (and subsection) prior to this one. Multiple .previous direc
tives in a row will flip between two sections (and their subsections).
In terms of the section stack, this directive swaps the current section with t
he top section on the section stack.

7.55 .previous
这是一个ELF段堆栈操作命令。其他的段堆栈操作命令还有.section (见 7.66 [Section]
, 52页), .subsection (见 7.79 [SubSection], 56页),.pushsection (见 7.61 [PushS
ection], 50页), 和 .popsection (见 7.56 [PopSection], 50页)。
本命令交换当前段(及其子段)和最近访问过的段(及其子段)。多个连续的.previous命
令将使当前位置两个段(及其子段)之间反复切换。
用段堆栈的术语来说,本命令使当前段和堆顶段交换位置。


7.56 .popsection
This is one of the ELF section stack manipulation directives. The others are .
section (see Section 7.66 [Section], page 52), .subsection (see Section 7.79 [
SubSection], page 56), .pushsection (see Section 7.61 [PushSection], page 50),
and .previous (see Section 7.55 [Previous], page 49).
This directive replaces the current section (and subsection) with the top sect
ion (and subsection) on the section stack. This section is popped off the stac
k.

7.56 .popsection
这是一个ELF段堆栈操作命令。其他的段堆栈操作命令还有.section(见 7.66 [Section],
52页), .subsection (见 7.79 [SubSection], 56页),.pushsection (见 7.61 [PushSe
ction], 50页), 和 .previous (见 7.55 [Previous], 49页).
本命令用堆栈顶段(及其子段)替代当前段(及其子段)。堆栈顶段出栈。

7.57 .print string
as will print string on the standard output during assembly. You must put stri
ng in double quotes.

7.57 .print string
as会在标准输出上打印string字符串。String必须使用双引号。


7.58 .protected names
This one of the ELF visibility directives. The other two are .hidden (see Sect
ion 7.32 [Hidden], page 42) and .internal (see Section 7.39 [Internal], page 4
4).
This directive overrides the named symbols default visibility (which is set by
their binding: local, global or weak). The directive sets the visibility to p
rotected which means that any references to the symbols from within the compon
ents that defines them must be resolved to the definition in that component, e
ven if a definition in another component would normally preempt this.

7.58 .protected names
这是一条ELF可见度的相关命令。其它两条是.hidden (参见 7.32 [Hidden], 42页)和 .i
nternal (参见 7.39 [Internal], 44页)。
本命令将取消指定符号的可见度缺省值(可见度由其他命令捆绑设定:local, global, w
eak)本命令将可见度设置为protected,这个可见度意味着:在定义此符号的部件内对此符
号的任何访问,都必须解析到这个部件内的定义体。即使其他部件中存在一个正常情况下
比此优先的定义体。

7.59 .psize lines, columns
Use this directive to declare the number of lines—and, optionally, the number
of columns—to use for each page, when generating listings.
If you do not use .psize, listings use a default line-count of 60. You may omi
t the comma and columns specification; the default width is 200 columns.
as generates formfeeds whenever the specified number of lines is exceeded (or
whenever you explicitly request one, using .eject).
If you specify lines as 0, no formfeeds are generated save those explicitly sp
ecified with .eject.

7.59 .psize lines , columns
当生成清单列表时,使用本命令声明每页的行数—还可以可选地声明列数。
如果您不使用本命令,清单列表的行数为默认的60行。可以省略逗号和列参数:默认值为
200列。
当指定的行数过多的话,as会产生进纸操作。(如果您确实需要一个进纸动作,可以使用
.eject命令)
如果您指定行数为0,则不产生进纸操作,除非您明确地使用了.eject命令。


7.60 .purgem name
Undefine the macro name, so that later uses of the string will not be expanded
. See Section 7.50 [Macro], page 47.

7.60 .purgem name
取消name的宏定义,后面使用字符串name不会被宏扩展。参见 7.50 [Macro], 47页。


7.61 .pushsection name , subsection
This is one of the ELF section stack manipulation directives. The others are .
section (see Section 7.66 [Section], page 52), .subsection (see Section 7.79 [
SubSection], page 56), .popsection (see Section 7.56 [PopSection], page 50), a
nd .previous (see Section 7.55 [Previous], page 49).
This directive is a synonym for .section. It pushes the current section (and s
ubsection) onto the top of the section stack, and then replaces the current se
ction and subsection with name and subsection.

7.61 .pushsection name , subsection
本命令是一个ELF段堆栈操作命令。其余的几个是.section (参见 7.66 [Section], 52页
) , .subsection (参见7.79 [SubSection], 56页),.popsection (参见 7.56 [PopSecti
on], 50页), 和 .previous (参见 7.55 [Previous], 49页)。
本命令与.section命令是等价的。它将当前段(及子段)推入段堆栈的顶部。并使用name
和subsection来替代当前段和子段。

7.62 .quad bignums
.quad expects zero or more bignums, separated by commas. For each bignum, it e
mits an 8-byte integer. If the bignum won’t fit in 8 bytes, it prints a warni
ng message; and just takes the lowest order 8 bytes of the bignum.
The term “quad” comes from contexts in which a “word” is two bytes; hence
quad-word for 8 bytes.

7.62 .quad bignums
.quad 可带0或多个bignum参数,每个参数由逗号分隔。对于每个bignum都汇编成一个8字
节的整数。如果某个bignum用8字节无法表示,则给出警告信息;只汇编这个bignum的最低
8字节。
术语“quad”源于一个“word”代表2个字节,所以quad-word代表8个字节。

7.63 .rept count
Repeat the sequence of lines between the .rept directive and the next .endr di
rective count times.
For example, assembling
.rept 3
.long 0
.endr
is equivalent to assembling
.long 0
.long 0
.long 0

7.63 .rept count
汇编.rept和.endr之间的语句count次。
如, 汇编下列语句:
.rept 3
.long 0
.endr
与下列语句是等价的:
.long 0
.long 0
.long 0

7.64 sbttl "subheading"
Use subheading as the title (third line, immediately after the title line) whe
n generating assembly listings.
This directive affects subsequent pages, as well as the current page if it app
ears within ten lines of the top of a page.

7.64 sbttl "subheading"
当生成汇编清单时,使用subheading作为标题(第3行,紧跟在标题行之后)。
本命令对清单的后续页起作用,如果它位于当前页的前10行内,则对当前页也起作用。



7.65 .scl class
Set the storage-class value for a symbol. This directive may only be used insi
de a .def/.endef pair. Storage class may flag whether a symbol is static or ex
ternal, or it may record further symbolic debugging information.
The ‘.scl’ directive is primarily associated with COFF output; when configur
ed to generate b.out output format, as accepts this directive but ignores it.


7.65 .scl class
设置一个符号的存储类型值(storage-class value)。本命令只能在.def/.endef之间使
用。符号的存储类型可以表明符号是static类型或是external类型,或者进一步记录符号
的调试信息。
‘.scl’命令主要与在COFF输出有关,当生成b.out输出格式时,as接受本命令,但忽略本
命令。




7.66 .section name (COFF version)
Use the .section directive to assemble the following code into a section named
name.
This directive is only supported for targets that actually support arbitrarily
named sections; on a.out targets, for example, it is not accepted, even with
a standard a.out section name.
For COFF targets, the .section directive is used in one of the following ways:

.section name [, "flags"]
.section name [, subsegment]
If the optional argument is quoted, it is taken as flags to use for the sectio
n. Each flag is a single character. The following flags are recognized:
b bss section (uninitialized data)
n section is not loaded
w writable section
d data section
r read-only section
x executable section
s shared section (meaningful for PE targets)
If no flags are specified, the default flags depend upon the section name. If
the section name is not recognized, the default will be for the section to be
loaded and writable. Note the n and w flags remove attributes from the section
, rather than adding them, so if they are used on their own it will be as if n
o flags had been specified at all.
If the optional argument to the .section directive is not quoted, it is taken
as a subsegment number (see Section 4.4 [Sub-Sections], page 25).

7.66 .section name (COFF 版本)
使用.section命令将后续的代码汇编进一个定名为name的段。
本命令只能在目标格式真正支持任意命名段时使用;例如,汇编一个a.out目标格式时,即
使name是一个标准的a.out段名,本命令也不被接受。
当目标格式为COFF时,.section命令的使用为下面某一种格式:
.section name[, "flags"]
.section name[, subsegment]
如可选参数使用了引号,它将被视为该段的标志(flags)。每个标记是单个的字符。下列是
认可的标志。
b bss 段 (未初始化的数据)
n 未装入内存的段
w 可写的段
d 数据段
r 只读段
x 代码段 (executable section)
s 共享段 (目标为PE格式有意义)
如果本命令没有指定标志,则依靠段名来确定标志缺省值。如果该段名没有使用标准段名
,则默认该段已装入内存并且可写。注意在使用n和w标志组合时,不是增加这组属性,而
是删除该段的属性。所以如果只存在这两个标志,就代表该段没有指定任何标志。
如果本命令的可选参数没有使用引号,参数将被视为子段的编号。(参见 4.4 [Sub-Secti
ons], 25页)。



7.67 .section name (ELF 版本)
This is one of the ELF section stack manipulation directives. The others are .
subsection (see Section 7.79 [SubSection], page 56), .pushsection (see Section
7.61 [PushSection], page 50), .popsection (see Section 7.56 [PopSection], pag
e 50), and .previous (see Section 7.55 [Previous], page 49).
For ELF targets, the .section directive is used like this:
.section name [, "flags"[, @type]]
The optional flags argument is a quoted string which may contain any combinati
on of the following characters:
a section is allocatable
w section is writable
x section is executable
The optional type argument may contain one of the following constants:
@progbits section contains data
@nobits section does not contain data (i.e., section only occupies space)

If no flags are specified, the default flags depend upon the section name. If
the section name is not recognized, the default will be for the section to hav
e none of the above flags: it will not be allocated in memory, nor writable, n
or executable. The section will contain data.
For ELF targets, the assembler supports another type of .section directive for
compatibility with the Solaris assembler:
.section "name"[, flags...]
Note that the section name is quoted. There may be a sequence of comma separat
ed flags:
#alloc section is allocatable
#write section is writable
#execinstr section is executable
This directive replaces the current section and subsection. The replaced secti
on and subsection are pushed onto the section stack. See the contents of the g
as testsuite directory gas/testsuite/gas/elf for some examples of how this dir
ective and the other section stack directives work.

7.67 .section name (ELF 版本)
本命令是ELF的段堆栈操作命令之一,其他的段堆栈命令为.subsection (见 Section 7.7
9 [SubSection], page 56), .pushsection (见Section 7.61 [PushSection], page 50)
, .popsection (见 Section 7.56 [PopSection], page 50), and .previous (见 Secti
on 7.55 [Previous], page 49).
当目标格式为ELF时,.section命令应如下使用:
.section name [, "flags"[, @type]]
可选参数flags是被引号包围的字符串,可以由下列字符的任意组合:
a 可分配的段(allocatable)
w 可写段
x 代码段
可选的参数type可以包含下列的任一常量:
@progbits 包含数据的段
@nobits 不包含数据的段(只占用空间的段)
如果本命令没有指定标志,则依靠段名来确定标志缺省值。如果段名不是标准的段名,则
默认的该段不包含上述标志:该段不可分配内存,不可写,不可执行。该段是包含数据的
段。
当目标格式为ELF时,as还支持另一种形式的.section命令,以便兼容Solaris的汇编器:

.section "name"[, flags...]
注意段名是使用引号包围的,可能存在一系列由逗号分隔分隔的标志:
#alloc 可分配的段(section is allocatable)
#write 可写的段
#execinstr 可执行的段
本命令将(用段名为name的段)替代当前段和子段。被替换的段将被推入段堆栈。参见ga
s的测试套件目录gas/testsuite/gas/elf,可以找到一些本命令和其他段堆栈操作命令的
例子。

7.68 .set symbol, expression
Set the value of symbol to expression. This changes symbol’s value and type t
o conform to expression. If symbol was flagged as external, it remains flagged
(see Section 5.5 [Symbol Attributes], page 30).
You may .set a symbol many times in the same assembly.
If you .set a global symbol, the value stored in the object file is the last v
alue stored into it.
The syntax for set on the HPPA is ‘symbol .set expression’.

7.68 .set symbol, expression
设置symbol为expression。这将改变symbol的值域和类型领域以符合expression参数。如
果symbol已被标志为external,则symbol保持它的标志。(见 5.5 [Symbol Attributes],
30页)。
您可以在同一个汇编程序中多次使用.set命令来设置同一个符号。
如果设置一个全局符号,该符号在目标文件中值为最后设定的值。
在HPPA上的语法是‘symbol .set expression’。

7.69 .short expressions
.short is normally the same as ‘.word’. See Section 7.92 [.word], page 59.

In some configurations, however, .short and .word generate numbers of differen
t lengths; see Chapter 8 [Machine Dependencies], page 61.

7.69 .short expressions
本命令通常和’.word’命令一样,见7.92 [.word], 59页.
然而在某些配置中,.short和.word命令生成的数字长度却不相同;见第8章 [Machine De
pendencies], 61页.



7.70 .single flonums
This directive assembles zero or more flonums, separated by commas. It has the
same effect as .float. The exact kind of floating point numbers emitted depen
ds on how as is configured. See Chapter 8 [Machine Dependencies], page 61.

7.70 .single flonums
本命令可以汇编0个或多个浮点参数,各个参数之间使用逗号分隔。它的作用和.float相同
。生成浮点数的具体类型视as的配置而定。见第8章 [Machine Dependencies], 61页。






--------------------
一切有为法 如梦幻泡影




# 回复:linux下汇编的Directive Operands 2004-09-08 3:25 PM n9871009
Re: 7 汇编器命令(下) [re: amtb]



位组合:bit pattern,想不出有什么特别的意义,大概指的是有限个数的0和1所有的组合
吧。Fix me.
sleb128/uleb128: 基于128位的低地址结尾带/无符号的数。您有什么好建议?
==========================下==================================
7.71 .size (COFF 版本)
This directive is generated by compilers to include auxiliary debugging inform
ation in the symbol table. It is only permitted inside .def/.endef pairs.
‘.size’ is only meaningful when generating COFF format output; when as is ge
nerating b.out, it accepts this directive but ignores it.

7.71 .size (COFF 版本)
本命令一般由编译器生成,以在符号表中加入辅助调试信息。本命令只能在.def/.endef命
令对之间使用。
本命令只在生成COFF格式的输出文件有意义。当as生成b.out时,as接受本命令但忽略之。




7.72 .size name , expression (ELF 版本)
This directive is used to set the size associated with a symbol name. The size
in bytes is computed from expression which can make use of label arithmetic.
This directive is typically used to set the size of function symbols.
本命令经常用来设置符号name的内存大小。内存大小的单位是字节, 通过计算参数expres
sion得到,参数expression中可以使用标签进行计算。本命令常用来设置函数符号的长度


7.73 .sleb128 expressions
sleb128 stands for “signed little endian base 128.” This is a compact, varia
ble length representation of numbers used by the DWARF symbolic debugging form
at. See Section 7.86 [Uleb128], page 58.

7.73 .sleb128 expressions
sleb128代表“signed little endian base 128”(低地址结尾的带符号128位基数)。这是
一个紧凑的,变长的数字表示方法,当使用DWARF符号调试格式时使用。参见7.86 [Uleb1
28], 58页。



7.74 .skip size , fill
This directive emits size bytes, each of value fill. Both size and fill are ab
solute expressions. If the comma and fill are omitted, fill is assumed to be z
ero. This is the same as ‘.space’.

7.74 .skip size , fill
本命令生成size个字节,每个字节的值都是fill。参数size和fill都必须是纯粹的表达式
。如果省略逗号和fill,则默认fill的值为0。这与’.space’相同。



7.75 .space size , fill
This directive emits size bytes, each of value fill. Both size and fill are ab
solute expressions. If the comma and fill are omitted, fill is assumed to be z
ero. This is the same as ‘.skip’.
Warning: .space has a completely different meaning for HPPA targets; use .bloc
k as a substitute. See HP9000 Series 800 Assembly Language Reference Manual (H
P 92432-90001) for the meaning of the .space directive. See Section 8.8.5 [HPP
A Assembler Directives], page 84, for a summary.
On the AMD 29K, this directive is ignored; it is accepted for compatibility wi
th other AMD 29K assemblers.
Warning: In most versions of the gnu assembler, the directive .space has the e
ffect of .block See Chapter 8 [Machine Dependencies], page 61.

7.75 .space size , fill
本命令生成size个字节,每个字节的值都是fill。参数size和fill都必须是纯粹的表达式
。如果省略了逗号和fill,则默认fill的值为0。这与’.skip’相同。
警告:在生成HPPA目标格式时,.space的意义完全不同。应该使用.block命令替代本命令
。在HP9000系列800汇编语言参考手册(HP 92432-90001),可以找到.space命令的用法。参
见 8.8.5 [HPPA Assembler Directives],84页, 可以找到使用摘要。
在AMD 29K上,本命令将被忽略。出于兼容其它一些AMD 29K汇编器的目的,as接受本命令

警告:在gnu汇编器大多数版本中,这个.space命令和.block命令等效。见第8章 [Machin
e Dependencies], 61页。

7.76 .stabd, .stabn, .stabs
There are three directives that begin ‘.stab’. All emit symbols (see Chapter
5 [Symbols], page 29), for use by symbolic debuggers. The symbols are not ent
ered in the as hash table:they cannot be referenced elsewhere in the source fi
le. Up to five fields are required:
string This is the symbol’s name. It may contain any character except ‘\000’
, so is more general than ordinary symbol names. Some debuggers used to code a
rbitrarily complex structures into symbol names using this field.
type An absolute expression. The symbol’s type is set to the low 8 bits of th
is expression. Any bit pattern is permitted, but ld and debuggers choke on sil
ly bit patterns.
other An absolute expression. The symbol’s “other” attribute is set to the
low 8 bits of this expression.
desc An absolute expression. The symbol’s descriptor is set to the low 16 bit
s of this expression.
Value An absolute expression that becomes the symbol’s value.
If a warning is detected while reading a .stabd, .stabn, or .stabs statement,
the symbol has probably already been created; you get a half-formed symbol in
your object file. This is compatible with earlier assemblers!
.stabd type , other , desc
The “name” of the symbol generated is not even an empty string. It is a null
pointer, for compatibility. Older assemblers used a null pointer so they didn
’t waste space in object files with empty strings.
The symbol’s value is set to the location counter, relocatably. When your pro
gram is linked, the value of this symbol is the address of the location counte
r when the .stabd was assembled.
.stabn type , other , desc , value
The name of the symbol is set to the empty string "".
.stabs string , type , other , desc , value
All five fields are specified.

7.76 .stabd, .stabn, .stabs
有3个以.stab开头的命令。它们都用来产生符号,(参见第5章 [Symbols], 29页),供符号
调试器使用。这些符号没有收入as的散列表中:这些符号不能被源文件其他地方所访问。
它们至少需要5个属性域:
string 这是符号的名字。它可以包含除‘\000’之外的任何字符,故此可用名比普通符号
名更广泛。很多调试器经常利用这个空间,把任意复杂的结构编码为符号名。
type 这是一个纯粹的表达式。符号的类型属性由这个表达式的低8位设定。任何的位组合
(bit pattern)都可以,但连接器和调试器会被没有义的位组合所中断。
other 这是一个纯粹的表达式。由这个表达式的低8位设定此符号的“其它”属性。
desc 这是一个纯粹的表达式。由这个表达式的低16位设定此符号的描述符。
Value 这个纯粹的表达式将作为符号的值。

如果汇编.stabd, .stabn, 或 .stabs语句时引发了一个警告,该符号有可能已经被创建;
在目标文件中存在一个半成品的符号。这样做兼容于早期的汇编器!

.stabd type , other , desc
生成符号的“名字”甚至不是空字符串,而是一个空指针(null),这样安排是出于对兼
容性要求。早期的汇编器经常使用空指针,以避免空字符串在目标文件中浪费空间。
这个符号的值(值域)在重定位时设置为位置计数器的值。当程序连接之后,这个符号的
值是.stabd命令汇编时位置计数器的地址。
.stabn type , other , desc , value
这个符号的名字被设置为空字符串“”。
.stabs string , type , other , desc , value
5个属性域全部指定好。



7.77 .string "str"
Copy the characters in str to the object file. You may specify more than one s
tring to copy, separated by commas. Unless otherwise specified for a particula
r machine, the assembler marks the end of each string with a 0 byte. You can u
se any of the escape sequences described in Section 3.6.1.1 [Strings], page 19
.

7.77 .string "str"
将参数str中的字符复制到目标文件中去。您可以指定多个字符串进行复制,之间使用逗号
分隔。除非另外指定了具体的机器,汇编器将在每个字符串后追加一个0字节作为标记。您
可以使用任意的逃逸序列,参见19页中3.6.1.1 [Strings]的描述。



7.78 .struct expression
Switch to the absolute section, and set the section offset to expression, whic
h must be an absolute expression. You might use this as follows:
.struct 0
field1:
.struct field1 + 4
field2:
.struct field2 + 4
field3:
This would define the symbol field1 to have the value 0, the symbol field2 to
have the value 4, and the symbol field3 to have the value 8. Assembly would be
left in the absolute section, and you would need to use a .section directive
of some sort to change to some other section before further assembly.

7.78 .struct expression
切换到独立地址段,并用expression设定段的偏移量,expression必须是个纯粹的表达式
。您可以如下使用:
.struct 0
field1:
.struct field1 + 4
field2:
.struct field2 + 4
field3:

定义符号field1的值为0,符号field2的值为4,符号field3的值为8。这段汇编程序将保存
在独立地址段中,在进行下一步汇编前,您需要使用一个某种类型的.section命令,以切
换到相应的段。



7.79 .subsection name
This is one of the ELF section stack manipulation directives. The others are .
section (see Section 7.66 [Section], page 52),.pushsection (see Section 7.61 [
PushSection], page 50), .popsection (see Section 7.56 [PopSection], page 50),
and .previous (see Section 7.55 [Previous], page 49).
This directive replaces the current subsection with name. The current section
is not changed. The replaced subsection is put onto the section stack in place
of the then current top of stack subsection.

7.79 .subsection name
本命令是一个ELF段堆栈操作命令。其它的几个命令是(参见 7.66 [Section], 52页),
.pushsection (参见 7.61 [PushSection],50页), .popsection (参见 7.56 [PopSectio
n], 50页), and .previous (参见7.55 [Previous], 49页)。
本命令用name子段替换当前子段。当前段并不改变。被替换的子段入段堆栈,成为段堆栈
的新栈顶。



7.80 .symver
Use the .symver directive to bind symbols to specific version nodes within a s
ource file. This is only supported on ELF platforms, and is typically used whe
n assembling files to be linked into a shared library. There are cases where i
t may make sense to use this in objects to be bound into an application itself
so as to override a versioned symbol from a shared library.
For ELF targets, the .symver directive can be used like this:
.symver name, name2@nodename
If the symbol name is defined within the file being assembled, the .symver dir
ective effectively creates a symbol alias with the name name2@nodename, and in
fact the main reason that we just don’t try and create a regular alias is th
at the @ character isn’t permitted in symbol names. The name2 part of the nam
e is the actual name of the symbol by which it will be externally referenced.
The name name itself is merely a name of convenience that is used so that it i
s possible to have definitions for multiple versions of a function within a si
ngle source file, and so that the compiler can unambiguously know which versio
n of a function is being mentioned. The nodename portion of the alias should b
e the name of a node specified in the version script supplied to the linker wh
en building a shared library. If you are attempting to override a versioned sy
mbol from a shared library, then nodename should correspond to the nodename of
the symbol you are trying to override.
If the symbol name is not defined within the file being assembled, all referen
ces to name will be changed to name2@nodename. If no reference to name is made
, name2@nodename will be removed from the symbol table.
Another usage of the .symver directive is:
.symver name, name2@@nodename
In this case, the symbol name must exist and be defined within the file being
assembled. It is similar to name2@nodename. The difference is name2@@nodename
will also be used to resolve references to name2 by the linker.
The third usage of the .symver directive is:
.symver name, name2@@@nodename
When name is not defined within the file being assembled, it is treated as nam
e2@nodename. When name is defined within the file being assembled, the symbol
name, name, will be changed to name2@@nodename.

7.80 .symver
使用.symver命令把符号装订到在源文件里指定的节点。本命令只在ELF平台上可用,如果
当前汇编的文件被连接到一个共享库中时常常用到。有些情况下应该在目标文件中使用本
命令,把目标文件自我装订进某个应用软件中,从而取代共享库中旧版本符号。
对于ELF目标,.symver命令可以这样使用:
.symver name, name2@nodename
如果符号name的定义在当前正在汇编的文件中,这个.symver命令实际用name2@nodename创
建一个符号别名,而且我们不打算创建一个正规的别名,因为在符号名中是不允许存在‘
@’这个字符的。别名中name2才是符号的真正名字,外部访问是通过这个名字进行的。符
号自己的名字name仅仅为了使用上的方便,这样在同一个源文件中的一个函数才可能有多
个定义体;编译器才能够清楚当前使用的函数是哪个具体的定义。别名中的nodename部分
应是某个节点的名字,这个节点的名字是在建立共享库时,提供给连接器的版本脚本中指
定的。如果您想覆盖共享库中的旧版本符号,则nodename应该是将被取代符号的节点名。

如果符号name的定义不在当前正在汇编的文件中,则所有对name的访问都变为对name2@no
dename的访问。如果根本没有对name的访问,将会把name2@nodename从符号表中删除。

.symver命令的另一种用法:
.symver name, name2@@nodename
在这种情况下,符号name必须存在,并且它必须在当前正在汇编的文件中被定义。这类似
与name2@nodename。区别是name2@@nodename还被连接器用来解析对name2的访问。//注:
对name2的访问被转向到nodename
.symver命令的第3种用法:
.symver name, name2@@@nodename
如果name不是在当前正在汇编的文件中被定义的时候,对符号的处理就如同name2@nodena
me。如果name是当前正在汇编的文件中定义的,符号的名字name,会被转换为name2@@nod
ename。



7.81 .tag structname
This directive is generated by compilers to include auxiliary debugging inform
ation in the symbol table. It is only permitted inside .def/.endef pairs. Tags
are used to link structure definitions in the symbol table with instances of
those structures.
‘.tag’ is only used when generating COFF format output; when as is generatin
g b.out, it accepts this directive but ignores it.

7.81 .tag structname
本命令由编译器生成,用来在符号表中增加调试辅助的信息。本命令只允许在.def/.ende
f语句对内使用。标饰(tags)常用来连接符号表中的结构定义和该结构实例。
‘.tag’只能在生成COFF格式的输出文件时使用。当as生成b.out格式的输出文件时,接受
本命令但忽略之。



7.82 .text subsection
Tells as to assemble the following statements onto the end of the text subsect
ion numbered subsection, which is an absolute expression. If subsection is omi
tted, subsection number zero is used.

7.82 .text subsection
通知as把后续语句汇编到编号为subsection的正文子段的末尾,subsection是一个纯粹的
表达式。如果省略了参数subsection,则使用编号为0的子段。


7.83 .title "heading"
Use heading as the title (second line, immediately after the source file name
and page number) when generating assembly listings.
This directive affects subsequent pages, as well as the current page if it app
ears within ten lines of the top of a page.

7.83 .title "heading"
当生成汇编清单时,把heading作为标题使用(标题在第2行,紧跟在源文件名和页号后)

如果这个命令出现在某页的前10行中,它不但作用影响到后续的页,也同样影响到当前页



7.84 .type int (COFF version)
This directive, permitted only within .def/.endef pairs, records the integer i
nt as the type attribute of a symbol table entry.
‘.type’ is associated only with COFF format output; when as is configured fo
r b.out output, it accepts this directive but ignores it.

7.84 .type int (COFF 版本)
本命令紧允许在.def/.endef 命令对之间使用,把整数int作为类型属性记录进符号表表项

‘.type’只和COFF格式的输出有关,当as配置生成b.out输出格式时,as接受本命令但忽
略之。

7.85 .type name , type description (ELF version)
This directive is used to set the type of symbol name to be either a function
symbol or an object symbol. There are five different syntaxes supported for th
e type description field, in order to provide compatibility with various other
assemblers. The syntaxes supported are:

.type <name>,#function
.type <name>,#object

.type <name>,@function
.type <name>,@object

.type <name>,%function
.type <name>,%object

.type <name>,"function"
.type <name>,"object"

.type <name> STT_FUNCTION
.type <name> STT_OBJECT

7.85 .type name , type description (ELF 版本)
本命令经常用来设置符号name的类型(属性)为函数符号或是目标符号两者之一。type d
escription部分允许使用5种不同的语法,以兼容众多的汇编器。这些语法是:

.type <name>,#function
.type <name>,#object

.type <name>,@function
.type <name>,@object

.type <name>,%function
.type <name>,%object

.type <name>,"function"
.type <name>,"object"

.type <name> STT_FUNCTION
.type <name> STT_OBJECT


7.86 .uleb128 expressions
uleb128 stands for “unsigned little endian base 128.” This is a compact, var
iable length representation of numbers used by the DWARF symbolic debugging fo
rmat. See Section 7.73 [Sleb128], page 54.

7.86 .uleb128 expressions
uleb128代表“unsigned little endian base 128”(低地址结尾的无符号128位基数)。这
是一个紧凑的,变长的数字表示方法,当使用DWARF符号调试格式时使用。参见7.83 [Sle
b128], 54页。



7.87 .val addr
This directive, permitted only within .def/.endef pairs, records the address a
ddr as the value attribute of a symbol table entry.
‘.val’ is used only for COFF output; when as is configured for b.out, it acc
epts this directive but ignores it.

7.87 .val addr
本命令只能在.def/.endef命令对之间使用,把addr的地址作为值属性存入符号表的表项中

‘.val’命令只能在COFF输出时使用;当as被配置成生成b.out输出时,接受本命令但忽略
之。



7.88 .version "string"
This directive creates a .note section and places into it an ELF formatted not
e of type NT VERSION. The note’s name is set to string.

7.88 .version "string"
本命令创建一个.note段,并把一个NT VERSION类型ELF格式的note放入该.note段。Note的
名字被设置为string。



7.89 .vtable_entry table, offset
This directive finds or creates a symbol table and creates a VTABLE_ENTRY relo
cation for it with an addend of offset.

7.89 .vtable_entry table, offset
本命令寻找或创建一个符号表,并用offset作偏移量的增量,为此符号表产生一个VTABLE
_ENTRY重定位。



7.90 .vtable_inherit child, parent
This directive finds the symbol child and finds or creates the symbol parent a
nd then creates a VTABLE_INHERIT relocation for the parent whose addend is the
value of the child symbol. As a special case the parent name of 0 is treated
as refering the *ABS* section.

7.90 .vtable_inherit child, parent
本命令寻找符号child, 并寻找或创建符号parent,为符号parent产生一个VTABLE_INHERI
T重定位,parent的偏移量增量为符号child的值。一个特例,如果parent的名字为0,则将
它交给*ABS*段处理。



7.91 .weak names
This directive sets the weak attribute on the comma separated list of symbol n
ames. If the symbols do not already exist, they will be created.

7.91 .weak names
本命令设置names中每个符号(由逗号分隔)的weak属性。如果这些符号尚不存在,则创建
这些符号。


7.92 .word expressions
This directive expects zero or more expressions, of any section, separated by
commas.
The size of the number emitted, and its byte order, depend on what target comp
uter the assembly is for.
Warning: Special Treatment to support Compilers
Machines with a 32-bit address space, but that do less than 32-bit addressing,
require the following special treatment. If the machine of interest to you do
es 32-bit addressing (or doesn’t require it; see Chapter 8 [Machine Dependenc
ies], page 61), you can ignore this issue.
In order to assemble compiler output into something that works, as occasionall
y does strange things to ‘.word’ directives. Directives of the form ‘.word
sym1-sym2’ are often emitted by compilers as part of jump tables. Therefore,
when as assembles a directive of the form ‘.word sym1-sym2’, and the differe
nce between sym1 and sym2 does not fit in 16 bits, as creates a secondary jump
table, immediately before the next label. This secondary jump table is preced
ed by a short-jump to the first byte after the secondary table. This short-jum
p prevents the flow of control from accidentally falling into the new table. I
nside the table is a long-jump to sym2. The original ‘.word’ contains sym1 m
inus the address of the long-jump to sym2.
If there were several occurrences of ‘.word sym1-sym2’ before the secondary
jump table, all of them are adjusted. If there was a ‘.word sym3-sym4’, that
also did not fit in sixteen bits, a long-jump to sym4 is included in the seco
ndary jump table, and the .word directives are adjusted to contain sym3 minus
the address of the long-jump to sym4; and so on, for as many entries in the or
iginal jump table as necessary.

7.92 .word expressions
本命令可不带表达式或带多个表达式,这些表达式可以属于任意段,每个表达式由逗号分
隔。
汇编生成的数字的大小,字节顺序视生成程序运行的目标机器而定。
警告:支持编译器的特殊处理
有些机器具有32位地址空间,但不能完全进行32位寻址,需要下列的特殊处理。如果您关
心的机器能够进行32位寻址,(或者根本不需要32位寻址;见第8章[机器相关性],61页)
则可以忽略这个问题。
为了使由编译器产生源码的汇编结果能够正确地运行,as偶尔会对'.word'命令进行些奇怪
的操作。编译器在跳转表部分经常生成类似'.word sym1-sym2'形式的命令。所以,当as汇
编一条形如'.word sym1-sym2'的命令,且sym1和sym2之间的偏移量大于16位时,as会在下
个标签前创建一个'次级跳转表',在'次级跳转表'前面加插上一个短-跳转指令,这个短-跳
转指令的目的地址是'次级跳转表'之后的第一个字节。 这个短跳转防止控制流程意外地落
入新的跳转表(次级跳转表)。在'次级跳转表'内是个目的地址为sym2的长-跳转指令。原
来的'.word'命令调整为sym1减去到sym2的长-跳转指令地址,。
如果在次级跳转表前出现了几个'.word sym1-sym2',这些命令都要进行调整。如果存在一
个'.word sym3-sym4',且地址差也大与16位,次级跳转表中将包含一个至sym4的长-跳转
指令,且.word命令将被调整为包含sym3减去到sym4长-跳转指令的地址; 如是类推,处理
原始跳转表中的需要处理的各个表项。



7.93 Deprecated Directives
One day these directives won’t work. They are included for compatibility with
older assemblers.
.abort
.line

7.93 不赞成使用的命令
将来下列命令可能不再被支持,它们的存在只是为了与老版本的汇编器相兼容。
.abort
.line

AT&T汇编指令

GAS中每个操作都是有一个字符的后缀,表明操作数的大小。

C声明

GAS后缀

大小(字节)

char

b

1

short

w

2

(unsigned) int / long / char*

l

4

float

s

4

double

l

8

long double

t

10/12

注意:GAL使用后缀“l”同时表示4字节整数和8字节双精度浮点数,这不会产生歧义因为浮点数使用的是完全不同的指令和寄存器。

 

 

操作数格式:

格式

操作数值

名称

样例(GAS = C语言)

$Imm

Imm

立即数寻址

$1 = 1

Ea

R[Ea]

寄存器寻址

%eax = eax

Imm

M[Imm]

绝对寻址

0x104 = *0x104

Ea

M[R[Ea]]

间接寻址

%eax= *eax

Imm(Ea)

M[Imm+R[Ea]]

(基址+偏移量)寻址

4(%eax) = *(4+eax)

Ea,Eb

M[R[Ea]+R[Eb]]

变址

(%eax,%ebx) = *(eax+ebx)

ImmEa,Eb

M[Imm+R[Ea]+R[Eb]]

寻址

9(%eax,%ebx)= *(9+eax+ebx)

(,Ea,s)

M[R[Ea]*s]

伸缩化变址寻址

(,%eax,4)= *(eax*4)

Imm(,Ea,s)

M[Imm+R[Ea]*s]

伸缩化变址寻址

0xfc(,%eax,4)= *(0xfc+eax*4)

(Ea,Eb,s)

M(R[Ea]+R[Eb]*s)

伸缩化变址寻址

(%eax,%ebx,4) = *(eax+ebx*4)

Imm(Ea,Eb,s)

M(Imm+R[Ea]+R[Eb]*s)

伸缩化变址寻址

8(%eax,%ebx,4) = *(8+eax+ebx*4)

注:M[xx]表示在存储器中xx地址的值,R[xx]表示寄存器xx的值,这种表示方法将寄存器、内存都看出一个大数组的形式。

 

 

数据传送指令:

指令

效果

描述

movl S,D

D <-- S

传双字

movw S,D

D <-- S

传字

movb S,D

D <-- S

传字节

movsbl S,D

D <-- 符号扩展S

符号位填充(字节->双字)

movzbl S,D

D <-- 零扩展S

零填充(字节->双字)

pushl S

R[%esp] <-- R[%esp] – 4;

M[R[%esp]] <-- S

压栈

popl D

D <-- M[R[%esp]]

R[%esp] <-- R[%esp] + 4;

出栈

注:均假设栈往低地址扩展。

 

 

算数和逻辑操作地址:

指令

效果

描述

leal S,D

D = &S

movl地版,S地址入DD仅能是寄存器

incl D

D++

1

decl D

D--

1

negl D

D = -D

取负

notl D

D = ~D

取反

addl S,D

D = D + S

subl S,D

D = D – S

imull S,D

D = D*S

xorl S,D

D = D ^ S

异或

orl S,D

D = D | S

andl S,D

D = D & S

sall k,D

D = D << k

左移

shll k,D

D = D << k

左移(sall)

sarl k,D

D = D >> k

算数右移

shrl k,D

D = D >> k

逻辑右移

 

 

特殊算术操作:

指令

效果

描述

imull S

R[%edx]:R[%eax] = S * R[%eax]

无符号64位乘

mull S

R[%edx]:R[%eax] = S * R[%eax]

有符号64位乘

cltd S

R[%edx]:R[%eax] = 符号位扩展R[%eax]

转换为4字节

idivl S

R[%edx] = R[%edx]:R[%eax] % S;

R[%eax] = R[%edx]:R[%eax] / S;

有符号除法,保存余数和商

divl S

R[%edx] = R[%edx]:R[%eax] % S;

R[%eax] = R[%edx]:R[%eax] / S;

无符号除法,保存余数和商

注:64位数通常存储为,高32位放在edx,低32位放在eax

 

 

条件码:

条件码寄存器描述了最近的算数或逻辑操作的属性。

CF:进位标志,最高位产生了进位,可用于检查无符号数溢出。

OF:溢出标志,二进制补码溢出——正溢出或负溢出。

ZF:零标志,结果为0

SF:符号标志,操作结果为负。

 

 

比较指令:

指令

基于

描述

cmpb S2,S1

S1 – S2

比较字节,差关系

testb S2,S1

S1 & S2

测试字节,与关系

cmpw S2,S1

S1 – S2

比较字,差关系

testw S2,S1

S1 & S2

测试字,与关系

cmpl S2,S1

S1 – S2

比较双字,差关系

testl S2,S1

S1 & S2

测试双字,与关系

 

 

访问条件码指令:

指令

同义名

效果

设置条件

sete D

setz

D = ZF

相等/

setne D

setnz

D = ~ZF

不等/非零

sets D

 

D = SF

负数

setns D

 

D = ~SF

非负数

setg D

setnle

D = ~(SF ^OF) & ZF

大于(有符号>

setge D

setnl

D = ~(SF ^OF)

小于等于(有符号>=)

setl D

setnge

D = SF ^ OF

小于(有符号<)

setle D

setng

D = (SF ^ OF) | ZF

小于等于(有符号<=)

seta D

setnbe

D = ~CF & ~ZF

超过(无符号>)

setae D

setnb

D = ~CF

超过或等于(无符号>=)

setb D

setnae

D = CF

低于(无符号<)

setbe D

setna

D = CF | ZF

低于或等于(无符号<=)

 

 

跳转指令:

指令

同义名

跳转条件

描述

jmp   Label

 

1

直接跳转

jmp   *Operand

 

1

间接跳转

je     Label

jz

ZF

等于/

jne    Label

jnz

~ZF

不等/非零

js     Label

 

SF

负数

jnz    Label

 

~SF

非负数

jg     Label

jnle

~(SF^OF) & ~ZF

大于(有符号>)

jge    Label

jnl

~(SF ^ OF)

大于等于(有符号>=)

jl     Label

jnge

SF ^ OF

小于(有符号<

jle     Label

jng

(SF ^ OF) | ZF

小于等于(有符号<=)

ja     Label

jnbe

~CF & ~ZF

超过(无符号>)

jae    Label

jnb

~CF

超过或等于(无符号>=)

jb     Label

jnae

CF

低于(无符号<)

jbe    Label

jna

CF | ZF

低于或等于(无符号<=)

 

 

转移控制指令:(函数调用):

指令

描述

call    Label

过程调用,返回地址入栈,跳转到调用过程起始处,返回地址是call后面那条指令的地址

call    *Operand

leave

为返回准备好栈,为ret准备好栈,主要是弹出函数内的栈使用及%ebp

 

 

GCCC中潜入汇编代码:

asm( code-string [:output-list [ : input-list [ :overwrite-list]]]);

注意,后面的参数(如overwrite-list)如果为空则不要相应的“:”,而如果前面参数(如output-list)为空则需要用“:”占位。

如:

asm ("..."

    :                    //output需要占位

    : "r" (src)       //后面的Overwrites不能写,我测试的结果是写了编译不过

};


如:

Int ok_umul(unsigned x,unsigned y,unsigned *dest)

{

  int result;

asm(“movl %2 , %%eax; mull %3; movl %%eax,%0;\

           setae %dl; movzbl %%dl,%1”

           :  “=r” (*dest)  ,  “=r” (result)         //output

           :  “r” (x)  ,  “r” (y)                         //inputs

           :  “%ebx”  , “%edx”                        //Overwrites

);

 

return result;

}

我们用%0--%n表示输入的参数,”r”表示整数寄存器,”=”表示对其进行了赋值。%eax要写成%%eax,这是c语言字符串的规则,别忘了code-string就是一个c语言的字符串。

Linux 汇编语言开发指南

汇编语言的优点是速度快,可以直接对硬件进行操作,这对诸如图形处理等关键应用是非常重要的。Linux 是一个用 C 语言开发的操作系统,这使得很多程序员开始忘记在 Linux 中还可以直接使用汇编这一底层语言来优化程序的性能。本文为那些在Linux 平台上编写汇编代码的程序员提供指南,介绍 Linux 汇编语言的语法格式和开发工具,并辅以具体的例子讲述如何开发实用的Linux 汇编程序。

一、简介

作为最基本的编程语言之一,汇编语言虽然应用的范围不算很广,但重要性却勿庸置疑,因为它能够完成许多其它语言所无法完成的功能。就拿 Linux 内核来讲,虽然绝大部分代码是用 C 语言编写的,但仍然不可避免地在某些关键地方使用了汇编代码,其中主要是在 Linux 的启动部分。由于这部分代码与硬件的关系非常密切,即使是 C 语言也会有些力不从心,而汇编语言则能够很好扬长避短,最大限度地发挥硬件的性能。

大多数情况下 Linux 程序员不需要使用汇编语言,因为即便是硬件驱动这样的底层程序在 Linux 操作系统中也可以用完全用 C 语言来实现,再加上 GCC 这一优秀的编译器目前已经能够对最终生成的代码进行很好的优化,的确有足够的理由让我们可以暂时将汇编语言抛在一边了。但实现情况是 Linux 程序员有时还是需要使用汇编,或者不得不使用汇编,理由很简单:精简、高效和 libc 无关性。假设要移植 Linux 到某一特定的嵌入式硬件环境下,首先必然面临如何减少系统大小、提高执行效率等问题,此时或许只有汇编语言能帮上忙了。

汇编语言直接同计算机的底层软件甚至硬件进行交互,它具有如下一些优点:

  • 能够直接访问与硬件相关的存储器或 I/O 端口;
  • 能够不受编译器的限制,对生成的二进制代码进行完全的控制;
  • 能够对关键代码进行更准确的控制,避免因线程共同访问或者硬件设备共享引起的死锁;
  • 能够根据特定的应用对代码做最佳的优化,提高运行速度;
  • 能够最大限度地发挥硬件的功能。

同时还应该认识到,汇编语言是一种层次非常低的语言,它仅仅高于直接手工编写二进制的机器指令码,因此不可避免地存在一些缺点:

  • 编写的代码非常难懂,不好维护;
  • 很容易产生 bug,难于调试;
  • 只能针对特定的体系结构和处理器进行优化;
  • 开发效率很低,时间长且单调。

Linux 下用汇编语言编写的代码具有两种不同的形式。第一种是完全的汇编代码,指的是整个程序全部用汇编语言编写。尽管是完全的汇编代码,Linux 平台下的汇编工具也吸收了 C 语言的长处,使得程序员可以使用 #include、#ifdef 等预处理指令,并能够通过宏定义来简化代码。第二种是内嵌的汇编代码,指的是可以嵌入到C语言程序中的汇编代码片段。虽然 ANSI 的 C 语言标准中没有关于内嵌汇编代码的相应规定,但各种实际使用的 C 编译器都做了这方面的扩充,这其中当然就包括 Linux 平台下的 GCC。



回页首

二、Linux 汇编语法格式

绝大多数 Linux 程序员以前只接触过DOS/Windows 下的汇编语言,这些汇编代码都是 Intel 风格的。但在 Unix 和 Linux 系统中,更多采用的还是 AT&T 格式,两者在语法格式上有着很大的不同:

  1. 在 AT&T 汇编格式中,寄存器名要加上 '%' 作为前缀;而在 Intel 汇编格式中,寄存器名不需要加前缀。例如:

    AT&T 格式 Intel 格式
    pushl %eax push eax
  2. 在 AT&T 汇编格式中,用 '$' 前缀表示一个立即操作数;而在 Intel 汇编格式中,立即数的表示不用带任何前缀。例如:

    AT&T 格式 Intel 格式
    pushl $1 push 1
  3. AT&T 和 Intel 格式中的源操作数和目标操作数的位置正好相反。在 Intel 汇编格式中,目标操作数在源操作数的左边;而在 AT&T 汇编格式中,目标操作数在源操作数的右边。例如:

    AT&T 格式 Intel 格式
    addl $1, %eax add eax, 1
  4. 在 AT&T 汇编格式中,操作数的字长由操作符的最后一个字母决定,后缀'b'、'w'、'l'分别表示操作数为字节(byte,8 比特)、字(word,16 比特)和长字(long,32比特);而在 Intel 汇编格式中,操作数的字长是用 "byte ptr" 和 "word ptr" 等前缀来表示的。例如:

    AT&T 格式 Intel 格式
    movb val, %al mov al, byte ptr val
  5. 在 AT&T 汇编格式中,绝对转移和调用指令(jump/call)的操作数前要加上'*'作为前缀,而在 Intel 格式中则不需要。
  6. 远程转移指令和远程子调用指令的操作码,在 AT&T 汇编格式中为 "ljump" 和 "lcall",而在 Intel 汇编格式中则为 "jmp far" 和 "call far",即:

    AT&T 格式 Intel 格式
    ljump $section, $offset jmp far section:offset
    lcall $section, $offset call far section:offset

    与之相应的远程返回指令则为:

    AT&T 格式 Intel 格式
    lret $stack_adjust ret far stack_adjust
  7. 在 AT&T 汇编格式中,内存操作数的寻址方式是

    section:disp(base, index, scale)
    

    而在 Intel 汇编格式中,内存操作数的寻址方式为:

    section:[base + index*scale + disp]
    

    由于 Linux 工作在保护模式下,用的是 32 位线性地址,所以在计算地址时不用考虑段基址和偏移量,而是采用如下的地址计算方法:

    disp + base + index * scale
    

    下面是一些内存操作数的例子:

    AT&T 格式 Intel 格式
    movl -4(%ebp), %eax mov eax, [ebp - 4]
    movl array(, %eax, 4), %eax mov eax, [eax*4 + array]
    movw array(%ebx, %eax, 4), %cx mov cx, [ebx + 4*eax + array]
    movb $4, %fs:(%eax) mov fs:eax, 4



回页首

三、Hello World!

真不知道打破这个传统会带来什么样的后果,但既然所有程序设计语言的第一个例子都是在屏幕上打印一个字符串 "Hello World!",那我们也以这种方式来开始介绍 Linux 下的汇编语言程序设计。

在 Linux 操作系统中,你有很多办法可以实现在屏幕上显示一个字符串,但最简洁的方式是使用 Linux 内核提供的系统调用。使用这种方法最大的好处是可以直接和操作系统的内核进行通讯,不需要链接诸如 libc 这样的函数库,也不需要使用 ELF 解释器,因而代码尺寸小且执行速度快。

Linux 是一个运行在保护模式下的 32 位操作系统,采用 flat memory 模式,目前最常用到的是 ELF 格式的二进制代码。一个 ELF 格式的可执行程序通常划分为如下几个部分:.text、.data 和 .bss,其中 .text 是只读的代码区,.data 是可读可写的数据区,而 .bss 则是可读可写且没有初始化的数据区。代码区和数据区在 ELF 中统称为 section,根据实际需要你可以使用其它标准的 section,也可以添加自定义 section,但一个 ELF 可执行程序至少应该有一个 .text 部分。 下面给出我们的第一个汇编程序,用的是 AT&T 汇编语言格式:

例1. AT&T 格式

#hello.s 
.data                    # 数据段声明
        msg : .string "Hello, world!\\n" # 要输出的字符串
        len = . - msg                   # 字串长度
.text                    # 代码段声明
.global _start           # 指定入口函数
        
_start:                  # 在屏幕上显示一个字符串
        movl $len, %edx  # 参数三:字符串长度
        movl $msg, %ecx  # 参数二:要显示的字符串
        movl $1, %ebx    # 参数一:文件描述符(stdout) 
        movl $4, %eax    # 系统调用号(sys_write) 
        int  $0x80       # 调用内核功能
        
                         # 退出程序
        movl $0,%ebx     # 参数一:退出代码
        movl $1,%eax     # 系统调用号(sys_exit) 
        int  $0x80       # 调用内核功能

初次接触到 AT&T 格式的汇编代码时,很多程序员都认为太晦涩难懂了,没有关系,在 Linux 平台上你同样可以使用 Intel 格式来编写汇编程序:

例2. Intel 格式

; hello.asm 
section .data            ; 数据段声明
        msg db "Hello, world!", 0xA     ; 要输出的字符串
        len equ $ - msg                 ; 字串长度
section .text            ; 代码段声明
global _start            ; 指定入口函数
_start:                  ; 在屏幕上显示一个字符串
        mov edx, len     ; 参数三:字符串长度
        mov ecx, msg     ; 参数二:要显示的字符串
        mov ebx, 1       ; 参数一:文件描述符(stdout) 
        mov eax, 4       ; 系统调用号(sys_write) 
        int 0x80         ; 调用内核功能
                         ; 退出程序
        mov ebx, 0       ; 参数一:退出代码
        mov eax, 1       ; 系统调用号(sys_exit) 
        int 0x80         ; 调用内核功能

上面两个汇编程序采用的语法虽然完全不同,但功能却都是调用 Linux 内核提供的 sys_write 来显示一个字符串,然后再调用 sys_exit 退出程序。在 Linux 内核源文件 include/asm-i386/unistd.h 中,可以找到所有系统调用的定义。



回页首

四、Linux 汇编工具

Linux 平台下的汇编工具虽然种类很多,但同 DOS/Windows 一样,最基本的仍然是汇编器、连接器和调试器。

1.汇编器

汇编器(assembler)的作用是将用汇编语言编写的源程序转换成二进制形式的目标代码。Linux 平台的标准汇编器是 GAS,它是 GCC 所依赖的后台汇编工具,通常包含在 binutils 软件包中。GAS 使用标准的 AT&T 汇编语法,可以用来汇编用 AT&T 格式编写的程序:

[xiaowp@gary code]$ as -o hello.o hello.s

Linux 平台上另一个经常用到的汇编器是 NASM,它提供了很好的宏指令功能,并能够支持相当多的目标代码格式,包括 bin、a.out、coff、elf、rdf 等。NASM 采用的是人工编写的语法分析器,因而执行速度要比 GAS 快很多,更重要的是它使用的是 Intel 汇编语法,可以用来编译用 Intel 语法格式编写的汇编程序:

[xiaowp@gary code]$ nasm -f elf hello.asm

2.链接器

由汇编器产生的目标代码是不能直接在计算机上运行的,它必须经过链接器的处理才能生成可执行代码。链接器通常用来将多个目标代码连接成一个可执行代码,这样可以先将整个程序分成几个模块来单独开发,然后才将它们组合(链接)成一个应用程序。 Linux 使用 ld 作为标准的链接程序,它同样也包含在 binutils 软件包中。汇编程序在成功通过 GAS 或 NASM 的编译并生成目标代码后,就可以使用 ld 将其链接成可执行程序了:

[xiaowp@gary code]$ ld -s -o hello hello.o

3.调试器

有人说程序不是编出来而是调出来的,足见调试在软件开发中的重要作用,在用汇编语言编写程序时尤其如此。Linux 下调试汇编代码既可以用 GDB、DDD 这类通用的调试器,也可以使用专门用来调试汇编代码的 ALD(Assembly Language Debugger)。

从调试的角度来看,使用 GAS 的好处是可以在生成的目标代码中包含符号表(symbol table),这样就可以使用 GDB 和 DDD 来进行源码级的调试了。要在生成的可执行程序中包含符号表,可以采用下面的方式进行编译和链接:

[xiaowp@gary code]$ as --gstabs -o hello.o hello.s
[xiaowp@gary code]$ ld -o hello hello.o

执行 as 命令时带上参数 --gstabs 可以告诉汇编器在生成的目标代码中加上符号表,同时需要注意的是,在用 ld 命令进行链接时不要加上 -s 参数,否则目标代码中的符号表在链接时将被删去。

在 GDB 和 DDD 中调试汇编代码和调试 C 语言代码是一样的,你可以通过设置断点来中断程序的运行,查看变量和寄存器的当前值,并可以对代码进行单步跟踪。图1 是在 DDD 中调试汇编代码时的情景:


图1 用 DDD 中调试汇编程序

汇编程序员通常面对的都是一些比较苛刻的软硬件环境,短小精悍的ALD可能更能符合实际的需要,因此下面主要介绍一下如何用ALD来调试汇编程序。首先在命令行方式下执行ald命令来启动调试器,该命令的参数是将要被调试的可执行程序:

[xiaowp@gary doc]$ ald hello
Assembly Language Debugger 0.1.3
Copyright (C) 2000-2002 Patrick Alken
hello: ELF Intel 80386 (32 bit), LSB, Executable, Version 1 (current)
Loading debugging symbols...(15 symbols loaded)
ald>

当 ALD 的提示符出现之后,用 disassemble 命令对代码段进行反汇编:

ald> disassemble -s .text
Disassembling section .text (0x08048074 - 0x08048096)
08048074  BA0F000000                 mov edx, 0xf
08048079  B998900408                 mov ecx, 0x8049098
0804807E  BB01000000                 mov ebx, 0x1
08048083  B804000000                 mov eax, 0x4
08048088  CD80                       int 0x80
0804808A  BB00000000                 mov ebx, 0x0
0804808F  B801000000                 mov eax, 0x1
08048094  CD80                       int 0x80

上述输出信息的第一列是指令对应的地址码,利用它可以设置在程序执行时的断点:

ald> break 0x08048088
Breakpoint 1 set for 0x08048088

断点设置好后,使用 run 命令开始执行程序。ALD 在遇到断点时将自动暂停程序的运行,同时会显示所有寄存器的当前值:

ald> run
Starting program: hello
Breakpoint 1 encountered at 0x08048088
eax = 0x00000004 ebx = 0x00000001 ecx = 0x08049098 edx = 0x0000000F
esp = 0xBFFFF6C0 ebp = 0x00000000 esi = 0x00000000 edi = 0x00000000
ds  = 0x0000002B es  = 0x0000002B fs  = 0x00000000 gs  = 0x00000000
ss  = 0x0000002B cs  = 0x00000023 eip = 0x08048088 eflags = 0x00000246
Flags: PF ZF IF
08048088  CD80                       int 0x80

如果需要对汇编代码进行单步调试,可以使用 next 命令:

ald> next
Hello, world!
eax = 0x0000000F ebx = 0x00000000 ecx = 0x08049098 edx = 0x0000000F
esp = 0xBFFFF6C0 ebp = 0x00000000 esi = 0x00000000 edi = 0x00000000
ds  = 0x0000002B es  = 0x0000002B fs  = 0x00000000 gs  = 0x00000000
ss  = 0x0000002B cs  = 0x00000023 eip = 0x0804808F eflags = 0x00000346
Flags: PF ZF TF IF
0804808F  B801000000                 mov eax, 0x1

若想获得 ALD 支持的所有调试命令的详细列表,可以使用 help 命令:

ald> help
Commands may be abbreviated.
If a blank command is entered, the last command is repeated.
Type `help <command>' for more specific information on <command>.
General commands
attach         clear          continue       detach         disassemble
enter          examine        file           help           load
next           quit           register       run            set
step           unload         window         write
Breakpoint related commands
break          delete         disable        enable         ignore
lbreak         tbreak



回页首

五、系统调用

即便是最简单的汇编程序,也难免要用到诸如输入、输出以及退出等操作,而要进行这些操作则需要调用操作系统所提供的服务,也就是系统调用。除非你的程序只完成加减乘除等数学运算,否则将很难避免使用系统调用,事实上除了系统调用不同之外,各种操作系统的汇编编程往往都是很类似的。

在 Linux 平台下有两种方式来使用系统调用:利用封装后的 C 库(libc)或者通过汇编直接调用。其中通过汇编语言来直接调用系统调用,是最高效地使用 Linux 内核服务的方法,因为最终生成的程序不需要与任何库进行链接,而是直接和内核通信。

和 DOS 一样,Linux 下的系统调用也是通过中断(int 0x80)来实现的。在执行 int 80 指令时,寄存器 eax 中存放的是系统调用的功能号,而传给系统调用的参数则必须按顺序放到寄存器 ebx,ecx,edx,esi,edi 中,当系统调用完成之后,返回值可以在寄存器 eax 中获得。

所有的系统调用功能号都可以在文件 /usr/include/bits/syscall.h 中找到,为了便于使用,它们是用 SYS_<name> 这样的宏来定义的,如 SYS_write、SYS_exit 等。例如,经常用到的 write 函数是如下定义的:

ssize_t write(int fd, const void *buf, size_t count);

该函数的功能最终是通过 SYS_write 这一系统调用来实现的。根据上面的约定,参数 fb、buf 和 count 分别存在寄存器 ebx、ecx 和 edx 中,而系统调用号 SYS_write 则放在寄存器 eax 中,当 int 0x80 指令执行完毕后,返回值可以从寄存器 eax 中获得。

或许你已经发现,在进行系统调用时至多只有 5 个寄存器能够用来保存参数,难道所有系统调用的参数个数都不超过 5 吗?当然不是,例如 mmap 函数就有 6 个参数,这些参数最后都需要传递给系统调用 SYS_mmap:

void  *  mmap(void *start, size_t length, int prot , int flags, int fd, off_t offset);

当一个系统调用所需的参数个数大于 5 时,执行int 0x80 指令时仍需将系统调用功能号保存在寄存器 eax 中,所不同的只是全部参数应该依次放在一块连续的内存区域里,同时在寄存器 ebx 中保存指向该内存区域的指针。系统调用完成之后,返回值仍将保存在寄存器 eax 中。

由于只是需要一块连续的内存区域来保存系统调用的参数,因此完全可以像普通的函数调用一样使用栈(stack)来传递系统调用所需的参数。但要注意一点,Linux 采用的是 C 语言的调用模式,这就意味着所有参数必须以相反的顺序进栈,即最后一个参数先入栈,而第一个参数则最后入栈。如果采用栈来传递系统调用所需的参数,在执行int 0x80 指令时还应该将栈指针的当前值复制到寄存器 ebx中。



回页首

六、命令行参数

在 Linux 操作系统中,当一个可执行程序通过命令行启动时,其所需的参数将被保存到栈中:首先是 argc,然后是指向各个命令行参数的指针数组 argv,最后是指向环境变量的指针数据 envp。在编写汇编语言程序时,很多时候需要对这些参数进行处理,下面的代码示范了如何在汇编代码中进行命令行参数的处理:

例3. 处理命令行参数

# args.s
.text
.globl _start
        
_start:
        popl	%ecx		# argc
vnext:
        popl	%ecx		# argv
        test 	%ecx, %ecx      # 空指针表明结束
        jz	exit
        movl	%ecx, %ebx
        xorl	%edx, %edx
strlen:
        movb	(%ebx), %al
        inc	%edx
        inc	%ebx
        test	%al, %al
        jnz	strlen
        movb	$10, -1(%ebx)
        movl	$4, %eax        # 系统调用号(sys_write) 
        movl	$1, %ebx        # 文件描述符(stdout) 
        int	$0x80
        jmp	vnext
exit:
        movl	$1,%eax         # 系统调用号(sys_exit) 
        xorl	%ebx, %ebx      # 退出代码
        int 	$0x80
		
        ret



回页首

七、GCC 内联汇编

用汇编编写的程序虽然运行速度快,但开发速度非常慢,效率也很低。如果只是想对关键代码段进行优化,或许更好的办法是将汇编指令嵌入到 C 语言程序中,从而充分利用高级语言和汇编语言各自的特点。但一般来讲,在 C 代码中嵌入汇编语句要比"纯粹"的汇编语言代码复杂得多,因为需要解决如何分配寄存器,以及如何与C代码中的变量相结合等问题。

GCC 提供了很好的内联汇编支持,最基本的格式是:

__asm__("asm statements");

例如:

__asm__("nop"); 

如果需要同时执行多条汇编语句,则应该用"\\n\\t"将各个语句分隔开,例如:

__asm__( "pushl %%eax \\n\\t"
         "movl $0, %%eax \\n\\t"
         "popl %eax");

通常嵌入到 C 代码中的汇编语句很难做到与其它部分没有任何关系,因此更多时候需要用到完整的内联汇编格式:

__asm__("asm statements" : outputs : inputs : registers-modified);

插入到 C 代码中的汇编语句是以":"分隔的四个部分,其中第一部分就是汇编代码本身,通常称为指令部,其格式和在汇编语言中使用的格式基本相同。指令部分是必须的,而其它部分则可以根据实际情况而省略。

在将汇编语句嵌入到C代码中时,操作数如何与C代码中的变量相结合是个很大的问题。GCC采用如下方法来解决这个问题:程序员提供具体的指令,而对寄存器的使用则只需给出"样板"和约束条件就可以了,具体如何将寄存器与变量结合起来完全由GCC和GAS来负责。

在GCC内联汇编语句的指令部中,加上前缀'%'的数字(如%0,%1)表示的就是需要使用寄存器的"样板"操作数。指令部中使用了几个样板操作数,就表明有几个变量需要与寄存器相结合,这样GCC和GAS在编译和汇编时会根据后面给定的约束条件进行恰当的处理。由于样板操作数也使用'%'作为前缀,因此在涉及到具体的寄存器时,寄存器名前面应该加上两个'%',以免产生混淆。

紧跟在指令部后面的是输出部,是规定输出变量如何与样板操作数进行结合的条件,每个条件称为一个"约束",必要时可以包含多个约束,相互之间用逗号分隔开就可以了。每个输出约束都以'='号开始,然后紧跟一个对操作数类型进行说明的字后,最后是如何与变量相结合的约束。凡是与输出部中说明的操作数相结合的寄存器或操作数本身,在执行完嵌入的汇编代码后均不保留执行之前的内容,这是GCC在调度寄存器时所使用的依据。

输出部后面是输入部,输入约束的格式和输出约束相似,但不带'='号。如果一个输入约束要求使用寄存器,则GCC在预处理时就会为之分配一个寄存器,并插入必要的指令将操作数装入该寄存器。与输入部中说明的操作数结合的寄存器或操作数本身,在执行完嵌入的汇编代码后也不保留执行之前的内容。

有时在进行某些操作时,除了要用到进行数据输入和输出的寄存器外,还要使用多个寄存器来保存中间计算结果,这样就难免会破坏原有寄存器的内容。在GCC内联汇编格式中的最后一个部分中,可以对将产生副作用的寄存器进行说明,以便GCC能够采用相应的措施。

下面是一个内联汇编的简单例子:

例4.内联汇编

/* inline.c */
int main()
{
    int a = 10, b = 0;
    __asm__ __volatile__("movl %1, %%eax;\\n\\r"
                         "movl %%eax, %0;"
                         :"=r"(b)      /* 输出 */    
                         :"r"(a)       /* 输入 */
                         :"%eax");     /* 不受影响的寄存器 */
    
    printf("Result: %d, %d\\n", a, b);
}

上面的程序完成将变量a的值赋予变量b,有几点需要说明:

  • 变量b是输出操作数,通过%0来引用,而变量a是输入操作数,通过%1来引用。
  • 输入操作数和输出操作数都使用r进行约束,表示将变量a和变量b存储在寄存器中。输入约束和输出约束的不同点在于输出约束多一个约束修饰符'='。
  • 在内联汇编语句中使用寄存器eax时,寄存器名前应该加两个'%',即%%eax。内联汇编中使用%0、%1等来标识变量,任何只带一个'%'的标识符都看成是操作数,而不是寄存器。
  • 内联汇编语句的最后一个部分告诉GCC它将改变寄存器eax中的值,GCC在处理时不应使用该寄存器来存储任何其它的值。
  • 由于变量b被指定成输出操作数,当内联汇编语句执行完毕后,它所保存的值将被更新。

在内联汇编中用到的操作数从输出部的第一个约束开始编号,序号从0开始,每个约束记数一次,指令部要引用这些操作数时,只需在序号前加上'%'作为前缀就可以了。需要注意的是,内联汇编语句的指令部在引用一个操作数时总是将其作为32位的长字使用,但实际情况可能需要的是字或字节,因此应该在约束中指明正确的限定符:

限定符 意义
"m"、"v"、"o" 内存单元
"r" 任何寄存器
"q" 寄存器eax、ebx、ecx、edx之一
"i"、"h" 直接操作数
"E"和"F" 浮点数
"g" 任意
"a"、"b"、"c"、"d" 分别表示寄存器eax、ebx、ecx和edx
"S"和"D" 寄存器esi、edi
"I" 常数(0至31)



回页首

八、小结

Linux操作系统是用C语言编写的,汇编只在必要的时候才被人们想到,但它却是减少代码尺寸和优化代码性能的一种非常重要的手段,特别是在与硬件直接交互的时候,汇编可以说是最佳的选择。Linux提供了非常优秀的工具来支持汇编程序的开发,使用GCC的内联汇编能够充分地发挥C语言和汇编语言各自的优点。

参考资料

  1. 在网站 http://linuxassembly.org上可以找到大量的Linux汇编资源。
  2. 软件包binutils提供了as和ld等实用工具,其相关信息可以在网站 http://sources.redhat.com/binutils/上找到。
  3. NASM是Intel格式的汇编器,其相关信息可以在网站 http://nasm.sourceforge.net上找到。
  4. ALD是一个短小精悍的汇编调试器,其相关信息可以在网站 http://dunx1.irt.drexel.edu/~psa22/ald.html上找到。
  5. intel2gas是一个能够将Intel汇编格式转换成AT&T汇编格式的小工具,其相关信息可以在网站 http://www.niksula.cs.hut.fi/~mtiihone/intel2gas/上找到。
  6. IBM developerWorks上有一篇介绍GCC内联汇编的文章( http://www.ibm.com/developerworks/cn/linux/sdk/assemble/inline/index.shtml)。
  7. 本文代码下载: 代码

AT&T汇编语法

0.3.1 Overview 

 

开 发一个OS,尽管绝大部分代码只需要用C/C++等高级语言就可以了,但至少和硬件相关部分的代码需要使用汇编语言,另外,由于启动部分的代码有大小限 制,使用精练的汇编可以缩小目标代码的Size。另外,对于某些需要被经常调用的代码,使用汇编来写可以提高性能。所以我们必须了解汇编语言,即使你有可 能并不喜欢它。

 

如果你是计算机专业的话,在大学里你应该学习过Intel格式的8086/80386汇编,这里就不再讨论。如果我们选择的OS开发工具是GCC以及GAS的话,就必须了解AT&T汇编语言语法,因为GCC/GAS只支持这种汇编语法。

 

本书不会去讨论8086/80386的汇编编程,这类的书籍很多,你可以参考它们。这里只会讨论AT&T的汇编语法,以及GCC的内嵌汇编语法。


0.3.2 Syntax 

1.寄存器引用

引用寄存器要在寄存器号前加百分号%,如“movl %eax, %ebx”。

80386有如下寄存器:

  • 8个32-bit寄存器 %eax,%ebx,%ecx,%edx,%edi,%esi,%ebp,%esp;
  • 8个16-bit寄存器,它们事实上是上面8个32-bit寄存器的低16位:%ax,%bx,%cx,%dx,%di,%si,%bp,%sp;
  • 8个8-bit寄存器:%ah,%al,%bh,%bl,%ch,%cl,%dh,%dl。它们事实上是寄存器%ax,%bx,%cx,%dx的高8位和低8位;
  • 6个段寄存器:%cs(code),%ds(data),%ss(stack), %es,%fs,%gs;
  • 3个控制寄存器:%cr0,%cr2,%cr3;
  • 6个debug寄存器:%db0,%db1,%db2,%db3,%db6,%db7;
  • 2个测试寄存器:%tr6,%tr7;
  • 8个浮点寄存器栈:%st(0),%st(1),%st(2),%st(3),%st(4),%st(5),%st(6),%st(7)。

2. 操作数顺序

操作数排列是从源(左)到目的(右),如“movl %eax(源), %ebx(目的)”

3. 立即数

使用立即数,要在数前面加符号$, 如“movl $0x04, %ebx”

或者:

para = 0x04

movl $para, %ebx

指令执行的结果是将立即数04h装入寄存器ebx。

4. 符号常数

符号常数直接引用 如

value: .long 0x12a3f2de

movl value , %ebx

指令执行的结果是将常数0x12a3f2de装入寄存器ebx。

引用符号地址在符号前加符号$, 如“movl $value, % ebx”则是将符号value的地址装入寄存器ebx。

5. 操作数的长度

操作数的长度用加在指令后的符号表示b(byte, 8-bit), w(word, 16-bits), l(long, 32-bits),如“movb %al, %bl”,“movw %ax, %bx”,“movl %eax, %ebx ”。

如果没有指定操作数长度的话,编译器将按照目标操作数的长度来设置。比如指令 “mov %ax, %bx”,由于目标操作数bx的长度为word,那么编译器将把此指令等同于“movw %ax, %bx”。同样道理,指令“mov $4, %ebx”等同于指令“movl $4, %ebx”,“push %al”等同于“pushb %al”。对于没有指定操作数长度,但编译器又无法猜测的指令,编译器将会报错,比如指令“push $4”。

6. 符号扩展和零扩展指令

绝大多数面向80386的AT&T汇编指令与Intel格式的汇编指令都是相同的,符号扩展指令和零扩展指令则是仅有的不同格式指令。

符号扩展指令和零扩展指令需要指定源操作数长度和目的操作数长度,即使在某些指令中这些操作数是隐含的。

在AT&T语法中,符号扩展和零扩展指令的格式为,基本部分 "movs"和"movz"(对应Intel语法的movsx和movzx),后面跟上源操作数长度和目的操作数长度。movsbl意味着movs (from)byte (to)long;movbw意味着movs (from)byte (to)word;movswl意味着movs (from)word (to)long。对于movz指令也一样。比如指令“movsbl %al, %edx”意味着将al寄存器的内容进行符号扩展后放置到edx寄存器中。

其它的Intel格式的符号扩展指令还有:

  • cbw -- sign-extend byte in %al to word in %ax;
  • cwde -- sign-extend word in %ax to long in %eax;
  • cwd -- sign-extend word in %ax to long in %dx:%ax;
  • cdq -- sign-extend dword in %eax to quad in %edx:%eax;

对应的AT&T语法的指令为cbtw,cwtl,cwtd,cltd。

7. 调用和跳转指令

段内调用和跳转指令为"call","ret"和"jmp",段间调用和跳转指令为"lcall","lret"和"ljmp"。

段间调用和跳转指令的格式为“lcall/ljmp $SECTION, $OFFSET”,而段间返回指令则为“lret $STACK-ADJUST”。

8. 前缀

操作码前缀被用在下列的情况:

  • 字符串重复操作指令(rep,repne);
  • 指定被操作的段(cs,ds,ss,es,fs,gs);
  • 进行总线加锁(lock);
  • 指定地址和操作的大小(data16,addr16);

在AT&T汇编语法中,操作码前缀通常被单独放在一行,后面不跟任何操作数。例如,对于重复scas指令,其写法为:

             repne
             scas

上述操作码前缀的意义和用法如下:

  • 指定被操作的段前缀为cs,ds,ss,es,fs,和gs。在AT&T语法中,只需要按照section:memory-operand的格式就指定了相应的段前缀。比如:lcall %cs:realmode_swtch
  • 操作数/地址大小前缀是“data16”和"addr16",它们被用来在32-bit操作数/地址代码中指定16-bit的操作数/地址。
  • 总 线加锁前缀“lock”,它是为了在多处理器环境中,保证在当前指令执行期间禁止一切中断。这个前缀仅仅对ADD, ADC, AND, BTC, BTR, BTS, CMPXCHG,DEC, INC, NEG, NOT, OR, SBB, SUB, XOR, XADD,XCHG指令有效,如果将Lock前缀用在其它指令之前,将会引起异常。
  • 字符串重复操作前缀"rep","repe","repne"用来让字符串操作重复“%ecx”次。

9. 内存引用

Intel语法的间接内存引用的格式为:

section:[base+index*scale+displacement]

而在AT&T语法中对应的形式为:

section:displacement(base,index,scale)

其中,base和index是任意的32-bit base和index寄存器。scale可以取值1,2,4,8。如果不指定scale值,则默认值为1。section可以指定任意的段寄存器作为段前 缀,默认的段寄存器在不同的情况下不一样。如果你在指令中指定了默认的段前缀,则编译器在目标代码中不会产生此段前缀代码。

下面是一些例子:

-4(%ebp):base=%ebp,displacement=-4,section没有指定,由于base=%ebp,所以默认的section=%ss,index,scale没有指定,则index为0。

foo(,%eax,4):index=%eax,scale=4,displacement=foo。其它域没有指定。这里默认的section=%ds。

foo(,1):这个表达式引用的是指针foo指向的地址所存放的值。注意这个表达式中没有base和index,并且只有一个逗号,这是一种异常语法,但却合法。

%gs:foo:这个表达式引用的是放置于%gs段里变量foo的值。

如果call和jump操作在操作数前指定前缀“*”,则表示是一个绝对地址调用/跳转,也就是说jmp/call指令指定的是一个绝对地址。如果没有指定"*",则操作数是一个相对地址。

任何指令如果其操作数是一个内存操作,则指令必须指定它的操作尺寸(byte,word,long),也就是说必须带有指令后缀(b,w,l)。

AT&T汇编

在阅读linux内核源代码的时候,必须先掌握汇编,大家都知道,内核代码用的编译器是gcc,而gcc采用的是AT&T的汇编格式,与MS的intel有些区别。

一 AT&T的基本语法

    语法上主要有以下几个不同. 

★ 寄存器命名原则 

AT&T: %eax Intel: eax 

★ 源/目的操作数顺序 

AT&T: movl %eax,%ebx Intel: mov ebx,eax 

★ 常数/立即数的格式 

AT&T: movl $_value,%ebx Intel: mov eax,_value 

把_value的地址放入eax寄存器 

AT&T: movl $0xd00d,%ebx Intel: mov ebx,0xd00d 

★ 操作数长度标识 

AT&T: movw %ax,%bx Intel: mov bx,ax 

★寻址方式 

AT&T: immed32(basepointer,indexpointer,indexscale) 

Intel: [basepointer + indexpointer*indexscale + imm32) 

Linux工作于保护模式下,用的是32位线性地址,所以在计算地址时 

不用考虑segment:offset的问题.上式中的地址应为: 

imm32 + basepointer + indexpointer*indexscale 

下面是一些例子: 

★直接寻址 

AT&T: _booga ; _booga是一个全局的C变量 

注意加上$是表示地址引用,不加是表示值引用. 

注:对于局部变量,可以通过堆栈指针引用. 

Intel: [_booga] 

★寄存器间接寻址 

AT&T: (%eax) 

Intel: [eax] 

★变址寻址 

AT&T: _variable(%eax) 

Intel: [eax + _variable] 

AT&T: _array(,%eax,4) 

Intel: [eax*4 + _array] 

AT&T: _array(%ebx,%eax,8) 

Intel: [ebx + eax*8 + _array] 


二 基本的行内汇编 

    基本的行内汇编很简单,一般是按照下面的格式 

asm("statements"); 

例如:asm("nop"); asm("cli"); 

asm 和 __asm__是完全一样的. 

如果有多行汇编,则每一行都要加上 "\n\t" 

例如: 

asm( "pushl %eax\n\t" 

"movl $0,%eax\n\t" 

"popl %eax"); 

实际上gcc在处理汇编时,是要把asm(...)的内容"打印"到汇编 

文件中,所以格式控制字符是必要的. 

再例如: 

asm("movl %eax,%ebx"); 

asm("xorl %ebx,%edx"); 

asm("movl $0,_booga); 

在上面的例子中,由于我们在行内汇编中改变了edx和ebx的值,但是 

由于gcc的特殊的处理方法,即先形成汇编文件,再交给GAS去汇编, 

所以GAS并不知道我们已经改变了edx和ebx的值,如果程序的上下文 

需要edx或ebx作暂存,这样就会引起严重的后果.对于变量_booga也 

存在一样的问题.为了解决这个问题,就要用到扩展的行内汇编语法. 


三 扩展的行内汇编 

    扩展的行内汇编类似于Watcom. 

基本的格式是: 

asm ( "statements" : output_regs : input_regs : clobbered_regs); 

clobbered_regs指的是被改变的寄存器. 

下面是一个例子(为方便起见,我使用全局变量): 

int count=1; 

int value=1; 

int buf[10]; 

void main() 

asm( 

"cld \n\t" 

"rep \n\t" 

"stosl" 

:  "c" (count), "a" (value) , "D" (buf[0]) 

:  "%ecx","%edi" ); 

得到的主要汇编代码为: 

movl count,%ecx 

movl value,%eax 

movl buf,%edi 

#APP 

cld 

rep 

stosl 

#NO_APP 

cld,rep,stos就不用多解释了. 

这几条语句的功能是向buf中写上count个value值. 

冒号后的语句指明输入,输出和被改变的寄存器. 

通过冒号以后的语句,编译器就知道你的指令需要和改变哪些寄存器, 

从而可以优化寄存器的分配. 

其中符号"c"(count)指示要把count的值放入ecx寄存器 

类似的还有: 

a eax 

b ebx 

c ecx 

d edx 

S esi 

D edi 

I 常数值,(0 - 31) 

q,r 动态分配的寄存器 

g eax,ebx,ecx,edx或内存变量 

A 把eax和edx合成一个64位的寄存器(use long longs) 

我们也可以让gcc自己选择合适的寄存器. 

如下面的例子: 

asm("leal (%1,%1,4),%0" 

:  "=r" (x) 

:  "0" (x) ); 

这段代码实现5*x的快速乘法. 

得到的主要汇编代码为: 

movl x,%eax 

#APP 

leal (%eax,%eax,4),%eax 

#NO_APP 

movl %eax,x 

几点说明: 

1.使用q指示编译器从eax,ebx,ecx,edx分配寄存器. 

使用r指示编译器从eax,ebx,ecx,edx,esi,edi分配寄存器. 

2.我们不必把编译器分配的寄存器放入改变的寄存器列表,因为寄存器 

已经记住了它们. 

3."="是标示输出寄存器,必须这样用. 

4.数字%n的用法: 

数字表示的寄存器是按照出现和从左到右的顺序映射到用"r"或"q"请求 

的寄存器.如果我们要重用"r"或"q"请求的寄存器的话,就可以使用它们. 

5.如果强制使用固定的寄存器的话,如不用%1,而用ebx,则 

asm("leal (%%ebx,%%ebx,4),%0" 

:  "=r" (x) 

:  "0" (x) ); 

注意要使用两个%,因为一个%的语法已经被%n用掉了. 

下面可以来解释letter 4854-4855的问题: 

1、变量加下划线和双下划线有什么特殊含义吗? 

加下划线是指全局变量,但我的gcc中加不加都无所谓. 

2、以上定义用如下调用时展开会是什么意思? 

#define _syscall1(type,name,type1,arg1) \ 

type name(type1 arg1) \ 

{ \ 

long __res; \ 

/* __res应该是一个全局变量 */ 

__asm__ volatile ("int $0x80" \ 

/* volatile 的意思是不允许优化,使编译器严格按照你的汇编代码汇编*/ 

:  "=a" (__res) \ 

/* 产生代码 movl %eax, __res */ 

:  "0" (__NR_##name),"b" ((long)(arg1))); \ 

/* 如果我没记错的话,这里##指的是两次宏展开. 

  即用实际的系统调用名字代替"name",然后再把__NR_...展开. 

  接着把展开的常数放入eax,把arg1放入ebx */ 

if (__res >= 0) \ 

return (type) __res; \ 

errno = -__res; \ 

return -1; \ 


////////////////////////////////////////////////////////////////////////

四.AT&T汇编与Intel汇编的比较

Intel和AT&T语法的区别
Intel和AT&T汇编语言的语法表面上各不相同,这将导致刚刚学会INTEL汇编的人第一次见到AT&T汇编时
会感到困惑,或者反之。因此让我们从基础的东西开始。

前缀
在Intel汇编中没有寄存器前缀或者立即数前缀。而在AT&T汇编中寄存器有一个“%”前缀,立即数有
一个“$”前缀。Intel语句中十六进制和二进制数据分别带有“h”和“b”后缀,并且如果十六进制
数字的第一位是字母的话,那么数值的前面要加一个“0”前缀。
例如,
Intex Syntax
mov    eax,1
mov    ebx,0ffh
int    80h

AT&T Syntax
movl    $1,%eax
movl    $0xff,%ebx
int     $0x80
就像你看到的,AT&T非常难懂。[base+index*scale+disp] 看起来比disp(base,index,scale)更好理解。
 

操作数的用法
intel语句中操作数的用法和AT&T中的用法相反。在Intel语句中,第一个操作数表示目的,第二个
操作数表示源。然而在AT&T语句中第一个操作数表示源而第二个操作数表示目的。在这种情形下AT&T语法
的好处是显而易见的。我们从左向右读,也从左向右写,这样比较自然。
例如,
Intex Syntax
instr    dest,source
mov    eax,[ecx]
    
AT&T Syntax
instr     source,dest
movl    (%ecx),%eax

存储器操作数
如同上面所看到的,存储器操作数的用法也不相同。在Intel语句中基址寄存器用“[”和“]”括起来
而在AT&T语句中是用“(”和“)”括起来的。
例如,
Intex Syntax
mov    eax,[ebx]
mov    eax,[ebx+3]
AT&T Syntax
movl    (%ebx),%eax
movl    3(%ebx),%eax
AT&T语法中用来处理复杂的操作的指令的形式和Intel语法中的形式比较起来要难懂得多。在Intel语句
中这样的形式是segreg:[base+index*scale+disp]。在AT&T语句中这样的形式是
%segreg:disp(base,index,scale)。
Index/scale/disp/segreg 都是可选并且可以去掉的。Scale在本身没有说明而index已指定的情况下
缺省值为1。segreg的确定依赖于指令本身以及程序运行在实模式还是pmode。在实模式下它依赖于
指令本身而pmode模式下它是不需要的。在AT&T语句中用作scale/disp的立即数不要加“$”前缀。
例如
Intel Syntax
instr     foo,segreg:[base+index*scale+disp]
mov    eax,[ebx+20h]
add    eax,[ebx+ecx*2h]
lea    eax,[ebx+ecx]
sub    eax,[ebx+ecx*4h-20h]    
AT&T Syntax
instr    %segreg:disp(base,index,scale),foo
movl    0x20(%ebx),%eax
addl    (%ebx,%ecx,0x2),%eax
leal    (%ebx,%ecx),%eax
subl    -0x20(%ebx,%ecx,0x4),%eax

后缀
就像你已经注意到的,AT&T语法中有一个后缀,它的意义是表示操作数的大小。“l”代表long,
“w”代表word,“b”代表byte。Intel语法中在处理存储器操作数时也有类似的表示,
如byte ptr, word ptr, dword ptr。"dword" 显然对应于“long”。这有点类似于C语言中定义的
类型,但是既然使用的寄存器的大小对应着假定的数据类型,这样就显得不必要了。
例子:
Intel Syntax
mov    al,bl
mov    ax,bx
mov    eax,ebx
mov    eax, dword ptr [ebx]    
AT&T Syntax
movb    %bl,%al
movw    %bx,%ax
movl    %ebx,%eax
movl    (%ebx),%eax

注意:从此开始所有的例子都使用AT&T语法
系统调用
本节将介绍linux中汇编语言系统调用的用法。系统调用包括位于/usr/man/man2的手册里第二部分所有
的函数。这些函数也在/usr/include/sys/syscall.h中列出来了。一个重要的关于这些函数的列表是
http://www.linuxassembly.org/syscall.html里。这些函数通过linux中断服务:int $0x80来被执行
小于六个参数的系统调用
对于所有的系统调用,系统调用号在%eax中。对于小于六个参数的系统调用,参数依次存放
在%ebx,%ecx,%edx,%esi,%edi中,系统调用的返回值保存在%eax中。
系统调用号可以在/usr/include/sys/syscall.h中找到。宏被定义成SYS_的形式,
如SYS_exit, SYS_close等。
例子:(hello world 程序)
参照write(2)的帮助手册,写操作被声明为ssize_t write(int fd, const void *buf, size_t count);
这样,fd应存放在%ebx中,buf放在 %ecx, count 放在 %edx , SYS_write 放在 %eax中,紧跟着是
int $0x80语句来执行系统调用。系统调用的返回值保存在%eax中。
$ cat write.s
.include "defines.h"
.data
hello:
    .string "hello world\n"

.globl    main
main:
    movl    $SYS_write,%eax
    movl    $STDOUT,%ebx
    movl    $hello,%ecx
    movl    $12,%edx
    int    $0x80

    ret
$
少于5个参数的系统调用的处理也是这样的。只是没有用到的寄存器保持不变罢了。象open或者fcntl这样
带有一个可选的额外参数的系统调用也就知道怎么用了。
大于5个参数的系统调用
参数个数大于五个的系统调用仍然把系统调用号保存在%eax中,但是参数存放在内存中,并且指向第一个
参数的指针保存在%ebx中。
如果你使用栈,参数必须被逆序压进栈里,即按最后一个参数到第一个参数的顺序。然后将栈的指针拷贝
到%ebx中。或者将参数拷贝到一块分配的内存区域,然后把第一个参数的地址保存在%ebx中。
例子:(使用mmap作为系统调用的例子)。在C中使用mmap():
#i nclude
#i nclude
#i nclude
#i nclude
#i nclude

#define STDOUT    1

void main(void) {
    char file[]="mmap.s";
    char *mappedptr;
    int fd,filelen;

    fd=fopen(file, O_RDONLY);
    filelen=lseek(fd,0,SEEK_END);
    mappedptr=mmap(NULL,filelen,PROT_READ,MAP_SHARED,fd,0);
    write(STDOUT, mappedptr, filelen);
    munmap(mappedptr, filelen);
    close(fd);
}
mmap()参数在内存中的排列:
%esp    %esp+4    %esp+8    %esp+12    %esp+16    %esp+20
00000000    filelen    00000001    00000001    fd    00000000
等价的汇编程序:
$ cat mmap.s
.include "defines.h"

.data
file:
    .string "mmap.s"
fd:
    .long     0
filelen:
    .long     0
mappedptr:
    .long     0

.globl main
main:
    push    %ebp
    movl    %esp,%ebp
    subl    $24,%esp

//    open($file, $O_RDONLY);

    movl    $fd,%ebx    // save fd
    movl    %eax,(%ebx)

//    lseek($fd,0,$SEEK_END);

    movl    $filelen,%ebx    // save file length
    movl    %eax,(%ebx)

    xorl    %edx,%edx

//    mmap(NULL,$filelen,PROT_READ,MAP_SHARED,$fd,0);
    movl    %edx,(%esp)
    movl    %eax,4(%esp)    // file length still in %eax
    movl    $PROT_READ,8(%esp)
    movl    $MAP_SHARED,12(%esp)
    movl    $fd,%ebx    // load file descriptor
    movl    (%ebx),%eax
    movl    %eax,16(%esp)
    movl    %edx,20(%esp)
    movl    $SYS_mmap,%eax
    movl    %esp,%ebx
    int    $0x80

    movl    $mappedptr,%ebx    // save ptr
    movl    %eax,(%ebx)
        
//     write($stdout, $mappedptr, $filelen);
//    munmap($mappedptr, $filelen);
//    close($fd);
    
    movl    %ebp,%esp
    popl    %ebp

    ret
$
注意:上面所列出的源代码和本文结束部分的例子的源代码不同。上面列出的代码中没有说明其它的
系统调用,因为这不是本节的重点,上面列出的源代码仅仅打开mmap.s文件,而例子的源代码要读
命令行的参数。这个mmap的例子还用到lseek来获取文件大小。
Socket系统调用
Socket系统调用使用唯一的系统调用号:SYS_socketcall,它保存在%eax中。Socket函数是通过位于
/usr/include/linux/net.h的一个子函数号来确定的,并且它们被保存在%ebx中。指向系统调用参数
的一个指针存放在%ecx中。Socket系统调用也是通过int $0x80来执行的。
$ cat socket.s
.include "defines.h"

.globl    _start
_start:
    pushl    %ebp
    movl    %esp,%ebp
    sub    $12,%esp

//    socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    movl    $AF_INET,(%esp)
    movl    $SOCK_STREAM,4(%esp)
    movl    $IPPROTO_TCP,8(%esp)

    movl    $SYS_socketcall,%eax
    movl    $SYS_socketcall_socket,%ebx
    movl    %esp,%ecx
    int    $0x80

    movl     $SYS_exit,%eax
    xorl     %ebx,%ebx
    int     $0x80

    movl    %ebp,%esp
    popl    %ebp
    ret
$

命令行参数
在linux中执行的时候命令行参数是放在栈上的。先是argc,跟着是一个由指向命令行中各字符串的
指针组成的数组(**argv)并以空指针结束。接下来是一个由指向环境变量的指针组成的
数组(**envp)。这些东西在asm中都可以很容易的获得,并且在例子代码(args.s)中有示范。

 
GCC内联汇编
本节中GCC内联汇编仅涉及x86的应用程序。操作数约束会和其它处理器上的有所不同。关于这部分
的说明放在本文的最后。
gcc中基本的内联汇编非常易懂,如
__asm__("movl    %esp,%eax");    // look familiar ?

或者是
__asm__("
            movl    $1,%eax        // SYS_exit
            xor    %ebx,%ebx
            int    $0x80
    ");
如果指定了用作asm的输入、输出数据并指出哪一个寄存器会被修改,会使程序的执行效率提高。
input/output/modify都不是必需的。格式如下:
__asm__("" : output : input : modify);
output和input中必须包含一个操作数约束字符串,并紧跟一个用圆括号括起来的C语言表达式。
输出操作数约束的前面必须有一个“=”,表示这是一个输出。可能会有多个输出,多个输入和
多个修改过的寄存器。每个“入口”应该用“,”分隔开,并且入口的总数不多有10个。
操作数约束字符串可以是包含整个寄存器的名称也可以是简写。
Abbrev Table
Abbrev    Register
a    %eax/%ax/%al
b    %ebx/%bx/%bl
c    %ecx/%cx/%cl
d    %edx/%dx/%dl
S    %esi/%si
D    %edi/%di
m    memory
例如:

    __asm__("test    %%eax,%%eax", : /* no output */ : "a"(foo));


或者是

    __asm__("test    %%eax,%%eax", : /* no output */ : "eax"(foo));
你可以在__asm__后使用关键字__volatile__:“你可以利用在__asm__后使用关键字__volatile__的
方法防止一条‘asm’指令被删除、移动或者被重新组合。”(出自gcc的info文件中"Assembler
Instructions with C Expression Operands" 部分)
$ cat inline1.c
#i nclude

int main(void) {
    int foo=10,bar=15;
    
    __asm__ __volatile__ ("addl     %%ebxx,%%eax"
        : "=eax"(foo)         // ouput
        : "eax"(foo), "ebx"(bar)// input
        : "eax"            // modify
    );
    printf("foo+bar=%d\n", foo);
    return 0;
}
$
你可能已经注意到现在寄存器使用“%%”前缀而不是“%”。这在使用output/input/modify域时是必要的,
这是因为此时基于其它域的寄存器的别名的使用。我马上来讨论这个问题。
你可以很简单的指定“a”而不是写“eax”或者强制使用一个特殊寄存器如"eax"、"ax"、"al",
这同样适用于其它一般用途的寄存器(在Abbrev表中列出的)。当你在当前的代码中使用特殊的寄存器
时这好像毫无用处,因此gcc提供了寄存器别名。最多有10个别名(%0—%9),这也是为什么只允许10个
输入/输出的原因。
$ cat inline2.c
int main(void) {
    long eax;
    short bx;
    char cl;

    __asm__("nop;nop;nop"); // to separate inline asm from the rest of
                // the code
    __volatile__ __asm__("
        test    %0,%0
        test    %1,%1
        test    %2,%2"
        : /* no outputs */
        : "a"((long)eax), "b"((short)bx), "c"((char)cl)
    );
    __asm__("nop;nop;nop");
    return 0;
}
$ gcc -o inline2 inline2.c
$ gdb ./inline2
GNU gdb 4.18
Copyright 1998 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i686-pc-linux-gnulibc1"...
(no debugging symbols found)...
(gdb) disassemble main
Dump of assembler code for function main:
... start: inline asm ...
0x8048427 : nop
0x8048428 : nop
0x8048429 : nop
0x804842a : mov 0xfffffffc(%ebp),%eax
0x804842d : mov 0xfffffffa(%ebp),%bx
0x8048431 : mov 0xfffffff9(%ebp),%cl
0x8048434 : test %eax,%eax
0x8048436 : test %bx,%bx
0x8048439 : test %cl,%cl
0x804843b : nop
0x804843c : nop
0x804843d : nop
... end: inline asm ...
End of assembler dump.
$
就像你看到的,由内联汇编生成的代码将变量的值放入它们在input域中指定的寄存器中,然后继续
执行当前的代码。编译器自动根据变量的大小来侦测操作数的大小,这样相应的寄存器就被
别名%0, %1 和 %2代替了(当使用寄存器别名时在存储器里指定操作数的大小回导致编译时发生错误)
在操作数约束里也可以使用别名。这不允许你在输入/输出域中指定多于10个的入口。我能想到的这样
做的唯一用法是在你指定操作数约束为“q”以便让编译器在a,b,c,d寄存器之间进行选择的时候。
当这个寄存器被修改时,我们不会知道选中了那个寄存器,因而不能在modify域中指定它。
这种情况下你只需指定""。
例子:
$ cat inline3.c
#i nclude

int main(void) {
    long eax=1,ebx=2;

    __asm__ __volatile__ ("add %0,%2"
        : "=b"((long)ebx)
        : "a"((long)eax), "q"(ebx)
        : "2"
    );
    printf("ebx=%x\n", ebx);
    return 0;
}