-1

I try to call printf function from asm code.

hello.asm:

%macro exit 0
    mov eax, 1
    mov ebx, 0
    int 80h
%endmacro

extern   printf      ; the C function, to be called

SECTION .data
    hello:     db   'Hello world!', 0

SECTION .text
    GLOBAL main

main:
    sub 8, rsp
    push dword hello
    call printf      ; Call C function
    add 8, rsp
    exit

Makefile:

all:
    nasm -f elf64 hello.asm -o hello.o
    ld hello.o -e main -o hello -lc -I/lib/ld-linux.so.2

clean:
    rm -f hello.o hello

make call:

nasm -f elf64 hello.asm -o hello.o
hello.asm:16: error: invalid combination of opcode and operands
hello.asm:19: error: invalid combination of opcode and operands
make: *** [all] Error 1

Please explain errors and how to fix code.

Thanks.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • 2
    You have your operands backwards for `sub` and `add`. Intel syntax is `add dst, src`, so you use `sub rsp, 8`. Did you translate from AT&T syntax and miss that? – Peter Cordes Sep 01 '18 at 20:12
  • 1
    Also, if you are using 64bit code (as indicated by RSP), you shouldn't be using int 80, but `syscall`, which also uses different registers. – David Wohlferd Sep 01 '18 at 20:13
  • @PeterCordes yep. I fixed it, now I receive error bash: ./hello: Accessing a corrupted shared library –  Sep 01 '18 at 20:17
  • @DavidWohlferd: Yes, but it will still work (on most systems) for system calls that don't need 64-bit inputs (like pointers) ([What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?](https://stackoverflow.com/q/46087730)). More importantly for this case, the x86-64 System V calling convention doesn't pass args on the stack. It should be `mov edi, hello` or `lea rdi, [rel hello]`. [What are the calling conventions for UNIX & Linux system calls on i386 and x86-64](https://stackoverflow.com/a/2538212). – Peter Cordes Sep 01 '18 at 20:18
  • 1
    @e42d3: link with gcc, it knows how to pass the right args. `gcc -v -nostartfiles -no-pie hello.o`. (`-v` prints the actual linker command it used, in case you're curious). And don't use `-e main`, call your entry point `_start`. (And don't mess with RSP, it's already 16-byte aligned at the ELF entry point, unlike at `main`. Or better, leave out `-nostartfiles` so the normal startup stuff runs before main, instead of depending on dynamic linker stuff to initialize libc before you call stdio functions.) – Peter Cordes Sep 01 '18 at 20:22
  • @PeterCordes Who was it that said [syscall system calls are faster than int 0x80 system calls](https://stackoverflow.com/q/46087730/2189500)? Or pointed out that passing pointers to string constants (as OP is doing) may not work for [position-independent executables, which many Linux distros now configure gcc to make by default](https://stackoverflow.com/a/46087731/2189500). Such being the case I feel comfortable saying that using int 0x80 is a bad habit to get into on x64. Clearly OP has other problems. But discouraging int 0x80 from x64 still seems appropriate. – David Wohlferd Sep 02 '18 at 03:04
  • @DavidWohlferd: yeah, IDK why I bothered to disagree about that. It's basically fine for `sys_exit` if you're writing a microbenchmark that can assemble as 32-bit or 64-bit code, but that's about the only good use-case for `int 0x80` in 64-bit code. I guess I was thinking that pointing out the actual bugs that would stop it from working should be done first. (And `sys_exit` after a stdio function is a potential bug no matter what ABI you use, because stdout is full-buffered if redirected to a file.) – Peter Cordes Sep 02 '18 at 03:13

2 Answers2

3

Both error messages give good clues. They happen in lines 16 and 19.

In line 16 you have:

sub 8, rsp

The problem here is that you cannot subtract (anything) from a literal constant. I think that the actual intention was

sub rsp, 8

Similarly for line 19. Instead of

add 8, rsp

what you want is

add rsp, 8

Take in account that for instructions such as sub and add, the first operand acquires the result of the operation. And literal constants cannot do that!

Leandro Caniglia
  • 14,495
  • 4
  • 29
  • 51
  • 1
    Also for better understanding, I think it's good to add here that NASM usually takes instructions in the way `op dest, src` whereas `op` is the operation, `dest` the destination and `src` the source. So `add esp, 8` equals to `esp += 8` in the C syntax. – christopher westburry Sep 04 '18 at 18:39
1

Working solution:

hello.c:

extern exit      ; the C function, to be called
extern puts      ; the C function, to be called

SECTION .data
    hello:     db   'Hello world!', 0

SECTION .text
    GLOBAL _start

_start:
    mov edi, hello
    call puts      ; Call C function
    mov edi, 0
    call exit      ; Call C function

Makefile:

all:
    nasm -f elf64 hello.asm -o hello.o
    gcc -nostartfiles -no-pie hello.o -o hello

clean:
    rm -f hello.o hello
  • 1
    Your push/pop misaligns the stack. It's already 16-byte aligned on entry to `_start`, unlike on entry to a function. (`_start` isn't a function, that's why you can't `ret` from it.) Also, you're still using the 32-bit ABI for sys_exit. [What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?](https://stackoverflow.com/q/46087730). – Peter Cordes Sep 01 '18 at 21:39
  • @PeterCordes: Thank you. –  Sep 01 '18 at 21:42
  • 1
    You still didn't fix the alignment bug. `puts` is allowed to crash if you call it like this. The version you're using just happens not to use any `movaps` instructions to copy stuff to/from the stack. I'd upvote if you fixed that. Using libc `exit(3)` instead of the system call is a good thing, though. It makes your program flush stdio buffers before exiting, so it still works if you redirect output to a file. So that is better than just switching to `mov eax,231` / `syscall`. – Peter Cordes Sep 01 '18 at 22:09