【问题标题】:NASM how to return an array of pointers?NASM如何返回一个指针数组?
【发布时间】:2018-08-22 07:29:31
【问题描述】:

我从 ctypes 调用 NASM dll。在我的 NASM dll 代码中,我使用 malloc 创建了三个数组。它们的指针分配给 final1_ptr、final2_ptr 和 final3_ptr。如果我单独返回任何指针,我会得到正确的结果。

但我想返回一个包含所有三个指针的数组。为此,我还在 .data 部分初始化了一个数组:Return_Pointer_Array: dq 0, 0, 0。

最后我以这种方式分配指针:

mov rdi,Return_Pointer_Array
mov rax,qword[final1_ptr]
mov qword [rdi],rax
mov rax,qword[final2_ptr]
mov qword [rdi+8],rax
mov rax,qword[final3_ptr]
mov qword [rdi+16],rax
mov rax,rdi
ret

然而,ctypes 得到的是一个由三个非常小的小数组成的数组,不是有效的指针地址。

实际的程序清单很长,所以我希望上面的代码足以理解问题。

非常感谢您的帮助。

编辑:如果我返回 Return_Pointer_Array 而不分配任何值,它会返回双(浮点)值。数组初始化为整数,所以我不明白为什么初始化为dq 0,0,0时默认为浮点数。

编辑#2: 根据请求,这里是 Python 代码:

def Test_Data_Read():

#Note:  in production, X is a list of 50,000 random floats read from file; 
#For simplicity, I have reduced it to a short simple list:  

X = [11.0,1.0,2.0,7.0,4.0,4.0,4.0,6.0,7.0,6.0,11.0,4.0,10.0,7.0,8.0,4.0,9.0,4.0,5.0,3.0,4.0,1.0,2.0,5.0,3.0,5.0,11.0,10.0,11.0,9.0,3.0,12.0]

PyGram_Test_01_asm(X)

#__________

def PyGram_Test_01_asm(X):

Input_Length_Array = []
Input_Length_Array.append(len(X)*8)

CA_X = (ctypes.c_double * len(X))(*X)
length_array_out = (ctypes.c_double * len(Input_Length_Array))(*Input_Length_Array)

hDLL = ctypes.WinDLL("C:/NASM_Test_Projects/Nested_For_Loops/Nested_For_Loops.dll")
CallName = hDLL.Main_Entry_fn
CallName.argtypes = [ctypes.POINTER(ctypes.c_double), ctypes.POINTER(ctypes.c_double)]
CallName.restype = ctypes.POINTER(ctypes.c_double)

start_time = timeit.default_timer()

ret_ptr = CallName(CA_X,length_array_out)

a = ret_ptr[:3]

这是整个 NASM 列表(有时最小和完整是相互矛盾的)

; Header Section
[BITS 64]

[default rel]

export Main_Entry_fn

extern malloc, realloc, free

section .data align=16
out_array_pointer: dq 0
call_var_length: dq 0
c_square: dq 0
final1_ptr: dq 0
final1_ctr: dq 0
final2_ptr: dq 0
final2_ctr: dq 0
final3_ptr: dq 0
final3_ctr: dq 0
float_temp_var: dq 0.0
temp_math_var: dq 0
Bool_0: dq 0.0
Bool_1: dq 1.0
data_master_ptr: dq 0
initial_dynamic_length: dq 0
X_ptr: dq 0
X_ctr: dq 0
X: dq 0
n: dq 0
i: dq 0
a: dq 0.0
range_loop_start_a: dq 0
range_loop_end_a: dq 0
b: dq 0.0
range_loop_start_b: dq 0
range_loop_end_b: dq 0
c: dq 0
X_length: dq 0
Input_Length_Array: dq 0,
Return_Pointer_Array: dq 0, 0, 0

section .text

PyGram_Test_01_fn:
xor rcx,rcx
mov [X_ctr],rcx
label_401:
lea rdi,[rel X_ptr]
mov rbp,qword [rdi]
mov rcx,[X_ctr]
cmp rcx,[X_length]
jge exit_label_for_PyGram_Test_01_fn
movsd xmm0,[rbp+rcx]
movsd [n],xmm0
add rcx,8
mov [X_ctr],rcx
movsd xmm0,qword[n]
cvttsd2si rax,xmm0
mov [n],rax
mov rax,[n]
mov rdx,1
add rax,rdx
mov [range_loop_end_a],rax
mov rax,1
sub rax,1
mov [range_loop_start_a],rax
mov [a],rax
label_801:
mov rcx,[a]
inc rcx
cmp rcx,[range_loop_end_a]
jge label_401
mov [a],rcx
mov rax,[a]
sub rax,1
mov [range_loop_start_b],rax
mov [b],rax
mov rax,[n]
mov [range_loop_end_b],rax
label_1201:
mov rcx,[b]
inc rcx
cmp rcx,[range_loop_end_b]
jge label_801
mov [b],rcx
mov rax,[a]
mov r8,[a]
xor rcx,rcx
add rcx,1
Exponent_Label_0:
mul r8
inc rcx
mov rdx,2
cmp rcx,rdx
jl Exponent_Label_0
mov[temp_math_var],rax
mov rax,[b]
mov r8,[b]
xor rcx,rcx
add rcx,1
Exponent_Label_1:
mul r8
inc rcx
mov rdx,2
cmp rcx,rdx
jl Exponent_Label_1
mov rdx,rax
mov rax,[temp_math_var]
add rax,rdx
mov [c_square],rax
mov rax,[c_square]
mov rax,[c_square]
cvtsi2sd xmm1,rax
sqrtsd xmm0,xmm1
cvttsd2si rax,xmm0
mov [c],rax
label_1601:
mov rax,[c]
mov rax,[c]
mov r8,[c]
xor rcx,rcx
add rcx,1
Exponent_Label_2:
mul r8
inc rcx
mov rdx,2
cmp rcx,rdx
jl Exponent_Label_2
mov [temp_math_var],rax
mov rax,[c_square]
mov rdx,[temp_math_var]
sub rax,rdx
mov rdx,0
cmp rax,rdx
jne label_1201
mov rdi,[final1_ptr]
mov rcx,[final1_ctr]
mov rax,[a]
cvtsi2sd xmm0,rax
movsd [rdi + rcx],xmm0
add rcx,8
mov [final1_ctr],rcx
mov rdi,[final2_ptr]
mov rcx,[final2_ctr]
mov rax,[b]
cvtsi2sd xmm0,rax
movsd [rdi + rcx],xmm0
add rcx,8
mov [final2_ctr],rcx
mov rdi,[final3_ptr]
mov rcx,[final3_ctr]
mov rax,[c]
cvtsi2sd xmm0,rax
movsd [rdi + rcx],xmm0
add rcx,8
mov [final3_ctr],rcx
jmp label_1201
label_900:
exit_label_for_PyGram_Test_01_fn:
mov rdi,Return_Pointer_Array
mov rax,qword[final1_ptr]
mov qword [rdi],rax
mov rax,qword[final2_ptr]
mov qword [rdi+8],rax
mov rax,qword[final3_ptr]
mov qword [rdi+16],rax
mov rax,rdi
ret

; __________
; Main Entry

Main_Entry_fn:
push rdi
push rbp
mov [X_ptr],rcx
mov [data_master_ptr],rdx
; Now assign lengths
lea rdi,[data_master_ptr]
mov rbp,[rdi]
xor rcx,rcx
movsd xmm0,qword[rbp+rcx]
cvttsd2si rax,xmm0
mov [X_length],rax
add rcx,8
; __________
; malloc for dynamic arrays
lea rdi,[data_master_ptr]
mov rbp,[rdi]
movsd xmm0,qword[rbp]
cvttsd2si rax,xmm0
mov [initial_dynamic_length],rax
mov rax,3529984
mov [initial_dynamic_length],rax
mov rcx,qword[initial_dynamic_length] ; Initial size
xor rax,rax
sub rsp,40
call malloc
mov qword [final1_ptr],rax
add rsp,40
mov rcx,qword[initial_dynamic_length] ; Initial size
xor rax,rax
sub rsp,40
call malloc
mov qword [final2_ptr],rax
add rsp,40
mov rcx,qword[initial_dynamic_length] ; Initial size
xor rax,rax
sub rsp,40
call malloc
mov qword [final3_ptr],rax
add rsp,40
; __________
call PyGram_Test_01_fn
exit_label_for_Main_Entry_fn:
pop rbp
pop rdi
ret

定义 PyGram_Test_01(X):

final1, final2, final3 = [],[],[]

这是汇编程序的 Python 源代码:

for i, n in enumerate(X):
    n = int(n)
    for a in range(1,n+1):
        for b in range(a,n):
            c_square = a**2 + b**2
            c = int(sqrt(c_square))

            if ((c_square - c**2) == 0):
                final1.append(a)
                final2.append(b)
                final3.append(c)

【问题讨论】:

  • 请提供您的完整 python 代码和完整的汇编代码以使其成为minimal reproducible example
  • @Michael Petch - 感谢您的回复。我发布了上面的代码。 NASM 列表很长;有问题的区域位于 exit_label_for_PyGram_Test_01_fn 下方。
  • 您的 python 代码不完整。不知道X 是什么,因为您没有向我们展示您如何调用 PyGram_Test_01_asm
  • @Michael Petch - 我在上面的 Python 代码中添加了调用 PyGram_Test_01_asm 的代码。如果需要更多,请询问。非常感谢您抽出宝贵时间。
  • 好吧,你说的是CallName.restype = ctypes.POINTER(ctypes.c_double)。这意味着返回值是一个指向双精度的指针。问题是当您返回 Return_Pointer_Array 时,它是一个指向双指针数组的指针。所以 Python 正试图将指针转换为双精度,因为这就是你告诉它的。

标签: python arrays nasm x86-64 ctypes


【解决方案1】:

我将其发布为答案,因为其他人可能需要此信息。在 Michael Petch(上图)的帮助下,我解决了这个问题。

要从 dll 返回一个指针数组到 ctypes:

  1. 在 dll 中声明一个数组(对于 NASM,它在 .data 部分中声明,例如 Return_Pointer_Array: dq 0, 0, 0)。

  2. 退出时,将指针分配给数组(对于汇编程序;在 C 中,使用 C 分配):

    mov rdi,Return_Pointer_Array
    mov rax,qword[final1_ptr]
    mov [rdi],rax
    mov rax,qword[final2_ptr]
    mov [rdi+8],rax
    mov rax,qword[final3_ptr]
    mov [rdi+16],rax
    mov rax,rdi
    
  3. 将返回类型设置为 CallName.restype = ctypes.POINTER(ctypes.c_int64)。

  4. 将每个返回的指针转换成Python数组(在这个例子中,我预先知道每个数组中有多少个元素;对于动态数组,长度可以作为返回数组):

    ret_ptr = CallName(CA_X,length_array_out)
    a = ret_ptr[:3]
    n1 = ctypes.cast(a[0], ctypes.POINTER(ctypes.c_double))
    x1 = n1[:50000]
    n2 = ctypes.cast(a[1], ctypes.POINTER(ctypes.c_double))
    x2 = n2[:50000]
    n3 = ctypes.cast(a[2], ctypes.POINTER(ctypes.c_double))
    x3 = n3[:50000]
    

或者,将返回类型设置为 CallName.restype = ctypes.POINTER(ctypes.POINTER(ctypes.c_double)),正如 Michael Petch 上面所说的,然后像这样提取值:

    ret_ptr = CallName(CA_X,length_array_out)
    a = ret_ptr[:3]
    n1 = a[0]
    x1 = n1[:50000]
    etc

【讨论】:

  • 应该让你意识到一些事情。如果你在 Cmalloc 某些东西,Python 将无法恢复该内存。如果你打算这样做,我建议在你的 DLL 中添加某种类型的 free 函数,该函数接受从 Main_Entry_fn 返回的指针,该指针可以沿着主数组向下并在所有 malloc'ed 指针上调用 free。完成结构后,您必须从 Python 代码中手动调用此免费函数。
  • 你在汇编程序中这样做是有原因的吗?这只是为了学习和实验。如果我为 Python 编写一个外部 DLL 或共享对象,我通常在 C 中进行。如果出于性能原因,我可以使用内联代码或具有功能的辅助程序集文件。但我使用 C 的主要原因是因为它使代码更易于维护和阅读,不易出错,并且更容易与 Python lib 交互以让我的函数创建 PyObject's直接。
  • 是的,在数组返回并被提取后,我从 ctypes 调用 free()。为简洁起见,上面的代码中省略了 NASM 中的免费代码。它为每个数组调用。我使用汇编程序编写代码是因为我长期以来一直使用 32 位汇编程序编写工作。我刚刚切换到 64 位 NASM。我喜欢汇编程序,但我真的不太喜欢 C(尽管 C 的灵感来源于 60 年代的一项努力)。此外,汇编程序通常比 C 更快,尽管在这一点上合理的想法有所不同,但证据在指标中。
  • 我的一般规则是这样的。用 C 对其进行编码。查看生成的指令。如果生成的代码看起来不好,请尝试修改 C 代码以生成更好的代码。这个手杖涉及重新编写代码,使用builtins 来使用特定的指令功能,或者如果需要内联汇编(如果你不知道自己在做什么,我不建议这样做),然后如果需要汇编文件最好手动操作的情况。
  • 64 位 Intel 芯片不再支持内联汇编程序,这就是 MS 从 C++ 放弃它的原因。这怎么可能是合理的?硬件不知道是什么产生了它正在执行的指令字节! Inline-asm 最终与编译器生成的 asm 指令混合在一起,这些指令都被组装成机器代码。 MSVC 放弃了内联 asm,因为 the implementation in their compiler is a total hack, and it has trouble with register-arg calling conventions
猜你喜欢
  • 1970-01-01
  • 2012-08-18
  • 1970-01-01
  • 2021-06-25
  • 2021-05-11
  • 2014-04-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多