syscall from within GCC inline assembly [duplicate]

ⅰ亾dé卋堺 提交于 2020-01-28 01:24:12

问题


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!


回答1:


Something like


char p = 'P';

int main()
{
__asm__ __volatile__
                    (
                     " movl $1,  %%edx \n\t"
                     " leal p , %%ecx \n\t"
                     " movl $0,  %%ebx \n\t"
                     " movl $4,  %%eax \n\t"
                     " int $0x80       \n\t"
                     ::: "%eax", "%ebx", "%ecx", "%edx"
                    );
}

Add: note that I've used lea to Load the Effective Address of the char into ecx register; for the value of ebx I tried $0 and $1 and it seems to work anyway ...

Avoid the use of external char

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

N.B.: it works because of the endianness of intel processors! :D




回答2:


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.




回答3:


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


来源:https://stackoverflow.com/questions/2958259/syscall-from-within-gcc-inline-assembly

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!