32位到64位内联汇编移植 [英] 32bit to 64bit inline assembly porting

查看:341
本文介绍了32位到64位内联汇编移植的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个C ++代码(在GNU / Linux环境下使用g ++编译)加载一个函数指针(如何做并不重要),使用一些内联汇编将一些参数推送到堆栈,然后调用该函数的代码如下:

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

/ *保存所有寄存器和堆栈指针* /
unsigned long esp;
asm __volatile__(pusha);
asm __volatile__(mov %% esp,%0:= m(esp));

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

unsigned long ret = function_pointer();

/ *恢复寄存器和堆栈指针* /
asm __volatile__(mov%0,%% esp::m(esp));
asm __volatile__(popa);

我想添加某种类型的

  #ifdef _LP64 
// 64位内联汇编
#else
// 32位版本如上例
#endif



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



感谢

解决方案

虽然它不应该是一个问题,内联汇编中的适当参数,我不认为在x64中天真地重新编码将帮助你,因为要使用的调用约定非常可能不同(32位和64位linux的默认值是绝对不同的)。有关详情,请查看此处。所以我想,如果你可以在这种情况下没有内联汇编(见另一个答案),它会更容易端口。



编辑:OK,我看到你可能要使用汇编。下面是一些指针。



根据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#为每个要移动的寄存器插入适当的i

(2)必须通过堆栈到目标函数。使用它来放置堆栈上所需的空间( sub rsp,0xSomeNumber2 ),考虑 0xSomeNumber1 堆栈将在结尾处以16字节对齐,即 rsp 必须是16的倍数。不要修改 rsp

(3)在堆栈中加载函数参数(如果需要)和用于参数传输的寄存器。在我看来,它是最简单的,如果你开始堆栈参数和加载寄存器参数最后。

 ;循环堆栈参数 - 像这样
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#];假设寄存器r#包含目标函数的地址

(5)恢复保存的寄存器, code> rsp 到它进入你的函数的值。如果需要,复制被调用函数的返回值,无论你想要它们。



:上面的草图不考虑要在XMM寄存器中传递的浮点值, 。
免责声明:我在Win64上做过类似的事情,但从来没有在Linux上,所以可能有一些细节我忽略。请仔细阅读,仔细测试并测试。


I have a piece of C++ code (compiled with g++ under a GNU/Linux environment) that load a function pointer (how it does that doesn't matter), pushes some arguments onto the stack with some inline assembly and then calls that function, the code is like :

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" );

I'd like to add some sort of

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

But i don't know inline assembly for 64bit machines, anyone could help me?

Thanks

解决方案

While it shouldn't be much of a problem to call a function pointer with the appropriate arguments in inline assembly, I don't think recoding this naively in x64 will help you, because the calling conventions to be used are very probably different (defaults for 32bit and 64bit linux are definitely different). Have a look here for details. So I guess, if you can get away without inline assembly in this case (see the other answer), it'll be easier to port.

Edit: OK, I see you may have to use assembly. Here are some pointers.

According to Agner Fog's document, linux x64 uses RDI, RSI, RDX, RCX, R8, R9 and XMM0-XMM7 for parameter transfer. This implies that in order to achieve what you want (disregarding floating-point use) your function will have to:

(1) save all registers that need to be saved (RBX, RBP, R12-R15): Set aside space on the stack and move these registers there. This will be somthing along the lines of (Intel syntax):

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

(2) Evaluate the number of arguments you will have to pass by stack to the target function. Use this to set aside the required space on the stack (sub rsp, 0xSomeNumber2), taking into account 0xSomeNumber1 so that the stack will be 16-byte aligned at the end, i.e. rsp must be a multiple of 16. Don't modify rsp after this until your called function has returned.

(3) Load your function arguments on the stack (if necessary) and in the registers used for parameter transfer. In my view, it's easiest if you start with the stack parameters and load register parameters last.

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

Depending on how you set up your routine, the offset to the first stack parameter etc. may be unnceccessary. Then set up the number of register-passed arguments (skipping those you don't need):

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

(4) Call the target function using a different register from the above:

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

(5) Restore the saved registers and restore rsp to the value it had on entry to your function. If necessary, copy the called function's return value wherever you want to have them. That's all.

Note: the above sketch does not take account of floating point values to be passed in XMM registers, but the same principles apply. Disclaimer: I have done something similar on Win64, but never on Linux, so there may be some details I am overlooking. Read well, write your code carefully and test well.

这篇关于32位到64位内联汇编移植的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆