C指针原理教程之AT&T汇编

 更新时间:2019年02月07日 11:03:50   作者:myhaspl  
AT&T 汇编是一种和intel汇编在语法上完全不同的汇编语言,为避免混淆intel语法,本文只介绍AT&T汇编,AT&T的第一个特点就是每个寄存器名前必须加‘%’,立即数前必须加‘$’

汇编在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为本地内存区域,即本地汇编外的不能进行访问。.comm是通用内存区域。
.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为本地内存区域,即本地汇编外的不能进行访问。.comm是通用内存区域。
.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为本地内存区域,即本地汇编外的不能进行访问,而.comm是通用内存区域
比如上面的定义
 .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段存放代码

相关文章

  • C/C++调用Fortran的DLL的操作过程

    C/C++调用Fortran的DLL的操作过程

    这篇文章主要介绍了C/C++调用Fortran的DLL,本文以一个简单的加法器为例,通过实例代码给大家介绍的非常详细,需要的朋友可以参考下
    2022-03-03
  • C++中友元的实例详解

    C++中友元的实例详解

    这篇文章主要介绍了C++中友元的实例详解的相关资料,希望通过本文大家能掌握友元的使用方法,需要的朋友可以参考下
    2017-09-09
  • C语言实现统计字符串单词数

    C语言实现统计字符串单词数

    这篇文章主要介绍了C语言实现统计字符串单词数,代码非常的简洁,有需要的小伙伴快来参考下。
    2015-03-03
  • C++结构体案例练习分享

    C++结构体案例练习分享

    这篇文章主要和大家分享几个C++ 结构体的案例练习,帮助大家更好的理解和学习c++,感兴趣的朋友可以了解下,希望能够给你带来帮助
    2022-04-04
  • 深入探究C++编程中的资源泄漏问题以及排查方法

    深入探究C++编程中的资源泄漏问题以及排查方法

    在C++程序开发维护过程中,时常会遇到资源泄漏问题,比如GDI对象泄漏、进程线程句柄泄漏以及内存泄漏问题,今天我们就来深入探讨一下这几类资源泄漏以及排查这些泄露的办法,需要的朋友可以参考下
    2023-10-10
  • C++之std::vector删除元素的几种方式及区别说明

    C++之std::vector删除元素的几种方式及区别说明

    这篇文章主要介绍了C++之std::vector删除元素的几种方式及区别说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-08-08
  • 详细分析Android中实现Zygote的源码

    详细分析Android中实现Zygote的源码

    这篇文章主要介绍了详细分析Android中实现Zygote的源码,包括底层的C/C++代码以及Java代码部分入口,需要的朋友可以参考下
    2015-07-07
  • vs code 配置python虚拟环境的方法

    vs code 配置python虚拟环境的方法

    这篇文章主要介绍了vs code 配置python虚拟环境的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-03-03
  • C++类和对象实战之Date类的实现方法

    C++类和对象实战之Date类的实现方法

    C++ 标准库没有提供所谓的日期类型,C++ 继承了C语言用于日期和时间操作的结构和函数,这篇文章主要给大家介绍了C++类和对象实战之Date类的实现方法,需要的朋友可以参考下
    2021-12-12
  • C语言container of()函数案例详解

    C语言container of()函数案例详解

    这篇文章主要介绍了C语言container of()函数案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-08-08

最新评论