在linux内核的源代码中,以汇编语言编写的程序或程序段,有两种不同的形式。
第一种事完全的汇编代码,这样的代码采用.s作为文件的后缀。事实上,尽管是完全的汇编代码,现代的汇编工具也吸收了C语言的长处,也在汇编之前加上了一趟预处理,而预处理之前的文件则以.s为后缀。此类(.s)文件也和C程序一样,可以使用#include、#ifdef等等成分,而数据结构也一样可以在.h的文件中加以定义。

第二种是嵌在C程序中的汇编语言片断。虽然在ANSI的C语言标准中并没有关于汇编片段的规定,事实上各种实际使用的C编译中都作了这方面的扩充,而 GNU的C编译gcc也在这方面作了很强的扩充。

在DOS/windows领域中,386汇编语言都采用Intel定义的语句格式。可是,在Unix领域中,采用的却是由AT&T定义的格式。

AT&T的汇编与Intel的汇编主要有以下的区别:
在Intel格式中大多使用大写字母,而在AT&T格式中都使用小写字母。
在AT&T格式中,寄存器名要加上“%”作为前缀 ,而在Intel格式中不带前缀。
在AT&T的386汇编语言中,指令的源操作数的顺序与在Intel的386汇编语言中正好相反。
在AT&T格式中,访问指令的操作数的宽度有操作码名称的最后一个字母(操作码的后缀决定)。用作操作码后缀的字母有b(8位)。 w(16位)和1(32位)。 而在Intel格式中,则是在表示内存单元的操作数前面加上“BYTE PTR”“WORD PTR”,“DWORD PTR”来表示。
在AT$T格式中,直接操作数要加上“$”作为前缀 ,而在Intel格式中则不带前缀。
在AT$T格式中,绝对转移和调用指令jump/call的操作数要加上“*”作为前缀 ,而在intel格式则不带。
远程的转移指令和子程序调用指令的操作码名称,在AT$T格式中为“ljump”和“lcall”, 而在intel格式中,则为“JMP FAR”和“CALL FAR”当转移和调用的目标为直接操作数时,两种不同的表示如下:
 CALL FAR SECTION:OFFSET(Intel 格式)
 JMP FAR SECTION:OFFSET(Intel 格式)
 lcall $section,$offset (AT$T格式)
 lcall $secton,$offset (AT$T格式). 与之相应的远程返回指令,则为:
 RET FAR STACK_ADJUST (Intel 格式)
 Lret $stack_adjust (AT$T 格式)
间接寻址的一般格式,两者的区别如下:
 SECTION :[BASE+INDEX*SCALE+DISP](Intel 格式)
 Section: disp(base,index,scale)(AT$T 格式)
 当需要在C语言的程序中嵌入一段汇编语言程序时,可以使用gcc提供的“asm”语句功能。
  一般而言,往C代码中插入汇编语言的代码片要比“纯粹”的汇编语言代码复杂的多,因为这里有个怎样分配使用寄存器,怎样与C代码中的变量结合的问题。为了这个目的,必须对所用的汇编语言作更多的扩充,增加对汇编工具的指导作用。其结果是其语法实际上变成了既不同于汇编语言,也不同于C语言的某种中间语言。

插入C代码的一个汇编语言代码片段可以分为四个部分,以“:”号加以分隔,其一般形式为:指令部: 输出部:输入部:损坏部

1:第一部分就是汇编代码本身,其格式和在汇编语言中使用基本相同。这一部分称为“指令部”,是必须有的,而其他部分可视具体情况而省略 。当将汇编语言代码片段嵌入到C代码中时,操作数与C代码中的变量如何结合显然是个问题。Gcc采用的策略是:程序员提供具体的指令,而对寄存器的使用则一般只提供一个“样板”和一些约束条件,而把到底如何与变量结合的问题留给了gcc和gas处理.

2:在指令部中,数字加上前缀%,如%0、%1等等,表示需要使用的寄存器的样板操作数 。可以使用的此类操作数的总数取决于具体CPU中通用寄存器的数量。这样,指令部中用到了几个不同的这种操作数,就说明有几个变量需要与寄存器结合,由gcc和gas在编译和汇编时根据后面的约束条件自行变通处理。由于这些样板操作数也使用“%”前缀,在涉及到具体的寄存器时就要在寄存器名前面加上两个“%”符 ,以免混淆。

3:紧接在指令部后面的是“输出部”,用以规定对输出变量如何结合的约束条件。每个这样的条件称为一个“约束”。必要是输出部可以有多个约束,互相以逗号分隔。每个输出约束以“=”号开始 ,然后是一个字母表示对操作数类型的说明,然后是关于变量结合的约束 。凡是与输出部中说明的操作数相结合的寄存器或操作数本身,在执行嵌入的汇编代码后均不保留执行之前的内容,这就给gcc提供了调度这些寄存器的依据。

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

  在有些操作中,除用于输入数据操作和输出数的寄存器以外,还要将若干个寄存器用于计算或操作的中间结果。这样,这些寄存器原有的内容就损坏了,所以要在损坏部队操作的副作用加以说明,让gcc采取相应的措施。

  操作数的编号从输出部的第一个约束(序号为0)开始,顺序数下来,每个约束记数一次。在指令部中引用这些操作或分配用于这些操作数的寄存器时,就在序号前面加上一个“%”号。在指令部中引用一个操作数时总是把它当作一个32位的“长字”, 但是对其实施的操作,则根据需要也可以是字节操作或是字操作。对操作数进行的字节操作时也允许明确指出是对哪一个字节的操作,此时在%与序号之间插入一个“b”表示最低字节,插入一个“h”表示次低字节。

表示约束调节的字母有很多:
“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)

此外,如果一个操作数要求使用与前某个约束中所要求的是同一个寄存器,那就把与那个约束相对应的操作数标号放在约束条件中。在损坏部常常会以 “memory”为约束条件,表示操作完成后内存中的内容已有改变,如果原来某个寄存器(也许在本次操作汇总并未使用到)的内容来自内存,则现在科能已经不一致。

  还要注意,当输出部为空,即没有输出约束时,如果有输入约束存在,则必须保留分隔标记“:”号。
备注:(举例)
一 基本语法
语法上主要有以下几个不同.
★ 寄存器命名原则
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,icon_cool.gif
Intel: [ebx + eax*8 + _array]

二 基本的行内汇编
·基本的行内汇编很简单,一般是按照下面的格式:
asm("statements");
例如:asm("nop"); asm("cli");

·asm 和 __asm__是完全一样的.
·如果有多行汇编,则每一行都要加上 " ".例如:
asm( "pushl %eax "
"movl $0,%eax "
"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 "
"rep "
"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寄存器

几点说明:
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) );

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

FAQ:
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;
}

转载于:https://my.oschina.net/alphajay/blog/5102

Linux - ATT汇编基础相关推荐

  1. linux ATT汇编 与 Intel汇编 语法格式对比

    由于绝大多数的国内程序员以前只接触过Intel格式的汇编语言,很少或几乎没有接触过AT&T汇编语言,虽然这些汇编代码都是Intel风格的.但在Unix和Linux系统中,更多采用的还是AT&a ...

  2. att汇编教程 linux,ATT 汇编语法

    6 个段寄存器:%cs(code),%ds(data),%ss(stack), %es,%fs,%gs; 3 个控制寄存器:%cr0,%cr2,%cr3; 6 个 debug 寄存器:%db0,%db ...

  3. att汇编教程 linux,att 汇编 helloworld

    博主在 archlinux x86_64 下测试, gcc版本 Using built-in specs. COLLECT_GCC=gcc COLLECT_LTO_WRAPPER=/usr/lib/g ...

  4. linux ATT汇编 输出hello world

    Centos8安装32位编译环境 安装32位glibc库文件命令:(安装C库文件) sudo yum install glibc-devel.i686 安装32位glibc++库文件命令 sudo y ...

  5. 《庖丁解牛Linux内核》笔记之:汇编基础

    <庖丁解牛Linux内核>笔记之:汇编基础 参考视频 下面的说法都来自x86CPU寄存器. 值的不同表示方法 %eax 类似这种就是直接操作该寄存器,效果类似于C语言中的指针:*和& ...

  6. 反汇编---汇编基础学习

    一.一些简单的说明 本文描述的是x86汇编,采用ATT汇编代码格式,讨论的是一个运行Linux的x86系统.使用gcc编译器,gdb调试器,objdump等相关工具观察反汇编代码. 关于寻址模式.堆栈 ...

  7. C指针原理(1)-ATT汇编

    汇编在LINUX系统下的意义远远大于WINDOWS系统,LINUX内核部分代码就是汇编编写的.然后,绝大多数 Linux 程序员以前只接触过DOS/Windows 下的汇编语言,这些汇编代码都是 In ...

  8. linux 没有那个文件或目录_【新视频进度】国庆节至今更新6节quot;嵌入式Linux应用开发基础quot;...

    说到做到,韦东山老师国庆没有休息,录了4节新视频,到目前快速入门-嵌入式Linux应用开发基础知识已更新6节: 01.HelloWorld背后没那么简单 02.GCC编译器的使用 03.Makefil ...

  9. 【嵌入式Linux】嵌入式Linux应用开发基础知识之输入系统应用编程

    文章目录 前言 1.输入系统应用编程 1.1.输入系统框架及调试 1.1.1.框架概述 1.1.2.编写APP需要的基础知识 1.2.调试技巧 1.2.1.查看设备信息 1.2.2.使用命令查看节点数 ...

  10. crash工具解析_crash工具和x86-64汇编基础

    在前面的文章中,已经出现了作为Linux重要调试手段之一的crash工具的身影.在后续的文章里,我们还会继续用到它.因此在这里,准备对Linux中的crash工具的原理和使用方法,做一个相对全面的介绍 ...

最新文章

  1. OEM/ODM指的是什么?
  2. Spark(四) -- Spark工作机制
  3. 中兴核心网服务器笔记本电脑,中兴通讯5G Common Core
  4. [links]一写国内外著名人物的blog链接
  5. 用平常心去对待不平常的事
  6. spring aop 环绕通知around和其他通知的区别
  7. 网站如何进行渠道跟踪_网站如何进行搜索引擎优化?
  8. 国庆档43.87亿收官:《长津湖》一家独大 影视股喜忧参半
  9. scrapy简单爬虫
  10. 快速从入门到精通!mysql字符串截取前两位
  11. CentOS上安装man手册
  12. 冷热分离和直接使用大数据库_用读写分离与分表分库解决高访问量和大数据量...
  13. Xfire的aegis绑定方式配置小结
  14. [2018.10.20 T2] 面包
  15. java期中 考试_java期中考试题
  16. 个人总结之I2C总线协议
  17. 原生JS实现上传图片预览效果
  18. python将PDF文件转换为图片
  19. 宝塔搭建网站教程php,怎么利用宝塔面板搭建网站详细教程
  20. DSP TMS320F280049C 新征程

热门文章

  1. java web 分页 基于sql 2005 数据库
  2. 运行aspnet时要求启用windows集成身份验证
  3. 一个简单的Java Web项目
  4. DataGear 1.13.1 发布,数据可视化分析平台
  5. 原生小程序和 mpvue对比(开发前)
  6. nodejs 中的 commonjs 模块化使用
  7. JSP与Servlet传值及对比
  8. 如何让应用出现在LINUX的右键/打开方式/更多应用
  9. WORD中输入已知编码的特殊字符
  10. 手机变慢怎么办?复位、重置可解决