Linux C語言內聯彙編使用

原文出處:原文在這裏

轉載過來,自己收藏看看,當然也是在和大家分享

本文檔的Copyleft歸rosetta所有,使用GPL發佈,可以自由拷貝、轉載,轉載時請保持文檔的完整性。
參考資料:《Linux內核完全剖析》,《新版彙編語言程序設計》,《Linux C編程一站式學習》

    最近要改個C語言算法的關鍵部分用彙編語言實現,Linux裏嵌入彙編基本使用AT&T彙編,比如Linux系統的啓動部分用的就是AT&T彙編 。以前學過AT&T彙編,但學過一段時間就忘了,但對Intel彙編基礎比較熟悉,兩者使用方法基本相似,所以對着Intel彙編,花點時間看AT&T彙編也就容易了。
下面看一下兩者的區別,然後給出Linux語言中嵌入AT&T彙編的具體的例子。
一、AT&T彙編和Intel彙編主要區別
  1,兩者源和目的操作數次序相反。 AT&T的源和目的是從左到右,並且其寄存器前要加“%”;Intel的是右到左,不需要加"%"。
    例如:AT&T: movl %ecx, %eax (ecx爲源操作數,eax爲目的操作數)
        Intel: mov dx, bx    (bx爲源操作數,dx爲目的操作數 
  2,AT&T立即操作數前需要加"$";Intel的不用
    例如:AT&T:movl $2, %eax
        Intel:mov ax, 2
  3,AT&T中內存操作的長度由操作碼最後一個字符來確定。"b","w","l"分別表示內存引用爲1字節8位,2字節16位,4字節32位。
    Intel使用操作前綴byte ptr, word ptr, dword ptr
    例如:AT&T:movl %ecx, %eax
          Intel:mov al, byte ptr ttt

  這三點是最重要的區別,除此之外還有跳轉/調用時不太一樣。

二、嵌入彙編基本格式

  asm("彙編語句"
    "輸出寄存器"
    "輸入寄存器"
    "會被修改的寄存器"//告訴編譯器在執行這條__asm__語句時這裏指定的寄存器要被改寫,所以在此期間不要用這裏的寄存器保存其它值。
    )
  
  //asm.c
  #include <stdio.h>

  unsigned int LeftShift(unsigned int uiNumber, unsigned char iBits)
  {
      register unsigned int _res;
      __asm__("rol %1, %%eax;"
              :"=a" (_res)
              :"c" (iBits),"0"(uiNumber)
          );
  
      return _res;
  }

  int main(void)
  {
      unsigned int ret = 0;
  
      ret = LeftShift(4, 2);
      printf("1,ret:%u\n", ret);
  
      return 0;
  }

  [root@xxx asm_study]# gcc asm.c -o app
  [root@xxx asm_study]# ./app
  1,ret:16

  以上給出一個循環左移的例子,不關注循環左移指信rol本身,只關注AT&T彙編用法。
  首先定義了個寄存器變量_res,用其保存返回值。
  因爲AT&T彙編使用寄存器,其前面需要帶“%”,而在c語言中“%”是個特殊格式字符,所以需要兩個百分號“%%”才最終表示一個“%”,這個和轉義字符有點類似。
  :"=a"(_res)表示用寄存器eax的值輸出給_res,用%0表示此寄存器, 
  :"c" (iBits),"0"(uiNumber),c表示輸入寄器ecx,0表示使用上面的輸出寄存器eax,所以ecx和eax分別保存iBits和uiNumber變量值。指令中的%1指寄存器ecx,所以其編號規則是從“輸出寄器”開始從左到右,從上到下進行的。
  可以發現rol %1,%%eax其實可以直接寫成:rol %1, %0

三、C語言版循環左移
  給出一個C語言版循環左移代碼,有興趣的朋友可以和上面的彙編版進行對比。
  //ori.c   
 unsigned int LeftShift2(unsigned int uiNumber, unsigned char iBits)
  {
      int i=0;
      unsigned int iret=uiNumber;
      unsigned int iR=0x01;
  
      for(i=0;i<iBits;i++)
      {
          iR=iret&0x80000000;
          iret=iret<<1;
          iR=iR>>31;
          iret=iret|iR;
      }
  
      return iret;
  }
  int main(void)
  {
      unsigned int ret = 0;
  
      ret = LeftShift2(4,2);
      printf("2,ret:%u\n", ret);
  
      return 0;
  }
  [root@xxx asm_study]# gcc ori.c -o app
  [root@xxx asm_study]# ./app
  2,ret:16

四、宏定義版彙編
  #define LeftShift(uiNumber, iBits) \
  ({\
    register unsigned int __res;\
    __asm__("rol %%cl, %%eax;" \
               :"=a"(__res)\
               :"c" (iBits),"a"(uiNumber));\
   __res;})
  和普通的彙編區別是每一行後面都要加"\",返回值寫最後,如上面最後的__res。

五、c程序轉變爲彙編程序
  1,把*.c程序轉變爲AT&T格式彙編*.s
  [root@xxx asm_study]# gcc -S asm.c -o asm.s
  [root@xxx asm_study]# ls -al asm.s
  -rw-r--r-- 1 root root 1387 06-30 10:41 asm.s
  
  2,把*.c程序轉變爲Intel格式彙編*.s
  [root@xxx asm_study]# gcc -masm=intel test.c -o test.s
  當然,要想把c程序轉爲Intel彙編時,其中不能包含AT&T格式的彙編,否則無法轉。

發佈了31 篇原創文章 · 獲贊 1 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章