歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

GCC 內聯匯編約束字符“m”的用法

首先是這麼一段代碼(例一):

#include<stdio.h>
void main()
{
    char c;
    //int tmp;
    char *s="abcdefg";
    asm("movb %1,%0\n\t"
    :"=d"(c)
    :"m"(*s));
    printf("out:%c\n",c);
 
}

    這段代碼運行後會出現什麼結果呢?很顯然,是out:a

    但是"m"(*s)是什麼意思呢?s是字符串指針,現在是把*s字符串傳進去了。

    事實上,對於"a"、"b"、"c"這些約束符他們都特別指定eax,ebx,ecx寄存器,我們使用它們時是把內存裡的值傳到寄存器,在輸出時再把寄存器值傳給內存變量。而"m"則直接指示原始內存位置。也就是說在內聯匯編裡面對於"m"標示的內存變量將直接對其進行修改。例如,在輸出語句指定:"m"(var),那麼在asm語句中對於var的修改將直接作用到它的內存位置。

    對於上面的代碼,看起來似乎是我們通知gcc把整個字符串傳進去,但是字符串首地址也是字符a的地址,相當於把字符a傳進去了,所以%1似乎代表的字符a。然後把它放到了%0也就是edx裡。

    如果你把"m"(*s)改為"m"(s),把指針傳進去會怎樣?我們修改代碼為(例二):

#include<stdio.h>
void main()
{
    //char c;
    int tmp;
    char *s="abcdefg";
    printf("%d\n",s);
    asm("movl %1,%0\n\t"
    :"=d"(tmp)
    :"m"(s));
    printf("out:%d\n",tmp);
 
}

    我們首先把指針s輸出,然後再在內聯匯編裡傳入s,不是傳入*s!!我們把%1按照四字節傳入edx,再把edx傳入tmp整型變量,最後把tmp輸出,其實就是我們費盡周折把%1給輸出出來了。

結果是怎樣呢?在我的電腦上,兩個printf輸出了同樣的數字。這說明了什麼,說明%1現在代表了指針s的數值。

    之前我們傳入字符a,它代表的是字符a,現在傳入指針s,%1又代表了指針s的值。是不是有點亂。

再來看下一個例子(例三):

#include<stdio.h>
void main()
{
    char c;
    //int tmp;
    char *s="abcdefg";
    //printf("%d\n",s);
    asm("movb %%ds:%1,%0\n\t"
    :"=d"(c)
    :"m"(*s));
    printf("out:%c\n",c);
 
}

    我們把第一段代碼的mov指令語句改為了movb %%ds:%1,%0,加了個段前綴。運行結果依然輸出out:a

    我們把這三段代碼分別gcc xxxx.c -S編譯成匯編代碼查看會發現,%1這個東西到底是什麼根本不能確定,它不像%0所代表的edx一樣,%1是隨著你代碼寫法的改變由gcc自動選擇合適的尋址方式。所以,第一和第三段代碼的差距是ds段前綴,但結果不變,因為gcc自動調整了尋址方式。%1隨著這種調整,它的意義也在不斷發生變化。有時它是字符a(例一),有時它是字符a的偏移地址(例三),有時候它又是一個指針值(例二)。

    引用國外一個網站的一句話:In addition to passing information in registers, gcc can understand references to raw memory. This will expand to some more complex addressing mode within the asm string.

    就是這樣,不必再糾結%1代表了什麼,只需記住,把字符串*s整個傳進去,相當於傳遞了它的第一個字符,%1(或者其他占位符)代表了這個字符。把字符串指針s傳進去,%1就是這個指針s的值,不過沒人這樣干,沒有意義。把整型變量var(int var=123;)傳進去,那%1就代指這個整數。

    可以在GDB裡慢慢調試這三段代碼。仔細看寄存器數值。

    如果我說的不對請指正。

Ubuntu 12.04嵌入式交叉編譯環境arm-linux-GCC搭建過程圖解 http://www.linuxidc.com/Linux/2013-06/85902.htm

Ubuntu 12.10安裝交叉編譯器arm-none-linux-gnueabi-GCC http://www.linuxidc.com/Linux/2013-03/82016.htm

Ubuntu下Vim+GCC+GDB安裝及使用 http://www.linuxidc.com/Linux/2013-01/78159.htm

Ubuntu下兩個GCC版本切換 http://www.linuxidc.com/Linux/2012-10/72284.htm

GCC 的詳細介紹:請點這裡
GCC 的下載地址:請點這裡

Copyright © Linux教程網 All Rights Reserved