用彙編實現C庫函數的調用

來源:互聯網
上載者:User

一、實現目標
 用彙編實現C庫函數的調用,即:當給定函數名和參數時,可以實現該函數的調用。
 
二、問題描述
 在實現C解譯器時,解析函數調用語句,例如:strlen( "linxr" ); 那麼,如何去調用strlen函數?
 首先,可以得到參數列表arg_listk,然後用如下形式的代碼去實現調用stlen函數:
 if( strcmp( token, "strlen" ) == 0 ){
  strlen( arg_list[0] );
 }
 else if( ... ){
 }
 ...
 [問題]這樣子,C的庫函數大致有幾百個,那麼這個代碼就會變得沒完沒了了。

 
三、解決問題
 根據上述的問題。我想到了一種方法,那就是用統一的入口來調用C庫函數。函數的原型暫訂為:
 int ccall( void *fapp, var_t *arg_list );
 fapp:  函數地址
 arg_list: 參數列表,var_t是我定義的變數類型,這裡不詳述。
 
 要調用ccall函數,首先必須做兩件事:
 1.  必鬚根據函數名得到相應的函數指標。
  現在我暫時用一個列表來保持所有C庫函數的指標,可以這麼定義:
  struct cpp_t{
   char *fname;
   char *fapp;
  }cpp_table[100] = {
   "strlen", (char*)strlen,
   "strcpy", (char*)strcpy,
   ....
  }
  根據函數名尋找函數指標,可以遍曆這個表實現,但是效率比較低。所有我用了雜湊表實現,這裡不詳述。
  
 2.  必鬚根據函數調用語句,得到一個參數列表。怎麼實現就是文法解析的問題了。:)

 
四、ccall函數實現
 如果你明白C的函數調用規則,那麼這個問題將是相當的簡單。這裡簡單的描述一下:
 例如,調用 printf( "%d %d", 1, 2 );
 C語言的調用規則(即__cdecl)是這樣的:
 push 2
 push 1
 push 0x123456 ;設0x123456是字串"%d %d"的地址
 call 0xA0B1C2   ;設0xA0B1C2是printf的地址
 add esp, 12      ;這裡要自己實現棧的平衡

 那麼事情就簡單了,我們只要讓C調用彙編就能實現這個ccall函數。
 

下面的彙編是用masm編譯的。編譯命令:Ml /c /coff /Zi /Fl ccall.asm
 [ccall.asm代碼]

title ccall 

.386
.model flat,c

.data?
arg_num dword 0
arg_tab dword 100 dup(0)
arg_tye dword 100 dup(0)
fun_ptr dword 0


.code

push_arg_ini proc
mov arg_num, 0
ret
push_arg_ini endp


push_arg proc, arg : sdword, tye : sdword
mov eax, arg_num
mov ebx, offset arg_tab
mov ecx, arg
mov [ebx+eax*4], ecx

mov ebx, offset arg_tye
mov ecx, tye
mov [ebx+eax*4], ecx

add eax, 1
mov arg_num, eax
ret
push_arg endp


push_fun proc, fun : sdword
mov eax,fun
mov fun_ptr, eax
ret
push_fun endp


i_fun_call proc
local count : dword
xor eax, eax
mov ebx, offset arg_tab
mov ecx, offset arg_tye
mov count, 0

.while eax < arg_num
mov edx, [ecx+eax*4]
;浮點型入棧;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.if edx
fld dword ptr [ebx+eax*4]
sub esp,8
fstp qword ptr [esp]
.elseif
push [ebx+eax*4]
.endif
add eax, 1
add count, 4
.endw

mov eax, fun_ptr
call eax
add esp, count
ret
i_fun_call endp

f_fun_call proc
local count : dword
xor eax, eax
mov ebx, offset arg_tab
mov ecx, offset arg_tye
mov count, 0

.while eax < arg_num
mov edx, [ecx+eax*4]
;浮點型入棧;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.if edx
fld dword ptr [ebx+eax*4]
sub esp,8
fstp qword ptr [esp]
.elseif
push [ebx+eax*4]
.endif
add eax, 1
add count, 4
.endw

mov eax, fun_ptr
call eax
add esp, count
ret
f_fun_call endp


end

 
 在C中聲明這些函數:
 int __cdecl push_arg_ini();
 int __cdecl push_arg( int arg, int tye );
 int __cdecl push_fun( int fun ); 
 int __cdecl i_fun_call();
 double __cdecl f_fun_call();
 
 這樣,在C中,如果要調用strlen函數,就可以這樣調用函數:
 push_arg_ini();
 push_arg((int)"linxr",0);  //這裡的第二個參數是參數類型,因為浮點型的入棧方式和整形不一樣
 push_fun((int)strlen);
 i_fun_call();     //f_fun_call用於返回double類型
 
 其實調用任意的C庫函數都可以這麼實現,所有我們就很容易定義實現上述的ccall函數了。
 int ccall( void *fapp, var_t *arg_list )
 {
  int i;
  push_arg_ini();
  for( i=0; i<arg_num; i++ ){
   push_arg( arg_list[i].value, arg_list[i].type );
  }
  push_fun((int)fapp);
  return i_fun_call();
 }
 同樣可以實現返回double類型的ccall...

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.