1

I have tried to use an external function in assembly code:

        .section    .rodata
    .LC0:
        .string "My number is: %lld"
        .text
        .globl  start
    start:
        pushq   %rbp
        movq    %rsp, %rbp
        subq    $16, %rsp
        movq    $12345, -8(%rbp)
        movq    -8(%rbp), %rax
        movq    %rax, %rsi
        movl    $.LC0, %edi
        movl    $0, %eax

        call    printf   # my external function

        # exit-syscall
        mov $1, %eax
        mov $0, %ebx
        int $0x80

I assembled and linked with:

as -o myObjfile.o mySourcefile.s
ld -e start -o myProgram -lc myObjfile.o

The executable is build, but it doesn't run, so what's wrong with it?

Sven Rode
  • 11
  • 1
  • 3
  • What error messages do you get? – Peter Cordes Sep 10 '16 at 00:15
  • 1
    BTW, using compiler output as a starting point is generally good, but you should use *optimize* compiler output. Use `gcc -O3 -S`. And use the 64-bit ABI (the `syscall` instruction instead of `int $0x80`) in 64-bit code. The system call numbers and registers for args are different, too. See the [x86 tag wiki](http://stackoverflow.com/tags/x86/info) for docs. See also [this answer about building code that defines `_start` instead of main, but still links libc](http://stackoverflow.com/questions/36861903/assembling-32-bit-binaries-on-a-64-bit-system-gnu-toolchain/36901649#36901649). – Peter Cordes Sep 10 '16 at 00:19
  • 1
    Since you are linking against the _C_ library already, I'd recommend just using the _C_ runtime too. Change `start` to `main` and instead of the `int 0x80` (or a syscall) do `mov %rbp, %rsp` `pop %rbp` `ret` . `ret` will return back to the _C_ runtime in this case cleanly exiting the process and flushing the output buffers. You can compile/assemble with GCC with something like `gcc -o myProgram mySourcefile.s` – Michael Petch Sep 10 '16 at 00:25
  • 1
    Or if you want to, you can split up assembling and linking (with GCC) using `as -o myObjfile.o mySourcefile.s` `gcc -o myProgram myObjfile.o` Getting GCC to do the linking passes the appropriate parameters to the linker including the appropriate dynamic linker object. – Michael Petch Sep 10 '16 at 00:28
  • @Peter Cordes I got no error messages at assembling and linking, but when I invoke the executable, I get 'no such file or directory' from shell. – Sven Rode Sep 10 '16 at 10:29
  • @SvenRode: that's a using-the-shell-wrong problem, and has nothing to do with your code. Run `./myProgram`. The current directory isn't in your `$PATH`, which is the normal, correct, and recommended setup. Using a relative path that includes the current directory override's the shell's behaviour of looking up bare command names in `$PATH` only. – Peter Cordes Sep 10 '16 at 10:45
  • Or wait a minute, actually I think you didn't set the ELF interpreter correctly for `ld`. Check by running `strace ./myProgram`: probably execve returns ENOENT. Just build with gcc to correctly set everything that a dynamically-linked binary needs, instead of using `ld` yourself. e.g. try this **`gcc -o myProgram -nostartfiles mySourcefile.S`** (Note the capital-S; that's recommended for hand-written asm. It also tells gcc to run it through the C preprocessor first, but more importantly some commands will overwrite a .s without asking as their default output.) – Peter Cordes Sep 10 '16 at 10:52
  • 1
    I've done this and now the executable is invokable. But there now it seems that the prinf function isn't anymore called; I don't see its message. – Sven Rode Sep 10 '16 at 11:29
  • @Peteer Cordes: Do I need include a header file? – Sven Rode Sep 10 '16 at 11:37
  • There was probably output but you used `int 0x80` to exit which didn't flush the buffers. One hack that might allow you to see your output is to end your string with a newline (although that isn't always a guarantee). If you had followed my advice from yesterday it should work. I actually made a comment about using `ret` and that it flushes the output buffers. I expected you would eventually run into the output not appearing problem once you got it linked and assembled. If you don't want to do the `ret` then at least do `call exit` instead of `int 0x80`. – Michael Petch Sep 10 '16 at 14:15
  • Of course _RET_ would only work if you also use the _C_ runtime startup code, and you'd have to also do what I recommended by changing `start` to `main` and alter the way you assemble and link. If you don't want to use the _C_ runtime startup then `call exit` would probably at least allow you to see the output since it will flush the output buffers before exiting. – Michael Petch Sep 10 '16 at 14:25
  • And if you want to really use system calls in 64-bit code, please consider using SYSCALL instruction rather than int 0x80. More about that can be found on [Ryan Chapman's blog](http://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/) (note that the system call numbers have changed, and the registers you pass parameters with are different) – Michael Petch Sep 10 '16 at 14:34
  • Yea, I thought for myself that this might be a matter of unflushed buffer. So I must find a way flushing the buffer before the program exits. Yes, of course, there is the easy way, embedding all in the C-environment. But I will try to build it without this. So I will try to make at next a modification by sys_write via syscall. I regard this Question as solved. Thank you all for your help! -edit-> and the exit will be entered via syscall :) – Sven Rode Sep 10 '16 at 14:47
  • If you want to flush the buffers used by the _C_ library (including functions like `printf`) and not use _RET_ to do it, then replace `int 0x80` call with a simple `call exit`. `exit` will flush the buffers before termination. `exit` is a _C_ library function like `printf` . – Michael Petch Sep 10 '16 at 15:00
  • If you put a `\n` at the end of your format string, printf will flush for you (but only when stdout is a tty). – Peter Cordes Sep 10 '16 at 22:59

1 Answers1

-3

If I understand correctly if the function you want to call is in the C library you can just declare it as an external function.

    .section    .rodata
.LC0:
    .string "My number is: %lld"
    .text
    .extern printf    #declaring  the external function
    .globl  start
start:
    pushq   %rbp
    movq    %rsp, %rbp
    subq    $16, %rsp
    movq    $12345, -8(%rbp)
    movq    -8(%rbp), %rax
    movq    %rax, %rsi
    movl    $.LC0, %edi
    movl    $0, %eax

    call    printf   # my external function

    # exit-syscall
    mov $1, %eax
    mov $0, %ebx
    int $0x80

If this doesn't work try linking it explicitly with stdlib.h

Hen3
  • 175
  • 8
  • 1
    `.extern` is not needed in the UNIX assembler. Also, OP says that his code builds, so this can't be the issue. Downvoting. – fuz Sep 10 '16 at 20:15
  • Yes, it builds correct with gcc/clang. It would be fine if someone knows why the build with `as -o myObj.o mySrc.S` and `ld -o myExe myObj.o -lc` is not executable. – Sven Rode Sep 10 '16 at 21:11
  • Seems that only the link process fails. If i run `as` like above and link its output with `cc` using the ` -nostartfiles` flag, then the executable is okay. – Sven Rode Sep 11 '16 at 16:41