2

I want to make a function in assembler to be called from c that will write a byte(char) to the file. Here is how function should look in c:

void writebyte (FILE *f, char b)
{
    fwrite(&b, 1, 1, f);
}

And here is the code that will call it:

#include <stdio.h>

extern void writebyte(FILE *, char);

int main(void) {
    FILE *f  = fopen("test.txt", "w");    
    writebyte(f, 1);
    fclose(f);
    return 0;
}

So far I came up with following assembler code:

    .global     writebyte  
writebyte:
    pushl   %ebp
    movl    %esp, %ebp  #standard params
    pushl   12(%ebp)    # pushing byte to the stack
    pushl   $1
    pushl   $1
    pushl   8(%ebp)     #file to write
    call    fwrite
    popl    %ebp
    ret

I keep getting from gdb:

Program terminated with signal SIGSEGV, Segmentation fault.
#0  0xffa9702c in ?? ()

How do I write such a function in assembly?

EDIT: I am using Ubuntu 16.04

polyx
  • 73
  • 1
  • 9

1 Answers1

3

According to cdecl convention, you should push the arguments in reverse order. So f should go first and b should go last. Also the stack should be cleaned up by the caller after calling fwrite().

As noted in the comments, b will be received as value, but we need to pass it to fwrite() as pointer. The pointer will be equal to the value of ebp + 12.

This seems to work for me:

    .global writebyte
writebyte:
    //create new stack frame
    pushl   %ebp
    movl    %esp, %ebp

    //push the four arguments to stack (in reverse order)
    pushl   8(%ebp)
    pushl   $1
    pushl   $1

    //get pointer of "b" argument (%ebp+12) and move it to %eax
    leal    12(%ebp), %eax
    pushl   %eax

    //call fwrite()
    call    fwrite

    //remove arguments from stack and pop %ebp
    leave

    ret
Anton Angelov
  • 1,223
  • 7
  • 19
  • why do you use underscore in call and function name? – polyx May 04 '16 at 15:36
  • 1
    Because I used Windows to compile this example and GCC on Windows requires the symbol names to begin with underscore, and actually prepends underscores to all the C functions. You can see that if you disassemble a compiled object files. But anyway, on Linux the underscores shoudn't be required, so I'll edit the post to remove them. – Anton Angelov May 04 '16 at 15:47
  • 1
    Why do you `pusha`/`popa`? You even comment the line with the fact that it's our callers problem if they're clobbered. That would let you use `leave` for the epilogue instead of `add $16, %esp` / `pop %ebp`. (And of course, the compiler wouldn't even bother making a stack frame, but [gcc is dumb and copies the char into a local](https://godbolt.org/g/1QJSgc), rather than passing the address of its location on the stack. Also, gcc keeps the stack 16B-aligned for `fwrite()`. This tends not to matter, since stdio functions don't spill SSE vectors to the stack.) – Peter Cordes May 06 '16 at 02:24
  • 1
    You are right, the `pusha`/`popa` are not neccessary at all. Perhaps I wasn't paying attention when I was writing this. I'll edit the post to remove them. And it's interesting to see how GCC behaves. – Anton Angelov May 06 '16 at 07:49