linux下汇编语言开发总结


汇编语言是直接对应系统指令集的低级语言,在语言越来越抽象的今天,汇编语言并不像高级语言那样使用广泛,仅仅在驱动程序,嵌入式系统等对性能要求苛刻的领域才能见到它们的身影。但是这并不表示汇编语言就已经没有用武之地了,通过阅读汇编代码,有助于我们理解编译器的优化能力,并分析代码中隐含的低效率,所以能够阅读和理解汇编代码也是一项很重要的技能。因为我平时都是在linux环境下工作的,这篇文章就讲讲linux下的汇编语言。

一、汇编语法风格

汇编语言分为intel风格和AT&T风格,前者被Microsoft Windows/Visual C++采用,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. 操作数顺序不同。在 Intel 汇编格式中,目标操作数在源操作数的左边;而在 AT&T 汇编格式中,目标操作数在源操作数的右边。例如:

AT&T

Intel

addl $1, %eax

add eax, 1

4. 字长表示不同。在 AT&T 汇编格式中,操作数的字长由操作符的最后一个字母决定,后缀'b'、'w'、'l'分别表示操作数为byte、word和long;而在 Intel 汇编格式中,操作数的字长是用 "byte ptr" 和 "word ptr" 等前缀来表示的。例如:

AT&T

Intel

movb val, %eax

mov al, byte ptr val

5. 寻址方式表示不同。在 AT&T 汇编格式中,内存操作数的寻址方式是 

section:disp(base, index, scale)

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

section:[base + index*scale + disp]

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

disp + base + index * scale

由此分为以下几种寻址方式:
 

Intel

AT&T

内存直接寻址

seg_reg: [base + index * scale + immed32]

seg_reg: immed32 (base, index, scale)

寄存器间接寻址

[reg]

(%reg)

寄存器变址寻址

[reg + _x]

_x(%reg)

立即数变址寻址

[reg + 1]

1(%reg)

整数数组寻址

[eax*4 + array]

_array (,%eax, 4)

二、IA32寄存器

1.通用寄存器

顾名思义,通用寄存器是那些你可以根据自己的意愿使用的寄存器,但有些也有特殊作用,IA32处理器包括8个通用寄存器,分为3组

1) 数据寄存器

EAX 累加寄存器,常用于运算;在乘除等指令中指定用来存放操作数,另外,所有的I/O指令都使用这一寄存器与外界设备传送数据。

EBX 基址寄存器,常用于地址索引

ECX 计数寄存器,常用于计数;常用于保存计算值,如在移位指令,循环(loop)和串处理指令中用作隐含的计数器.
EDX 数据寄存器,常用于数据传递。

2) 变址寄存器

ESI 源地址指针

EDI 目的地址指针

3) 指针寄存器

EBP为基址指针(Base Pointer)寄存器,存储当前栈帧的底部地址。

ESP为堆栈指针(Stack Pointer)寄存器,一直记录栈顶位置,不可直接访问,push时ESP减小,pop时增大。

2. 指令指针寄存器

EIP 保存了下一条要执行的指令的地址, 每执行完一条指令EIP都会增加当前指令长度的位移,指向下一条指令。用户不可直接修改EIP的值,但jmp、call和ret等指令也会改变EIP的值,jmp将EIP修改为目的指令地址,call修改EIP为被调函数第一条指令地址,ret从栈中取出(pop)返回地址存入EIP。

三、函数调用过程

函数调用时的具体步骤如下:

1. 调用函数将被调用函数参数入栈,入栈顺序由调用约定规定,包括cdecl,stdcall,fastcall,naked call等,c编译器默认使用cdecl约定,参数从右往座入栈。

2. 执行call命令。

call命令做了两件事情,一是将EIP寄存器内的值压入栈中,称为返回地址,函数完成后还要到这个地址继续执行程序。然后将被调用函数第一条指令地址存入EIP中,由此进入被调函数。

3. 被调函数开始执行,先准备当前栈帧的环境,分为3步

pushl %ebp 保存调用函数的基址到栈中,

movl %esp, %ebp 设置EBP为当前被调用函数的基址指针,即当前栈顶

subl $xx, %esp 为当前函数分配xx字节栈空间用于存储局部变量

4. 执行被调函数主体

5. 被调函数结束返回,恢复现场,第3步的逆操作,由leave和ret两条指令完成,

leave 主要恢复栈空间,相当于

movl %ebp, %esp 释放被调函数栈空间

popl %ebp 恢复ebp为调用函数基址

ret 与call指令对应,等于pop %EIP,

6. 返回到调用函数,从下一条语句继续执行

我们来看两个具体例子,第一个求数组和,

复制代码
int ArraySum(int *array, int n){int t = 0;for(int i=0; i<n; ++i) t += array[i];return t;
}int main() {int a[5] = {1, 2, 3, 4, 5 };int sum = ArraySum(a, 5);return sum;
}
复制代码

编译成汇编代码

gcc -std=c99 -S -o sum.s sum.c

gcc加入了很多汇编器和连接器用到的指令,与我们讨论的内容无关,简化汇编代码如下:

复制代码
ArraySum:pushl    %ebpmovl    %esp, %ebp   subl    $16, %esp  //分配16字节栈空间movl    $0, -8(%ebp)  //初始化tmovl    $0, -4(%ebp)  //初始化ijmp    .L2
.L3:movl    -4(%ebp), %eaxsall    $2, %eax  //i<<2, 即i*4, 一个int占4字节addl    8(%ebp), %eax  //得到array[i]地址,array+i*4movl    (%eax), %eax   //array[i]addl    %eax, -8(%ebp) //t+=array[i]addl    $1, -4(%ebp)
.L2:movl    -4(%ebp), %eax   cmpl    12(%ebp), %eax  //比较i<njl    .L3movl    -8(%ebp), %eax //return t; 默认eax存函数返回值leaveretmain:
.LFB1:pushl    %ebpmovl    %esp, %ebpsubl    $40, %esp       movl    $1, -24(%ebp) //初始化a[0]movl    $2, -20(%ebp) //初始化a[1]movl    $3, -16(%ebp) //初始化a[2]movl    $4, -12(%ebp) //初始化a[3]movl    $5, -8(%ebp)   //初始化a[4]movl    $5, 4(%esp)    //5作为第二个参数传给 ArraySumleal    -24(%ebp), %eax  //leal产生数组a的地址movl    %eax, (%esp)   //作为第一个参数传给ArraySumcall    ArraySummovl    %eax, -4(%ebp)  //返回值传给summovl    -4(%ebp), %eax  //return sumleaveret
复制代码

栈变化过程如下:

                         执行call指令前                                 执行call指令后

从图中可以看出

1. 数组连续排列,用move指令逐个赋值,读取数组元素方法是,用leal得到数组首地址,再计算偏移量

2. 参数从右往左入栈

3. gcc为了保证数据是严格对齐的,分配的空间大于使用的空间,有部分空间是浪费的

下面这个例子说明了struct结构的实现方法,

复制代码
struct Point{int x;int y;
};
void PointInit(struct Point *p, int x, int y){p->x = x;p->y = y;
}int main() {struct Point p;int x = 10;int y = 20;PointInit(&p, x, y);return 0;
}
复制代码

编译成汇编代码,简化如下:

复制代码
PointInit:pushl    %ebpmovl    %esp, %ebpmovl    8(%ebp), %eax    //p的地址movl    12(%ebp), %edx  //xmovl    %edx, (%eax)      //p->x=xmovl    8(%ebp), %eaxmovl    16(%ebp), %edx  //ymovl    %edx, 4(%eax)    //p->y=ypopl    %ebpretmain:pushl    %ebpmovl    %esp, %ebpsubl    $28, %espmovl    $10, -8(%ebp)  //x=10movl    $20, -4(%ebp)  y=20movl    -4(%ebp), %eaxmovl    %eax, 8(%esp)movl    -8(%ebp), %eaxmovl    %eax, 4(%esp)leal    -16(%ebp), %eax  //取p地址&pmovl    %eax, (%esp)call    PointInitmovl    $0, %eaxleaveret
复制代码

栈图就不画了,可以清楚地看出struct跟数组类似,连续排列,通过相对位移访问struct的成员,p->y与*(p+sizeof(p->x))有一样的效果。

四、disassemble和objdump

在linux下有两个跟汇编有重要关系的命令,一个是objdump,另一个是gdb中的disassemble。

objdump帮助我们从可执行文件中反汇编出汇编代码,从而逆向分析工程。

objdump -d sum

部分汇编代码如下

复制代码
080483b4 <ArraySum>:80483b4:    55                       push   %ebp80483b5:    89 e5                    mov    %esp,%ebp80483b7:    83 ec 10                 sub    $0x10,%esp80483ba:    c7 45 f8 00 00 00 00     movl   $0x0,-0x8(%ebp)80483c1:    c7 45 fc 00 00 00 00     movl   $0x0,-0x4(%ebp)80483c8:    eb 12                    jmp    80483dc <ArraySum+0x28>80483ca:    8b 45 fc                 mov    -0x4(%ebp),%eax80483cd:    c1 e0 02                 shl    $0x2,%eax80483d0:    03 45 08                 add    0x8(%ebp),%eax80483d3:    8b 00                    mov    (%eax),%eax80483d5:    01 45 f8                 add    %eax,-0x8(%ebp)80483d8:    83 45 fc 01              addl   $0x1,-0x4(%ebp)80483dc:    8b 45 fc                 mov    -0x4(%ebp),%eax80483df:    3b 45 0c                 cmp    0xc(%ebp),%eax80483e2:    7c e6                    jl     80483ca <ArraySum+0x16>80483e4:    8b 45 f8                 mov    -0x8(%ebp),%eax80483e7:    c9                       leave  80483e8:    c3                       ret
复制代码

disassemble可以显示调试程序的汇编代码,用法如下

disas 反汇编当前函数

disas sum 反汇编sum函数

disas 0x801234 反汇编位于地址 0x801234附近的函数

disas 0x801234 0x802234 返汇编指定范围内函数

 

 

reference:

http://zh.wikipedia.org/wiki/%E6%B1%87%E7%BC%96

http://www.ibm.com/developerworks/cn/linux/l-assembly/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/351405.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用openocd调试Linux内核,openocd安装与调试

环境&#xff1a;硬件&#xff1a;PC机ARM仿真器v8.00已下载好bit流的Xinlinx SoC开发板(其上有arm cortex-a9核)软件&#xff1a;Redhat Linux6(或虚拟机) openocd使用openocd下载程序&#xff0c;调试arm cortex-a9核。一、openocd安装下载libusb库安装或直接yum install li…

execl中设置的格式无法实现

在一次项目中&#xff0c;需要导出execl表&#xff0c;并且要给表中的表格设置格式&#xff0c;因为每列的格式都不一样&#xff0c;需要单独设置设置这些格式&#xff0c;在后期使用中因为导入的数据过多&#xff0c;是的后面的单元格中设置的格式无法实现。 每次打开execl表格…

loadrunner监控linux性能指标,使用LoadRunner监控Linux系统性能.doc

使用LoadRunner监控Linux系统性能性能监控案例■秘密 □机密 □绝密PAGELinux系统性能监控案例(仅供内部使用)版 本 号&#xff1a;V0.1保 密 等 级&#xff1a;■秘密 □机密 □绝密编 制&#xff1a;XXX审 核&#xff1a;修订记录日期版本号描述作者2011-06-130.1初稿完成目录…

github gists_Eclipse中的Github Gists

github gists我想描述有关在Eclipse中集成GitHub Gists的简单步骤。 有几个来源促使我这样做&#xff1a; Eclipse的GitHub Mylyn连接器 EGit / GitHub /用户指南 http://eclipse.github.com 我一直在使用Eclipse Java EE发行版&#xff0c;其中已经安装了Mylyn插件&#…

分析.cpp文件编译生成的汇编文件里语句的作用

1234int main(int argc,char** argv){return 1;}1g -S test.cpp生成test.s汇编文件 .file"null-test.cpp".text.globl main.type main, functionmain:.LFB0:.cfi_startproc pushq %rbp.cfi_def_cfa_offset 16.cfi_offset 6, -16movq %rsp, %rbp.cfi_def_cfa…

vue-cli3.0使用及配置(部分)

好长一段时间没有关注vue脚手架了&#xff0c;昨天因为需要个后台模板&#xff0c;用脚手架 搞了一下&#xff0c;竟然发现指令不能用了&#xff0c;看官方文档已经升级3.0&#xff0c;也是试的玩了一下&#xff0c; 大致写写怎么玩的&#xff01;12341.先全局安装vue-cli3.0 …

centos arm-linux-gcc,CentOS 6.4配置arm-linux-gcc交叉环境

首先解压arm-linux-gcc到制定文件夹[armacer ~]$ sudo tar xvf arm-linux-gcc-3.4.5-glibc-2.3.6.tar.bz2 -C /opt/tool/配置环境变量&#xff1a;[armacer ~]$ sudo vi /etc/profile添加:PATH/opt/tool/gcc-3.4.5-glibc-2.3.6/bin:$PATHexport PATH保存退出。执行[armacer ~]$…

使用JavaSymbolSolver解决Java代码中的方法调用

为什么创建java-symbol-solver&#xff1f; 几年前&#xff0c;我开始使用JavaParser &#xff0c;然后开始做出贡献。 不久之后&#xff0c;我意识到我们想对Java代码执行的许多操作不能仅通过使用解析器生成的抽象语法树来完成&#xff0c;我们还需要解析类型&#xff0c;符号…

kali2.0安装搜狗输入法

其实方法应该是是通用的 &#xff08;一开始源要配置好&#xff1a; 修改软件源APT-sources.list [python] view plaincopy vim /etc/apt/sources.list 将原来的注释掉&#xff0c;加了个阿里的#阿里云kali源deb http://mirrors.aliyun.com/kali sana main non-free contrib…

linux+vim+动不了,linux的vim按了ctrl+s之后假死的解决办法

Aqua Data Studio中文乱码使用Aqua Data Studio 查询数据时,如果表中的数据有中文时,会显示乱码,如下图: 解决方法很简单,只能更改字体即可,步骤如下: 更改字体后,显示的结果如下:C&num;调用脚本语言(三)-- IronJS 与 IronLua 简单方法性能比较1. 测试环境 1.1. 硬件环境…

aix linux操作系统,AIX--操作系统安装(AIX 6.1)

前一段时间&#xff0c;胡哥我接到老板的任务&#xff0c;要在AIX系统上安装zabbix agent呀。可是胡哥只是玩过linux对于AIX这种需要硬件支持的才能玩的高端玩意可是从来没接触过呀&#xff0c;虽说linux是类unix&#xff0c;可是与unix系统还是有区别。有啥办法了&#xff0c;…

秋季学习总结

经过这个学期的学习让我对软件工程这个专业有了新的认识&#xff0c;从c语言的学习中&#xff0c;让我慢慢懂得了如何去编写一个程序&#xff0c;但是编写一个程序也并不是那么的容易。在这个学期的学期中我总是感觉容易忘记上一节课所讲的东西&#xff0c;这个是课后自己没花时…

permgen_打破PermGen神话

permgen在我的最新文章中&#xff0c;我解释了可能导致java.lang.OutOfMemoryError&#xff1a;PermGen空间崩溃的原因 。 现在该讨论该问题的可能解决方案了。 或更确切地说&#xff0c;是关于互联网对可能解决方案的建议。 不幸的是&#xff0c;我只能说&#xff0c;当我通过…

到底是32位系统运行快还是64位系统快

首先声明一下&#xff0c;这个标题“到底是32位系统运行快还是64位系统运行快”的提法本身就是存在问题的&#xff0c;主要是由于很多网友把这个问题提炼的层次太高&#xff0c;那就是“我现在的机子是装32位系统快还是64位系统更快&#xff1f;”&#xff0c;所以就拿这个问题…

linux系统证书存储,Linux系统下如何配置Nginx的SSL安全证书

刚刚介绍了ownCloud的安装&#xff0c;有朋友问我SSL是怎么配置的&#xff0c;哎&#xff0c;为什么不去官方找&#xff0c;却来找我要呢&#xff0c;好吧&#xff0c;我知道你懒得看那些英文&#xff0c;我就介绍一下我是怎么一步步在Nginx上配置SSL的吧。首先你要确保你安装了…

命令界面:使用Java中的动态API处理Redis

Redis是一个数据存储&#xff0c;支持190多个已记录命令和450多个命令排列。 社区积极支持Redis开发&#xff1b; 每个主要的Redis版本都附带新命令。 今年&#xff0c;Redis向第三方供应商开放&#xff0c;以开发可扩展Redis功能的模块。 对于客户端开发人员和Redis用户而言&a…

Python数据库连接池DBUtils

DBUtils是Python的一个用于实现数据库连接池的模块 此连接池有两种连接模式&#xff1a; DBUtils提供两种外部接口&#xff1a; PersistentDB &#xff1a;提供线程专用的数据库连接&#xff0c;并自动管理连接。 PooledDB &#xff1a;提供线程间可共享的数据库连接&…

够用的 Python 写日志的知识——标准日志模块logging简介

前一段工作的时候用到了python写后台系统&#xff0c;需要把一些系统的行为记录下来。本着不要去重复发明轮子的精神&#xff0c;就去搜索了一下python的系统库本身是否有写日志的模块。果然有。python语言作为一门接口简单&#xff0c;标准库强大的语言&#xff0c;果然没有令…

vivox7刷linux系统,Vivo 找来宋仲基帮你送 X7

虽说有极致的 Xplay5 用来打品牌&#xff0c;但真正跑起量来&#xff0c;Vivo 实际上靠得还是以明星代言、外型、自拍等特性为卖点的中端产品线。这不&#xff0c;他们刚刚又在北京发布了全新的 X7 系列&#xff0c;这次找来了大势韩星宋仲基&#xff0c;美其名曰「有了你就有了…

Gradle配置

配置远程仓库 在gradle目录下的init.d目录中创建名为init.gradle文件&#xff0c;内容如下&#xff1a; allprojects{repositories {def REPOSITORY_URL http://localhost:8081/nexus/content/groups/public/all { ArtifactRepository repo ->if(repo instanceof MavenArti…