Linux彙編初識

2021-06-29 00:12:19 字數 4102 閱讀 5557

2011-06-11 20:29:02

|  分類:

x86與arm

|  標籤:

movl

彙編器linux

**att |

舉報 |

字型大小訂閱

# hello.s---->  intel彙編的注釋用的; 而att用的## display a string "hello, world."

.section

.rodata

msg:

.ascii

"hello, world.\n"

.section

.text

.globl _start

_start

:movl $2,%

eax  

#呼叫fork系統呼叫

int$0x80

movl $4,%

eax

# system call系統呼叫號(sys_write)movl $1,%

ebx

# file descriptor引數一:檔案描述符(stdout)movl $msg,%

ecx

# string address引數二:要顯示的字串movl $14,%

edx

# string length引數三:字串長度

int$0x80          

#呼叫核心功能

movl $1,%

eax# 系統呼叫號(sys_exit)movl $0,%

ebx# 引數一:退出**

int$0x80

# 呼叫核心功能

wanny@wanny-c-notebook-***x:~$ as hello.s -o hello.o

wanny@wanny-c-notebook-***x:~$ ld hello.o -o hello

wanny@wanny-c-notebook-***x:~$ ./hello

hello, world.

hello, world.

二,linux 彙編工具

linux 平台下的彙編工具雖然種類很多,但同 dos/windows 一樣,最基本的仍然

是彙編器、聯結器和偵錯程式。

2.1.彙編器

彙編器(assembler)的作用是將用組合語言編寫的源程式轉換成二進位制形式的目標

**。linux 平台的標準彙編器是 gas,它是 gcc 所依賴的後台彙編工具,通常

包含在 binutils 軟體包中。gas 使用標準的 at&t 彙編語法,可以用來彙編用

at&t 格式編寫的程式:

例如:wangzhe@wangzhe-laptop:~$ as -o hello.o hello.s

linux 平台上另乙個經常用到的彙編器是 nasm,它提供了很好的巨集指令功能,並

能夠支援相當多的目標**格式,包括 bin、a.out、coff、elf、rdf 等。nasm 採用

的是人工編寫的語法分析器,因而執行速度要比 gas 快很多,更重要的是它使用

的是 intel 彙編語法,可以用來編譯用 intel 語法格式編寫的匯程式設計序:

例如:wangzhe@wangzhe-laptop:~$ nasm -f elf hello2.asm

2.2 鏈結器

由彙編器產生的目標**是不能直接在計算機上執行的,它必須經過鏈結器的處理

才能生成可執行**。鏈結器通常用來將多個目標**連線成乙個可執行**,這

樣可以先將整個程式分成幾個模組來單獨開發,然後才將它們組合(鏈結)成乙個應

用程式。 linux 使用 ld 作為標準的鏈結程式,它同樣也包含在 binutils 軟體包中。

匯程式設計序在成功通過 gas 或 nasm 的編譯並生成目標**後,就可以使用 ld 將

其鏈結成可執行程式了:

例如:wangzhe@wangzhe-laptop:~$ as --gstabs -o hello.o hello.s

wangzhe@wangzhe-laptop:~$ ld -o hello hello.o

wangzhe@wangzhe-laptop:gdb -q hello

三,系統呼叫

即便是最簡單的匯程式設計序,也難免要用到諸如輸入、輸出以及退出等操作,而要進

行這些操作則需要呼叫作業系統所提供的服務,也就是系統呼叫。除非你的程式只完成

加減乘除等數**算,否則將很難避免使用系統呼叫,事實上除了系統呼叫不同之外,各種

作業系統的彙編程式設計往往都是很類似的。

在 linux 平台下有兩種方式來使用系統呼叫:利用封裝後的 c 庫(libc)或者通過

彙編直接呼叫。其中通過組合語言來直接呼叫系統呼叫,是最高效地使用 linux 核心

服務的方法,因為最終生成的程式不需要與任何庫進行鏈結,而是直接和核心通訊。

和 dos 一樣,linux 下的系統呼叫也是通過中斷(int 0x80)來實現的。在執行 int

80 指令時,暫存器 eax 中存放的是系統呼叫的功能號,而傳給系統呼叫的引數則必須按

順序放到暫存器 ebx,ecx,edx,esi,edi 中,當系統呼叫完成之後,返回值可以在暫存器 eax

中獲得。

所有的系統呼叫功能號都可以在檔案 /usr/include/bits/syscall.h 中找到,為了便

於使用,它們是用 sys_這樣的巨集來定義的,如 sys_write、sys_exit 等。例

如,經常用到的 write 函式是如下定義的:

ssize_t write(int fd, const void *buf, size_t count);

該函式的功能最終是通過 sys_write 這一系統呼叫來實現的。根據上面的約定,參

數 fb、buf 和 count 分別存在暫存器 ebx、ecx 和 edx 中,而系統呼叫號 sys_write

則放在暫存器 eax 中,當 int 0x80 指令執行完畢後,返回值可以從暫存器 eax 中獲得。

或許你已經發現,在進行系統呼叫時至多只有 5 個暫存器能夠用來儲存引數,難道

所有系統呼叫的引數個數都不超過 5 嗎?當然不是,例如 mmap 函式就有 6 個參

數,這些引數最後都需要傳遞給系統呼叫 sys_mmap:

整理void * mmap(void *start, size_t length, int prot , int flags, int fd,

off_t offset);

當乙個系統呼叫所需的引數個數大於 5 時,執行 int 0x80 指令時仍需將系統呼叫功能號儲存

在暫存器 eax 中,所不同的只是全部引數應該依次放在一塊連續的記憶體區域裡,同時在暫存器

ebx 中儲存指向該記憶體區域的指標。系統呼叫完成之後,返回值仍將儲存在暫存器 eax 中。

由於只是需要一塊連續的記憶體區域來儲存系統呼叫的引數,因此完全可以像普通的

函式呼叫一樣使用棧(stack)來傳遞系統呼叫所需的引數。但要注意一點,linux 採

用的是 c 語言的呼叫模式,這就意味著所有引數必須以相反的順序進棧,即最後

乙個引數先入棧,而第乙個引數則最後入棧。如果採用棧來傳遞系統呼叫所需的參

數,在執行 int 0x80 指令時還應該將棧指標的當前值複製到暫存器 ebx 中。

四,c語言中內聯彙編

例如:#include

int main(void)

通常嵌入到 c **中的彙編語句很難做到與其它部分沒有任何關係,因此更多時

候需要用到完整的內聯彙編格式:

__asm__("asm statements" : outputs : inputs : registers-modified);

插入到 c **中的彙編語句是以":"分隔的四個部分,其中第一部分就是彙編**

本身,通常稱為指令部,其格式和在組合語言中使用的

彙編(1)初識彙編

7.記憶體位址空間 8.主機板 9.介面卡 10.各類儲存器晶元 組合語言是直接在硬體之上工作的程式語言,首先要了解硬體系統的結構,才能有效的應用組合語言對其程式設計 計算機能讀懂的只有機器指令,什麼是機器指令?例1 指令 01010000 push ax 例2 s 768 12288 1280 機...

初識彙編 Debug命令

1,彙編debug操作符 debug命令操作符 r命令 檢視 改變cpu暫存器的內容 d命令 以16進製制形式顯示一塊記憶體區域的內容 e命令 改寫記憶體中的內容 a命令 以彙編指令的格式在記憶體中寫入一條機器指令 u命令 將記憶體中的機器指令翻譯為彙編指令 t命令 執行一條機器指令 g命令 執行到...

組合語言 匯程式設計序初識

mov ax,bx 將bx暫存器中的資料複製移動到ax中 add ax,bx 執行ax bx運算,將運算結果賦值給ax,即ax ax bx sub ax,bx 執行ax bx運算,將運算結果賦值給ax,即ax ax bx inc ax 執行 ax 1 運算,將運算結果賦值給ax,即ax ax 1 j...