C指针原理教程之ATT汇编
汇编在LINUX系统下的意义远远⼤于WINDOWS系统,LINUX内核部分代码就是汇编编写的。然后,绝⼤多数 Linux 程序员以前只接触过DOS/Windows 下的汇编语⾔,这些汇编代码都是 Intel 风格的。但在 Unix 和 Linux 系统中,更多采⽤的还是
AT&T 格式,两者在语法格式上有着很⼤的不同,因此应对AT&T汇编应有⼀个基本的了解和熟悉。
我们在LINUX下⽤C编写⼀段最简单的helloworld程序,命令为hello.c
#include <stdio.h>
int main()
{
printf("hello,world\n");
exit(0);
}
然后,使⽤GCC编译,同时使⽤-s参数⽣成中间汇编代码,看看AT&T汇编的真实⾯⽬
.section .data#初始化的变量
output:
.ascii "hello,world\n"
#要打印的字符串,.data为初始化值的变量。output是标签,指⽰字符串开始的位置,ascii为数据类型
.section .bss#未初始化的变量,由0填充的缓冲区
.lcomm num,20
#lcomm为本地内存区域,即本地汇编外的不能进⾏访问。m是通⽤内存区域。
.section .text#汇编语⾔指令码
.globl _start#启动⼊⼝
_start:
movl $4,%eax#调⽤的系统功能,4为write
movl $output,%ecx#要打印的字符串
movl $1,%ebx#⽂件描述符,屏幕为1
movl $12,%edx#字符串长度
int $0x80#显⽰字符串hello,world
movl $0,%eax
movl $num,%edi
movl $65,1(%edi)#A 的ascii
movl $66,2(%edi)#B 的ascii
movl $67,3(%edi)#C 的ascii
movl $68,4(%edi)#D 的ascii
movl $10,5(%edi)#\n的ascii
movl $4,%eax#调⽤的系统功能,4为write
movl $num,%ecx#要打印的字符串
movl $1,%ebx#⽂件描述符,屏幕为1
movl $6,%edx#字符串长度
int $0x80#显⽰字符串ABCD
movl $1,%eax#1为退出
movl $0,%ebx#返回给shell的退出代码值
int $0x80#内核软中断,退出系统
gcc -S hello.c
.file "hello.c"
.
section .rodata
.LC0:
.string "hello,world"
.text
.globl main
.type main, @function
main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $16, %esp
movl $.LC0, (%esp)
call puts
movl $0, (%esp)
call exit
.size main, .-main
.ident "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
.section .note.GNU-stack,"",@progbits
汇编器(assembler)的作⽤是将⽤汇编语⾔编写的源程序转换成⼆进制形式的⽬标代码。Linux 平台的标准汇编器是 GAS,它是 GCC 所依赖的后台汇编⼯具,通常包含在 binutils 软件包中。
AT&T汇编主要有以下特点:
1、在 AT&T 汇编格式中,寄存器名要加上 '%' 作为前缀。
如:
把eax寄存器的内容复制到ebx中
movl %eax,%ebx
2、⽤ '$' 前缀表⽰⼀个⽴即操作数。
如:将1复制到eax中
movl $1, %eax
3、⽬标操作数在源操作数的右边
movl %eax,%ebx
eax是源操作数,ebx是⽬标操作数
4、在 AT&T 汇编格式中,操作数的字长由操作符的最后⼀个字母决定,后缀'b'、'w'、'l'分别表⽰操作数为字节(byte,8 ⽐特)、字(word,16 ⽐特)和长字(long,32⽐特)
⽐如:
movl对32位进⾏操作,将eax寄存器32位的内容复制到ebx中
movl %eax, %ebx
movw对16位进⾏操作,将ax寄存器的内容复制到bx中
movw %ax, %bx
movb对8位进⾏操作,将al寄存器的内容复制到bl中
movb %al, %bl
我们再以⼊栈为例:
pushl %ecx # 32位ecx的内容⼊栈
pushw %cx # 16位ecx的内容⼊栈
pushl $180 # 80做为⼀个32位整数⼊栈
pushl data # data变量内容⼊栈,长度为32位
pushl $data # 这⼀个操作很特别,在变量前⾯加上$表⽰取变量的地址,这是将data变量的地址⼊栈
5、在 AT&T 汇编格式中,绝对转移和调⽤指(jump/call)的操作数前要加上'*'作为前缀
6、远程转移指令和远程⼦调⽤指令的操作码,在 AT&T 汇编格式中为 ljump和lcall
我们从⽣成的中间代码可以看出这⼏个特点。
我们再来看⼀段⽤AT&T汇编编写的helloworld程序。
.section .data#初始化的变量
output:
.ascii "hello,world\n"
#要打印的字符串,.data为初始化值的变量。output是标签,指⽰字符串开始的位置,ascii为数据类型
.section .bss#未初始化的变量,由0填充的缓冲区
.lcomm num,20
#lcomm为本地内存区域,即本地汇编外的不能进⾏访问。m是通⽤内存区域。
.
字符串长度不能超过32位section .text#汇编语⾔指令码
.globl _start#启动⼊⼝
_start:
movl $4,%eax#调⽤的系统功能,4为write
movl $output,%ecx#要打印的字符串
movl $1,%ebx#⽂件描述符,屏幕为1
movl $12,%edx#字符串长度
int $0x80#显⽰字符串hello,world
movl $0,%eax
movl $num,%edi
movl $65,1(%edi)#A 的ascii
movl $66,2(%edi)#B 的ascii
movl $67,3(%edi)#C 的ascii
movl $68,4(%edi)#D 的ascii
movl $10,5(%edi)#\n的ascii
movl $4,%eax#调⽤的系统功能,4为write
movl $num,%ecx#要打印的字符串
movl $1,%ebx#⽂件描述符,屏幕为1
movl $6,%edx#字符串长度
int $0x80#显⽰字符串ABCD
movl $1,%eax#1为退出
movl $0,%ebx#返回给shell的退出代码值
int $0x80#内核软中断,退出系统
我们对上⾯这段汇编代码的结构和内容进⾏解说:
1、.section .data段存放着初始化的变量, .section .bss段存放着未初始化的变量
2、变量的定义采⽤以下格式:
变量名:
变量类型变量值
上⾯代码中的output变量就是这么定义的
output:
.ascii "hello,world\n"
下⾯例⼦定义了多个变量
.section .data
msg:
.ascii “This is a text”
x:
.double 109.45, 2.33, 19.16
y:
.int 89
z:
.int 21, 85, 27
.equ a 8
其中,msg为字符符,x为双精度符点数,y和z为整数,a是⼀个特别的定义,它的是⼀个静态变量的定义,使⽤.equ 变量名变量值来实现
3、.section .bss段中变量访问区域的定义规则为:
lcomm为本地内存区域,即本地汇编外的不能进⾏访问,⽽m是通⽤内存区域
⽐如上⾯的定义
.lcomm num,20
num为本地内存区域。
4、section .text段为汇编语⾔指令码,使⽤.globl _start指⽰_start标记后的代码为程序启动⼊⼝。
5、#表⽰注释,上⾯代码的其它部分均有注释,有汇编基础的程序员应很容易理解
变量的类型有以下⼏种:
.ascii ⽂本字符串
.asciz 以NULL结束的⽂本字符串
.byte 字节值
.double 双精度符点数
.
float 单精度符点数
.int 32位整数
.long 32位整数
.octa 16位整数
.quad 8位整数
.short 16位整数
.single 单精度符点数
此外,AT&T汇编经常会涉及字节顺序反转,⽐较加载,交换,压⼊弹出所有寄存器等操作,以下例⼦涉及了这些操作,每⾏代码都有详细的注释。
.bss段定义的数据元素为未初始化的变量,在运⾏时对其进⾏初始化。
可分为数据通⽤内存区域和本地通⽤内存区域
本地通⽤内存区域不能从本地汇编代码之外进⾏访问。
.text段存放代码
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论