天天看點

C語言及AT&T彙編格式改寫《現代X86彙編語言程式設計》範例(一)

作者:C語言與彙編

《現代X86彙編語言程式設計》的示範程式在windows作業系統“Core”CPU的操作環境下,采用C++語言嵌套intel風格的MASN彙編語言進行編寫,本文拟在Linux作業系統、“Celeron N2840”CPU的環境下,使用C語言嵌套AT&T彙編格式,對書中第一個示範程式ch02_01(中文版第18頁)進行改寫。改寫的程式中變量名稱、變量值、程式結構與示範程式一緻,其餘内容均屬原創。文中主要涉及的知識包括:1、如何在C語言中嵌套彙編語言;2、如何使用gdb調試工具;3、C語言中對變量的定義和指派存放在哪些硬體空間中。程式實作的功能是對變量a、b、c、d進行指派,并輸出a+b+c-d的計算值。

一、在同一檔案夾内建立.c、.asm、Makefile檔案

$touch ch02_01.c

$touch ch02_01_01.asm

$touch Makefile

說明:書中的彙程式設計式檔案名為ch02_01.asm,若采用這個檔案名,在編譯時c程式與彙程式設計式都會産生同名的目标檔案ch02_01.o,導緻編譯失敗。是以彙程式設計式與C程式不能同名。

二、編輯ch02_01.c檔案如下:

#include<stdio.h>

#include<inttypes.h> //該頭檔案的作用是進行轉換進制,如果沒有轉換進制,彙編引用時會被認為是十六進制數

int64_t IntegerAddSub_(int64_t a,int64_t b,int64_t c,int64_t d); //定義即将引用的彙編函數IntegerAddSub_

int main(void)

{

int64_t a;

int64_t b;

int64_t c;

int64_t d;

int result; //由于IntegerAddSub_函數已經轉化為十進制,是以result變量不需在轉進制

a=10;

b=20;

c=30;

d=18;

result=IntegerAddSub_(a,b,c,d); //引起彙編函數

printf("test1\n %ld\n %ld\n %ld\n %ld\n result=%" PRIi64 "\n",a,b,c,d,result);

//PRIi64兩邊有空格,表示列印64位有符号資料,i表示有符号,本案例也可換為%ld

a=101;

b=34;

c=5; //原書案例中此數為負數,本例直接采用負數會出現計算錯誤,因時間關系,以後在繼續研究如何采用負值計算

d=18;

result=IntegerAddSub_(a,b,c,d); //再次引用彙編函數

printf("test2\n %ld\n %ld\n %ld\n %ld\n result=%" PRIi64 "\n",a,b,c,d,result);

return 0;

}

說明:書中的示範程式可以對有符号的負數進行計算,由于時間關系,這次改寫尚不能滿足負數計算,暫時隻能進行無符号的正整數計算。

三、編輯ch02_01_01.asm檔案(以下先展示錯誤代碼,錯誤代碼由書中代碼直接改寫,後面再說明如何調試、修正)

.text //以下為代碼段

.global IntegerAddSub_ //定義标簽IntegerAddSub_,該标簽處的代碼将被C程式作為函數引用

IntegerAddSub_: //标簽入口

movq %rcx,%rax //将寄存器rcx的值賦予rax,意味着第一個變量a的值在rcx中

addq %rdx,%rax //将寄存器rdx與rax的值相加,并将和值存入rax中,意味着第二個變量b存放在rdx中

addq %r8d,%rax //同上

subq %r9d,%rax //将寄存器rax的值減去r9d中,計算值存入rax

ret

四、編輯Makefile檔案

ch02_01:ch02_01_01.o ch02_01.c //表示可執行程式ch02_01由ch02_01_01.o和ch02_01.c共同形成

gcc ch02_01_01.o ch02_01.c -o ch02_01 //用gcc編譯、連結ch02_01_01.o和ch02_01.c,形成可執行檔案ch02,主要開頭縮進要用“TAB”鍵,不能用空格鍵。

ch02_01_01.o:ch02_01_01.asm //表示目标檔案ch02_01_01由彙程式設計式ch02_01_01.asm形成

as ch02_01_01.asm -o ch02_01_01.o //用as編譯彙程式設計式ch02_01_01.asm,形成目标檔案ch02_01_01.o

五、編譯、運作可執行檔案

$make

$./ch02_01

六、調試

步驟五中程式可以正常通過編譯,但是執行可執行檔案後産生錯誤的計算結果,用gdb調試,檢視錯誤原因。

$gdb ./ch02_01

(gdb)break main //在C程式main 函數處打斷

(gdb)run //運作程式,運作至main函數入口處會被中斷

(gdb)disas //顯示彙編代碼

顯示的彙編代碼如下:

push %rbp //在為變量提供位址前,将rbp的位址壓棧儲存

mov %rsp,%rbp //将rsp的位址指派于rbp

sub $0x30,%rsp

movq $0xa,-0x8(%rbp) //rbp位址偏移-0x8的位置處存放數值0xa,即十進制10

movq $0x14,-0x10(%rbp) //rbp位址偏移-0x10的位置處存放數值0x14,即十進制20

movq $0x1e,-0x18(%rbp) //rbp位址偏移-0x18的位置處存放數值0x1e,即十進制30

movq $0x12,-0x20(%rbp) //rbp位址偏移-0x20的位置處存放數值0x12,即十進制18

mov -0x20(%rbp),%rcx //将rbp位址偏移-0x20處的資料指派于rcx

mov -0x18(%rbp),%rdx //将rbp位址偏移-0x18處的資料指派于rdx

mov -0x10(%rbp),%rsi //将rbp位址偏移-0x10處的資料指派于rsi

mov -0x8(%rbp),%rax //将rbp位址偏移-0x8處的資料指派于rax

mov %rax,%rdi

call ...<IntegerAddSub_> //調用IntegerAddSub_函數

說明:由上可知變量a、b、c、d的指派分别存放在寄存器rax、rsi、rdx、rcx中,變量指派位置與書中代碼展示的分别位于ecx、edx、r8d、r9d不同,原因可能是因為使用的CPU不同,是以CPU指令集不同。

七、修正彙程式設計式如下

.text

.global IntegerAddSub_

IntegerAddSub_:

addq %rsi,%rax

addq %rdx,%rax

subq %rcx,%rax

ret

八、編譯、運作

$make

$./ch02_01

最終産生正确計算結果。

繼續閱讀