【问题标题】:x86 memory access segmentation faultx86 内存访问分段错误
【发布时间】:2016-02-26 15:27:04
【问题描述】:

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

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 时)。我在想用纯 asm 构建一个程序可以让我不受限制地访问我的进程的虚拟地址空间。不是这样吗?

您将如何在汇编中实现诸如堆之类的东西?

【问题讨论】:

  • 即使地址可用,您也会通过将 32 位值写入奇数地址来破坏机器对齐。您可以将ebx 写入0xFFC0x1000,但不能写入其间的任何地址。
  • 0xFFF 分配是否正确意味着地址有效?
  • @WeatherVane 我怀疑是否强制执行对齐(在 x86 上强制执行对于常规内存访问是可选的,CPU 可以处理未对齐的读/写)。问题很可能是没有物理内存映射到访问位置的地址空间。页 0(偏移量 0 到 0xFFF)通常未映射以捕获 NULL 指针/零地址。然后,默认情况下,程序映像加载到地址零上方,例如 0x08048000。那么,为什么要在没有程序的地方映射内存呢?
  • @AlexeyFrunze 是的,我的评论很弱:在某些 other 处理器上强制执行,但在 8086 中只会导致另一个读取或写入周期。
  • 如何访问程序可以使用的全部虚拟内存?你将如何在汇编中实现堆之类的东西?

标签: linux assembly x86 segmentation-fault nasm


【解决方案1】:

在 Linux(x86) 上 - 尽管您的进程中有 4gb 的虚拟地址范围,但并非所有这些都可以访问。上面的 1gb 是内核所在的地方,有一些内存不足的区域不能使用。无法写入或读取虚拟内存地址 0xfff(默认情况下),因此您的程序会因段错误而崩溃。

在后续评论中,您建议您打算在汇编程序中创建一个堆。这可以做到,一种方法是使用sys_brk 系统调用。它通过 int 0x80EAX=45 访问。它需要一个 EBX 中的指针来表示新的堆顶。通常,堆区域的底部被初始化为刚好超出程序数据段的区域(在内存中的程序上方)。要获取初始堆位置的地址,您可以调用 sys_break 并将 EBX 设置为 0。系统调用后 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 的记录保存表明我已经超出了我的堆所属段的分配限制?跨度>
  • 正确,内核将分配具有正确权限的页面(我认为一般是读/写执行)。如果您要在没有 sys_brk 的情况下访问这些内存位置,则会出现段错误。
【解决方案2】:

您没有不受限制的 RAM。此外,您不能不受限制地访问由 RAM 支持的地址空间部分。代码页以只读方式映射。而作为一个 ring-3 程序,你不能自己改变它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-12-07
    • 2013-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多