简体   繁体   English

按地址从 jit 代码调用 c 函数

[英]Calling a c-function from jitted code by address

I am currently trying to JIT via python.我目前正在尝试通过 python 进行 JIT。 I found peachpy via another SO question.我通过另一个 SO question 找到了 peachpy For most part this is easy, but I am failing to use external c-functions.在大多数情况下,这很容易,但我没有使用外部 c 函数。 I want to call putchar, so a function with a single argument.我想调用 putchar,所以是一个带有单个参数的 function。 Since I am on windows, with x86-64, I expect the single argument to be put into rcx , and then running call with the function-pointer address.由于我在 windows 上,使用 x86-64,我希望将单个参数放入rcx ,然后使用函数指针地址运行call For this I wrote this code:为此,我编写了以下代码:

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

This crashes with OSError: exception: access violation writing 0x0000029E58C1A978 on the final code.这与OSError: exception: access violation writing 0x0000029E58C1A978崩溃。

I looked through lots of other SO answers, but none really help to solve this problem, and the code is actually the result of these.我查看了许多其他 SO 答案,但没有一个真正有助于解决这个问题,而代码实际上是这些的结果。 The most useful was this one: Handling calls to (potentially) far away ahead-of-time compiled functions from JITed code最有用的是这个: 处理来自 JITed 代码的(可能)很远的提前编译函数的调用

Edit: A few more things I tried.编辑:我尝试了更多的东西。

PeachPy does specifically not expose rsp directly, claiming that it already deals with it correctly. PeachPy 并没有明确地直接暴露rsp ,声称它已经正确处理了它。 But I can still influence it directly, leading to this code:但我仍然可以直接影响它,导致这段代码:

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)

This changes the error to a crash with exit code 0xC0000409 , meaning stack access beyond top of stack.这会将错误更改为退出代码为0xC0000409的崩溃,这意味着堆栈访问超出了堆栈顶部。

Here are the disassemble result of what PeaachPy generates:以下是 PeaachPy 生成的反汇编结果:

Without rsp没有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 

With rsprsp

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 

(From https://defuse.ca/online-x86-assembler.htm ) (来自https://defuse.ca/online-x86-assembler.htm

Based on the output of the c compiler (here: https://godbolt.org/z/BKgk7Y ), I created the following code基于c编译器的output(这里: 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)

which creates the same assembler code as the c compiler:它创建与 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 

This fails, meaning the problem is not in the generated code.这失败了,这意味着问题不在生成的代码中。 (And I didn't use putchar, and I still get the same exit code 0xC0000409) (而且我没有使用putchar,我仍然得到相同的退出代码0xC0000409)

With the help of @PeterCordes I figured out the important problems.在@PeterCordes 的帮助下,我发现了重要的问题。

  • I misunderstood the windows call convention.我误解了 windows 调用约定。 You need to reserve shadow space and align the stack, so 'sub rsp, 40' is required.您需要保留阴影空间并对齐堆栈,因此需要 'sub rsp, 40'。
  • ctypes.addressof(ctypes.cdll.msvcrt.putchar) gives not the start of the code, but the address of a pointer to the start of the code. ctypes.addressof(ctypes.cdll.msvcrt.putchar)给出的不是代码的开头,而是指向代码开头的指针的地址。

Problem 1 is easy to solve, and Problem 2 needed a bit of tinkering.问题 1 很容易解决,问题 2 需要一些修补。 In the end, this code works:最后,这段代码有效:

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

This generates this assembly:这会生成这个程序集:

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 

And works exactly as expected.并且完全按预期工作。

(Just remember which registers are saved/need to be saved.) (只需记住哪些寄存器已保存/需要保存。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM