1

I'm trying to teach myself assembly. I've found a good website; however, everything is written for x86 and I use a 64-bit machine.

I know what the problem is, but I don't know how to fix it. If I run the program with strace, then here is the results:

execve("./file", ["./file", "hello"], [/* 94 vars */]) = 0
creat(NULL, 0)                          = -1 EINVAL (Invalid argument)
write(0, NULL, 0 <unfinished ...>
+++ exited with 234 +++

So, I know that when I call creat, that the file name "hello" is not being passed and as a result I don't have a file descriptor.

Here is the code in question:

section .text
  global _start

_start:
  pop rbx ; argc
  pop rbx ; prog name
  pop rbx ; the file name

  mov eax,85 ; syscall number for creat()
  mov ecx,00644Q ; rw,r,r
  int 80h ; call the kernel 

I know that I can use the syscall command; however, I want to use interrupt.

Any ideas or suggestions would be helpful. Also, I'm using nasm an assembler.

SailorCire
  • 548
  • 1
  • 7
  • 24
  • 1
    If you are writing 64-bit code, you will save yourself a lot of time and energy if you switch to _SYSCALL_. Although `int 0x80` can be made to work in 64-bit code in some cases, anything that might contain an address on the stack will not work as a pointer with `int 0x80`. The last `pop rbx` will put an address in _RBX_ which points to the string. That address usually points to another place on the stack. A pointer to data on the stack generally can't be passed to `int 0x80` using the lower 32-bits of the address (_EBX_ in this case) and will usually end with a seg fault b/c of a Bad Address. – Michael Petch Mar 17 '16 at 23:37
  • Three `pop`s into the same register is a weird way to get to `argv[1]`. I guess it's actually fewer code bytes than `mov rbx, [rsp+16]`, and thanks to the red zone in the x86-64 Linux ABI, `argc` and `argv[0]` aren't in danger of being clobbered asynchronously even though they're below `rsp`. (Although AFAIK nothing can use the stack asynchronously other than signal handlers, and you obviously don't install any.) – Peter Cordes Mar 18 '16 at 02:18
  • @MichaelPetch: Usually the system call will just fail, leaving `-EFAULT` in `eax`, but not crash your program. This is confusing for people that don't at least use `strace` as a substitute for checking return values (which is inconvenient enough in C). – Peter Cordes Mar 18 '16 at 02:19
  • 1
    Write the programs in C. Compile to assembler (`-c -S`) without optimizations. Do it again with optimizations (e.g., `-O2`). Pencil, paper, and a copy of the x86-64 ELF ABI, until it all clicks:) It seems like flippant advice, but I think it's good. – Brett Hale Mar 18 '16 at 11:29
  • @PeterCordes How could there be a race condition? The code is sequential. – SailorCire Mar 18 '16 at 14:40
  • @SailorCire: There can't be in this case, because you don't have a signal handler, but leaving data below `rsp` is dangerous in general. A unix process can receive a signal from another process at any time. See `kill(2)`. If a process has installed a signal handler, and hasn't set it up to use an alt stack (`sigaltstack(2)`), the kernel will execute the signal hander with its `rsp` pointing just below the red-zone in the current stack. IDK what other mechanisms could asynchronously clobber space below the stack, but possibly some debuggers might? – Peter Cordes Mar 18 '16 at 15:28

1 Answers1

7

You attempted to use the 32 bit mechanism. If you have a 32 bit tutorial, you can of course create 32 bit programs and those will work as-is in compatibility mode. If you want to write 64 bit code however, you will need to use the 64 bit conventions and interfaces. Here, that means the syscall instruction with the appropriate registers:

  global _start

_start:
  mov eax,85       ; syscall number for creat()
  mov rdi,[rsp+16] ; argv[1], the file name
  mov esi,00644Q   ; rw,r,r
  syscall          ; call the kernel 
  xor edi, edi     ; exit code 0
  mov eax, 60      ; syscall number for exit()
  syscall

See also the x86-64 sysv abi on wikipedia or the abi pdf for more details.

Jester
  • 56,577
  • 4
  • 81
  • 125
  • 2
    Not sure it is worth mentioning that one reason `int 0x80` won't work here is that `[rsp+16]` usually contains an address that points into the stack, and of course 64-bit stack based addresses (the way Linux uses them) can't be represented in a 32-bit register so won't work with the `int 0x80` convention. I totally agree they should be using `syscall` and I was curious as to why the OP didn't want to go that route. – Michael Petch Mar 17 '16 at 23:27
  • 1
    Also, the `int 0x80` is just for the 32 bit compatibility mode and could theoretically be disabled in a pure 64 bit system. Of course nobody does that in practice. – Jester Mar 17 '16 at 23:39
  • Absolutely! People shouldn't be using `int 0x80`, and quite correct - with IA32 emulation turned off it won't work period. I seem to recall answering a question about that in the past month or so. – Michael Petch Mar 17 '16 at 23:41
  • 2
    Oh it wasn't an answer, it was in the comments of this question. https://stackoverflow.com/questions/35546021/seg-fault-on-hello-world . Someone couldn't get `int 0x80` to work, and discovered that had built their kernel without IA32 emulation. – Michael Petch Mar 17 '16 at 23:43