1

I wrote the code that reads from stdin and writes to the stdout:

#include <stdio.h>
#include <unistd.h>

int main()  /* copy input to output */
{
    char buf[BUFSIZ];
    int n;

    while ((n = read(0, buf, BUFSIZ)) > 0)
        write(1, buf, n);

    return 0;
}

After I converted into the assembly code (a .s file) in 32-bit AT&T syntax:

    .text
    .globl  _start
_start:
    pushl   %ebp
    movl    %esp, %ebp
    andl    $-16, %esp  #16 bit alignment
    subl    $8224, %esp #space for local variables
    jmp _READ
_WRITE:
    movl    8220(%esp), %eax
    movl    %eax, 8(%esp)
    leal    28(%esp), %eax
    movl    %eax, 4(%esp)
    movl    $1, (%esp)
    call    write
    int $0x80
_READ:
    movl    $8192, 8(%esp)  #buffer length
    leal    28(%esp), %eax
    movl    %eax, 4(%esp)
    movl    $0, (%esp)
    call    read
    movl    %eax, 8220(%esp)
    cmpl    $0, 8220(%esp)
    jg  _WRITE
    movl    $0, %eax
    leave
    ret

It works fine, but I'm not sure how to making the "read" and "write" system calls using plain assembly(i.e. moving numbers into certain registers and use "int 0x80" to execute the system calls).

My goal is to make it work even if it is compiled with the "-nostdlib" option.

Etheryte
  • 24,589
  • 11
  • 71
  • 116
Meteorite
  • 344
  • 1
  • 5
  • 17
  • System calls are OS-dependent. Please specify your OS. – Raymond Chen Dec 03 '14 at 22:48
  • linux:) @Raymond Chen – Meteorite Dec 03 '14 at 22:49
  • I'd recommend to start with [x86 calling conventions](http://en.wikipedia.org/wiki/X86_calling_conventions) and later ask more specifically. How call OS stuff depends on OS used. Linux? – alexander Dec 03 '14 at 22:49
  • @alexander I'm not sure if it really depends on the OS, but I just wanted to answer Raymond's question... Do you know how to put everything together? – Meteorite Dec 03 '14 at 22:53
  • Do you want to replace read and write functions to custom ones? If yes, use '-nostdlib' option and provide obj-file to linker with custom read/write functions. And as your asm code use cdecl calling conventions, read/write functions must be the same. int 80 is sys call to linux kernel. Not sure it is needed. Thats how I understand the question. – alexander Dec 03 '14 at 23:05
  • Yes. I am replacing the read and write functions to custom ones. The problem is I'm not sure how they are done using plain assembly. Do you have any idea? Also, I think "int 0x80" is needed after I assign the correct system call numbers to corresponding registers, but I'm not sure which registers the numbers should go to... – Meteorite Dec 03 '14 at 23:08
  • See a [system call list](http://syscalls.kernelgrok.com/). – Jester Dec 03 '14 at 23:10
  • I've seen it and I have been playing around with those numbers and registers, but none of my attempts worked. Could you give me an example that actually works? Thanks for your help @Jester! – Meteorite Dec 03 '14 at 23:12
  • read/write functions is part of libc library. They are written in C. You could use gdb to see assembler code of the functions. Or you could see libc source. Sorry, can't tell what values should be written in the registers to make 'int 80h' – alexander Dec 03 '14 at 23:13
  • @alexander Okay, do you mind sharing the command lines used to see the assembler code? I definitely thought of this, but I just know the 'man' command. Thanks! – Meteorite Dec 03 '14 at 23:17
  • 1
    The idea is that **you** show your code and we tell you why it doesn't work, and how you failed to find the error yourself :) Anyway, [here you go](http://stackoverflow.com/questions/24047839/gnu-assembler-dot-notation-current-address/24048194#24048194). – Jester Dec 03 '14 at 23:18

1 Answers1

4

Hint: 32-bit x86 is old, slow, weird and deprecated. You should use amd64 instead.


The list of system calls for Linux i386 is available in Linux source code:
https://github.com/torvalds/linux/blob/master/arch/x86/entry/syscalls/syscall_32.tbl
Or in glibc headers in asm/unistd_32.h. You can and should #include <asm/unistd.h> so you can use $__NR_write instead of $4 to make your asm source code self-documenting.

The system call number goes in eax. Parameter sequence is always ebx, ecx, edx, esi, edi, ebp. So code becomes:

        .text
        .globl  _start
    _start:
        pushl   %ebp
        movl    %esp, %ebp
        andl    $-16, %esp  #16 bit alignment
        subl    $8224, %esp #space for local variables
        jmp _READ
    _WRITE:
        movl    8220(%esp), %edx
        leal    28(%esp), %ecx
        movl    $1, %ebx
        movl    $4, %eax
        int $0x80
    _READ:
        movl    $8192, %edx  #buffer length
        leal    28(%esp), %ecx
        movl    $0, %ebx
        movl    $3, %eax
        int $0x80
        movl    %eax, 8220(%esp)
        cmpl    $0, 8220(%esp)
        jg  _WRITE
        movl    $1, %eax
        movl    $0, %ebx
        int $0x80

Assemble and link with:

$ as --32 hel.s -o hel.o
$ ld -melf_i386 hel.o -o hel

http://www.linuxjournal.com/article/4048

See also

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
hdante
  • 7,685
  • 3
  • 31
  • 36
  • @jghfun-run - IA64 is not x86 or x86-64! Itanium is a totally separate ISA. Please be more careful when editing links to make sure you still link to the correct thing. – Peter Cordes Jul 17 '19 at 04:13