GCC内联汇编错误:“int'的操作数大小不匹配”

时间:2017-09-02 16:59:50

标签: c gcc assembly x86 inline-assembly

首先,如果有人知道标准C库的功能,那么就会打印出来 一个没有查找二进制零的字符串,但需要绘制的字符数,请告诉我!

否则,我有这个问题:

SQLAlchemy

GCC告诉“int”指令出现以下错误:

void printStringWithLength(char *str_ptr, int n_chars){

asm("mov 4, %rax");//Function number (write)
asm("mov 1, %rbx");//File descriptor (stdout)
asm("mov $str_ptr, %rcx");
asm("mov $n_chars, %rdx");
asm("int 0x80");
return;

}

有人可以告诉我这个问题吗?

2 个答案:

答案 0 :(得分:4)

您的代码存在许多问题。让我一步一步地看看它们。

首先,int $0x80系统调用接口仅适用于32位代码。您不应该在64位代码中使用它,因为它只接受32位参数。在64位代码中,使用syscall接口。系统调用类似,但有些数字不同。

其次,在AT& T汇编语法中,immediates必须以美元符号为前缀。它是mov $4, %rax,而不是mov 4, %rax。后者会尝试将地址4的内容移至rax,这显然不是您想要的。

第三,您不能仅仅参考内联汇编中的自动变量名称。如果需要,您必须告诉编译器使用扩展汇编要使用哪些变量。例如,在您的代码中,您可以执行以下操作:

asm("mov $4, %%eax; mov $1, %%edi; mov %0, %%esi; mov %2, %%edx; syscall"
    :: "r"(str_ptr), "r"(n_chars) : "rdi", "rsi", "rdx", "rax", "memory");

第四,gcc是一个优化编译器。默认情况下,它假定内联汇编语句仅具有您指定的那些副作用。如果您没有指定任何副作用,gcc会假定它没有任何副作用,并删除asm语句。要解决这个问题,您可以将语句标记为asm volatile(不好主意)或使用extended inline assembly指定副作用。当您执行后者时,您应该通过不将数据手动移动到寄存器中来简化代码,而是让编译器执行此操作。例如,您的程序集变为:

asm("syscall" :: "a"(SYS_write), "D"(STDOUT_FILENO), "S"(str_ptr), "d"(n_chars) : "memory");

SYS_WRITE中的<sys/syscall.h>STDOUT_FILENO中的<stdio.h>定义了syscall。我不打算向您解释扩展内联汇编的所有细节。一般使用内联汇编通常是个坏主意。如果您有兴趣,请阅读文档。

第五,尽可能避免使用内联汇编。例如,要进行系统调用,请使用unistd.h中的syscall(SYS_write, STDOUT_FILENO, str_ptr, (size_t)n_chars); 函数:

write(STDOUT_FILENO, str_ptr, n_chars);

这是正确的。

第六,始终检查您要呼叫的系统呼叫是否已在C标准库中可用。在这种情况下,它是,所以你应该写

stdio

完全避免所有这些。

第七,如果您更喜欢使用fwrite,请改用fwrite(str_ptr, 1, n_chars, stdout);

master

答案 1 :(得分:3)

你的代码有很多问题(并且没有理由使用内联asm),不值得尝试实际修正它们。相反,use the write(2) system call通常的方式,通过手册页中记录的POSIX函数/ libc包装器,或使用ISO C <stdio.h> fwrite(3)

#include <unistd.h>

static inline
void printStringWithLength(const char *str_ptr, int n_chars){
    write(1, str_ptr, n_chars);
    // TODO: check error return value
}

为什么您的代码无法汇编

在AT&amp; T语法中,immediates 总是需要一个$装饰器。如果您使用asm("int $0x80"),您的代码将会汇总。

汇编程序抱怨0x80,它是绝对地址0x80的内存引用。没有form of int将中断向量作为立即数以外的任何东西。我不确定为什么它抱怨 size ,因为内存引用在AT&amp; T语法中没有隐含的大小。

这将使它组装,此时你会得到链接器错误

In function `printStringWithLength':
5 : <source>:5: undefined reference to `str_ptr'
6 : <source>:6: undefined reference to `n_chars'
collect2: error: ld returned 1 exit status

(from the Godbolt compiler explorer)

mov $str_ptr, %rcx

表示将符号str_ptr地址立即移动到%rcx。在AT&amp; T语法中,您不必在使用外部符号之前声明外部符号,因此假定未知名称是全局/静态标签。如果你有一个名为str_ptr的全局变量,那么该指令将引用它的地址(这是一个链接时间常量,因此可以用作立即数)。

正如其他人所说,这是完全使用GNU C inline asm进行操作的错误方法。有关指南的更多链接,请参阅代码Wiki。

另外,你使用的是错误的ABI。 int $0x80是x86 32位系统调用ABI,因此它不适用于64位指针。 What are the calling conventions for UNIX & Linux system calls on x86-64

另请参阅标记wiki。