0

There are a few related questions to this which I've come across, such as Printf with gas assembly and Calling C printf from assembly but I'm hoping this is a bit different.

I have the following program:

.section .data
format:
    .ascii "%d\n"

.section .text
.globl _start

_start:
    
    // print "55"
    mov $format, %rdi
    mov $55, %rsi
    mov $0, %eax
    call printf # how to link?

    // exit
    mov $60, %eax
    mov $0, %rdi
    syscall

Two questions related to this:

  • Is it possible to use only as (gas) and ld to link this to the printf function, using _start as the entry point? If so, how could that be done?
  • If not, other than changing _start to main, what would be the gcc invocation to run things properly?
carl.hiass
  • 1,526
  • 1
  • 6
  • 26
  • Your question is exactly the same we've seen here many times before. Neverthless I've written an answer for you. I hope it helps you. For future questions, make sure to write what you tried and why what you tried didn't work for you. That makes others understand your situation better. – fuz Mar 25 '21 at 23:21
  • @NateEldredge no but that one is perfect, thank you for the link! – carl.hiass Mar 25 '21 at 23:39
  • Does this answer your question? [How to link a gas assembly program that uses the C standard library with ld without using gcc?](https://stackoverflow.com/questions/3577922/how-to-link-a-gas-assembly-program-that-uses-the-c-standard-library-with-ld-with) – Nate Eldredge Mar 25 '21 at 23:39

1 Answers1

2

It is possible to use ld, but not recommended: if you use libc functions, you need to initialise the C runtime. That is done automatically if you let the C compiler provide _start and start your program as main. If you use the libc but not the C runtime initialisation code, it may seem to work, but it can also lead to strange spurious failure.

If you start your program from main (your second case) instead, it's as simple as doing gcc -o program program.s where program.s is your source file. On some Linux distributions you may also need to supply -no-pie as your program is not written in PIC style (don't worry about this for now).

Note also that I recommend not mixing libc calls with raw system calls. Instead of doing a raw exit system call, call the C library function exit. This lets the C runtime deinitialise itself correctly, including flushing any IO streams.

Now if you assemble and link your program as I said in the first paragraph, you'll notice that it might crash. This is because the stack needs to be aligned to a multiple of 16 bytes on calls to functions. You can ensure this alignment by pushing a qword of data on the stack at the beginning of each of your functions (remember to pop it back off at the end).

fuz
  • 88,405
  • 25
  • 200
  • 352
  • thanks, regarding `Note also that I recommend not mixing libc calls with raw system calls. Instead of doing a raw exit system call, call the C library function exit`. What if I just did `ret` at the end, would that work? – carl.hiass Mar 25 '21 at 23:24
  • @carl.hiass: No, it would not. You can read in [the x86-64 ABI](https://stackoverflow.com/questions/18133812/where-is-the-x86-64-system-v-abi-documented), Figure 3.9, what's on the stack at program entry. Note there is no return address there; the bottom of the stack contains the argument count. If you try to `ret` you'll jump to address 1 or 2 or whatever, and segfault. – Nate Eldredge Mar 25 '21 at 23:27
  • @NateEldredge it works for me when invoked via gcc. let me post another question related to it... – carl.hiass Mar 25 '21 at 23:32
  • 1
    @NateEldredge If `main` is used instead of `_start`, of course you can return. – fuz Mar 25 '21 at 23:43
  • I see, I wasn't clear which case we were talking about. You can't `ret` from `_start` but yes you can from `main`. – Nate Eldredge Mar 25 '21 at 23:50