使用内联汇编C替换函数

5

我有一个函数,其中的代码我想转换成汇编(因为各种原因):

int foo(int x, int y, int z);

我使用以下方法生成汇编代码:
clang -S -mllvm --x86-asm-syntax=intel foo.c

汇编输出:foo.s 开始是这样的:

汇编输出:foo.s 开始是这样的:

_foo:                               ## @foo
    .cfi_startproc
## BB#0:
    push    RBP
Ltmp2:
    .cfi_def_cfa_offset 16
...

我认为这是该函数对应的汇编代码。我的问题是,我应该将汇编输出中的哪一部分复制到C代码中(我正在尝试使用内嵌汇编),以使函数能够工作?代码应该如下所示:

int foo(int x, int y, int z) {
   __asm__("..."); // <-- What goes inside?
}

谢谢


你可以让调试器运行汇编代码,然后逐步执行该函数,找出实际上需要使用的部分。但请记住,编译器会对很多东西进行优化,因此并不总是正确的。 - Shiplu Mokaddim
嗯,如果我找到了函数实际开始的位置,将汇编代码直接复制粘贴到C语言中(用__asm__等包装起来)会起作用吗?我不需要指定操作数列表或破坏列表或任何其他东西,对吧? - Peter
除非你的汇编代码很小,而且你认为它应该被内联,否则请在一个汇编源文件中编写整个函数,并调用它。(当然,在 C 头文件中保留一个原型。)然后,你的构建系统必须知道为 x86 和/或 x86-64 构建编译该汇编源文件,而不仅仅是在 C 中使用 #ifdef。正确编写内联汇编,特别是准确设置约束、破坏和输入/输出,是困难的,但这是使其正常工作所必需的。(如果你选择这条路,请检查反汇编输出,以确保你的汇编代码按照你的预期插入。) - Peter Cordes
1个回答

1
你需要查看该函数的反汇编并编写 __asm__。例如下面的代码: int foo(int x, int y, int z) {
x = y+z; return x; }
将产生以下反汇编结果:
int foo(int x, int y, int z) {
  push        ebp  
  mov         ebp,esp 
  sub         esp,0C0h 
  push        ebx  
  push        esi  
  push        edi  
  lea         edi,[ebp-0C0h] 
  mov         ecx,30h 
  mov         eax,0CCCCCCCCh 
  rep stos    dword ptr es:[edi] 

  x = y+z;
  mov         eax,dword ptr [y] 
  add         eax,dword ptr [z] 
  mov         dword ptr [x],eax 

  return x;
  mov         eax,dword ptr [x] 
}

所以你必须添加以下for语句:x = y + z

 mov         eax,dword ptr [y] 
 add         eax,dword ptr [z] 
 mov         dword ptr [x],eax 

1
好的,我在这方面有一个小问题,编译器抱怨我没有返回变量(因为我是通过汇编返回的),有什么解决办法吗? - Peter
1
包含 mov eax,dword ptr [x],它可以工作。`int foo(int x, int y, int z) { x = y+z; _asm{ mov eax,dword ptr [x] }}` - Balu
你是用MSVC编译器编译的吗?你知道如何使用clang做同样的事情吗? - Peter
@Peter:不要从内联汇编中返回。我不确定如果函数体除了内联汇编之外什么都没有是否安全,但是如果函数有本地变量会在堆栈上分配的话,那么这绝对是不安全的。此时,栈指针将指向错误的内容,无法正确执行ret - Peter Cordes
你用没有优化的编译选项编译了那个示例吗?因为那个汇编代码是一个糟糕的起点。它甚至导致你包含了一条无用的指令。它将 x 存储到堆栈上,然后立即将其加载回来。你应该将结果留在 eax 中,这是 SysV 和 Windows x86 和 x86-64 ABIs 中的返回值。gcc -O3 -m32 将整个函数编译为 movl 12(%esp), %eax; addl 8(%esp), %eax; ret。请注意,第一个参数(4(%esp))被忽略了,因为你从未读取传入的 x 的值。x86-64 编译版本:leal (%rsi,%rdx), %eax; ret - Peter Cordes
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接