C指針原理教程之AT&T彙編

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段存放代碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章