32位到64位内联汇编移植

我有一段C ++代码(在GNU / Linux环境下用g ++编译)加载一个函数指针(它是如何做的无关紧要),用一些内联汇编将一些参数压入堆栈然后调用该函数,代码如下:

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位机器的内联汇编,有人可以帮助我吗?

谢谢

虽然在内联汇编中使用适当的参数调用函数指针应该没什么大不了的,但我不认为在x64中天真地重新编码会对你有所帮助,因为要使用的调用约定很可能是不同的(默认值)对于32位和64位的linux肯定是不同的)。 详细了解这里 。 所以我想,如果你能在没有内联assembly的情况下离开(参见另一个答案),那么移植起来会更容易。

编辑 :好的,我看到你可能不得不使用汇编。 这里有一些指示。

根据Agner Fog的文档,linux x64使用RDI,RSI,RDX,RCX,R8,R9和XMM0-XMM7进行参数传输。 这意味着为了实现您想要的function(忽略浮点使用),您的function必须:

(1)保存所有需要保存的寄存器(RBX,RBP,R12-R15):在堆栈上留出空间并将这些寄存器移到那里。 这将是(英特尔语法)的一些方面:

 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)恢复保存的寄存器并将rsp恢复为进入函数时的值。 如有必要,将被调用函数的返回值复制到您想要的位置。 就这样。

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

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

主要问题:

  • x64上缺少pushad / popad,您必须按下要保存的各个寄存器
  • 你需要将rsp(64位堆栈指针)保存在合适的64位寄存器中(rax?,r8?等)
  • 调用约定几乎肯定从32位变为64位

从x86到x64的更改摘要:

  • 从E开始的寄存器现在具有64位当量,以R. RAX,RBX,RCX,RDX,RDI,RSI,RIP,RSP,RBP开始。
  • 新寄存器:R8 … R15
  • 没有pushad,没有popad

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

我同意之前的回答,如果您可以使用替代方法而不是本地编码,请执行此操作。 在我的情况下,我无法避免它。