Search code examples
assemblygdbx86-64memory-segmentation

MOV DS, EAX segfaults?


When I run mov ds,rax, it will throw error Program terminated with signal SIGSEGV, Segmentation fault

What's wrong with the assembly code?


global main

main:
    mov rax,0ffffH
    mov ds,rax
    mov rbx,6
    ret

See screen shot


Solution

  • mov to a segment register loads the internal segment base/limit / permissions stuff from the GDT (Global Descriptor Table). (The base and limit are treated as 0 / -1 respectively in 64-bit mode, but mov to a segment register still has a real effect and still checks stuff. You can't just expect arbitrary values to not cause problems.)

    According to Intel's manual for mov, mov Sreg, r/m faults with #GP(selector) if the "segment selector index" (index into the GDT or LDT) "is outside the descriptor table limits".

    Linux delivers SIGSEGV if user-space causes an invalid page fault, or any kind of #GP exception.

    Since bit 2 is set (1<<2), this is indexing into the LDT (Local Descriptor Table), not GDT. There probably isn't an LDT at all for your process if you didn't ask your OS (Linux?) to create one, e.g. with a modify_ldt() system call.

    If you cleared that bit (mov eax, 0xfffb), it still faults on my Linux desktop. From that we can infer that Linux didn't configure a GDT that large. There's no reason to expect it would; it only needs a handful of segment descriptors for normal operation. e.g. if you use info reg, you can see the segment register values are:

    cs             0x33                51
    ss             0x2b                43
    ds             0x0                 0
    es             0x0                 0
    fs             0x0                 0
    gs             0x0                 0
    

    (0 works as a "null selector" that has a special meaning of keeping x86-64's minimal remnants of segmentation happy; it's not actually descriptor privilege level 0 (a kernel-only data segment) even though the low 2 bits are 00. The low bits of cs are the expected 11 (ring 3 = user-space).)

    Other possible reasons for exceptions include: "If the DS, ES, FS, or GS register is being loaded and the segment pointed to is not a data or readable code segment."


    I'm assuming you don't actually know much about segmentation, and I'm not trying to explain how to actually use segment registers. The point I'm trying to make is that you can't just use ds as 16 bits of scratch space for arbitrary integer data.

    If you want to know in more detail exactly what you can and can't put in ds, read Intel's manuals. And the kernel source to see how it configures its GDT and LDT, or make a modify_ldt() system call.