cs
/ds
/es
/ss
segment override prefixes are allowed in machine code in 64-bit mode. But some assemblers choose not to allow them.
But note that they have literally no effect on instructions with memory operands. This might be why masm apparently chooses not to allow it, to save you from the "mistake" of wasting space on such prefixes. Other assemblers, like NASM, do allow it.
All those segments have the same fixed base address=0, and don't even change which exception would be raised.
Intel manual vol.1 3.3.7.1 Canonical Addressing:
If an instruction uses base registers RSP/RBP and uses a segment override prefix to specify a non-SS segment, a
canonical fault generates a #GP (instead of an #SS). In 64-bit mode, only FS and GS segment-overrides are applicable in this situation. Other segment override prefixes (CS, DS, ES and SS) are ignored. Note that this also means
that an SS segment-override applied to a “non-stack” register reference is ignored. Such a sequence still produces
a #GP for a canonical fault (and not an #SS).
e.g. if mov eax, ds:[rbp]
faults from a non-canonical address (high 16 bits aren't the sign-extension of the low 48), it's still a #SS fault (because the base register is RBP or RSP), not #GP. The DS prefix is really ignored.
The only other way you could tell the difference would be if it's possible to give DS a segment selector that will fault if any load or store tries to use it, then run mov eax, [ds:rbp]
. But I'm not sure that's possible; I was getting a segfault right away on mov ds, eax
when trying to set a non-zero value in Linux. NULL segment selectors "work" for DS and ES. Given the assembler warnings / behaviour of NASM and FASM, the developers of those assemblers at least think the segment prefixes truly are meaningless.
fs
and gs
are the only segment overrides that do anything in 64-bit mode.
They can have non-zero base addresses.
CS/DS/ES/SS are only useful for padding to make instructions longer (for alignment reasons). What methods can be used to efficiently extend instruction length on modern x86?
Note that ds
is already the default segment for all base registers other than rbp
/ rsp
, so an explicit ds
prefix wouldn't change the meaning of the instruction at all even if it wasn't ignored. If your assembler refuses to accept mov rcx,qword ptr ds:[rax+20]
, it would be reasonable to also refuse to accept mov ecx, dword ptr ds:[eax+20]
in 32-bit mode.
For some assemblers (e.g. NASM), mentioning a segment explicitly in an asm source line emits a segment prefix on the instruction, even if it's already the default. If that's how MASM works, then it's not a good idea to use it for "documentation" purposes.
If you do want to emit DS prefixes for padding, just encode them explicitly:
mov rax, gs:[60]
db 3Eh ; ds prefix
mov rcx, [rax+20]
Other assemblers (GAS, NASM)
Interestingly, in GAS's .intel_syntax noprefix
(which is MASM-like), ds:[rax+20]
assembles without an explicit prefix (because it's already the default), but ss:[rax+20]
does emit a prefix. So GAS doesn't assume that cs/ds/es/ss are equivalent even in 64-bit code.
NASM warns: segments.asm:5: warning: ds segment base generated, but will be ignored in 64-bit mode
mov rax, [gs: 60] ; NASM requires the seg: to be
mov rcx, [ds: rax+20] ; inside the [] if present
mov rcx, [rax+20]
assembles with NASM to this (objdump -drwC -Mintel
output):
4000b0: 65 48 8b 04 25 3c 00 00 00 mov rax,QWORD PTR gs:0x3c
4000b9: 3e 48 8b 48 14 mov rcx,QWORD PTR ds:[rax+0x14]
4000be: 48 8b 48 14 mov rcx,QWORD PTR [rax+0x14]
ds
in them, I think? In 64 bit mode, the only segment overrides that do anything arefs
andgs
, which is why your assembler might complain. Just leave out theds:
and it should just work. – Cleanlymov rax, qword [ ds:rax+60 ]
) – Liminal