2010-03-25 287 views
2

我有一段C++代码(在GNU/Linux环境下用g ++编译)加载函数指针(它的作用如何并不重要),并将一些参数推入堆栈一些内联汇编,然后调用该函数,代码如下:32位到64位内联汇编移植

unsigned long stack[] = { 1, 23, 33, 43 }; 

/* save all the registers and the stack pointer */ 
unsigned long esp; 
asm __volatile__ ("pusha"); 
asm __volatile__ ("mov %%esp, %0" :"=m" (esp)); 

for(i = 0; i < sizeof(stack); i++){ 
    unsigned long val = stack[i]; 
    asm __volatile__ ("push %0" :: "m"(val)); 
} 

unsigned long ret = function_pointer(); 

/* restore registers and stack pointer */ 
asm __volatile__ ("mov %0, %%esp" :: "m" (esp)); 
asm __volatile__ ("popa"); 

我想补充某种

#ifdef _LP64 
    // 64bit inline assembly 
#else 
    // 32bit version as above example 
#endif 

但我不知道64位的机器,任何人都内嵌汇编可以帮助我吗?

感谢

+0

Simone,我相信你正在寻找应用程序二进制接口ABI。 – 2010-03-25 22:27:39

+0

您是否可以首先了解为什么使用内联汇编调用函数的原因? 当您移动堆栈指针时,用C代码混合程序块会很危险。我很高兴ret的价值能够幸免于难。 – nategoose 2010-03-26 18:47:18

回答

4

尽管在内联程序集中调用带有适当参数的函数指针应该不是什么大问题,但我不认为在x64中重新编码会帮助你,因为要使用的调用约定非常有用可能不同(默认情况下32位和64位Linux是完全不同的)。详情请看here。所以我猜,如果在这种情况下没有内联汇编可以离开(参见其他答案),那么它将更容易移植。

编辑:好的,我看你可能不得不使用程序集。这里有一些指针。根据Agner Fog的文档,linux x64使用RDI,RSI,RDX,RCX,R8,R9和XMM0-XMM7进行参数传输。这意味着为了达到您想要的效果(不考虑浮点使用),您的功能将不得不:

(1)保存所有需要保存的寄存器(RBX,RBP,R12-R15):预留堆栈中的空间并将这些寄存器移到那里。这将是沿(Intel语法)的财产以后行:

sub rsp, 0xSomeNumber1 
mov [rsp+i*8], r# ; insert appropriate i for each register r# to be moved 

(2)计算,你将不得不通过堆栈传递给目标函数参数的个数。使用这个来预留堆栈上的所需空间(sub rsp, 0xSomeNumber2),考虑到0xSomeNumber1,这样堆栈将在最后16字节对齐,即rsp必须是16的倍数。此后不要修改rsp直到你的被调用函数已经返回。 (3)将你的函数参数加载到堆栈上(如果需要的话)和用于参数传输的寄存器中。在我看来,最简单的方法是最后从堆栈参数和加载寄存器参数开始。

;loop over stack parameters - something like this 
mov rax, qword ptr [AddrOfFirstStackParam + 8*NumberOfStackParam] 
mov [rsp + OffsetToFirstStackParam + 8*NumberOfStackParam], rax 

根据你如何设置你的例程,第一个堆栈参数等的偏移可能是不必要的。然后设置的寄存器传递的参数数目(跳过那些你不需要):

mov r9, Param6 
mov r8, Param5 
mov rcx, Param4 
mov rdx, Param3 
mov rsi, Param2 
mov rdi, Param1 

(4)使用不同的寄存器从上面的调用目标函数:

call qword ptr [r#] ; assuming register r# contains the address of the target function 

( 5)恢复保存的寄存器,并将其恢复为其在进入你的功能时的值。如果需要,可将被调用函数的返回值复制到任何你想要的位置。就这样。

注意:上述草图没有考虑要在XMM寄存器中传递的浮点值,但同样的原则适用。 免责声明:我在Win64上做了类似的事情,但从来没有在Linux上做过,因此可能会有一些细节我忽略。仔细阅读,仔细编写代码并进行测试。

+0

如果我只能找到一些这些功能的实现的例子来实现我目前用内联汇编做的很好的XD – 2010-03-25 19:40:29

+0

@Simone:做到这一点,要回答的第一个问题就是被调用函数的x64版本是如何期望将参数传递给它们的。 – PhiS 2010-03-25 19:47:55

+0

这应该是'天真',或'本地'?要么在这方面工作...;) – 2010-03-25 19:51:59

2

没有真正回答你的问题,但我想你也许可以通过使用setcontext(或makecontext)独立于平台的方式实现这一目标。

+0

是的,我认为你的可能是正确的,我会学习这个功能集,谢谢 – 2010-03-25 18:53:34

1

主要问题:

  • PUSHAD/POPAD是在x64丢失,你必须把你要保存
  • 您需要保存您的RSP(64位堆栈指针),在合适的各个寄存器64位寄存器(RAX ?, R8等?)
  • 调用约定已经从86到64的变化的32位到64位

摘要几乎肯定改变:

  • 以E开头的寄存器现在具有以R开头的64位等效项.RXX,RBX,RCX,RDX,RDI,RSI,RIP,RSP,RBP。
  • 新的寄存器:R8 ...... R15
  • 没有PUSHAD,无POPAD

我移植一些内嵌x86代码到x64的Windows。您应该花一些时间阅读x64指令集,并阅读操作系统的调用约定。 Windows上的改变是激进的,新的调用约定更简单。我怀疑GNU/Linux的变化也会有所不同,我绝对不会认为它是一样的。

我同意以前的答案,如果您可以使用替代方法而不是本地编码,请这样做。就我而言,我无法避免它。

+0

是的,但是使用(set | make)上下文将参数压入堆栈然后调用通用函数指针的方式是什么? – 2010-03-25 23:32:22