按地址从jitted代码调用cfunction

2024-05-16 01:53:47 发布

您现在位置:Python中文网/ 问答频道 /正文

我目前正试图通过python实现JIT。我通过另一个问题找到了peachpy。在大多数情况下,这很容易,但我没有使用外部c函数。我想调用putchar,所以是一个只有一个参数的函数。由于我在windows上,使用x86-64,我希望将单个参数放入rcx,然后使用函数指针地址运行call。为此,我编写了以下代码:

from peachpy import *
from peachpy.x86_64 import *
import ctypes


putchar_address = ctypes.addressof(ctypes.cdll.msvcrt.putchar)
c = Argument(uint64_t)

with Function("p", (c,), int64_t) as asm_function:
    LOAD.ARGUMENT(rcx, c)
    MOV(r8, putchar_address)
    CALL(r8)
    RETURN(rax)


raw = asm_function.finalize(abi.detect()).encode()
python_function = raw.load()

print(python_function(48))

这会导致最终代码上的OSError: exception: access violation writing 0x0000029E58C1A978崩溃

我看了很多其他的SO答案,但是没有一个能真正帮助解决这个问题,代码实际上就是这些答案的结果。最有用的是这个:Handling calls to (potentially) far away ahead-of-time compiled functions from JITed code

编辑:我还尝试了一些事情

PeachPy没有明确地直接公开rsp,声称它已经正确地处理了它。但我仍然可以直接影响它,导致以下代码:

from peachpy.x86_64.registers import rsp
#...
    LOAD.ARGUMENT(rcx, c)
    SUB(rsp, 40)
    MOV(r8, putchar_address)
    CALL(r8)
    ADD(rsp, 40)
    RETURN(rax)

这将错误更改为崩溃,退出代码为0xC0000409,这意味着堆栈访问超出堆栈顶部

以下是PeaachPy生成的结果:

没有rsp

0:  49 b8 a8 a8 1a 84 1f    movabs r8,0x21f841aa8a8
7:  02 00 00
a:  41 ff d0                call   r8
d:  c3                      ret 

rsp

0:  48 83 ec 28             sub    rsp,0x28
4:  49 b8 a8 98 ad 9e ac    movabs r8,0x1ac9ead98a8
b:  01 00 00
e:  41 ff d0                call   r8
11: 48 83 c4 28             add    rsp,0x28
15: c3                      ret 

(从https://defuse.ca/online-x86-assembler.htm

基于c编译器的输出(这里:https://godbolt.org/z/BKgk7Y),我创建了以下代码

    MOV([rsp + 16], rdx)
    MOV([rsp + 8], rcx)
    SUB(rsp, 40)
    MOV(rcx, [rsp + 56])
    CALL([rsp + 48])
    ADD(rsp, 40)
    RETURN(rax)

它创建与c编译器相同的汇编代码:

0:  48 89 54 24 10          mov    QWORD PTR [rsp+0x10],rdx
5:  48 89 4c 24 08          mov    QWORD PTR [rsp+0x8],rcx
a:  48 83 ec 28             sub    rsp,0x28
e:  48 8b 4c 24 38          mov    rcx,QWORD PTR [rsp+0x38]
13: ff 54 24 30             call   QWORD PTR [rsp+0x30]
17: 48 83 c4 28             add    rsp,0x28
1b: c3                      ret 

这失败了,意味着问题不在生成的代码中。(我没有使用putchar,我仍然得到相同的退出代码0xC0000409)


Tags: 代码fromimportfunctioncallctypesx86mov
1条回答
网友
1楼 · 发布于 2024-05-16 01:53:47

在@PeterCordes的帮助下,我解决了重要的问题

  • 我误解了windows呼叫约定。您需要保留阴影空间并对齐堆栈,因此需要“sub-rsp,40”
  • ctypes.addressof(ctypes.cdll.msvcrt.putchar)给出的不是代码的开头,而是指向代码开头的指针的地址

问题1很容易解决,问题2需要稍加修改。最后,该代码起作用:

c_void_p_p = ctypes.POINTER(ctypes.c_void_p)

putchar_address = ctypes.addressof(ctypes.cast(ctypes.cdll.msvcrt.putchar, c_void_p_p).contents)
func_ptr = Argument(ptr())
c = Argument(uint64_t)

with Function("p", (c,), int64_t) as asm_function:
    MOV(r12, putchar_address)
    SUB(rsp, 40)
    CALL(r12)
    ADD(rsp, 40)
    RETURN()

raw = asm_function.finalize(abi.detect()).encode()
print(raw.code_section.content.hex())
python_function = raw.load()
print(python_function(54))

这将生成此程序集:

0:  41 54                   push   r12
2:  49 bc 90 77 75 4d fa    movabs r12,0x7ffa4d757790
9:  7f 00 00
c:  48 83 ec 28             sub    rsp,0x28
10: 41 ff d4                call   r12
13: 48 83 c4 28             add    rsp,0x28
17: 41 5c                   pop    r12
19: c3                      ret 

并且完全按照预期工作

(只需记住哪些寄存器已保存/需要保存。)

相关问题 更多 >