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

6
  • 1
    As you don't have have std library with assembler, then the code will be OS-dependent. Commented May 4, 2016 at 13:48
  • I am using ubuntu 16.04 Commented May 4, 2016 at 13:50
  • 5
    The first parameter in calling fwrite is an address that points to the byte, not the byte itself; assuming that you have linked the program correctly, you are most likely getting a seg fault because fwrite is trying to use your byte as an address, and access memory at addresses that aren't mapped in your virtual address space. Commented May 4, 2016 at 13:53
  • 1
    stackoverflow.com/questions/29545056/… Commented May 4, 2016 at 14:02
  • 4
    Also you don't clean up the stack. Commented May 4, 2016 at 14:04

1 Answer 1

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
Sign up to request clarification or add additional context in comments.

4 Comments

why do you use underscore in call and function name?
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.
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, 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.)
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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.