因為在linux的核心中,很多跟底層硬體接觸的都使用組合語言,但是Linux不僅使用一種組合語言,除了Intel的組合語言之外,還是用AT&T的組合語言,因此可以說這兩個是一個基礎,Intel的彙編相信很多學電腦的人都學習過,但是AT&T的就不一定了,個人認為他們的思想都是一樣的,只不過是文法不同,初級學習可以看如下的文章(文章轉自http://blog.chinaunix.net/u1/59572/showart_1148334.html)
一、AT&T 格式Linux 彙編文法格式
在 AT&T 彙編格式中,寄存器名要加上 '%' 作為首碼;而在 Intel 彙編格式中,寄存器名不需要加首碼。例如:
AT&T 格式
Intel 格式
pushl %eax
push eax
在 AT&T 彙編格式中,用 '$' 首碼表示一個立即運算元;而在 Intel 彙編格式中,立即數的表示不用帶任何首碼。例如:
AT&T 格式
Intel 格式
pushl $1
push 1
AT&T 和 Intel 格式中的源運算元和目標運算元的位置正好相反。在 Intel 彙編格式中,目標運算元在源運算元的左邊;而在 AT&T 彙編格式中,目標運算元在源運算元的右邊。例如:
AT&T 格式
Intel 格式
addl $1, %eax
add eax, 1
在 AT&T 彙編格式中,運算元的字長由操作符的最後一個字母決定,尾碼'b'、'w'、'l'分別表示運算元為位元組(byte,8 位元)、字(word,16 位元)和長字(long,32位元);而在 Intel 彙編格式中,運算元的字長是用 "byte ptr" 和 "word ptr" 等首碼來表示的。例如:
AT&T 格式
Intel 格式
movb val, %al
mov al, byte ptr val
在 AT&T 彙編格式中,絕對轉移和調用指令(jump/call)的運算元前要加上'*'作為首碼,而在 Intel 格式中則不需要。
遠程轉移指令和遠程子調用指令的作業碼,在 AT&T 彙編格式中為 "ljump" 和 "lcall",而在 Intel 彙編格式中則為 "jmp far" 和 "call far",即:
AT&T 格式
Intel 格式
ljump $section, $offset
jmp far section:offset
lcall $section, $offset
call far section:offset
與之相應的遠程返回指令則為:
AT&T 格式
Intel 格式
lret $stack_adjust
ret far stack_adjust
在 AT&T 彙編格式中,記憶體運算元的定址方式是
section:disp(base, index, scale)
而在 Intel 彙編格式中,記憶體運算元的定址方式為:
section:[base + index*scale + disp]
由於 Linux 工作在保護模式下,用的是 32 位線性地址,所以在計算地址時不用考慮段基址和位移量,而是採用如下的地址計算方法:
disp + base + index * scale
下面是一些記憶體運算元的例子:
AT&T 格式
Intel 格式
movl -4(%ebp), %eax
mov eax, [ebp - 4]
movl array(, %eax, 4), %eax
mov eax, [eax*4 + array]
movw array(%ebx, %eax, 4), %cx
mov cx, [ebx + 4*eax + array]
movb $4, %fs:(%eax)
mov fs:eax, 4
二、Hello World!
既然所有程式設計語言的第一個例子都是在螢幕上列印一個字串 "Hello World!",那我們也以這種方式來開始介紹 Linux 下的組合語言程式設計。
在 Linux 作業系統中,你有很多辦法可以實現在螢幕上顯示一個字串,但最簡潔的方式是使用 Linux 核心提供的系統調用。使用這種方法最大的好處是可以直接和作業系統的核心進行通訊,不需要連結諸如 libc 這樣的函數庫,也不需要使用 ELF 解譯器,因而代碼尺寸小且執行速度快。
Linux 是一個運行在保護模式下的 32 位作業系統,採用 flat memory 模式,目前最常用到的是 ELF 格式的二進位代碼。一個 ELF 格式的可執行程式通常劃分為如下幾個部分:.text、.data 和 .bss,其中 .text 是唯讀代碼區,.data 是可讀可寫的資料區,而 .bss 則是可讀可寫且沒有初始化的資料區。代碼區和資料區在 ELF 中統稱為 section,根據實際需要你可以使用其它標準的 section,也可以添加自訂 section,但一個 ELF 可執行程式至少應該有一個 .text 部分。下面給出我們的第一個組譯工具,用的是 AT&T 組合語言格式:
例1. AT&T 格式
#hello.s
.data # 資料區段聲明
msg : .string "Hello, world!//n" # 要輸出的字串
len = . - msg # 字串長度
.text # 程式碼片段聲明
.global _start # 指定入口函數
_start: # 在螢幕上顯示一個字串
movl $len, %edx # 參數三:字串長度
movl $msg, %ecx # 參數二:要顯示的字串
movl $1, %ebx # 參數一:檔案描述符(stdout)
movl $4, %eax # 系統調用號(sys_write)
int $0x80 # 調用核心功能
# 退出程式
movl $0,%ebx # 參數一:結束代碼
movl $1,%eax # 系統調用號(sys_exit)
int $0x80 # 調用核心功能
初次接觸到 AT&T 格式的彙編代碼時,很多程式員都認為太晦澀難懂了,沒有關係,在 Linux 平台上你同樣可以使用 Intel 格式來編寫組譯工具:
例2. Intel 格式
; hello.asm
section .data ; 資料區段聲明
msg db "Hello, world!", 0xA ; 要輸出的字串
len equ $ - msg ; 字串長度
section .text ; 程式碼片段聲明
global _start ; 指定入口函數
_start: ; 在螢幕上顯示一個字串
mov edx, len ; 參數三:字串長度
mov ecx, msg ; 參數二:要顯示的字串
mov ebx, 1 ; 參數一:檔案描述符(stdout)
mov eax, 4 ; 系統調用號(sys_write)
int 0x80 ; 調用核心功能
; 退出程式
mov ebx, 0 ; 參數一:結束代碼
mov eax, 1 ; 系統調用號(sys_exit)
int 0x80 ; 調用核心功能
上面兩個組譯工具採用的文法雖然完全不同,但功能卻都是調用 Linux 核心提供的 sys_write 來顯示一個字串,然後再調用 sys_exit 退出程式。在 Linux 核心源檔案 include/asm-i386/unistd.h 中,可以找到所有系統調用的定義。
三、Linux 彙編工具
Linux 平台下的彙編工具雖然種類很多,但同 DOS/Windows 一樣,最基本的仍然是彙編器、連接器和調試器。
1.彙編器
彙編器(assembler)的作用是將用組合語言編寫的來源程式轉換成二進位形式的目標代碼。Linux 平台的標準彙編器是 GAS,它是 GCC 所依賴的後台彙編工具,通常包含在 binutils 軟體包中。GAS 使用標準的 AT&T 彙編文法,可以用來彙編用 AT&T 格式編寫的程式:
[xiaowp@gary code]$ as -o hello.o hello.s
Linux 平台上另一個經常用到的彙編器是 NASM,它提供了很好的巨集指令功能,並能夠支援相當多的目標代碼格式,包括 bin、a.out、coff、elf、rdf 等。NASM 採用的是人工編寫的文法分析器,因而執行速度要比 GAS 快很多,更重要的是它使用的是 Intel 彙編文法,可以用來編譯用 Intel 文法格式編寫的組譯工具:
[xiaowp@gary code]$ nasm -f elf hello.asm
2.連結器
由彙編器產生的目標代碼是不能直接在電腦上啟動並執行,它必須經過連結器的處理才能產生可執行代碼。連結器通常用來將多個目標代碼串連成一個可執行代碼,這樣可以先將整個程式分成幾個模組來單獨開發,然後才將它們組合(連結)成一個應用程式。 Linux 使用 ld 作為標準的連結程式,它同樣也包含在 binutils 軟體包中。組譯工具在成功通過 GAS 或 NASM 的編譯並產生目標代碼後,就可以使用 ld 將其連結成可執行程式了:
[xiaowp@gary code]$ ld -s -o hello hello.o
3.調試器
有人說程式不是編出來而是調出來的,足見調試在軟體開發中的重要作用,在用組合語言編寫程式時尤其如此。Linux 下調試彙編代碼既可以用 GDB、DDD 這類通用的調試器,也可以使用專門用來調試彙編代碼的 ALD(Assembly Language Debugger)。
從調試的角度來看,使用 GAS 的好處是可以在產生的目標代碼中包含符號表(symbol table),這樣就可以使用 GDB 和 DDD 來進行源碼級的調試了。要在產生的可執行程式中包含符號表,可以採用下面的方式進行編譯和連結:
[xiaowp@gary code]$ as --gstabs -o hello.o hello.s
[xiaowp@gary code]$ ld -o hello hello.o
執行 as 命令時帶上參數 --gstabs 可以告訴彙編器在產生的目標代碼中加上符號表,同時需要注意的是,在用 ld 命令進行連結時不要加上 -s 參數,否則目標代碼中的
本文來自CSDN部落格,轉載請標明出處:http://blog.csdn.net/daiguoliangfirst/archive/2010/08/07/5796170.aspx