Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

syscall from within GCC inline assembly [duplicate]

is it possible to write a single character using a syscall from within an inline assembly block? if so, how? it should look "something" like this:

__asm__ __volatile__
                    (
                     " movl $1,  %%edx \n\t"
                     " movl $80, %%ecx \n\t"
                     " movl $0,  %%ebx \n\t"
                     " movl $4,  %%eax \n\t"
                     " int $0x80       \n\t"
                     ::: "%eax", "%ebx", "%ecx", "%edx"
                    );

$80 is 'P' in ascii, but that returns nothing.

any suggestions much appreciated!

like image 938
guest Avatar asked Jun 02 '10 13:06

guest


2 Answers

You can use architecture-specific constraints to directly place the arguments in specific registers, without needing the movl instructions in your inline assembly. Furthermore, then you can then use the & operator to get the address of the character:

#include <sys/syscall.h>

void sys_putc(char c) {
    // write(int fd, const void *buf, size_t count); 
    int ret;
    asm volatile("int $0x80" 
            : "=a"(ret)                    // outputs
            : "a"(SYS_write), "b"(1), "c"(&c), "d"(1)  // inputs
            : "memory");                   // clobbers
}

int main(void) {
    sys_putc('P');
    sys_putc('\n');
}

(Editor's note: the "memory" clobber is needed, or some other way of telling the compiler that the memory pointed-to by &c is read. How can I indicate that the memory *pointed* to by an inline ASM argument may be used?)


(In this case, =a(ret) is needed to indicate that the syscall clobbers EAX. We can't list EAX as a clobber because we need an input operand to use that register. The "a" constraint is like "r" but can only pick AL/AX/EAX/RAX. )

$ cc -m32 sys_putc.c && ./a.out
P

You could also return the number of bytes written that the syscall returns, and use "0" as a constraint to indicate EAX again:

int sys_putc(char c) {
    int ret;
    asm volatile("int $0x80" : "=a"(ret) : "0"(SYS_write), "b"(1), "c"(&c), "d"(1) : "memory");
    return ret;
}

Note that on error, the system call return value will be a -errno code like -EBADF (bad file descriptor) or -EFAULT (bad pointer).

The normal libc system call wrapper functions check for a return value of unsigned eax > -4096UL and set errno + return -1.


Also note that compiling with -m32 is required: the 64-bit syscall ABI uses different call numbers (and registers), but this asm is hard-coding the slow way of invoking the 32-bit ABI, int $0x80.

Compiling in 64-bit mode will get sys/syscall.h to define SYS_write with 64-bit call numbers, which would break this code. So would 64-bit stack addresses even if you used the right numbers. What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code? - don't do that.

like image 66
Dato Avatar answered Nov 03 '22 23:11

Dato


IIRC, two things are wrong in your example. Firstly, you're writing to stdin with mov $0, %ebx Second, write takes a pointer as it's second argument, so to write a single character you need that character stored somewhere in memory, you can't write the value directly to %ecx

ex:

.data
char: .byte 80
.text
mov $char, %ecx

I've only done pure asm in Linux, never inline using gcc, you can't drop data into the middle of the assembly, so I'm not sure how you'd get the pointer using inline assembly.

EDIT: I think I just remembered how to do it. you could push 'p' onto the stack and use %esp

pushw $80
movl %%esp, %%ecx
... int $0x80 ...
addl $2, %%esp
like image 31
cthom06 Avatar answered Nov 04 '22 00:11

cthom06