Linux內核系列—C語言中內嵌彙編 asm __volatile__

来源:http://www.cnblogs.com/joey-hua/archive/2016/03/15/5279875.html
-Advertisement-
Play Games

在內嵌彙編中,可以將C語言表達式指定為彙編指令的操作數,而且不用去管如何將C語言表達式的值讀入哪個寄存器,以及如何將計算結果寫回C 變數,你只要告訴程式中C語言表達式與彙編指令操作數之間的對應關係即可, GCC會自動插入代碼完成必要的操作。1、簡單的內嵌彙編例:        __asm__ __v


在內嵌彙編中,可以將C語言表達式指定為彙編指令的操作數,而且不用去管如何將C語言表達式的值讀入哪個寄存器,以及如何將計算結果寫回C 變數,你只要告訴程式中C語言表達式與彙編指令操作數之間的對應關係即可, GCC會自動插入代碼完成必要的操作。

1、簡單的內嵌彙編
例:

       __asm__ __volatile__("hlt"); "__asm__"表示後面的代碼為內嵌彙編,"asm"是"__asm__"的別名。"__volatile__"表示編譯器不要優化代碼,後面的指令 保留原樣,"volatile"是它的別名。括弧裡面是彙編指令。

2、內嵌彙編舉例
   使用內嵌彙編,要先編寫彙編指令模板,然後將C語言表達式與指令的操作數相關聯,並告訴GCC對這些操作有哪些限制條件。例如在下麵的彙編語句:
    

__asm__ __violate__ ("movl %1,%0" : "=r" (result) : "m" (input));


"movl %1,%0"是指令模板;"%0"和"%1"代表指令的操作數,稱為占位符,內嵌彙編靠它們將C 語言表達式與指令操作數相對應。指令模板後面用小括弧括起來的是C語言表達式,本例中只有兩個:"result"和"input",他們按照出現的順序分 別與指令操作數"%0","%1"對應;註意對應順序:第一個C 表達式對應"%0";第二個表達式對應"%1",依次類推,操作數至多有10 個,分別用"%0","%1"...."%9"表示。在每個操作數前面有一個用引號括起來的字元串,字元串的內容是對該操作數的限制或者說要求。 "result"前面的限制字元串是"=r",其中"="表示"result"是輸出操作數,"r" 表示需要將"result"與某個通用寄存器相關聯,先將操作數的值讀入寄存器,然後在指令中使用相應寄存器,而不是"result"本身,當然指令執行 完後需要將寄存器中的值存入變數"result",從錶面上看好像是指令直接對"result"進行操作,實際上GCC做了隱式處理,這樣我們可以少寫一 些指令。"input"前面的"r"表示該表達式需要先放入某個寄存器,然後在指令中使用該寄存器參加運算。
   C表達式或者變數與寄存器的關係由GCC自動處理,我們只需使用限制字元串指導GCC如何處理即可。限制字元必須與指令對操作數的要求相匹配,否則產生的 彙編代碼將會有錯,讀者可以將上例中的兩個"r",都改為"m"(m表示操作數放在記憶體,而不是寄存器中),編譯後得到的結果是:
             movl input, result
很明顯這是一條非法指令,因此限制字元串必須與指令對操作數的要求匹配。例如指令movl允許寄存器到寄存器,立即數到寄存器等,但是不允許記憶體到記憶體的操作,因此兩個操作數不能同時使用"m"作為限定字元。

內嵌彙編語法如下:
       __asm__(彙編語句模板: 輸出部分: 輸入部分: 破壞描述部分)
共四個部分:彙編語句模板,輸出部分,輸入部分,破壞描述部分,各部分使用":"格開,彙編語句模板必不可少,其他三部分可選,如果使用了後面的部分,而前面部分為空,也需要用":"格開,相應部分內容為空。例如:
             __asm__ __volatile__("cli": : :"memory")

1、彙編語句模板
    彙編語句模板由彙編語句序列組成,語句之間使用";"、"\n"或"\n\t"分開。指令中的操作數可以使用占位符引用C語言變數,操作數占位符最多10個,名稱如下:%0,%1,...,%9。指令中使用占位符表示的操作數,總被視為long型(4個位元組),但對其施加的操作根據指令可以是字或者位元組,當把操作數當作字或者位元組使用時,預設為低字或者低位元組。對位元組操作可以顯式的指明是低位元組還是次位元組。方法是在%和序號之間插入一個字母,"b"代表低位元組,"h"代表高位元組,例如:%h1。

2、輸出部分
    輸出部分描述輸出操作數,不同的操作數描述符之間用逗號格開,每個操作數描述符由限定字元串和C 語言變數組成。每個輸出操作數的限定字元串必須包含"="表示他是一個輸出操作數
例:
           __asm__ __volatile__("pushfl ; popl %0 ; cli":"=g" (x) )
描述符字元串表示對該變數的限制條件,這樣GCC 就可以根據這些條件決定如何分配寄存器,如何產生必要的代碼處理指令操作數與C表達式或C變數之間的聯繫。

3、輸入部分
輸入部分描述輸入操作數,不同的操作數描述符之間使用逗號格開,每個操作數描述符由限定字元串和C語言表達式或者C語言變數組成。
例1 :
             __asm__ __volatile__ ("lidt %0" : : "m" (real_mode_idt));
例二(bitops.h):


Static __inline__ void __set_bit(int nr, volatile void * addr)
{
         __asm__(
                         "btsl %1,%0"
                         :"=m" (ADDR)
                         :"Ir" (nr));
}

後 例功能是將(*addr)的第nr位設為1。第一個占位符%0與C 語言變數ADDR對應,第二個占位符%1與C語言變數nr對應。因此上面的彙編語句代碼與下麵的偽代碼等價:btsl nr, ADDR,該指令的兩個操作數不能全是記憶體變數,因此將nr的限定字元串指定為"Ir",將nr 與立即數或者寄存器相關聯,這樣兩個操作數中只有ADDR為記憶體變數

4、限制字元
   4.1、限制字元列表
   限制字元有很多種,有些是與特定體繫結構相關,此處僅列出常用的限定字元和i386中可能用到的一些常用的限定符。它們的作用是指示編譯器如何處理其後的C語言變數與指令操作數之間的關係。

      分類            限定符                    描述
  通用寄存器         "a"               將輸入變數放入eax
                                              這裡有一個問題:假設eax已經被使用,那怎麼辦?
                                              其實很簡單:因為GCC 知道eax 已經被使用,它在這段彙編代碼
                                       的起始處插入一條語句pushl %eax,將eax 內容保存到堆棧,然
                                              後在這段代碼結束處再增加一條語句popl %eax,恢復eax的內容
                         "b"               將輸入變數放入ebx
                         "c"               將輸入變數放入ecx
                         "d"               將輸入變數放入edx
                         "s"               將輸入變數放入esi
                         "d"               將輸入變數放入edi
                         "q"              將輸入變數放入eax,ebx,ecx,edx中的一個
                         "r"               將輸入變數放入通用寄存器,也就是eax,ebx,ecx,
                                              edx,esi,edi中的一個
                         "A"              把eax和edx合成一個64 位的寄存器(use long longs)

       記憶體            "m"             記憶體變數
                         "o"             操作數為記憶體變數,但是其定址方式是偏移量類型,
                                            也即是基址定址,或者是基址加變址定址
                         "V"             操作數為記憶體變數,但定址方式不是偏移量類型
                         " "             操作數為記憶體變數,但定址方式為自動增量
                         "p"             操作數是一個合法的記憶體地址(指針)

     寄存器或記憶體   "g"             將輸入變數放入eax,ebx,ecx,edx中的一個
                                            或者作為記憶體變數
                         "X"            操作數可以是任何類型

     立即數
                         "I"             0-31之間的立即數(用於32位移位指令)
                         "J"             0-63之間的立即數(用於64位移位指令)
                        "N"             0-255之間的立即數(用於out指令)
                         "i"             立即數  
                        "n"            立即數,有些系統不支持除字以外的立即數,
                                          這些系統應該使用"n"而不是"i"

     匹配             " 0 ",         表示用它限制的操作數與某個指定的操作數匹配,
                        "1" ...               也即該操作數就是指定的那個操作數,例如"0"
                        "9"            去描述"%1"操作數,那麼"%1"引用的其實就
                                          是"%0"操作數,註意作為限定符字母的0-9 與
                                          指令中的"%0"-"%9"的區別,前者描述操作數,
                                          後者代表操作數。
                         &             該輸出操作數不能使用過和輸入操作數相同的寄存器

    操作數類型      "="          操作數在指令中是只寫的(輸出操作數)  
                        "+"          操作數在指令中是讀寫類型的(輸入輸出操作數)

     浮點數           "f"          浮點寄存器
                        "t"           第一個浮點寄存器
                        "u"          第二個浮點寄存器
                        "G"          標準的80387浮點常數
                         %           該操作數可以和下一個操作數交換位置
                                         例如addl的兩個操作數可以交換順序
                                        (當然兩個操作數都不能是立即數)
                         #            部分註釋,從該字元到其後的逗號之間所有字母被忽略
                         *            表示如果選用寄存器,則其後的字母被忽略

5、破壞描述部分
   破壞描述符用於通知編譯器我們使用了哪些寄存器或記憶體,由逗號格開的字元串組成,每個字元串描述一種情況,一般是寄存器名;除寄存器外還有"memory"。例如:"%eax","%ebx","memory"等。

"memory"比較特殊,可能是內嵌彙編中最難懂部分。為解釋清楚它,先介紹一下編譯器的優化知識,再看C關鍵字volatile。最後去看該描述符。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 單機搭建Android開發環境四,介紹了基於64位Ubuntu 12.04 Server版安裝git、repo、編譯環境,從內部伺服器下載Android5.1代碼並編譯測試。
  • 第二個界面中的lable顯示第一個界面textField中的文本 首先我們建立一個RootViewControllers和一個DetailViewControllers,在DetailViewControllers中聲明一個textString屬性,用於接收傳過來的字元串, RootViewCont
  • 面向對象編程進階和字元串 Δ一.類的設計模式—單例 【單例】程式允許過程中,有且僅有一塊記憶體空間存在,這種類的設計模式成為單例 【問】什麼時候用到單例 【答】數據共用的時候 1.單例的書寫格式 以+(加號)開頭的方法,可以直接被類調用;-(減號)開頭方法 ,是對象方法,用對象來調用; 2.認識單例
  • 首先創建2個新界面 , 然後創建一個類,如下圖   然後在AppDeleate.h   然後在AppDeleate.m中  然後進入AppStatus.h    AppStatus.m   FirstViewController.h中    FirstViewController.m中   Seco
  • ContentProvider是不同應用程式之間進行數據交換的一種方式或者稱標準API. http://www.qq.com:80/news.jsp ContentProvider要求的;Uri與上面類似   在AndroidManifest.xml中註冊ContentProvider,指定 建立一
  •  
  •   首先:自定義一個漸變視圖,在自定義視圖中重寫drawRect  接下來主要分為5步   步驟還是和上面一樣!     作者: 清澈Saup出處: http://www.cnblogs.com/qingche/本文版權歸作者和博客園共有,歡迎轉載,但必須保留此段聲明,且在文章頁面明顯位置給出原文連
  • 1.NSString類型的屬性為什麼用copy   NSString類型的屬性可以用strong修飾,但會造成一些問題,請看下麵代碼   2016-03-15 10:10:25.772 string[619:12684] 可變字元串修改前nameStrong= zhangsan nameCopy=z
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...