I read this page containing a good list of differences between Intel and AT&T syntax for GAS but it did not cover the case of specifying an address with a displacement only.
Here I've assembled four lines with AT&T syntax:
.text
0000 48C7C008000000 mov $8, %rax
0007 488B042508000000 mov (8), %rax
000f 4889F0 mov %rsi, %rax
0012 488B06 mov (%rsi), %rax
The first two lines are different, as expected. The first moves the immediate value 8 into rax and the second moves the contents of address 8 into rax. But with Intel syntax I get the following odd behavior:
.text
.intel_syntax
0000 48C7C008000000 mov %rax, 8
0007 48C7C008000000 mov %rax, [8]
000e 4889F0 mov %rax, %rsi
0011 488B06 mov %rax, [%rsi]
Here the first and second lines assembled to the same machine code! First I thought the square brackets were wrong, so I added the third and fourth lines to the test, and the square brackets do work for memory addressing at least when registers are involved.
All of the documentation I have read show memory addressing examples with at least a base or index register, and sometimes a scale and displacement, but never a displacement only.
I do have experience with the Intel syntax using the NASM assembler, which does distinguish mov rax, 8
and mov rax, [8]
.
Is this a bug in GAS? Or if not, how do I specify the equivalent of NASM's mov rax, [8]
?
I realize it is probably uncommon to specify a displacement-only address but I would like to get a complete understanding of all of the memory addressing forms with this syntax.
There was indeed such a bug in gas
-- see http://sourceware.org/bugzilla/show_bug.cgi?id=10637 .
It appears to be fixed in (or perhaps before) binutils 2.21.51.
You're seeing a very special corner case of AT&T syntax here. Ordinarily, for address operands, you have:
<op> [ src, ] displacement(base,index,scale) [, tgt ]
Any of the constituents of an address operand in AT&T syntax are optional, so you can write mov (%rax, %rbx), ...
or mov 0(%rax, %rbx, 1), ...
or any other such combination.
Inside the ()
brackets the only number you ordinarily can have is the scale factor (if present).
But the assembler also accepts (and creates identical code for):
mov <absolute>, ...
mov (<absolute>), ...
This works only if the operand inside the ()
is a simple number / absolute address, otherwise the assembler complains that what you gave isn't a valid scale factor. This equivalence is a special case in AT&T syntax - I'm not sure why it was/is allowed.
The use of $
in AT&T syntax, though, always specifies a constant, not an address operand, which is the same as a naked number in Intel Syntax.
The following illustrates the equivalences:
$ cat t.s
mov (8), %rax
mov $8, %rax
mov 8, %rax
.intel_syntax
mov %rax, [ 8 ]
mov %rax, 8
mov %rax, %ds:8
$ objdump -w -d t.o
t.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <.text>:
0: 48 8b 04 25 08 00 00 00 mov 0x8,%rax
8: 48 c7 c0 08 00 00 00 mov $0x8,%rax
f: 48 8b 04 25 08 00 00 00 mov 0x8,%rax
17: 48 8b 04 25 08 00 00 00 mov 0x8,%rax
1f: 48 c7 c0 08 00 00 00 mov $0x8,%rax
26: 48 8b 04 25 08 00 00 00 mov 0x8,%rax
$ objdump -w -M intel -d t.o
t.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 :
0: 48 8b 04 25 08 00 00 00 mov rax,ds:0x8
8: 48 c7 c0 08 00 00 00 mov rax,0x8
f: 48 8b 04 25 08 00 00 00 mov rax,ds:0x8
17: 48 8b 04 25 08 00 00 00 mov 0x8,%rax
1f: 48 c7 c0 08 00 00 00 mov rax,0x8
26: 48 8b 04 25 08 00 00 00 mov rax,ds:0x8
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With