为什么访问 16 位寄存器保存的值会导致段错误,而在 32 位寄存器上执行相同的操作却可以正常工作?

问题描述 投票:0回答:1

我在使用 16 位寄存器完成大学作业时遇到问题。我尝试运行的代码如下:

myArray db 1,2,3,4; declared in the correct section
;-----------------
mov si, myArray
mov ax, [si] ; segfault here
sub ax, [si+1]
mov [si+3], ax

mov dx, 1234h
push dx
mov dx, 0ABCDh
push dx
mov bp, sp
mov ax, ss:[bp]

两者都会导致分段错误。但是,当我使用 ESI 和 RBP 寄存器运行相同的代码时,它的汇编没有问题。

我在 Ubuntu 22.04.3 的虚拟机上运行它。我从一位朋友那里听说(不确定这有多可靠),x86 64 位可能会阻止对某些 16 位寄存器的直接访问,这就是我遇到问题的原因。我向教授提出了这个问题,但他们驳回了我,说这可能是我的语法错误。

我尝试寻找解决方案,但没有成功。我最初使用的是 WSL,认为是问题所在,于是改用 VirtualBox VM,但没有效果。

assembly x86 segmentation-fault
1个回答
0
投票
mov eax, 1
mov ebx, 0
int 0x80 

评论已经确定您正在编写一个32位程序。
使用16位寄存器保存16位值是可以的,但不能保存地址!您的

mov si, myArray
mov bp, sp
正在加载不完整的地址。您可以通过
mov esi, myArray
mov ebp, esp
获得完整地址。
出于性能原因,您不仅应该加载 16 位寄存器,还应该加载整个 32 位寄存器。为此,请使用
movzx
(带有零扩展的 MOVe)。

注意缓冲区溢出

mov ax, [si]
sub ax, [si+1]
同时寻址两个字节大小的数组元素,但
mov [si+3], ax
还写入数组后面的字节!这可能是另一个变量,并且会以这种方式被破坏。目前尚不清楚第一个片段应该实现什么,但重写如下:

mov   esi, myArray
movzx eax, word [esi]  ; -> AX = 0201h  (EAX = 00000201h)
sub   ax, [esi+1]      ; -> AX = FEFFh  (EAX = 0000FEFFh)
mov   ???

留意裁员

mov ax, ss:[bp]
中提到BP作为基地址已经使CPU使用SS段寄存器。这是默认值。无需显式添加段覆盖
ss:
,这可能会消耗额外的字节,具体取决于汇编器。
由于您根本不需要使用 EBP,因此它会变得更好。您可以通过 ESP 寄存器对堆栈进行寻址。
第二个片段的等效代码变为:

push  1234ABCDh
movzx eax, word [esp]  ; -> AX = 0ABCDh  (EAX = 0000ABCDh)
© www.soinside.com 2019 - 2024. All rights reserved.