GCC 内联汇编约束字符"m"的用法
2014-10-06 20:24
330 查看
首先是这么一段代码(例一):
这段代码运行后会出现什么结果呢?很显然,是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),把指针传进去会怎样?我们修改代码为(例二):
我们首先把指针s输出,然后再在内联汇编里传入s,不是传入*s!!我们把%1按照四字节传入edx,再把edx传入tmp整型变量,最后把tmp输出,其实就是我们费尽周折把%1给输出出来了。
结果是怎样呢?在我的电脑上,两个printf输出了同样的数字。这说明了什么,说明%1现在代表了指针s的数值。
之前我们传入字符a,它代表的是字符a,现在传入指针s,%1又代表了指针s的值。是不是有点乱。
再来看下一个例子(例三):
我们把第一段代码的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里慢慢调试这三段代码。仔细看寄存器数值。
如果我说的不对请指正。
#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里慢慢调试这三段代码。仔细看寄存器数值。
如果我说的不对请指正。
相关文章推荐
- GCC 内联汇编约束字符"m"的用法
- Stirng类的常见用法:输出str长度,输出第一个"o",和最后一个"o"的索引,将str中的字符"l"替换成"m",字符串str按空格“ ”分割为2个字符串,比较这两个字符串是否相等。
- ASP中双引号"";单引号'';连接字符&号的用法
- gcc 内联汇编用法介绍
- C++ 中 extern "C" 用法小结
- document.getElementById("MyFile")和insertAdjacentHTML("beforeEnd", str)的含义和用法
- 编写一个程序,接受用户输入的一段英文文字后,输出其中的字符数、单词数和句子数(设句子以"?"、"!"、"."结束)。
- 字符串转成字符数组,同时控制输出字符数——ToCharArray、Response.Write新用法(2006-04-20 15:32)
- [转载]SDL 用法:"Pirates Ho!" 的诞生
- 举例说明在汇编语言中,"[]"的用法
- "约束驱动的软件分析&设计新技术"开篇
- 宽字符标量L"xx"在VC6.0/7.0和GNU g++中的不同实现。
- "路径和文件名总长度必须不能超过 260 个字符! 系统找不到指定的路径"的解决方法
- extern "C"的惯用法
- asp.net(c#) datelist DataGrid 中截取字符串加"..." 和 鼠标放上去字符全部显示
- 2006-07-28 Java的常用包,"=="和"equals"的用法,基本数据类型与引用类型,对象的克隆
- ---" ~ " 的用法---
- Scanner 类 useDelimiter("")用法
- extern "C" 用法 (转贴)
- GRIDVIEW "傻瓜"用法