assembly 在.data中索引地址比数组高得多的数组会出错

puruo6ea  于 2023-10-19  发布在  其他
关注(0)|答案(1)|浏览(80)

我的作业是在MASM中改变数组中的值,交换对。

array[0] = array[1]
array[1] = array[0]
array[2] = array[3]
array[3] = array[2]

等等……
我已经放弃使用var1var2变量,因为我似乎无法让它们工作。我有

.data
; add data here
array01 DWORD 100, 200, 300, 400, 500, 600
var1 DWORD ?
var2 DWORD ?

.code
main PROC
    mov edi, OFFSET array01         ; 1: EDI = address of array01
    mov ecx, LENGTHOF array01       ; 2: initialize loop counter
    mov eax, 0                      ; 3: sum =0

L1:                                 ; 4: Mark beginning of loop
    mov eax, [edi]                  ; 5: Get var1
    add edi, TYPE array01           ; 6: increment index
    mov ebx, [edi]                  ; 7: Get var2
    sub edi, TYPE array01           ; 8: decrement to get index to point to the right location

    mov array01[edi], ebx           ; 9: Change array01[i] to var1
    add edi, TYPE array01           ; 10: increment index
    mov array01[edi], eax           ; 11: Change array01[i] to var1
    loop L1                         ; 12: repeats until ecx is 0

    INVOKE ExitProcess, 0
main ENDP
End main

我的问题是在指令9。它给了我一个错误
Assignment_03.exe中0x007C1032处的未处理异常:0xC0000005:访问冲突写入位置0x00F88004。
从调试器中,我的EDI = 007C4000&array01位于0x007C4000彼此匹配,但不是错误报告的数据地址。(我的EIP = 007C1032,错误指令的代码地址。)
早期的运行具有Exception thrown at 0x00171029 in Assignment_03.exe: 0xC0000005: Access violation writing location 0x002E8000.
我知道它告诉我我正在访问一些我不被允许访问的东西,但我不知道如何修复它。
我已经尝试了mov [numList+ebx],dhhttps://termspar.wordpress.com/2019/10/12/x86-assembly-swap-array-elements/,但我仍然得到同样的错误。
我做错了什么?你能解释一下解决方案吗?我说清楚我不想要作业的答案。我需要帮助来改变数组中的值。
mov dword ptr [array01], 700可以更改第一个元素,但使用EDI进行索引不起作用。

vof42yt1

vof42yt11#

EDI已经是一个指针:使用mov [edi], ebx
mov array01[edi], ebx将EDI中的指针添加到数组的地址。如果EDI是一个很小的数字,即从数组开始的一个字节偏移量,那么这是正确的。但这件案子不是这样的在循环之前,你用mov edi, OFFSET array01把一个指针放进EDI。
该地址计算类似于C int *ptr = array01;array01[ (int)ptr ] = tmp2;,但没有按类型宽度进行缩放。在聊天中的讨论中,您使用调试器发现array01的实际地址是0x007C4000,但错误地址报告为写入0x00F88004。这是更仔细地查看代码到底在做什么的线索。
0x007C4000 * 2 = 0xF88000,故障地址。我不知道额外的...4是从哪里来的。第一次存储是在sub edi, 4之后,所以应该用EDI恢复到其原始值。(内存保护使用4K页面粒度,所以如果0xF 88004出错,0xF 88000应该在第一次迭代中出错,就像调试器显示的那样。

**那么,为什么它故障的商店,而不是负载?**您为加载使用了正确的寻址模式,[edi],只是解引用指针,而不是向其添加另一个地址!

一开始我觉得很奇怪,那个地址有只读存储器,但是没有,第一次访问是在商店里。
顺便说一句,从[edi][edi+4]加载和存储要简单得多,所以你的循环只需要一个add edi, 8就可以前进到下一对。(目前,每次迭代前进8步,后退4步,净前进一个4字节元素。)
你必须调整你的循环计数器,以避免去结束;使用指向数组末尾的指针和cmp / jb L1 against that, instead of calculating an iteration count for the slow loop instruction. You need to stop even if there's 0 or 1 elements left, like the bottom of a do{.; ptr+=8;}while(ptr < endp-4);`其中endp是数组末尾的标签。
顺便说一句,有趣的方法是使用SIMD加载4x 32位元素(2对), Shuffle ,并将它们存储回来。由于在这种情况下问题大小恰好是2对,我们可以放弃循环:P

movdqu xmm0, xmmword ptr [array01]   ; 16-byte load
   pshufd xmm0, xmm0, 0b10_11_00_01  ; 2-bit indices for 4x 4-byte elements, packed into an 8-bit immediate.
              ; NASM accepts that syntax for binary literals, MASM probably doesn't
              ; _mm_shuffle_epi32(vec, _MM_SHUFFLE(2,3, 0,1))
   movdqu xmmword ptr [array01], xmm0   ; 16-byte store

您可能需要告诉MASM允许SSE 2向量指令。C编译器默认使用这些,甚至在32位代码中也是如此。但是很明显,使用16字节的shuffle并不能教会你写循环。
另一种有趣的方法是编写64位代码,并执行ror qword ptr [rdi], 32之类的操作,以执行64位旋转32,从而交换64位内存块的一半。

相关问题