Author Topic: [Solved] short jump with relative offset  (Read 17754 times)

Offline bfr

  • Jr. Member
  • *
  • Posts: 4
[Solved] short jump with relative offset
« on: May 09, 2019, 06:51:33 AM »
Hi everyone,

I'm trying to use a short relative jump to conditionally skip over a known sequence of instructions. I've calculated that the offset should be 30 bytes (0x1e), given the code and disassembly output below. I want it to jump from from 0x100D to 0x102D, and the offset should be relative to the instruction after the jump instruction (0x100F), so 0x102D - 0x100F = 0x1e.

When I write "jne short 0x1e" and compile the program with NASM, this becomes 751D (jnz +0x1d) and the offset is 1 byte too small. I have no idea why this happens. It work if I instead write "jne short $ + 0x20", which only confuses me more. If anyone could explain why "jne short 0x1e" only jumps by 0x1d bytes, I'd greatly appreciate it!

Here's the complete code:

Code: [Select]
section .data
  SYS_EXIT equ 60
  EXIT_CODE equ 0
  READ equ 0
  WRITE equ 1
  STDIN equ 0
  STDOUT equ 1
  IOCTL equ 16
  FDFLUSH equ 10

section .bss
  tape: resb 32768
  must_flush: resb 1

section .text

  global _start

  _start:
    mov rbx, 0
    cmp [must_flush], byte 1
    jne short 0x1e          // Jump from here
    mov rax, IOCTL
    mov rdi, STDOUT
    mov rsi, FDFLUSH
    mov rdx, 0
    syscall
    mov [must_flush], byte 0
    mov rax, READ          // Jump to here
    mov rdi, STDIN
    mov rsi, tape
    add rsi, rbx
    mov rdx, 1
    syscall

  _exit:
    mov rax, SYS_EXIT
    mov rdi, EXIT_CODE
    syscall

And here's what I get when I run ndisasm on the compiled program:

Code: [Select]
00000FFF  00BB00000000      add [rbx+0x0],bh
00001005  803C2500A0400001  cmp byte [0x40a000],0x1
0000100D  751D              jnz 0x102c          // This should jump to 0x102D
0000100F  B810000000        mov eax,0x10
00001014  BF01000000        mov edi,0x1
00001019  BE0A000000        mov esi,0xa
0000101E  BA00000000        mov edx,0x0
00001023  0F05              syscall
00001025  C6042500A0400000  mov byte [0x40a000],0x0
0000102D  B800000000        mov eax,0x0
00001032  BF00000000        mov edi,0x0
00001037  48BE002040000000  mov rsi,0x402000
         -0000
00001041  4801DE            add rsi,rbx
00001044  BA01000000        mov edx,0x1
00001049  0F05              syscall
0000104B  B83C000000        mov eax,0x3c
00001050  BF00000000        mov edi,0x0
00001055  0F05              syscall
« Last Edit: May 10, 2019, 09:01:05 AM by bfr »

Offline ig

  • Jr. Member
  • *
  • Posts: 12
Re: short jump with relative offset
« Reply #1 on: May 09, 2019, 07:29:39 AM »
I might be missing something, but I always thought that when you use something like a jump instruction in assembly, the parameter you give to the jump instruction is the target [address] - not the relative offset encoded in the instruction. In other words, it's not your task to compute the difference in addresses, it's up to the assembler (which knows the lengths of the instructions, the actual encoding etc.)

Personally, I would interpret "jne short 0x1e" as "jump to the address 0x1e" (not jump 0x1e bytes forward)... so I'm kinda surprised you're jumping even close to the desired location :)
Using the $ operator can fix the problem, yes - but it still seems to be unnecessary.

Why don't you just create a label for the instruction you want to jump to, and then use it?

Code: [Select]
  ...
  jne short _flush_done
  ...
_flush_done:
  mov rax, READ          // Jump to here

Offline bfr

  • Jr. Member
  • *
  • Posts: 4
Re: short jump with relative offset
« Reply #2 on: May 09, 2019, 07:40:41 AM »
Thanks very much for your reply!

This is a short relative jump (https://thestarman.pcministry.com/asm/2bytejumps.htm) -- it's position-independent and has a range of -128 to +127.

The reason I'm not generating a label is that (a) I'm actually using this approach in generating much larger programs where this pattern comes up many, many times, so I'll need to generate lots of labels (not hard, admittedly, but messy); and (b) I'm always going to be jumping over the same sequence of instructions, so the size of the jump is constant.

But even if I switch to using labels (which I'm using for, e.g., loop constructs where the loop body has an unknown size) I'm still curious why this offset has 1 byte subtracted when being converted into a short jump instruction.

Offline fredericopissarra

  • Full Member
  • **
  • Posts: 373
  • Country: br
Re: short jump with relative offset
« Reply #3 on: May 09, 2019, 01:43:50 PM »
Notice NASM will optimize your code by default (-Ox, multipass optimization), so, instructions as:
Code: [Select]
mov rax,0Will be encoded as
Code: [Select]
mov eax,0Without the REX prefix... If you want your code to not be optimized, use -O0 option.

ALL jumps are relative (to RIP), including CALL and JMP instructions, except when absolute jumps are explicit made or indirect ones... In optimized compilation, NASM will try to select shorter instructions, so your conditional jump will use a signed byte offset.

To use an explicit value is dangerous because of optimization. But it seems NASM is wise enough to recalculate it...

Offline bfr

  • Jr. Member
  • *
  • Posts: 4
Re: short jump with relative offset
« Reply #4 on: May 09, 2019, 09:40:35 PM »
Ah, I see, that makes a lot of sense. So the only way to produce a short relative jump instruction is to jump to a nearby explicit address, or to use an offset relative to RIP. There's no way to write the short relative jump explicitly and have NASM interpret it as such? Thanks very much!

Offline ig

  • Jr. Member
  • *
  • Posts: 12
Re: short jump with relative offset
« Reply #5 on: May 10, 2019, 08:52:48 AM »
I'd put it this way - you are "interfering" with nasm's job (you are doing part of the compilation yourself and trying to make it accept your computation of the offset as part of its internal work).

If you want to force a specific (pre-computed, pre-compiled) instruction into the code, you can just use
Code: [Select]
db 75h, 20h- and you'll have the short "jne" instruction jumping forward by 20h bytes (bypassing nasm altogether for that instruction).

Or, use the $+0x20 expression - so that nasm knows that you are binding the target to the current address; that's basically what you are looking for. Pure "jne 0x20" is, IMHO, just bad syntax - meaning something different from what you want.

Offline bfr

  • Jr. Member
  • *
  • Posts: 4
Re: [Solved] short jump with relative offset
« Reply #6 on: May 10, 2019, 09:04:01 AM »
Thanks for clarifying, I didn't fully appreciate how much work NASM was doing and that this would be interfering with that.

Since the size of the jump is sensitive to optimization, I've switched to using labels. And I've got a much clearer idea of how this all works, thanks to you and Frederico. Much appreciated!