x86内存访问在硬编码地址0xFFF上出现分段错误。

5

我出于好奇正在学习x86汇编语言。目前我正在使用基于Linux的操作系统和NASM汇编器。我很难理解为什么。

SECTION .text

global _start

_start:

    nop
    mov ebx, 25
    mov [0xFFF], ebx

   ;Exit the program
   mov eax, 1
   mov ebx, 0
   int 0x80

将ebx寄存器的内容移动到内存位置0xFFF时会导致分段错误。我曾经认为用纯汇编语言构建程序可以让我无限制地访问进程的虚拟地址空间。这不是事实吗?

你会如何在汇编中实现像堆这样的东西?


即使地址可用,通过向奇数地址写入32位值,您也会破坏机器对齐。您可以将ebx写入0xFFC0x1000,但不能写入中间的任何地址。 - Weather Vane
0xFFF是否被正确分配,即表示一个有效地址? - zx485
2
@WeatherVane 我怀疑对齐没有被强制执行(对于常规内存访问,x86上的强制执行是可选的,CPU可以处理不对齐的读/写)。问题很可能是在访问位置的地址空间中没有映射到物理内存。第0页(偏移量从0到0xFFF)通常保留未映射以捕获NULL指针/零地址。然后,默认情况下,程序映像加载到远高于地址零的位置,例如0x08048000。那么,在没有程序的地方映射内存有什么意义呢? - Alexey Frunze
@AlexeyFrunze 是的,我的评论有点弱:在某些其他处理器上是强制执行的,但在8086上只会导致另一个读取或写入周期。 - Weather Vane
你如何访问程序可以使用的完整虚拟内存范围?你会如何在汇编中实现像堆这样的东西? - crawfordr4
显示剩余2条评论
2个回答

6
在Linux(x86)上-尽管您的进程具有4GB的虚拟地址范围,但并非所有内容都可以访问。最高的1GB是内核所在的位置,还有一些无法使用的低内存区域。默认情况下,无法写入或读取虚拟内存地址0xfff(by default),因此您的程序会崩溃,并出现段错误。
在您的后续评论中,您提到您打算使用汇编语言创建堆。这是可行的,其中一种方法是使用 sys_brk 系统调用。通过 int 0x80EAX=45 访问它。它接受 EBX 中表示堆顶的指针。通常来说,堆区域底部被初始化为刚超过程序数据段(在内存中位于程序之上)的区域。要获取初始堆位置的地址,可以将 EBX 设置为 0 调用 sys_break。系统调用后,EAX 将是堆的当前基指针。当您需要访问堆内存或分配更多堆空间时,您可以保存它。
以下代码提供了一个示例,以便更清晰地理解(而非性能),但可能是理解如何操作堆区域的起点:
SECTION .data
heap_base: dd 0          ; Memory address for base of our heap

SECTION .text
global _start
_start:
    ; Use `brk` syscall to get current memory address
    ; For the bottom of our heap This can be achieved
    ; by calling brk with an address (EBX) of 0
    mov eax, 45          ; brk system call
    xor ebx, ebx         ; don't request additional space, we just want to 
                         ; get the memory address for the base of our processes heap area.
    int 0x80
    mov [heap_base], eax ; Save the heap base

    ;Now allocate some space (8192 bytes)
    mov eax, 45          ; brk system call
    mov ebx, [heap_base] ; ebx = address for base of heap
    add ebx, 0x2000      ; increase heap by 8192 bytes
    int 0x80

    ; Example usage
    mov eax, [heap_base]      ; Get pointer to the heap's base
    mov dword [eax+0xFFF], 25 ; mov value 25 to DWORD at heapbase+0xFFF

    ;Exit the program
    mov eax, 1
    xor ebx, ebx
    int 0x80

谢谢,这非常有帮助。出于好奇和简单的角度来看,Linux内核是否只是为我的程序创建足够的页面,然后在我通过sys_brk更改堆大小时添加更多页面?当我尝试访问尚未分配给我的地址时,是否会发生页错误,通过它Linux的记录表明我已经超过了我的堆所属段的分配限制? - crawfordr4
正确的,内核将分配具有正确权限(我认为一般是读/写/执行)的页面。如果您未使用sys_brk访问这些内存位置,则会收到segfault错误。 - Michael Petch

1

你没有无限制的RAM。此外,你也没有对由RAM支持的地址空间部分的无限制访问权限。代码页面映射为只读。作为一个Ring-3程序,你无法自己更改这个。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接