You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

向NASM 64位DLL传递数组指针后无法正常访问的问题排查

问题分析与解决方案

首先,你的核心问题出在Python中传递数组指针的方式错误,导致DLL接收到的不是数组数据缓冲区的地址,而是Python对象本身的指针。让我们逐个拆解你的问题:

1. 为何rcx指向的数组可读但无法写入?

你用ctypes.byref(PVarrNew)传递的是Python中PVarrNew这个ctypes数组对象的指针,而非数组实际数据的起始地址。当你在NASM中读取[rcx+32]时,刚好命中了该Python对象内部的某个可访问字段(可能是缓冲区地址或其他元数据),所以能读取到值;但写入这个位置时,你实际上在修改Python对象的内部结构——这要么触发Python的内存保护(比如写入只读区域),要么破坏对象完整性,导致看似无法写入(或出现未定义行为)。

另外,你在Python中写的PVarrNew[0] = id(PVarrNew)是错误的:id()返回的是Python对象自身的内存地址,不是数组数据缓冲区的地址,这进一步混淆了两个地址的概念。

2. 为何rdx指向的数组既不能读也不能写?

同样,ctypes.byref(arrNew)传递的是arrNew这个Python对象的指针,而非数组数据地址。对于1000个元素的大数组,其Python对象内部结构中,[rdx+32]的位置不属于有效内存区域,或者被Python内存管理器标记为不可访问,因此读写都会触发内存访问错误(只是你的代码没有显式捕获到)。


解决方案

第一步:修正Python的参数传递方式

去掉ctypes.byref(),直接传递数组实例即可——ctypes会自动将其转换为指向数组第一个元素的C指针(符合你声明的c_void_p类型):

# 替换原有的调用代码
retvar = SimpleTest(PVarrNew, arrNew)

如果你需要显式获取指针,也可以用ctypes.addressof()获取缓冲区地址后转换为c_void_p:

retvar = SimpleTest(
    ctypes.c_void_p(ctypes.addressof(PVarrNew)),
    ctypes.c_void_p(ctypes.addressof(arrNew))
)

第二步:修正NASM的数组访问逻辑

现在rcx和rdx直接指向数组的第一个元素,访问第n个元素(从0开始)需要计算偏移量:索引 * 8(因为c_int64是8字节)。比如:

[BITS 64]
export SimpleTest
section .data
tempvar: dq 0
section .text
finit
SimpleTest:
    push rdi
    push rbp
    mov rdi, rcx        ; rdi = PVarrNew数组的起始地址
    mov rbp, rdx        ; rbp = arrNew数组的起始地址

    ; 读取PVarrNew[3](偏移3*8=24)到tempvar
    mov rax, [rdi + 24]
    mov [tempvar], rax

    ; 写入PVarrNew[3]为4567
    mov rax, 4567
    mov [rdi + 24], rax

    ; 写入arrNew[5]为1235
    mov rax, 1235
    mov [rbp + 40], rax ; 5*8=40

    mov rax, 1235       ; 设置返回值
    pop rbp
    pop rdi
    ret

第三步:修正Python中的错误赋值

如果你需要在数组中存储缓冲区地址,应该用ctypes.addressof()而非id():

# 替换原有的PVarrNew[0]赋值
PVarrNew[0] = ctypes.addressof(PVarrNew)

验证修正后的完整代码

修正后的Python代码

import ctypes

OutputArrayType = ctypes.c_int64 * 1000
arrNew = OutputArrayType()
PassArrayType = ctypes.c_int64 * 10
PVarrNew = PassArrayType()

# 正确存入数组缓冲区的地址(如果需要)
PVarrNew[0] = ctypes.addressof(PVarrNew)
PVarrNew[1] = 2
PVarrNew[2] = len(PVarrNew)

ThisDll = ctypes.WinDLL(r"C:/Temp2/Std_Math_Formulas.dll")
SimpleTest = ThisDll.SimpleTest
SimpleTest.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
SimpleTest.restype = ctypes.c_int64

# 直接传递数组实例,无需byref
retvar = SimpleTest(PVarrNew, arrNew)

# 验证结果
print(f"返回值: {retvar}")
print(f"PVarrNew[3] 修改后的值: {PVarrNew[3]}")
print(f"arrNew[5] 修改后的值: {arrNew[5]}")

编译链接命令保持不变

nasm -Z myfile.err -f Win64 C:\Temp2\Std_Math_Formulas.asm -l myfile.lst -F cv8 -g -o C:\Temp2\Std_Math_Formulas.obj
GoLink Std_Math_Formulas.obj /dll /entry SimpleTest msvcrt.dll

这样修改后,你就能正常读写两个数组了。

内容的提问来源于stack exchange,提问作者RTC222

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:30:41