4

I have written a small piece of assembly with AT&T syntax and have currently declared three variables in the .data section. However, when I attempt to move any of those variables to a register, such as %eax, an error from gcc is raised. The code and error message is below:

.data
  x:.int 14
  y:.int 4
  str: .string "some string\n"

.globl _main

_main:
  pushq %rbp
  movq %rsp, %rbp
  subq $16, %rsp
  movl x, %eax; #attempting to move the value of x to %eax;
  leave
  ret

The error raised is:

call_function.s:14:3: error: 32-bit absolute addressing is not supported in 64-bit mode

movl x, %eax;

^

I have also tried moving the value by first adding the $ character in front of x, however, a clang error is raised:

clang: error: linker command failed with exit code 1 (use -v to see invocation)

Does anyone know how the value stored in x can be successfully moved to %eax? I am using x86 assembly on Mac OSX and compiling with gcc.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Ajax1234
  • 69,937
  • 8
  • 61
  • 102
  • 6
    Use `movl x(%rip), %eax`. – Jester May 06 '18 at 22:34
  • @Jester Thank you, `x(%rip)` works! If you could write an answer with an explanation, I will accept it. – Ajax1234 May 06 '18 at 22:37
  • 1
    The error message is slightly dishonest. Yes, `movl x, %eax` is supported by the architecture, but you are not supposed to use it. – fuz May 06 '18 at 22:37
  • @fuz Why is that? – Ajax1234 May 06 '18 at 22:37
  • @Ajax1234 Because (a) the encoding is a byte longer and (b) `movl x, %eax` uses direct addressing whereas `movl x(%rip), %eax` uses relative addressing. Direct address makes your code no longer position independent, which is why you should not use it when referring to variables. – fuz May 06 '18 at 22:43
  • 1
    [intel syntax duplicate](https://stackoverflow.com/questions/47300844/mach-o-64-bit-format-does-not-support-32-bit-absolute-addresses-nasm-accessing) – Jester May 06 '18 at 22:44
  • 1
    @fuz: In AT&T syntax, the `moffs` forms of `mov` (with a 64-bit absolute address) use the `movabs` mnemonic, so plain `mov` implies you want the `mov r32, r/m32` opcode with a ModR/M byte. The MachO64 output format doesn't support 32-bit absolute relocations at all, and the image base address is above 2^32 on OS X so a `[disp32]` addressing mode can't work. Thus for `mov`, RIP-relative is the only option. – Peter Cordes May 07 '18 at 01:48

1 Answers1

7

A RIP-relative addressing mode is the only good option for addressing static data on MacOS; the image base address is above 2^32 so 32-bit absolute addresses aren't usable even in position-dependent code (unlike x86-64 Linux). RIP-relative addressing of static data is position-independent, so it works even in position-independent executables (ASLR) and libraries.

movl x(%rip), %eax is the AT&T syntax for RIP-relative.

mov eax, dword ptr [rip+x] in GAS .intel_syntax noprefix.

Or, to get the address of a symbol into a register, lea x(%rip), %rdi


NASM syntax: mov eax, [rel x], or use default rel so [x] is RIP-relative.

See Mach-O 64-bit format does not support 32-bit absolute addresses. NASM Accessing Array for more background on what you can do on OS X, e.g. movabs x, %eax would be possible because the destination register is AL/AX/EAX/RAX. (64-bit absolute address, but don't do that because it's larger and not faster than a RIP-relative load.)

See also http://felixcloutier.com/x86/MOV.html.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • Does this mean that in the GNU assembler, for something like `movq x, %rdx`, a label `x` is treated as `m32` even when building 64-bit code? – c-x-berger Sep 16 '22 at 08:47
  • @c-x-berger: no, `m32` in Intel's manuals means 32-bit operand-size. It implies nothing about which addressing mode is used to reach those 4 bytes of memory. `movq x, %rdx` is an `m64` memory operand to match the `q` operand-size and the 64-bit destination register, but you told it not to use RIP-relative, and you didn't use `movabs` for a 64-bit absolute address (only available for an RAX destination anyway), so the assembler has to encode the address in a Mod/RM byte. The only non-RIP-relative mode that doesn't include any other registers is `[sign-extended-disp32]` – Peter Cordes Sep 16 '22 at 18:07