Linux下的ATT语法(即GNU as 汇编语法)入门
学习这么长时间,一直在C语言这一层面上钻研和打拼,日积月累,很多关于C的疑惑在书本和资料中都难以找到答案。程序员是追求完美的一个种群,其头 脑中哪怕是存在一点点的思维黑洞都会让其坐卧不宁。不久前在itput论坛上偶得《Computer Systems A Programmer's Perspective》(以下称CS.APP)这本经典好书,中文有翻译的《深入理解计算机系统》。是遂连夜拜读以求解惑。虽说书中没有能正面的回答我的一些疑惑,但是它却为我指明了一条通向 “无惑”之路 -- 这就是打开汇编之门。
汇编语言是一门非常接近机器语言的语言,其语句与机器指令之间的对应关系更加简单和清晰。打开汇 编之门不仅仅能解除高级语言给你带来的疑惑,它更能让你更加的理解现代计算机的运行体系,还有一点更加重要的是它给你带来的是一种自信的感觉,减少了你在 高处摇摇欲坠的恐惧,响应了侯捷老师的“勿在浮沙筑高台”的号召。现在学习汇编的目的已与以前大大不同了。正如CS.APP中所说那样“程序员学习汇编的 需求随着时间的推移也发生了变化,开始时是要求程序员能直接用汇编编写程序,现在则是要求能够阅读和理解优化编译器产生的代码”。能阅读和理解,这也恰恰 是我的需求和目标。
以前接触过汇编,主要是Microsoft MASM宏汇编,不过那时的认识高度不够加上态度不端正,错失了一个很好的学习机会。现在绝大部分时间是使用GCC在Unix系列平台上工作,选择汇编语 言当然是GNU汇编了,恰好CS.APP中使用的也是GNU的汇编语法。由于学习汇编的主要目的还是“解惑”,所以形式上多是以C代码和汇编代码的比较。
1、汇编让你看到更多
随 着你使用的语言的层次的提高,你眼中的计算机将会越来越模糊,你的关注点也越来越远离语言本身而靠近另一端“问题域”,比如通过JAVA,你更多看到的是 其虚拟机,而看不到真实的计算机;通过C,你看到的也仅仅是内存一层;到了汇编语言,你就可以深入到寄存器一层自由发挥了。汇编程序员眼里的“独特风景” 包括:
a) “程序计数器(%eip)” -- 一个特殊寄存器,其中永远存储下一条将要执行的指令的地址;
b) 整数寄存器 -- 共8个,分别是%eax、%ebx、%ecx、%edx、%esi、%ebi、%esp和%ebp,它们可以存整数数据,可以存地址,也可以记录程序状态 等。早期每个寄存器都有其特殊的用途,现在由于像linux这样的平台多采用“平面寻址[1]”,寄存器的特殊性已经不那么明显了。
c) 条件标志寄存器 -- 保存最近执行的算术指令的状态信息,用来实现控制流中的条件变化。
d) 浮点数寄存器 -- 顾名思义,用来存放浮点数。
虽说寄存器的特殊性程度已经弱化,但是实际上每个编译器在使用这些寄存器时还是遵循一定的规则的,以后再说。
2、初窥汇编
下面是一个简单的C函数:
void dummy() {
int a = 1234;
int b = a;
}
我们使用gcc加-S选项将之转换成汇编代码如下(省略部分内容):
movl $1234, -4(%ebp)
movl -4(%ebp), %eax
movl %eax, -8(%ebp)
看 了一眼又一眼,还是看不懂,只是发现些熟悉的内容,因为上面提过如%ebp、%eax等。这只是个引子,让我们感性的认识一下汇编的“容貌”。我们一点点 地来看。咋看一眼汇编代码长得似乎很相似,没错,汇编代码就是一条一条的“指令+操作数”的语句的集合。汇编指令是固定的,每条指令都有其固定的用途,而 操作数表示则有多种类型。
1) 操作数表示
大部分汇编指令都有一个或多个操作数,包括指令操作中的源和目的。一条标准的指令格式大 致是这样的:“指令 + 源操作数 + 目的操作数”,其中源操作数可以是立即数、从寄存器中读出的数或从内存中读出的数;而目的操作数则可以是寄存器或内存。按这么一分类,操作数就大致有三 种:
a) 立即数表示法 -- 如“movl $1234, -4(%ebp)”中的“$1234”,就是一个立即数作为操作数,按照GNU汇编语法,立即数表示为“$+整数”。立即数常用来表示代码中的一些常数, 如上例中的“$1234”。注意一点的是立即数不能作为目的操作数。
b) 寄存器表示法 -- 这种比较简单,它就是表示寄存器之内容。如上面的“movl -4(%ebp), %eax”中的%eax就是使用寄存器表示法作源操作数,而“movl %eax, -8(%ebp)”中的%eax则是使用寄存器表示法作目的操作数。
c) 内存引用表示法 -- 计算出的该操作数的值表示的是相应的内存地址。汇编指令根据这个内存地址访问相应的内存位置。如上例“movl -4(%ebp), %eax”中的“-4(%ebp)”,其表示的内存地址为(%ebp寄存器中的内容-4)得到的值。
2) 数据传送指令
汇编语言中最最常用的指令 -- 数据传送指令,也是我们接触的第一种类别的汇编指令。其指令的格式为:“mov 源操作数, 目的操作数”。
mov 系列支持从最小一个字节到最大双字的访问与传送。其中movb用来传送一字节信息,movw用来传送二字节,即一个字的信息,movl用来传送双字信息。 这些不详说了。除此以外mov系列还提供两个带位扩展的指令movsbl和movzbl
==============================================================
汇编语言作为一种高效的,而且紧密结合硬件平台的编程语言,在操作系统,嵌入式开发等领域都有着十分重要的作用。正因为汇编依赖于硬件结构(CPU指令码),因此不同体系结构上的汇编语言也大相径庭。本文简单介绍了Linux下的AT&T语法(即GNU as 汇编语法),以及在Linux下汇编的基本方法。
AT&T语法起源于AT&T贝尔实验室,是在当时用于实现Unix系统的处理器操作码语法之上而形成的,AT&T语法和Intel语法主要区别如下:
AT&T使用$表示立即数,Intel不用,因此表示十进制2时,AT&T为$2,而Intel就是2
AT&T在寄存器前加%,比如eax寄存器表示为%eax
AT&T 处理操作数的顺序和Intel相反,比如,movl %eax, %ebx是将eax中的值传递给ebx,而Intel是这样的mov ebx, eax
AT&T在助记符的后面加上一个单独字符表示操作中数据的长度,比如movl $foo, %eax等同于Intel的mov eax, word ptr foo
长跳转和调用的格式不同,AT&T为ljmp $section, $offset,而Intel为jmp section:offset
主要的区别就是这些,其他的细节还有很多,下面给出一个具体的例子来说明
#cpuid.s Sample program
.section .data
output:
.ascii "The processor Vendor ID is 'xxxxxxxxxxxx'\n"
.section .text
.globl _start
_start:
movl $0, %eax
cpuid
movl $output, %edi
movl %ebx, 28(%edi)
movl %edx, 32(%edi)
movl %ecx, 36(%edi)
movl $4, %eax
movl $1, %ebx
movl $output, %ecx
movl $42, %edx
int $0x80
movl $1, %eax
movl $0, %ebx
int $0x80
这个程序的作用是查询CPU的厂商ID,其中:
,ascii定义字符串(和Intel格式完全不同).section是声明段的语句,.data和.text是段名,分别为数据段和代码段, _start是gas(GNU汇编器)的默认入口标签,表示程序从这里开始执行。.globl将_start声明成了外部程序访问的标签。cpuid为指令请求CPU的指定信息,该指令用eax作为输入,ebx,edx,ecx作为输出,这里将0作为cpuid的输入指令,请求返回CPU的厂商ID字符串。返回的结果,一个12字节的字符串,分别存储在三个寄存器中,其中ebx存放低4位,edx中间4位,ecx高4位(注意顺序!)。接下来定义一个指针edi,edi指向output的开始地址,然后接着的3条语句将output里的x替换为厂商信息。28(%edi)中的28表示偏移量,即整个地址为%edi里的地址加上28个字节,这个地址正好是output里第一个x的地址。再接下来就是打印结果了,这里用到了Linux的一个系统调用(int 0x80),该系统调用的参数分别为:eax 系统调用号,ebx 要写入的文件描述符,ecx 字符串首地址,edx 字符串长度,程序里这些个参数的值分别为4,1(标准输出),output的地址和42。最后再次调用1号系统调用-退出函数,返回shell,这次 ebx中的值是返回给shell的退出代码,0表示无异常
然后汇编连接运行程序:
[root@zieckey-laptop src]# as -o cpuid.o cpuid.s
[root@zieckey-laptop src]# ld cpuid.o -o cpuid
[root@zieckey-laptop src]# ./cpuid
The processor Vendor ID is 'GenuineIntel'
[root@zieckey-laptop src]#
本人的电脑是Pentium M的CPU所以返回的结果是GenuineIntel。
几点说明:
1)Linux的标准汇编环境为as,ld,gdb,gprof,objdump等GNU开发调试工具,除了gdb外,其他全部随binutils包发布。其中as使用的是AT&T语法。在Linux下也可以使用Nasm来进行Intel格式的汇编程序编写
2)Linux下汇编的系统调用为int 0x80,和DOS下的int 21h大同小异,只不过传递参数不同
3)段声明语句.section不需要像Intel格式那样在段结尾的时候加上段结束标志(SEGMENT/ENDS),下一个段的开始自动标志着上个段的结束
4)简单程序的入口标签不是必须要定义的,ld会自己判断入口,但是会给出警告
===========================================例子2
例 2. 求一组数的最大值的汇编程序
#PURPOSE: This program finds the maximum number of a # set of data items. # #VARIABLES: The registers have the following uses: # # %edi - Holds the index of the data item being examined # %ebx - Largest data item found # %eax - Current data item # # The following memory locations are used: # # data_items - contains the item data. A 0 is used # to terminate the data # .section .data data_items: #These are the data items .long 3,67,34,222,45,75,54,34,44,33,22,11,66,0 .section .text .globl _start _start: movl $0, %edi # move 0 into the index register movl data_items(,%edi,4), %eax # load the first byte of data movl %eax, %ebx # since this is the first item, %eax is # the biggest start_loop: # start loop cmpl $0, %eax # check to see if we've hit the end je loop_exit incl %edi # load next value movl data_items(,%edi,4), %eax cmpl %ebx, %eax # compare values jle start_loop # jump to loop beginning if the new # one isn't bigger movl %eax, %ebx # move the value as the largest jmp start_loop # jump to loop beginning loop_exit: # %ebx is the status code for the exit system call # and it already has the maximum number movl $1, %eax #1 is the exit() syscall int $0x80
汇编、链接、执行:
$ as max.s -o max.o $ ld max.o -o max $ ./max $ echo $?
这个程序在一组数中找到一个最大的数,并把它作为程序的退出状态。这组数在.data
段给出:
data_items: .long 3,67,34,222,45,75,54,34,44,33,22,11,66,0
.long
指示声明一组数,每个数占32位,相当于C语言中的数组。这个数组开头有一个标号data_items
,汇编器会把数组的首地址作为data_items
符号所代表的地址,data_items
类似于C语言中的数组名。data_items
这个标号没有用.globl
声明,因为它只在这个汇编程序内部使用,链接器不需要知道这个名字的存在。除了.long
之外,常用的数据声明还有:
.byte
,也是声明一组数,每个数占8位.ascii
,例如.ascii "Hello world"
,声明了11个数,取值为相应字符的ASCII码。注意,和C语言不同,这样声明的字符串末尾是没有'\0'字符的,如果需要以'\0'结尾可以声明为.ascii "Hello world\0"
。
data_items
数组的最后一个数是0,我们在一个循环中依次比较每个数,碰到0的时候让循环终止。在这个循环中:
edi
寄存器保存数组中的当前位置,每次比较完一个数就把edi
的值加1,指向数组中的下一个数。ebx
寄存器保存到目前为止找到的最大值,如果发现有更大的数就更新ebx
的值。eax
寄存器保存当前要比较的数,每次更新edi
之后,就把下一个数读到eax
中。
_start: movl $0, %edi
初始化edi
,指向数组的第0个元素。
movl data_items(,%edi,4), %eax
这条指令把数组的第0个元素传送到eax
寄存器中。data_items
是数组的首地址,edi
的值是数组的下标,4表示数组的每个元素占4字节,那么数组中第edi
个元素的地址应该是data_items + edi * 4
,从这个地址读数据,写成指令就是上面那样,这种地址的表示方式在下一节还会详细解释。
movl %eax, %ebx
ebx
的初始值也是数组的第0个元素。下面我们进入一个循环,在循环的开头用标号start_loop
表示,循环的末尾之后用标号loop_exit
表示。
start_loop: cmpl $0, %eax je loop_exit
比较eax
的值是不是0,如果是0就说明到达数组末尾了,就要跳出循环。cmpl
指令将两个操作数相减,但计算结果并不保存,只是根据计算结果改变eflags
寄存器中的标志位。如果两个操作数相等,则计算结果为0,eflags
中的ZF位置1。je
是一个条件跳转指令,它检查eflags
中的ZF位,ZF位为1则发生跳转,ZF位为0则不跳转,继续执行下一条指令。可见条件跳转指令和比较指令是配合使用的,前者改变标志位,后者根据标志位做判断,如果参与比较的两数相等则跳转,je
的e就表示equal。
incl %edi movl data_items(,%edi,4), %eax
将edi
的值加1,把数组中的下一个数传送到eax
寄存器中。
cmpl %ebx, %eax jle start_loop
把当前数组元素eax
和目前为止找到的最大值ebx
做比较,如果前者小于等于后者,则最大值没有变,跳转到循环开头比较下一个数,否则继续执行下一条指令。jle
也是一个条件跳转指令,le表示less than or equal。
movl %eax, %ebx jmp start_loop
更新了最大值ebx
然后跳转到循环开头比较下一个数。jmp
是一个无条件跳转指令,什么条件也不判断,直接跳转。loop_exit
标号后面的指令用exit
系统调用退出程序。
相关文章:

汽车高级驾驶辅助系统ADAS激光雷达创新者Cepton与Growth Capital达成企业合并协议
汽车高级驾驶辅助系统 (ADAS) 和车辆自动驾驶领域光感测距技术(激光雷达)的创新者Cepton Technologies, Inc.(以下简称“Cepton”)将与Growth Capital Acquisition Corp.(以下简称“Growth Capital”,纳斯达…

通过Navicat for MySQL远程连接的时候报错mysql 1130的解决方法
在用本地的navicat连接服务器的mysql数据库时候出现下面的问题: 解决的方法: 解决方法: 1、改表法。可能是你的帐号不允许从远程登陆,只能在localhost。这个时候只要在localhost的那台电脑,登入mysql后,更改…

17张图揭密支付宝系统架构
支付宝的系统架构图,仅供参考。不管是不是支付行业,都值得我们参考,学习。 imageimageimageimageimageimageimageimageimageimageimageimageimageimageimageimageimage推荐阅读 阿里高级Java面试题(首发,70道ÿ…

Facebook性能大提升的秘密:HipHop
facebook / hiphop-php https://github.com/facebook/hiphop-php Facebook神秘的PHP项目HipHop for PHP终于揭开面纱。这个项目由一个PHP到C的转换程序,一个重新实现的PHP运行库,和许多常用PHP扩展的重写版本构成,目的是旨在加速和优化PHP…

Android必备:Android的体系结构
2019独角兽企业重金招聘Python工程师标准>>> 链接地址:http://www.xx566.com/detail/107.html 最近一个月接触Android,开始做一些app的开发,通过参考网络上的资料,阅读Android相关的书籍,从无知到了解&…

豆瓣评分 9.4 的算法巨著,这本书带无数读者入门算法
说到算法巨著,你可能想到的是《算法导论》这本经典。但在入门算法时,还有一本与之比肩的巨著,不得不提,它就是《算法(第4版)》。这本豆瓣评分 9.4 的算法巨著,可谓是算法经典好书,给…

zabbix企业应用之监控oracle
本次介绍如何使用zabbix监控oracle,主要使用pyora这个python脚本来监控,具体地址可以参考https://github.com/bicofino/Pyora 我的zabbix版本为2.0.6,oracle为11g 下面是部分效果图 1、Oracle/Active user count 2、Oracle/Bytes sent and re…

ZendFramework的介绍、安装和实例运行
框架主要是为了提高开发效率、使得团队开发人员之间的更容易沟通和协作以及提高应用程序的可维护性。学习了解一种或多种框架对实际项目的应用会有所帮助。一、ZendFramework 的介绍:ZendFramework(以下简称:ZF),以 Model-View-Controller(MV…
input type=hidden /在IE中占空间(转)
input 的 type 设为 hidden 的话,就是隐藏域(废话)。隐藏域在页面中不显示,但可以有值。既然是“隐藏”的,怎么还说会占空间呢。这是 IE 的 bug,不过这个 bug 的出现需要一定的条件,换句话说就是…

网友:Java岗,自学一个月跳槽计算机视觉,其实入门很简单
笔者在脉脉上看到一条帖子:原来Java岗,自学一个月成功跳槽视觉算法岗。这已经不是笔者第一次看到转行成功的程序员案例了,而大家的跳槽动机基本上都离不开,发展趋势、岗位高薪、职业兴趣。计算机视觉行业真相:竞争压力…

MapReduce对交易日志进行排序的Demo(MR的二次排序)
1.日志源文件 (各个列分别是: 账户,营业额,花费,日期) zhangsan163.com 6000 0 2014-02-20 lisi163.com 2000 0 2014-02-20 lisi163.com 0 100 2014-02-20 zhangsan163.com 3000 0 2014-02-20 wangwu126.com 9000 0 2014-02-20 w…

HTTP中Get与Post的区别
Http定义了与服务器交互的不同方法,最基本的方法有4种,分别是GET,POST,PUT,DELETE。URL全称是资源描述符,我们可以这样认 为:一个URL地址,它用于描述一个网络上的资源,而…

sdut AOE网上的关键路径(spfa+前向星)
http://acm.sdut.edu.cn/sdutoj/showproblem.php?pid2498&cid1304 题目描述 一个无环的有向图称为无环图(Directed Acyclic Graph),简称DAG图。 AOE(Activity On Edge)网:顾名思义,用边表示活动的网ÿ…

苹果新功能惹网友众怒,还有隐私可言吗?
编译 | 禾木木出品 | AI科技大本营(ID:rgznai100)大部分人选择 iPhone 的一大理由就是信息安全,这家公司对于个人隐私的保护一直为人称赞。最近苹果公司宣布,为了让儿童能够更加安全地上网,他们决定在iOS 15、iPADOS 15、macOS Monterey系统中…

让Ubuntu拥有SUSE一样的GRUB启动界面
SUSE的漂亮大家可能都见识过,尤其是那个Grub启动画面。我身边的朋友为了在自己的系统上也能使用SUSE的GRUB启动画面,用了一种原理比较简 单,过程比较白痴的方法:先安装SUSE,把/boot单独分区,然后把除了/boo…

计算机编程简史图
计算机编程简史图www.21kaiyun.com 21世纪开运网 算准你每天的桃花运 帮忙推广下我的网站 谢谢

HTML5 模板推荐
http://www.yundic.com/转载于:https://www.cnblogs.com/lsl8966/p/4133484.html

Windows 11 再惹“众怒”!网友:微软就是逼我去买新电脑!
整理 | 郑丽媛出品 | CSDN(ID:CSDNnews)一般来说,不论是移动还是桌面操作系统,如若要升级版本,大多用户都不会产生过大的抵触情绪,毕竟更新往往都是为了确保用户获得最佳体验。但近来用户对微软…

刚学习了linux的DHCP 配置.呵呵.自己上来总结下.
先来看DHCP的工作原理.DHCP (Dynamic Host Configuration Protocol)下面的部分是google找的....~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~DHCP来自ITwiki,开放的信息技术大百科DHCP是Dynamic Host Configuration Protocol的…

App_Offline.htm 一个静态页面实现整站维护时统一页面
在ASP.NET 2.0 站点根目录下,只要存在 App_Offline.htm 文件,那么所有对.aspx的请求都将转向App_Offline.htm 。而且浏览器的地址栏显示的是所请求的.aspx的URL。 这样当我们的站点需要维护时,只要把App_Offline.htm 拷贝到站点根目录下即…

C++编程思想重点笔记(上)
C和C指针的最重要的区别在于:C是一种类型要求更强的语言。就void *而言,这一点表现得更加突出。C虽然不允许随便地把一个类型的指针指派给另一个类型,但允许通过void *来实现。例如: bird* b;rock* r;void* v;v r;b v; C不允许…

一行命令实现录屏,支持热键和鼠标操作,区域、帧率、格式任你选择
作者:天元浪子来源:CSDN 博客市面上的录屏工具软件有很多,基本都是窗口程序。毕竟,离开GUI的支持,设置参数、选择录像区域等操作都会变得非常困难。不过,窗口程序也并非无往不胜,即便是屏幕录像…

SMO学习笔记(二)——还原(恢复)篇之完整恢复
SQLSERVER2005恢复介绍: 三种恢复模式(一).简单恢复模式 事务日志被自动截断,不能使用日志文件进行恢复。(二).完整恢复模式 保留所有操作的完整事务日志。(三).大容量日志恢复模式 简要记录大容量操作(索引创建和大容…

linux内核map图
linux内核map图

Linux下tcpdump用法
根据使用者的定义对网络上的数据包进行截获的包分析工具。tcpdump将网络中传送的数据包的“头”完全截获下来提供分析。它支持针对网络层、协议、主机、网络或端口的过滤,并提供了and、 or、not等逻 辑语句来帮助过滤不必要的信息; 默认情况下&#…

终于有人站出来为程序员说话了
【CSDN 编者按】刘少山博士是《程序员》杂志的作者之一,多年来投稿了大量无人驾驶领域相关的优质内容,《新程序员》上线后,他带着自己多年来对技术行业的思考以及对程序员群体的殷切期望重新回归,希望能对大家有所启迪。作者 | 刘…

给 Windows 驱动程序安装提速
对比各种主流操作系统,在 Windows 上安装驱动程序是最直观最方便的,不仅可以通过设备管理器查看所有硬件的信息并安装驱动,在有新硬件插入时也有人性化的驱动程序安装提示和安装向导,甚至还可以在线安装驱动,这都是其他…

web标准化设计:常用的CSS命名规则
常用的CSS命名规则 头:header 内容:content/container 尾:footer 导航:nav 侧栏:sidebar 栏目:column 页面外围控制整体布局宽度:wrapper 左右中:left right center 登录条ÿ…

鲲鹏应用创新大赛山西区域赛圆满落幕,鲲鹏生态助力信创变革
鲲鹏入晋,万里腾飞,8 月 6 日,2021 鲲鹏应用创新大赛山西赛区决赛在太原圆满落幕。今年鲲鹏应用创新大赛区域赛山西赛区是山西省内数字化转型的重要赛事,经过层层选拔,共 35 个队伍进入山西赛区决赛,参加政…

视频分享网站首页:最新视频特效
2019独角兽企业重金招聘Python工程师标准>>> <!DOCTYPE> <html> <head><title></title><style>.newVideo{width:208px;height:116px;border:0px solid #000; position:relative;cursor:pointer;}.newVideoImg{position:relativ…