1、 參數(shù)傳遞
十余年的江干網(wǎng)站建設(shè)經(jīng)驗(yàn),針對(duì)設(shè)計(jì)、前端、開發(fā)、售后、文案、推廣等六對(duì)一服務(wù),響應(yīng)快,48小時(shí)及時(shí)工作處理。成都營(yíng)銷網(wǎng)站建設(shè)的優(yōu)勢(shì)是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動(dòng)調(diào)整江干建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無(wú)論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計(jì),從而大程度地提升瀏覽體驗(yàn)。創(chuàng)新互聯(lián)從事“江干網(wǎng)站設(shè)計(jì)”,“江干網(wǎng)站推廣”以來,每個(gè)客戶項(xiàng)目都認(rèn)真落實(shí)執(zhí)行。
二、匯編程序、C程序相互調(diào)用舉例
1、 C程序調(diào)用匯編程序
匯編程序的設(shè)計(jì)要遵守ATPCS(ARM—Thumb Procedure Call Standard),保證程序調(diào)用時(shí)參數(shù)的正確傳遞。在匯編程序中使用EXPORT 偽操作聲明本程序,使得本程序可以被別的程序調(diào)用。在C程序使用extern聲明該匯編程序。
下面是一個(gè)C程序調(diào)用匯編程序的例子。其中匯編程序strcopy實(shí)現(xiàn)字符串復(fù)制功能,C程序調(diào)用strcopy完成字符串復(fù)制的工作。
淺談C程序中調(diào)用匯編模塊的方法
C語(yǔ)言是目前非常流行的一種編程語(yǔ)言,除具有高級(jí)語(yǔ)言使用方便靈活、數(shù)據(jù)處理能力強(qiáng)、 編程簡(jiǎn)單等優(yōu)點(diǎn)外,還可實(shí)現(xiàn)匯編語(yǔ)言的大部分功能,如可直接對(duì)硬件進(jìn)行操作、生成的 目標(biāo)代碼質(zhì)量較高且執(zhí)行的速度較快等。所以在工程上對(duì)硬件處理速度要求不很高的情況下, 基本可以用C代替匯編語(yǔ)言,編寫接口電路的控制軟件。但C也不能完全取代匯編語(yǔ)言,如在一些對(duì)速度要求很高的實(shí)時(shí)控制系統(tǒng)中,以及對(duì)硬件的特殊控制方面,C有時(shí)也不能完全很好勝任,還需要匯編語(yǔ)言來編寫。因?yàn)閰R編語(yǔ)言目標(biāo)代碼更精練,對(duì)硬件直接控制能力更強(qiáng)和執(zhí)行速度更快,但匯編語(yǔ)言編程煩難、表達(dá)能力差也顯而易見。比較好的解決辦法是C與匯編語(yǔ)言混合編程,即用C編寫軟件的調(diào)度程序、用戶界面以及速度要求不高的控制部分,而用匯編語(yǔ)言對(duì)速度敏感部分提供最高速度的處理模塊,供C調(diào)用。這種方法提供了最佳的軟件設(shè)計(jì)方案,做到了兼顧速度效率高和靈活方便。由于本人的畢業(yè)設(shè)計(jì)需要C程序中調(diào)用匯編模塊的方法來提高ARM定點(diǎn)指令的執(zhí)行速度,故對(duì)這方面進(jìn)行了學(xué)習(xí)。學(xué)習(xí)心得如下:
對(duì)于C和匯編語(yǔ)言的接口主要有兩個(gè)問題需要解決。
一、調(diào)用者與被調(diào)用者的參數(shù)傳遞
這種數(shù)據(jù)傳遞通過堆棧完成,在執(zhí)行調(diào)用時(shí)從調(diào)用程序參數(shù)表中的最后一個(gè)參數(shù)開始 ,自動(dòng)依次壓入堆棧;將所有參數(shù)壓入堆棧后,再自動(dòng)將被調(diào)用程序執(zhí)行結(jié)束后的返回地址 (斷點(diǎn))壓入堆棧,以使被調(diào)程序結(jié)束后能返回主調(diào)程序的正確位置而繼續(xù)執(zhí)行。例如一調(diào)用名為add匯編程序模塊的主函數(shù):main( ){...... add(dest,op1,op2,flages);......}。在此例中對(duì)主函數(shù)進(jìn)行反匯編,主函數(shù)在調(diào)用add函數(shù)前自動(dòng)組織的堆棧。
.
.
.
lea 0xfffffffe8(%ebp),%eax #flages數(shù)組的首地址入棧
push %eax
pushl 0xfffffff8(%ebp) #OP2入棧
pushl 0xfffffffc(%ebp) #OP1 入棧
pushl 0xfffffff0(%ebp) #dest地址入棧
call 0x80483f0 add #調(diào)用add函數(shù)
.
.
執(zhí)行完add調(diào)用語(yǔ)句后,棧內(nèi)數(shù)據(jù)結(jié)果如圖一所示。
進(jìn)入?yún)R編子程序后,為了能正確獲取主調(diào)程序并存入堆棧中的數(shù)據(jù),被調(diào)的匯編子程序先后要做如下一些工作:
1、 保存esp的副本
進(jìn)入?yún)R編子程序后,子程序中免不了要有壓棧和出棧的操作,故ESP時(shí)刻在變化。為了能用 ESP訪問堆棧中的參數(shù),安全辦法是一進(jìn)入子程序后,先為ESP制副本,以后對(duì)傳遞參數(shù)的訪問 都用副本進(jìn)行。一般可用EBP保存ESP,如:
push %ebp
mov %ebp,%esp
2、保留數(shù)據(jù)空間
如果匯編子程序中需要一些局部數(shù)據(jù),可以簡(jiǎn)單地減小ESP的值,以便在??臻g中保留出一段存貯區(qū),用于存放局部數(shù)據(jù),該區(qū)域須在子程序結(jié)束后恢復(fù)。如下語(yǔ)句可以保留一個(gè)局部數(shù)據(jù)區(qū):
push %ebp
mov %ebp ,%esp
subl space,%esp;設(shè)space=4
movl $0x0,%ebp
movl $0x0,-2(%ebp)
如上語(yǔ)句段中,space是局部數(shù)據(jù)的總字節(jié)數(shù)。在以后的應(yīng)用中,由于ESP是變化的,而 EBP是 固定的,用負(fù)偏移量可以存取局部變量。上例利用EBP及偏移量,將兩個(gè)字的局部數(shù) 據(jù)初始化為0。
3、保留寄存器值
如果在被調(diào)子程序中用到ESI、EDI等其它寄存器,則應(yīng)先把它們壓入堆棧,以保留寄存器原值 。例如,下例就是將ESI和EDI寄存器的值壓棧:
pushl %ebp
movl %ebp ,%esp
subl $space ,%esp,
pushl %esi
pushl %edi
4、獲取傳遞參數(shù)
作完了1~3步的操作后,結(jié)合上面C程序傳送參數(shù)這一例子,現(xiàn)在棧結(jié)構(gòu)如圖二所示。
由此可見,EBP保留了ESP在參數(shù)傳遞完并將EBP壓棧后的一個(gè)副本,利用EBP可以很方便地訪問各參數(shù)。現(xiàn)假設(shè)各參數(shù)都是2字節(jié)的整數(shù)值,在小模式編譯方式共占用2個(gè)字節(jié)。如果要將傳遞的參數(shù)op1、op2取出,并分別賦給ebx、ecx寄存器,可由下列語(yǔ)句完成這一功能:
movl 0x8(%ebp),%eax
movl 0xc(%ebp),%ecx
5、子程序返回值
當(dāng)子程序的執(zhí)行結(jié)果需要返回時(shí),根據(jù)返回值的字長(zhǎng),C按如下約定接收返回值:1字節(jié)在AL 寄存器中;2字節(jié)在EAX寄存器中;4字節(jié)則高位部分在EDX中、低位部分在EAX寄存器中。C可從這些寄存器中取出返回值。
6、退出匯編子程序
結(jié)束匯編子程序的步驟如下:
1) 若ESS、EDS、ESI或EDI已被壓棧,則需按保存它們的相反順序彈出它們。
2) 若在過程開始時(shí)分配了局部數(shù)據(jù)空間,則以指令 mov %esp和%ebp 恢復(fù)%esp。
3) 以指令pop %ebp 恢復(fù)%ebp ,該步是必須的?;蛘呖梢杂胠eave語(yǔ)句來恢復(fù)%ebp 。它相當(dāng)于movl %ebp, %esp; popl %ebp
4) 最后以ret結(jié)束匯編程序。
二、 說明和建立調(diào)用者與被調(diào)用者間的連系
為了建立調(diào)用與被調(diào)用模塊間的連接關(guān)系,被調(diào)用的匯編程序應(yīng)用global,說明其可被外部模塊調(diào)用;而調(diào)用程序則應(yīng)預(yù)先說明要引用的外部模塊名。下面通過我的例子進(jìn)行說明,該例是C調(diào)用add0的匯編子程序。程序清單如下:
/* add.c */
#include stdio.h
extern void add(int *dest,int op1,int op2,short int*flages);
/*聲明調(diào)用外部的匯編函數(shù)*/
int main(void){
int op1,op2,result;
int *dest=result;
short int flages[4]={0,0,0,0};
printf("please enter two soure operater:");
scanf("%x%x",op1,op2);
add(dest,op1,op2,flages);/*調(diào)用add0函數(shù)*/
printf("The result of ADD is :%x\n flages N(negative) Z(zero) C(carry) V(overflow:%d,%d,%d,%d\n",*dest,flages[3],flages[2],flages[1],flages[0]);
return 0;
}
#add.s
.text
.align 2
.global add
.type add,function
#定義add為外部可調(diào)用的函數(shù)
add:
push %ebp #ebp寄存器內(nèi)容壓棧,保存add函數(shù)的上級(jí)調(diào)用函數(shù)的?;刂?/p>
mov %esp,%ebp #esp值賦給ebp,設(shè)置add函數(shù)的?;刂?/p>
mov 0x8(%ebp),%edx
mov 0x10(%ebp),%eax
add 0xc(%ebp),%eax
mov %eax,(%edx)
mov 0x14(%ebp),%eax
jo OF
C:
jc CF
S:
js SF
jz ZF
jmp out
OF:
movw $0x1,(%eax)
jmp C
CF:
movw $0x1,0x2(%eax)
jmp S
SF:
movw $0x1,0x6(%eax)
movw $0x0,0x4(%eax)
jmp out
ZF:
movw $0x1,0x4(%eax)
movw $0x0,0x6(%eax)
out:
leave #將ebp值賦給esp,pop先前棧內(nèi)的上級(jí)函數(shù)棧的基地址給#ebp,恢復(fù)原棧基址
ret #add函數(shù)返回,回到上級(jí)的調(diào)用函數(shù)
其中.text 標(biāo)志一個(gè)代碼段的開始,這是ATT的段格式;global add;\n
type add,function說明add是公用的,可以由外部其它單獨(dú)編譯模塊調(diào)用。
將C源程序以文件名add.c存盤,匯編語(yǔ)言源程序以add.s 存盤;通過MAKE進(jìn)行編譯和連接連接代碼如下:
all: myadd
myadd: adds.o addc.o
gcc –o myadd adds.o adc.o
adds.o: add.s
as –o adds.o add.s
addc.o: add.c
gcc –g –o addc.o add.c
由上可見,在C中調(diào)用匯編模塊很方便。所以我們?cè)趯?shí)際軟件開發(fā)中,可以采用混合編程的技術(shù),從而盡可能利用各語(yǔ)言的優(yōu)勢(shì)。既滿足實(shí)際問題的需要,又簡(jiǎn)化設(shè)計(jì)過程,達(dá)到事半功倍的效果。
1、如果匯編程序是可執(zhí)行文件,比如exe文件,則可以使用system函數(shù)直接調(diào)用。比如下面的代碼,用system()打開windows上的記事本程序。
#include?stdio.h
#include?stdlib.h
int?main()
{
system("notepad.exe");
return?0;
}
2、在C語(yǔ)言源碼中,可以通過內(nèi)聯(lián)匯編來直接編寫匯編程序代碼。不同的編譯器使用內(nèi)聯(lián)匯編的方法不同,vc/vs編譯器中一般使用__asm關(guān)鍵字來使用內(nèi)聯(lián)匯編,gcc編譯器一般使用asm關(guān)鍵字來使用內(nèi)聯(lián)匯編,以vc6.0為例,下面的代碼通過使用內(nèi)聯(lián)匯編來計(jì)算1+1,并將結(jié)果保存到int型變量result中。
#include?stdio.h
int?main()
{
int?result;
_asm?{
mov?eax,1
mov?ebx,1
add?eax,ebx
mov?result,?eax
}
printf("1+1=%d\n",?result);
return?0;
}
一般都是C調(diào)用匯編的,你倒是反過來了,在C函數(shù)中,如果要嚴(yán)格時(shí)序或者精確操作的要求,某一段會(huì)用匯編來寫,不管是C調(diào)用匯編,還是匯編調(diào)用C,你都要對(duì)編譯環(huán)境相當(dāng)熟悉,C的零時(shí)變量一邊用在第一組的R2、R3、R4、R5;R6和R7一般用來傳遞數(shù)據(jù),個(gè)人理解。
1。對(duì)于“匯編調(diào)用”:
我知道你要調(diào)用func,而不是它本身,但如果這個(gè)函數(shù)比較復(fù)雜時(shí)是必須用逆向先分析func這個(gè)函數(shù),然后再確定參數(shù)列表和返回值的……
2。對(duì)于你的內(nèi)聯(lián)匯編的代碼:
這里到底要不要用add %3, %%rsp;還是一個(gè)問題,因?yàn)橐春瘮?shù)使用的是什么調(diào)用標(biāo)準(zhǔn),有標(biāo)準(zhǔn)C的,VB的,Pascal的,包括fastcall,stdcall,cdecl等……
3。對(duì)于“知道函數(shù)參數(shù)的起始地址和長(zhǎng)度”:
這個(gè)的話,除了參數(shù)中有字符數(shù)組和直接結(jié)構(gòu)體什么的,所有的基本變量基本都是每8字節(jié)(64位)一個(gè),并且Intel一般都用bigendian的,也就是說,在內(nèi)存中 01 02 03 04 05 06 07 08 讀入寄存器后會(huì)變?yōu)? 0x0807060504030201
所以說對(duì)于簡(jiǎn)單的函數(shù),用8字節(jié)一個(gè)參數(shù)來做就好了……
而對(duì)于有字符數(shù)組什么的就必須用逆向分析了……
這個(gè)……只能進(jìn)行逆向分析了……
反正你知道了函數(shù)的地址和長(zhǎng)度……
就是你把編譯為機(jī)器碼的程序用反編譯工具翻譯成匯編,然后分析一下就好了,C語(yǔ)言的匯編還是比較簡(jiǎn)單……
比如這個(gè)函數(shù):
int func(int a, int* b) {
// float要用到CPU的FPU,指令記不得,要查下
// 為了簡(jiǎn)單就改為int*
printf("a = %d, b = %d\n", a, *b);
return a;
}
編譯成機(jī)器碼后,反編譯,如果不加優(yōu)化,一般都會(huì)這樣:
(假設(shè)函數(shù)入口地址為0400000h)
sub_0400000:
push rbp
mov rbp,rsp ; C函數(shù)參數(shù)度取使用堆棧式
; 參數(shù)在內(nèi)存中這樣: |...| a | b | ... |
; 由于是64位,故8字節(jié)對(duì)齊
mov rax,[rbp+8] ; rax = *(rbp+8) // 這里就是 rax = a
push rsi
mov rsi,[rbp+16] ; rsi = *(rbp+16) // rsi = b
; 調(diào)用C函數(shù)都是這樣堆棧式,最后一個(gè)參數(shù)最先入棧
push [rsi]
push rax
push "a = %d, b = %d\n" ; 這里是便于理解,實(shí)際上是push這個(gè)字符串常量的指針
call printf ; printf("a = %d, b = %d\n",rax,*rsi)
add rsp,24 ; 平衡堆棧,用了3個(gè)參數(shù),要還原3*8=24字節(jié),但根據(jù)函數(shù)類型的不同去平衡,像調(diào)用VB的函數(shù)就不需要平衡堆?!?/p>
; 還原數(shù)據(jù)
mov rsp,rbp
pop rsi
pop rbp
; 一般返回?cái)?shù)據(jù)都用rax裝載
mov rax,[rbp+8] ;rax=a
ret ; return rax
想調(diào)用未知參數(shù)列表的函數(shù)就是把以上過程倒過來,看著匯編把C的代碼寫出來……
破解注冊(cè)碼什么也是這樣玩的……
實(shí)際上你可以直接用反編譯的軟件,比如IDA,直接自動(dòng)分析,它反編譯的雖然是匯編,但參數(shù)列表還是大部分都顯示的……
但是,當(dāng)編譯器加優(yōu)化大部分情況就必須自己分析了,因?yàn)椋?/p>
int func(int a, int* b) {
printf("a = %d, b = %d\n", a, *b);
return a;
}
在優(yōu)化情況下可能為(直接用寄存器傳遞數(shù)據(jù)):
sub_040000:
push rdx
mov rdx,rax
push rax
push rbx
push "a = %d, b = %d\n"
call printf
mov rax,rdx
pop rdx
ret
其實(shí)像看雪學(xué)院有不少這方面的教程……
把匯編寫在另一個(gè)文件里 在main寫個(gè)原型聲明 再把兩個(gè)文件同時(shí)編譯可不可以。
我的gcc編譯器里是這樣寫的
匯編文件m.s
.file "stdio.h"
#hellowrold.s print "hello,world!"
.section .data
output:
.ascii "%d %d %d\0"
.section .text
.globl _fun
_fun:
pushl %ebp
movl %esp, %ebp
subl $16, %esp
movl 0x8(%ebp), %eax
movl %eax, 0x4(%esp)
movl 0xc(%ebp), %eax
movl %eax, 0x8(%esp)
movl 0x10(%ebp), %eax
movl %eax, 0xc(%esp)
movl $output, %eax
movl %eax, (%esp)
call _printf
movl %ebp, %esp
popl %ebp
ret
.end
主函數(shù)前的原型聲明 extern int fun(int i, int j, int k);
本文題目:匯編編寫函數(shù)c語(yǔ)言調(diào)用 C語(yǔ)言中有幾種調(diào)用匯編語(yǔ)言的方法
文章路徑:http://muchs.cn/article40/docscho.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供軟件開發(fā)、網(wǎng)站改版、品牌網(wǎng)站建設(shè)、微信小程序、做網(wǎng)站、網(wǎng)站排名
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)