AT&T彙編語言——簡單實例及工具演示

今天就來用具體實例代碼來運用一下昨天所說的只個工具的用法吧

這幾個實例主要的目的是來熟悉一下彙編相關工具的用法及應用一下昨天剛說的彙編程序模板

 我們用到的工具主要有as,ld,gcc,gdb,當然,它們是運行在linux系統下的

廢話少說,直接來例子了。嗯,再說一句,下面的例子是參考或來自《彙編語言程序設計》Richard Blum的

 

例一:打印出"hello,world!"

#hellowrold.s print "hello,world!"

.section .data
	output:
		.ascii "hello,world\n"

.section .text
.globl _start
_start:
	movl $4, %eax
	movl $1, %ebx
	movl $output,%ecx
	movl $12,%edx
	int  $0x80
	movl $1, %eax
	movl $0, %ebx
	int  $0x80

簡單說一下代碼:

首先,在數據段中聲明一個字符串:

output:
		.ascii "hello,world\n"

.asscii聲明使用ASCII字符聲明一個文本字符串。字符串元素被預定義並且放在內存中,其起始內存位置由標籤output指示。

下面是聲明程序的指令碼段和一般的起始標籤,_start是鏈接器默認的起始代碼:

.section .text
.globl _start
_start:
下面是直接調用write系統調用來顯示文本內容

        movl $4, %eax
	movl $1, %ebx
	movl $output,%ecx
	movl $12,%edx
	int  $0x80
Linux下write系統調用的參數:

EAX包含系統調用值,write是4

EBX包含要寫入的文件描述符,我們知道,Linux終端中0表示標準輸入,1表示標準輸出,2表示錯誤輸出,這裏將1傳入EBX,也就是表示標準輸出

ECX包含字符串的開頭

EDX包含字符串的長度

用樣,下面也是系統調用 ,1表示退出函數,返回值爲0

        movl $1, %eax
	movl $0, %ebx
	int  $0x80

編譯運行結果如下:


先解釋編譯參數,

第一步:首先編譯成二進制文件  as --32 -o hellowrold.o hellowrold.s

as表示用as彙編器,

--32表示將目標代碼編譯成ia-32代碼格式

-o hellowrold.o 表示目標文件是hellowrold.o(好像,寫錯文件名了Orz尷尬)

hellowrold.s就是源代碼了(本來要定成helloworld.s的,錯了就錯了吧)

第二步:然後,將hellowrold.o鏈接成可執行文件

ld -m elf_i386 -o hellowrold hellowrold.o 

ld表示是用ld鏈接

-m elf_i386 表示生成32 elf位 elf格式文件

-o hellowrold表示生成的文件是hellowrold

hellowrold.o 是在第一階段生成的二進制文件


再來試試gdb這個調試工具,彙編器as的多了個參數 -g,表示生成debug 代碼。gdb  hellowrold運行調試,界面如下:

gdb的用法主要有幾個:list顯示代碼,break設置段點, info register顯示所有寄存器的值,print打印特定變量的值,x顯示特定內存位置的值,step下一指令,run運行代碼。

演示一下:

list,列出代碼


break設置斷點,這裏是在特定的標籤中設置,break有以下方式設置斷點:

1.到達某個標籤

2.到達源代碼中的某個行號

3.數據值到達特定值

4.函數執行了指寶的次數之後


print 打印出相應的值,print 的輸出格式有:

print/d 輸出十進制值

print/t  輸出二進制值

print/x 輸出十六進制值


info register 打印出所有寄存器值


當然,我們的例子只要改一下,將 代碼入口標籤_start改成main就可以用gcc來編譯。

gcc -m32 -o hellowrold hellowrold.s  


就可以編譯成功了。

例二、下面再說個在彙編語言中調用c函數庫的例子。

.section .data
	output:
		.ascii "The number is %d\n"
.section .bss
	.lcomm buffer,18
.section .text
		
.globl _start
_start:
	
	pushl $520
	pushl $output
	call  printf
	addl  $8,%esp
	pushl $0
	call  exit
如下方法編譯該代碼,可以看出,ld鏈接的時候多了幾個參數。

讓我來一一說一下多出來的兩個參數的含義吧。

我們知道 ,在linux中,把C函數連接到彙編語言程序有兩種方法。第一種中做靜態鏈接(static linking).靜態鏈接把函數目標代碼直接連接到應用 程序的可執行程序文件中。這樣會創建巨大的可執行程序,而且,如果同時運行程序的多個實例,會造 成內在浪費(每個函數都有其自己的相同函數拷貝)

第二種方法是動態鏈接。

在Linux中,標準C的動態庫位於lib.so.x文件中,在我的系統(ubutnu 14.04 )中,這個文件是libc.so.6,由於我採用兼容方式運行,所以,我的系統有兩個該文件,一個是32位的(/lib/i386-linux-gnu/libc.so.6),還有一個是64位的(/lib/x86_64-linux-gnu/libc.so.6)。在使用gcc時,gcc是自動將c語言鏈接到該庫,我們使用ld,爲了鏈接libc.so文件,必須使用gnu連接器的-l 參數,不用指定完整的庫名稱。連接器假設在它能找到的位置存在libxso文件,基中x是命令行參數指定的庫名稱,我們的是c,故使用

-lc

理論上,我們不用加參數 -dynamic-linker就可以運行了,可事實上,編譯是通過了,但是運行不了。

bash: ./print: No such file or directory
爲什麼呢?
問題在於連接器是能夠解析C函數了,但是函數本身沒有包含在最終可執行程序中。鏈接器假設運行時程序能夠找到該庫文件,所以編譯進不出錯。但事實上,我們的程序找不到該庫文件。爲了解決這個問題,還必須指定在程序運行時加載動態庫的程序。對於LINUX,這個程序是linux.so.2,在我的系統下,它位於/lib下。爲了指定這個程序,必須使用gnu鏈接器的 -dynamic-linker,故還要添加參數

-dynamic-linker
其實,我們也可以直接用 gcc編譯,只要把_start標籤改成 main就可以如下方法 編譯了

gcc -o print print.s



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