5

是否可以使用内联汇编块中的系统调用编写单个字符?如果是这样,如何?它应该看起来像这样的“东西”:

__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 美元是 ascii 中的“P”,但没有返回任何内容。

任何建议非常感谢!

4

3 回答 3

8

您可以使用特定于体系结构的约束将参数直接放置在特定寄存器中,而无需movl内联汇编中的指令。此外,然后您可以使用&运算符来获取字符的地址:

#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');
}

(编者注:"memory"需要clobber,或以其他方式告诉编译器&c已读取指向的内存。 如何指示可以使用内联ASM参数*指向的内存?


(在这种情况下,=a(ret)需要指示系统调用破坏EAX。我们不能将 EAX 列为破坏,因为我们需要输入操作数来使用该寄存器。"a"约束类似于"r"但只能选择 AL/AX/EAX/RAX . )

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

您还可以返回系统调用返回的写入字节数,并"0"用作再次指示EAX的约束:

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;
}

请注意,发生错误时,系统调用返回值将是(错误文件描述符)或(错误指针)之-errno类的代码。-EBADF-EFAULT

正常的 libc 系统调用包装函数检查返回值 unsignedeax > -4096UL并设置 errno + return -1


还要注意编译-m32是必需的:64 位syscallABI 使用不同的调用号(和寄存器),但是这个 asm 是硬编码调用 32 位 ABI 的缓慢方式,int $0x80.

在 64 位模式下编译将使用 64 位调用号sys/syscall.h进行定义,这会破坏此代码。SYS_write即使您使用了正确的数字,64 位堆栈地址也是如此。 如果在 64 位代码中使用 32 位 int 0x80 Linux ABI 会发生什么?- 不要那样做。

于 2017-08-06T18:45:12.460 回答
2

IIRC,您的示例中有两件事是错误的。首先,您正在使用 mov $0, %ebx 写入 stdin 其次, write 将指针作为第二个参数,因此要写入单个字符,您需要将该字符存储在内存中的某个位置,您不能将值直接写入 % ecx

前任:

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

我只在 Linux 中做过纯 asm,从不使用 gcc 内联,你不能将数据放到程序集的中间,所以我不确定你如何使用内联程序集获取指针。

编辑:我想我只记得怎么做。您可以将 'p' 推入堆栈并使用 %esp

pushw $80
movl %%esp, %%ecx
... int $0x80 ...
addl $2, %%esp
于 2010-06-02T14:19:01.637 回答
0

就像是


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"
                    );
}

添加:请注意,我曾经lea将 char 的有效地址加载到ecx寄存器中;对于ebx我尝试了 $0 和 $1 的价值,它似乎无论如何都可以工作......

避免使用外部字符

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"
                    );
}

注意:它之所以有效,是因为英特尔处理器的字节序!:D

于 2010-06-02T14:24:37.043 回答