GCC内联汇编错误:“'int'的操作数大小不匹配" [英] GCC Inline-Assembly Error: "Operand size mismatch for 'int'"

查看:168
本文介绍了GCC内联汇编错误:“'int'的操作数大小不匹配"的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

首先,如果有人知道标准C库的功能,则打印一个没有寻找二进制零的字符串,但是需要绘制的字符数,请告诉我!

first, if somebody knows a function of the Standard C Library, that prints a string without looking for a binary zero, but requires the number of characters to draw, please tell me!

否则,我有这个问题:

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;

}

GCC将以下错误告知"int"指令:

GCC tells the following error to the "int" instruction:

"Error: operand size mismatch for 'int'"

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

Can somebody tell me the issue?

推荐答案

您的代码存在许多问题.让我一步一步地检查它们.

There are a number of issues with your code. Let me go over them step by step.

首先, int $ 0x80 系统调用接口仅适用于32位代码.您不应在64位代码中使用它,因为它仅接受32位参数.在64位代码中,使用 syscall 界面.系统调用相似,但一些数字不同.

First of all, the int $0x80 system call interface is for 32 bit code only. You should not use it in 64 bit code as it only accepts 32 bit arguments. In 64 bit code, use the syscall interface. The system calls are similar but some numbers are different.

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

Second, in AT&T assembly syntax, immediates must be prefixed with a dollar sign. So it's mov $4, %rax, not mov 4, %rax. The latter would attempt to move the content of address 4 to rax which is clearly not what you want.

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

Third, you can't just refer to the names of automatic variables in inline assembly. You have to tell the compiler what variables you want to use using extended assembly if you need any. For example, in your code, you could do:

asm volatile("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是一个优化的编译器.默认情况下,它假定内联汇编语句就像纯函数,输出是显式输入的纯函数.如果未使用输出,则可以优化asm语句,或者如果使用相同的输入运行,则可以将其提升到循环之外.

Fourth, gcc is an optimizing compiler. By default it assumes that inline assembly statements are like pure functions, that the outputs are a pure function of the explicit inputs. If the output(s) are unused, the asm statement can be optimized away, or hoisted out of loops if run with the same inputs.

但是像 write 这样的系统调用具有副作用,您需要编译器保留该副作用,因此它不是纯粹的.您需要asm语句以与C抽象机相同的次数运行,并以相同的顺序运行. asm volatile 会使这种情况发生.(没有输出的asm语句是隐式可变的,但是当副作用是asm语句的主要目的时,最好将其明确显示.此外,我们确实希望使用输出操作数来告诉编译器RAX已被修改,以及作为输入,这是我们无法使用的.)

But a system call like write has a side-effect you need the compiler to keep, so it's not pure. You need the asm statement to run the same number of times and in the same order as the C abstract machine would. asm volatile will make this happen. (An asm statement with no outputs is implicitly volatile, but it's good practice to make it explicit when the side effect is the main purpose of the asm statement. Plus, we do want to use an output operand to tell the compiler that RAX is modified, as well as being an input, which we couldn't do with a clobber.)

您始终需要使用我如何指示可以使用内联ASM参数*指向*的内存?表明单独的指针输入操作数不是并不意味着指向的内存还是输入.请使用虚拟"m" 输入或"memory" 破坏器强制所有可访问的内存同步.)

You do always need to accurately describe your asm's inputs, outputs, and clobbers to the compiler using Extended inline assembly syntax. Otherwise you'll step on the compiler's toes (it assumes registers are unchanged unless they're outputs or clobbers). (Related: How can I indicate that the memory *pointed* to by an inline ASM argument may be used? shows that a pointer input operand alone does not imply that the pointed-to memory is also an input. Use a dummy "m" input or a "memory" clobber to force all reachable memory to be in sync.)

您应该通过不编写自己的 mov 指令将数据放入寄存器,而是让编译器执行此指令来简化代码.例如,您的程序集将变为:

You should simplify your code by not writing your own mov instructions to put data into registers but rather letting the compiler do this. For example, your assembly becomes:

ssize_t retval;
asm volatile ("syscall"            // note only 1 instruction in the template
    : "=a"(retval)                 // RAX gets the return value
    : "a"(SYS_write), "D"(STDOUT_FILENO), "S"(str_ptr), "d"(n_chars)
    : "memory", "rcx", "r11"       // syscall destroys RCX and R11
  );

其中 SYS_WRITE < sys/syscall.h> 中定义,而 STDOUT_FILENO < stdio.h> .我不会向您解释扩展内联汇编的所有详细信息.通常,使用内联汇编通常不是一个好主意.如果您有兴趣,请阅读文档.( https://stackoverflow.com/tags/inline-assembly/info )

where SYS_WRITE is defined in <sys/syscall.h> and STDOUT_FILENO in <stdio.h>. I am not going to explain all the details of extended inline assembly to you. Using inline assembly in general is usually a bad idea. Read the documentation if you are interested. (https://stackoverflow.com/tags/inline-assembly/info)

第五,您应该在可能的情况下避免使用内联汇编.例如,要进行系统调用,请使用 unistd.h 中的 syscall 函数:

Fifth, you should avoid using inline assembly when you can. For example, to do system calls, use the syscall function from unistd.h:

syscall(SYS_write, STDOUT_FILENO, str_ptr, (size_t)n_chars);

这做对了.但是它没有内联到您的代码中,因此,如果您想真正内联syscall而不是调用libc函数,请使用MUSL的包装宏.

This does the right thing. But it doesn't inline into your code, so use wrapper macros from MUSL for example if you want to really inline a syscall instead of calling a libc function.

第六,始终检查要调用的系统调用在C标准库中是否已经可用.在这种情况下,所以您应该只写

Sixth, always check if the system call you want to call is already available in the C standard library. In this case, it is, so you should just write

write(STDOUT_FILENO, str_ptr, n_chars);

并完全避免所有这些情况.

and avoid all of this altogether.

第七,如果您更喜欢使用 stdio ,请改用 fwrite :

Seventh, if you prefer to use stdio, use fwrite instead:

fwrite(str_ptr, 1, n_chars, stdout);

这篇关于GCC内联汇编错误:“'int'的操作数大小不匹配"的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆