Linux中的汇编简介

摘要:
如果指令名没有字符后缀,并且指定的语句不包含内存操作数,as汇编器将尝试根据寄存器操作数确定操作数宽度。其中,asm是内联汇编的关键词。汇编语句用于编写汇编指令。输出寄存器指示在执行此汇编语句后,哪些寄存器用于存储输出数据。输入寄存器指示在代码执行开始时应放置在某些寄存器中的输入值。将被修改的寄存器表示所列寄存器中的值已被修改。

GNU as汇编语法

GNU汇编语法使用的是AT&T汇编它和Intel汇编的语法主要有以下一些不同:

  • AT&T汇编中的立即操作数前面要加上'$',寄存器操作数名前要加上百分号'%',绝对跳转操作数前要加上'*',Intel的语法均不包含这些符号;
  • AT&T语法与Intel语法中使用的源操作数和目的操作数顺序正好相反,AT&T的源操作数和目的操作数是从左到右,Intel语法是从右到左,例如add eax, 4在AT&T语法中是addl $4, %eax;
  • AT&T语法中内存操作数长度由操作码的最后一位字符来确定,操作码后缀有b/w/l分别代表内存引用宽度为8位,16位和32位,Intel语法则通过在内存操作数前使用前缀byte ptr,word ptr和dword ptr来达到此目的。例如Intel中的mov al, byte ptr foo对应AT&T中的语句为movb $foo, %al;
  • AT&T语法中立即跳转和调用为ljmp/lcall $section, $offset,而Intel中的是jmp/call far section:offset,AT&T中的返回指令lret $stack-adjust对应Intel的ret far stack-adjust;
  • AT&T汇编器不支持多代码段程序,类UNIX系统要求所有代码在一个段中;

预处理:as汇编器能对汇编语言进行简单的预处理,包括删除多余的空格和制表符,删除注释语句,把字符常数转换为对应的数值,但此预处理不能处理宏,也没有处理包含文件的功能,如果需要这些功能,可以使用gcc的预处理器cpp来实现。

符号:GNU汇编语言中的符号是由字符组成的标识符,组成符号的字符包含大小写字母,数字和_.$三个字符,符号不允许以数字开始,且区分大小写。在汇编程序中符号长度没有限制,符号使用空格来界定开始和结束。语句以换行符或者行分割符(;)作为结束,文件最后必须以换行符作为结束,语句有0个或多个标号开始(Label),后面可跟随一个确定语句类型的关键符号,标号由符号后面跟随一个冒号构成,关键符号确定了余下部分语句的语义,如果关键符号以.开始,那么当前语句就是一个汇编命令,如果关键符号以字母开始,那么当前语句就是一条汇编语言指令语句,一it哦啊语句的通用格式为:

标号:汇编命令     注释语句
或者
标号:指令助记符 操作数1, 操作数2     注释语句

常数:常数是一个数字,分为字符常数和数字常数两类,字符常数分为单个字符和字符串,数字常数分为整数和浮点数。汇编语言的单个字符表示一版为在该字符前加上单引号,而字符串需要添加双引号。

指令:指令是CPU执行的操作,通常指令称为操作码,操作数是指令操作的对象,而地址是指令数据在内存中的位置,一条指令语句一版包括:标号,操作码(指令助记符),操作数,注释。

操作数:操作数包含立即操作数,寄存器和内存。间接操作数含有实际操作数的地址值,AT&T语法通过在操作数前加上*来表示间接操作数,只有跳转和调用指令才能使用间接操作数。立即操作数前面需要加上$符号,寄存器名前面需要加上%符号,内存操作数由变量名或者含有变量地址的寄存器指定,变量名隐含指出了变量的地址,并指示cpu引用改地址内存处的内容。

操作码:AT&T语法中指令操作码最后一个字符用来指明操作数的宽度,字符b,w和l分别制定byte,word和long类型的操作数。如果指令名没有带字符后缀,并且指定语句中不含内存操作数,那么as汇编器会根据寄存器操作数来尝试确定操作数宽度。操作码前缀用于修饰随后的操作码,他们用于重复字符串指令,提供区覆盖,执行总线锁定操作或者指定操作数和地址宽度。

内存引用:AT&T语法的间接内存引用形式为section:disp(base, index, scale),其中base和index是32位的基寄存器和索引寄存器,disp是可选的偏移值,scale是比例因子,scale乘上index用来表示操作数地址,section为内存操作数指定的段寄存器。

movl var, %eax                 #把内存地址var处的内容放入寄存器%eax
movl %cs:var, %eax             #把代码段中的内存地址var处的内容放入%eax
movb $0xa0, %es:(%ebx)         #把0xa0放入到es段的%ebx指定的偏移处
movl $var, %eax                #把var的地址放入%eax
movl array(%esi), %eax         #把array+%esi内存地址的内容放入eax
movl (%ebx, %esi, 4), %eax     #把ebx+esi*4地址处的内容放到eax
movl array(%ebx, %esi, 4), %eax#把array+ebx+esi*4地址的内容放到eax
movl -4(%ebp), %eax            #把ebp-4地址处的内容放到eax

指令跳转:跳转指令用于把执行点转移到程序的另外的位置执行,跳转的目的位置通常用一个标号来表示。jmp是无条件跳转,可分为直接跳转和间接跳转,直接跳转语句的写法是给出跳转目标处的标号,间接跳转语句的写法是使用*作为操作指示符的前缀字符。

:段用于表示一个地址范围,它主要用来表示编译器生成的目标文件中的不同的信息区域。例如代码段,数据段,bss段。

符号:符号有很多作用,可以用来命名对象,连接器通过符号执行链接操作,调试器利用符号进行调试。标号是后面接着一个冒号的符号,该符号用来表示代码当前的位置。特殊符号.用来表示汇编的当前位置。除了名字之外每个符号都有值和类型属性。符号的值通常是32位的,链接器会对未定义的符号值做特殊处理,如果未定义的符号值为0则表示该符号在此汇编程序中没有定义,链接器会尝试使用其他链接的文件来确定此符号值,

as汇编器

as汇编器的命令行格式为: as [选项] [-o objfile] [srcfile.s],如果没有指定输出文件名,会默认输出a.out文件。可以再as命令行上给出0个或多个输入文件名,as会按照从左到右的顺序读取这些文件中的内容,在命令行上的参数如果没有实际意义将会被当做输入文件名看待,如果命令行上没有任何文件名,那么as将会试图从终端读取输入文件内容。

as的输出文件:由输入的汇编文件编译生成的二进制文件,目标文件最终作为连接器ld的输入文件。目标文件中包含汇编代码,协助ld产生可执行文件的信息,以及用于调试的符号信息。

内联汇编

内联汇编的基本格式为:

asm("汇编语句"
      : 输出寄存器
      : 输入寄存器
      : 会被修改的寄存器)

除了汇编语句是必须的的以外,其他若不使用都可以省略。其中asm是内联汇编的关键词,汇编语句用于编写汇编指令,输出寄存器表示当这段汇编语句执行完之后,哪些寄存器用于存放输出数据,输入寄存器表示开始执行代码时,制定某些寄存器中应该放入的输入值,会被修改的寄存器表示对列出的寄存器中的值进行了修改。

常用的寄存器加载代码

代码  说明代码   说明
a使用寄存器eaxm使用内存地址
b使用寄存器ebxo使用内存地址并且可以加偏移值
c使用寄存器ecxI使用常数0~31
d使用寄存器edxJ使用常数0~63
S使用esiK使用常数0~255
D使用ediL使用常数0~65535
q使用动态分配字节可寻址寄存器M使用常数0~3
r使用任意动态分配寄存器N使用1字节常数
g使用通用有效地址O使用常数0~31
A使用eax和edx联合=输出操作数,输出值替换前值
+表示操作数可读可写&在使用完操作数之前内容会被修改

在执行代码时,如果不希望汇编语句被gcc优化,需要在asm符号后面添加关键字volatile。关键字volatile也可以放在函数名前来修饰函数,用来通知gcc该函数不会返回。

免责声明:文章转载自《Linux中的汇编简介》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇使用 Tye 辅助开发 k8s 应用竟如此简单(五)idea 中main 方法不能运行下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

第51章 设置FLASH的读写保护及解除—零死角玩转STM32-F429系列

第51章     设置FLASH的读写保护及解除 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/firege 本章参考资料:《STM32F4xx 中文参考手册》、《STM32F4xx规格书》、库说明文档《stm32f4xx_dsp_stdperi...

关于对JMM(java内存模型)的个人理解

java内存模型是一种虚拟机规范,它定义了Java内存模型,用于屏蔽各种不同硬件和操作系统访问内存差异,以实现让java程序在各种平台下都能达到一致的并发效果.JMM规范了java虚拟机与计算机内存是如何协同工作的,规定了一个线程如何和何时可以看到由其他线程修改过后的共享变量的值,以及在必须时如何同步的访问共享变量. 关于主内存与工作内存之间的具体交互协...

【转载】Perl异常处理方法总结

    程序脚本在运行过程中,总会碰到这样那样的问题,我们会预知一些问题并为其准备好处理代码,而有一些不能预知。好的程序要能尽可能多的处理可能出现的异常问题,本文就总结了一些方法来解决这些异常,当然perl在这个处理了不及其它同类语言,但也不会差到那里。在开始前,我们先盘点一些关于perl的优缺点。0. 历史太悠久了。你可以在1997年的计算机上找到per...

ASM

在学习汇编之前,我们要介绍一下常用的函数调用约定,以便我们对于一些指令的理解。 函数调用约定 常见的函数调用约定:stdcall , cdecl, fastcall, thiscall, naked call 1, __cdecl(C调用约定.)C/C++ 缺省调用方式 1)压栈顺序: 函数参数从右到左 2)参数栈维护: 由调用函数把参数弹出栈,传送参数的...

汇编指令MOV

格式:MOV DST,SRC 例如: MOV EAX,#050aH ;将十六进制050a 传送到通用寄存器eax中 MOV DI,BX(寄存器到寄存器之间传数) MOV ES,AX(通用寄存器与段寄存器之间传数) MOV AX,DS(段寄存器至通用寄存器) MOV AL,23H(将立即数"复制"到寄存器) MOV [2000H],02H(直接寻址) MOV...

linux 应用程序直接读写寄存器或物理内存

1.程序说明: 调试驱动程序时,经常遇到候需要查看或设置寄存器的情况,但是直接更改内核代码又不方便。 这里提供一个应用程序源码能在应用层访问底层寄存器。(网上找到的,进行过更改)。 这里只提供4字节数据的访问,如果需要其他字节宽度则需要更改代码。 line40 增加了O_DSYNC标志,防止cache导致数据写入不及时。 2.应用程序源码 1 #incl...