前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >在gcc中使用intel风格的内联汇编

在gcc中使用intel风格的内联汇编

作者头像
战神伽罗
发布2019-07-24 14:41:22
2.9K0
发布2019-07-24 14:41:22
举报
文章被收录于专栏:Eureka的技术时光轴

很简单,内联汇编使用asm(“.intel_syntax noprefix/n”)声明一下,以后的内联汇编就可以用intel风格了,构建可执行文件时给gcc加上-masm=intel参数。 先写一个小程序测试一下:

[cpp] view plain copy

  1. #include <stdio.h>
  2. int main() {
  3. int a = 3;
  4. asm(".intel_syntax noprefix/n");
  5. asm("mov dword ptr a,10/n");
  6. printf("%d/n", a);
  7. return 0;
  8. }

[root@jcwkylk src]# gcc -masm=intel test.c -o test /tmp/ccgWTkUF.o: In function `main': test.c:(.text+0x1a): undefined reference to `a' collect2: ld returned 1 exit status 出错,说符号a没有定义。看看编译后的结果是什么样子: [root@jcwkylk src]# gcc -S test.c 输出不长,把test.s的内容全部贴出来: [c-sharp] view plain copy

  1. .file "test.c"
  2. .section .rodata
  3. .LC0:
  4. .string "%d/n"
  5. .text
  6. .globl main
  7. .type main, @function
  8. main:
  9. leal 4(%esp), %ecx
  10. andl $-16, %esp
  11. pushl -4(%ecx)
  12. pushl %ebp
  13. movl %esp, %ebp
  14. pushl %ecx
  15. subl $36, %esp
  16. movl $3, -8(%ebp)
  17. #APP
  18. .intel_syntax noprefix
  19. mov dword ptr a,10
  20. #NO_APP
  21. movl -8(%ebp), %eax
  22. movl %eax, 4(%esp)
  23. movl $.LC0, (%esp)
  24. call printf
  25. movl $0, %eax
  26. addl $36, %esp
  27. popl %ecx
  28. popl %ebp
  29. leal -4(%ecx), %esp
  30. ret
  31. .size main, .-main
  32. .ident "GCC: (GNU) 4.1.1 20061011 (Red Hat 4.1.1-30)"
  33. .section .note.GNU-stack,"",@progbits

从上面看出来,夹在#APP和#NO_APP之间的部分就是.intel_syntax,它保持了原样,而代码中的a原本是个局部变量,只有在函数运行时它才会动态在栈上分配,使用ebp加上偏移量来访问它,这就是问题所在。因为全局变量的变量名会保存在符号表中,所以如果要在内联汇编中使用变量名,也只能使用全局变量的变量名。只为在内联汇编中用名称来访问变量而把一个局部变量变成全局的是不合理的,所以我们这里也用ebp+offset的方式来访问局部变量。 要这么做,就得了解gcc编译时是如何为函数分配栈的,以及调用函数时寄存器约定是怎样的。从上面的汇编代码可以看出来: [c-sharp] view plain copy

  1. main:
  2. leal 4(%esp), %ecx ; ecx=[esp+4],
  3. andl $-16, %esp
  4. pushl -4(%ecx)
  5. pushl %ebp
  6. movl %esp, %ebp
  7. pushl %ecx
  8. subl $36, %esp

这几行代码用来初始化mai函数的调用栈,和cl编译器不同的是在push ebp前面多出来了几行,有个esp &= -16的操作,-16=0xfffffff0,这个作用可能是为了对齐,esp应该是保持16字节对齐的。但这些细节在这里作用都不大。最关键的是这三行: pushl %ebp movl %esp, %ebp pushl %ecx 有一个把ecx寄存器压栈的操作,所以第一个局部变量的起始地址应该是ebp-8。 另一个注意的地方是printf的调用: movl -8(%ebp), %eax movl %eax, 4(%esp) movl $.LC0, (%esp) call printf 在这个代码中printf有两个参数,但却没有看到期望的push。gcc用了另外一种方法:直接操作esp。上面这三行代码,首先把[ebp-8]也就是第一个局部变量即a的值给了eax,然后把这个值传递到esp+4这个地址指向的内存单元,然后esp指向printf的第一个参数——那个格式控制字符串。之后call printf会把下一条指令的地址压入栈中,然后跳转到printf,所以,对printf来说,ebp+4仍然是返回地址,ebp+8仍然是第一个参数,ebp+0xc仍然是第二个参数。一切都没变。 最后有这一行代码:movl $0, %eax 看来返回值仍然是存放在eax寄存器中。 好,现在写一个比较完整的测试程序: [cpp] view plain copy

  1. #include <stdio.h>
  2. int add(int a, int b) {
  3. return a+b;
  4. }
  5. int main() {
  6. int a = 3;
  7. asm(".intel_syntax noprefix/n");
  8. asm("mov dword ptr [ebp-8],10/n");
  9. printf("%d/n", a);
  10. asm("push dword ptr [ebp-8]/n");
  11. asm("push 25/n");
  12. asm("call add/n");
  13. asm("add esp, 8/n");
  14. asm("mov dword ptr [ebp-8], eax/n");
  15. printf("%d/n", a);
  16. return 0;
  17. }

[root@jcwkylk src]# gcc -masm=intel test.c -o test [root@jcwkylk src]# ./test 10 35

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云开发 CloudBase
云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为200万+企业和开发者提供高可用、自动弹性扩缩的后端云服务,可用于云端一体化开发多种端应用(小程序、公众号、Web 应用等),避免了应用开发过程中繁琐的服务器搭建及运维,开发者可以专注于业务逻辑的实现,开发门槛更低,效率更高。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档