通过ctypes byref将Python3接口到NASM
我正在将Python3与Linux Mint 18和NASM结合使用,并希望编写直接使用x87 FPU寄存器的汇编代码。我应该指出的是,我是一名硬件工程师,也会编程,而不是程序员。虽然我对python和大多数汇编程序都很在行,但我很少使用C语言,而且在接口方面也不是很在行 搜索之后,我在ActiveState(如下)上找到了一个配方,这让我开始将NASM代码放入内存,并通过值传递int和float 由于我想使用FPU,并且只能从RAM加载FPU,接下来我要做的是在内存中传入一个浮点值,由输入寄存器rdi指向,我理解为byref(),计划从[rdi]加载FPU 我已经在下面代码的if name=='main'部分实现了简单的'helloworld'addinteger和move double函数。最后一个函数是我第n次尝试理解(但失败了)通过ctyes向汇编程序传递双byref的语法。我仍然在学习如何理解语法错误消息,以及不太适合这种情况的ctypes示例 有人能帮我举一个正确的例子吗通过ctypes byref将Python3接口到NASM,python,linux,nasm,x86-64,ctypes,Python,Linux,Nasm,X86 64,Ctypes,我正在将Python3与Linux Mint 18和NASM结合使用,并希望编写直接使用x87 FPU寄存器的汇编代码。我应该指出的是,我是一名硬件工程师,也会编程,而不是程序员。虽然我对python和大多数汇编程序都很在行,但我很少使用C语言,而且在接口方面也不是很在行 搜索之后,我在ActiveState(如下)上找到了一个配方,这让我开始将NASM代码放入内存,并通过值传递int和float 由于我想使用FPU,并且只能从RAM加载FPU,接下来我要做的是在内存中传入一个浮点值,由输入寄存
#!/usr/bin/env python
# python 2 example
# downloaded from http://code.activestate.com/recipes/579037-how-to-execute-x86-64-bit-assembly-code-directly-f/
# change to python 3 only required string.encode to get bytes, and print()
import subprocess, os, tempfile
from ctypes import *
PAGE_SIZE = 4096
class AssemblerFunction(object):
def __init__(self, code, ret_type, *arg_types):
# get handle and file names for temp files
(fd, source_name) = tempfile.mkstemp(".S", "assembly", os.getcwd())
object_name = os.path.splitext(source_name)[0]
# write code string out to source file
os.write(fd, code.encode('utf-8')) ## convert code string to code bytes
os.close(fd)
# run NASM on the source
subprocess.check_call(["nasm",source_name])
# it creates an object file without the extension
# get the ojbect code
with open(object_name, 'rb') as fbin:
binary = fbin.read()
bin_len = len(binary)
# clean up temporary files
os.unlink(source_name)
os.unlink(object_name)
# align our code on page boundary.
self.code_buffer = create_string_buffer(PAGE_SIZE*2+bin_len)
addr = (addressof(self.code_buffer) + PAGE_SIZE) & (~(PAGE_SIZE-1))
memmove(addr, binary, bin_len)
# Change memory protection
self.mprotect = cdll.LoadLibrary("libc.so.6").mprotect
mp_ret = self.mprotect(addr, bin_len, 4) # execute only.
if mp_ret: raise OSError("Unable to change memory protection")
self.func = CFUNCTYPE(ret_type, *arg_types)(addr)
self.addr = addr
self.bin_len = bin_len
def __call__(self, *args):
return self.func(*args)
def __del__(self):
# Revert memory protection
if hasattr(self,"mprotect"):
self.mprotect(self.addr, self.bin_len, 3)
if __name__ == "__main__":
# input parameters go in via the following registers
# 1, RDI; 2, RSI; 3, RDX; 4, RCX; 5, R8; 6, R9
# add integer example, WORKS
add_func_2i = """ BITS 64
mov rax, rdi ; Move the first parameter
add rax, rsi ; add the second parameter
ret ; rax will be returned
"""
Add_int_2 = AssemblerFunction(add_func_2i, c_int, c_int, c_int)
print(Add_int_2(1, 2))
print(Add_int_2(4, 5))
# move a floating value from input to rax, WORKS
move_dub = """ BITS 64
mov rax, rdi ; Move the first parameter
ret ; rax will be returned
"""
echo_dub = AssemblerFunction(move_dub, c_double, c_double)
print(echo_dub(4.321))
# move a floating value from input* to rax, no joy yet
move_deref_dub = """ BITS 64
mov rax, [rdi] ; Move the first parameter
ret ; rax will be returned
"""
dub_in = c_double(1.0)
echo_ddub = AssemblerFunction(move_deref_dub, c_double, byref(dub_in))
print(echo_ddub(54.321))
在Linux上的64位代码中,浮点值将在向量寄存器XMM0到XMM7中传递。并且将返回XMM0/XMM1(不是RAX)中的浮点值。您应该查看64位Linux系统V ABI。你可以在这里查看ABI:(rev 252是最新的64位版本)@MichaelPetch我在跑步之前试着走路,使用的是与x86/x87兼容的最低级别的东西。我用的是i5。我的第二个示例可以工作,使用rdi和rax作为输入/输出寄存器,并且似乎可以工作。您的评论是指rdi和xmm0上的值,还是什么?一旦我能以正确的语法输入输出一些东西,那么我就可以尽情地玩其他所有时髦的东西了。要通过指针传递一个double,那么你需要使用类似于
echo\u ddub=AssemblerFunction的东西(move\u deref\u dub,c\u double,pointer(c\u double))
然后print(echo\u ddub(c\u doub(54.321)))
在浮点测试的测试代码中,您可以尝试movsd xmm0,[rdi]
获取[rdi]
处的64位双浮点数,并将其移动到xmm0寄存器中。作为一个实验,xmm0将使xmm0中的值加倍。XMM0恰好也是用于返回浮点和双精度的向量寄存器。然后你只需要做ret
。最终结果是它应该打印108.642
@MichaelPetch,我的意思是我更喜欢显式地传递byref(c_double(54.321))
。当您在不使用byref的情况下传递c_double(54.321)
时,您依靠ctypes为您隐式地创建指针,这将很乐意做到,但这会使读取代码时,在不必搜索原型定义的源代码的情况下,对传递的内容产生歧义。