Beware that this will truncate EIP to 16-bit IP.
The YASM/NASM syntax is jmp word label
. Tested with both.
YASM also incorrectly allows it in 64-bit mode1, but NASM only allows it in 32 and 16-bit modes.
Your choices in 32-bit code (default address/operand-size = 32) are:
- normal 2-byte
jmp/jcc rel8
. Enforce with jmp short
- normal 5-byte
jmp rel32
or 6-byte jcc rel32
(2-byte opcode + 4-byte rel32). jmp dword
in any mode, and jmp near
in 32/64-bit code.
- operand-size prefix for 4-byte
jmp rel16
/ 5-byte jcc rel16
which zeros the upper 16 bits of EIP. (Not encodeable in 64-bit mode, only 16 or 32-bit mode.)
jmp word
in 16/32-bit mode, jmp near
in 16-bit mode.
strict
is optional, e.g. jmp strict short foo
, in all of these overrides. Even without strict, it's an error not just a warning if a rel8 can't reach for both NASM and YASM. My examples also used jmp
, but work with ja
, jle
, or any other jcc
. Note that call rel8
doesn't exist, only rel16
and rel32
(and indirect), using the same override syntax.
From the Operation section of Intel's jmp
documentation:
# near jump
... EIP <- EIP + DEST for non-64-bit mode relative jumps
IF OperandSize = 32
THEN
EIP ← tempEIP; # in 64-bit mode, this truncates RIP to EIP
ELSE
IF OperandSize = 16
THEN (* OperandSize = 16 *)
EIP ← tempEIP AND 0000FFFFH; #### This line
ELSE (* OperandSize = 64)
RIP ← tempRIP;
FI;
FI;
So you can't usefully save 1 byte by adding an operand-size prefix to get a rel16
, unless your code is executing from the low 64kiB of virtual address space. (Or with a non-zero CS base, with IP=EIP.)
Just for fun, I verified that this is a real thing on my Skylake CPU: in a 32-bit Linux static executable, single-stepping 0x8049000 <foo> jmpw 0x9000
in GDB gives Cannot access memory at address 0x9000
. Binutils objdump
disassembles the instruction as:
# objdump -d -Mintel output from a 32-bit ELF executable
08049000 <foo>:
8049000: 66 e9 fc ff jmpw 9000 <foo-0x8040000>
So real execution matches this disassembly, truncating EIP to IP.
Footnote 1: In 64-bit mode: YASM and binutils bugs vs. real CPU
YASM incorrectly allows it in 64-bit mode, and GNU Binutils incorrectly decodes it as jmp rel16
in 64-bit mode. NASM correctly rejects jmp word
in 64-bit mode.
But actually running it (on a Skylake) decodes it as jmp rel32
, as documented by Intel. (The rel16
encoding is marked N.E. Not Encodeable in long mode).
e.g.
foo: jmp word foo
db 1, 1, 1, 1
assembled + linked into a Linux static executable disassembles like this with GNU Binutils 2.31.1 objdump
:
0000000000401000 <foo>:
401000: 66 e9 fc ff jmp 1000 <foo-0x400000>
401004: 01 01 add DWORD PTR [rcx],eax
401006: 01 01 add DWORD PTR [rcx],eax
Actually running it in GDB (starti
/ si
) shows that we fault on code-fetch from 0x1421002
, i.e. from 0x401004 + 0x0101fffc
.
That's consistent with (66 e9 fc ff 01 01
) ignoring the meaningless operand-size prefix and decoding as a jmp +0x0101fffc
.