1

I'm quite new to x86 assembly, and I'm trying to build off a hello world program. I'm trying to make a subroutine, that writes a single byte to stdout, but i've hit a problem.

The line mov ebx, [esp+1] (to load the byte passed, when I call the subroutine) causes a segfault.

I've tried xoring the ebx register with itself, to make sure that it is empty, to make sure, that it doesn't mess with the syscall

_start:
    push 32h
    call _writeByte

    ; This just jumps to an exit routine
    jmp  _exit

_writeByte:
    ; This line causes the problem. If I remove it the program works fine
    mov  ebx, [esp+1]
    xor  ebx, ebx

    mov  eax, 1
    mov  edi, 1
    mov  esi, tmp
    mov  edx, 1
    syscall

    ret

Why is the program segfaulting?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
XerneraC
  • 66
  • 5
  • 4
    You tagged this x86-64. Are you in 64 bit mode? You should use `rsp` not `esp` as the latter is just the low 32 bits and that will likely point to an invalid address. – Jester Feb 02 '19 at 22:20
  • `ebx` is a 32 register. If you want to load a single byte, use `bl` or `bh`. The problem might be the unaligned read, not sure about that though. Also, `esp` points to the return address currently. Adding `1` isn't enough to get to the argument. – tkausl Feb 02 '19 at 22:24
  • Just dealing with the why `mov ebx, [esp+1]` fails. In 64-bit code the stack pointer may span addresses >= 4gb. In64-bit code on most (not all) OSes (including Linux and MacOS - the stack pointer is at an address >= 4gb so memory addresses for the stack should be using RSP (not ESP).Of course that line doesn't do much when in the next instruction you set the entire 64-bit register RBX to zero. – Michael Petch Feb 02 '19 at 23:57
  • That mov to EBX will of course move 4 bytes, not just 1 byte. If you want to mov one byte and have the value zero extended through all the upper bits of RBX you can use `movzx ebx, byte [rsp+1]` . If you intend just to use a byte and wish to compare against BL (lower 8 bits of the RBX register) later on you could just do `mov bl, [rsp+1]` – Michael Petch Feb 03 '19 at 00:04
  • 2
    Not to mention that `[rsp]` is the return address so to get the first byte of the pushed argument, he'd need to use `[rsp+8]`. – Jester Feb 03 '19 at 00:06
  • You probably want `lea rsi, [rsp+8]` to write the byte you passed on the stack. `sys_write` takes a pointer. Normally you'd want to pass args in registers; the x86-64 System V calling convention is well-designed. But in this case you need the byte in memory anyway, so you might as well have the caller push it. `mov esi, tmp` is efficient for a static data address, though, as long as you don't want to link it into a PIE executable. – Peter Cordes Feb 04 '19 at 00:04

1 Answers1

3

I'm in x64 mode, and like a bunch of people suggested in the comments using mov ebx, [rsp+8] worked, because esp are just the 4 lower bytes of the register. The stack is outside the low 4 GiB of virtual address space, so ESP != RSP and [esp] will be an unmapped page.

Note that x86-64 calling conventions pass the first few args in register, not on the stack, so you normally don't want to do this at all (unless your function has lots of args).

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
XerneraC
  • 66
  • 5