Why NASM on Linux changes registers in x86_64 assembly
assembly, micro-optimization, nasm, shellcode, x86-64
Solution
In 64-bit mode `mov eax, 1` will clear the upper part of the `rax` register (see here for an explanation) thus `mov eax, 1` is semantically equivalent to `mov rax, 1`.
The former however spare a REX.W (`48h` numerically) prefix (a byte necessary to specify the registers introduced with x86-64), the opcode is the same for both instructions (`0b8h` followed by a DWORD or a QWORD). So the assembler goes ahead and picks up the shortest form.
This is a typical behavior of NASM, see Section 3.3 of the NASM's manual where the example of `[eax*2]` is assembled as `[eax+eax]` to spare the `disp32` field after the SIB byte1 (`[eax*2]` is only encodable as `[eax*2+disp32]` where the assembler set `disp32` to 0).
I was unable to force NASM to emit a real `mov rax, 1` instruction (i.e. `48 B8 01 00 00 00 00 00 00 00`) even by prefixing the instruction with `o64`. If a real `mov rax, 1` is needed (this is not your case), one must resort to assembling it manually with `db` and similar.
EDIT: Peter Cordes' answer shows that there is, in fact, a way to tell NASM not to optimize an instruction with the `strict` modifier. `mov rax, STRICT 1` produces the 10-byte version of the instruction (`mov r64, imm64`) while `mov rax, STRICT DWORD 1` produces a 7-byte version (`mov r64, imm32` where `imm32` is sign-extended before use).
Side note: It's better to use the RIP-relative addressing, this avoids 64-bit immediate constants (thus reducing code size) and is mandatory in MacOS (in case you cared). Change the `mov esi, msg` to `lea esi, [REL msg]` (RIP-relative is an addressing mode so it needs an "addressing", the square bracket, to avoid reading from that address we use `lea` that only computes the effective address but does no access). You can use the directive `DEFAULT REL` to avoid typing `REL` in each memory access.
I was under the impression that the Mach-O file format required PIC code but this may not be the case.
1 The Scale Index Base byte, used to encode the new addressing mode introduced back then with the 32-bit mode.
Problem
I am new to x86_64 assembly programming. I was writing simple "Hello World" program in x86_64 assembly. Below is my code, which runs perfectly fine. ``` global _start section .data msg: db "Hello to the world of SLAE64", 0x0a mlen equ $-msg section .text _start: mov rax, 1 mov rdi, 1 mov rsi, msg mov rdx, mlen syscall mov rax, 60 mov rdi, 4 syscall ``` Now when I disassemble in gdb, it gives below output: ``` (gdb) disas Dump of assembler code for function _start: => 0x00000000004000b0 <+0>: mov eax,0x1 0x00000000004000b5 <+5>: mov edi,0x1 0x00000000004000ba <+10>: movabs rsi,0x6000d8 0x00000000004000c4 <+20>: mov edx,0x1d 0x00000000004000c9 <+25>: syscall 0x00000000004000cb <+27>: mov eax,0x3c 0x00000000004000d0 <+32>: mov edi,0x4 0x00000000004000d5 <+37>: syscall End of assembler dump. ``` My question is why NASM behaves in such way? I know it changes instructions based on opcode, but I am not sure about same behaviour with registers. Also does this behaviour affects functionality of executable? I am using Ubuntu 16.04 (64 bit) installed in VMware on i5 processor. Thank you in advance.
Related problems
- Difference between movq and movabsq in x86-64
- How many ways to set a register to zero?
- What is the best way to set a register to zero in x86 assembly: xor, mov or and?
- Why do x86-64 instructions on 32-bit registers zero the upper part of the full 64-bit register?
- The advantages of using 32bit registers/instructions in x86-64
- How to load address of function or label into register
- Receiving 32-bit registers from 64-bit nasm code