骆驼空间站

简单就是美,不受任何商业的驱使,我们有自己的圈子

博客园 首页 新随笔 联系 订阅 管理
工具准备:
在上一次所记录的 Linux 编译过程中,已经提及了编译和连接所需的工具.
 
前提:
这里假设在学习 AT&T 汇编时,已对 Intel 格式的 8086/80386 汇编有了一个相当的了解.同时在学习 GCC inline assembler 时,也假设已经对 GCC 有相当的了解. (其实我 GCC 也没有好到哪里, Intel 汇编也只是随便学过几下 ^_^,所以说"假设")
 
AT&T Assembler Syntax:
1] 寄存器引用
在 AT&T 汇编格式中,寄存器名要加上 '%' 作为前缀;而在 Intel 汇编格式中,寄存器名不需要加前缀.
AT&T 格式 Intel 格式
pushl %eax push eax
 
2] 立即数
AT&T 和 Intel 格式中的源操作数和目标操作数的位置正好相反.在 Intel 汇编格式中,目标操作数在源操作数的左边;而在 AT&T 汇编格式中,目标操作数在源操作数的右边.
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 bit), 字(word, 16 bit) 和 长字(long, 32 bit).而在 Intel 汇编格式中,操作数的字长是用 "byte ptr" 和 "word ptr" 等前缀来表示的.
AT&T 格式 Intel 格式
movb val, %al mov al, byte ptr val
 
5] 调用和跳转指令
绝对转移和调用指令 (jump/call) 的操作数前要加上 '*' 作为前缀.远程转移指令和远程子调用指令的操作码,在 AT&T 汇编格式中为 'ljump' 和 'lcall'
AT&T 格式 Intel 格式
ljump $section, $offset jmp far section:offset
lcall $section, $offset call far section:offset
 
与之相应的远程返回 (return) 指令则为
AT&T 格式 Intel 格式
lret $stack_adjust ret far stack_adjust
 
6] 内存引用

在 AT&T 汇编格式中,内存操作数的寻址方式是 section:disp(base, index, scale).而在 Intel 汇编格式中,内存操作数的寻址方式为 section:[base + index*scale + disp].

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

 

GCC inline assembler:

1]基本格式和说明:

内联汇编格式 __asm__("asm statements" : outputs : inputs : registers-modified);

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

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

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

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

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

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

在内联汇编中用到的操作数从输出部的第一个约束开始编号,序号从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)

 

 

posted on 2006-11-15 13:37  骆驼SPACE  阅读(369)  评论(0编辑  收藏  举报