GCC 最适合 AT&T 风格的程序集,而 GAS 并没有实现所有的 Intel 语法。您的直接问题来自110b
没有被解释为数字,但这还不是全部。
您不能在 GCC 的内联汇编语法中直接引用变量。你必须这样写(使用默认值-masm=att
):
int foobar(int num) {
int result;
asm("mov %1, %%eax\n\t"
"add $6, %%eax\n\t"
"sub $2, %%eax\n\t"
"mov %%eax, %0"
: "=g" (result)
: "g" (num)
: "eax", "cc");
return result;
}
第一个冒号之后是逗号分隔的输出操作数列表。因为"=g" (result)
是第一个约束,所以result
获取 alias %0
。 "=g"
向 GCC 指示%0
可以是任何通用寄存器或内存,并且只会被写入。(+
而不是=
表示读写。GCC 可能会决定将同一个寄存器用于多种目的,因此您必须明确告诉它所有内容将如何使用。)
第二个冒号之后是逗号分隔的输入操作数列表。因为"g" (num)
是第二个约束,所以num
获取 alias %1
。 "g"
意味着它只会被读取。
第三个冒号之后是逗号分隔的 clobbers 列表。这告诉 GCC 内联程序集可能会更改这些寄存器/内存,即使它们不是输入也不是输出,因此 GCC 必须重新加载它通过内联程序集保存在其中的任何信息。在这里,我们显然改变%eax
了,条件码(标志)寄存器也受到了影响add/sub
。
查看编译器生成的程序集:
$ cc -S -o- -m32 asmtest.c | sed -n /globl.foobar/,/-foobar/p
.globl foobar
.type foobar, @function
富吧:
推%ebp
movl %esp, %ebp
低于 $16, %esp
#应用程序
# 15 "asmtest.c" 1
mov 8(%ebp), %eax
添加 $6, %eax
低于 $2, %eax
mov %eax, -4(%ebp)
#0“”2
#NO_APP
movl -4(%ebp), %eax
离开
ret
.size foobar, .-foobar
编译器决定直接使用 和 的堆栈num
位置result
。如果我们使用:"=r":"r"
约束(这意味着只允许寄存器)而不是:"=g":"g"
(允许寄存器或内存位置),编译器将在内联汇编之前/之后将它们复制到寄存器中/从寄存器中复制它们。
$ cc -S -o- -m32 asmtest.c | sed -n /globl.foobar/,/-foobar/p
.globl foobar
.type foobar, @function
富吧:
推%ebp
movl %esp, %ebp
低于 $16, %esp
movl 8(%ebp), %edx
#应用程序
# 15 "asmtest.c" 1
移动 %edx, %eax
添加 $6, %eax
低于 $2, %eax
移动 %eax, %edx
#0“”2
#NO_APP
movl %edx, -4(%ebp)
movl -4(%ebp), %eax
离开
ret
.size foobar, .-foobar
如果您真的想使用 Intel 语法,请将其放在单独的.s
源文件中,使用NASM独立组装,然后将对象链接在一起。
$猫 asmtest.c
#include <stdio.h>
int foobar(int);
/* int foobar(int) __attribute__((fastcall)); */
int main() {
诠释 n = 0;
printf("编号:");
scanf("%d", &n);
printf("%d\n", foobar(n));
返回0;
}
$猫foobar.s
全局 foobar
富吧:
mov eax,[esp+4] # 如果 C 原型标记为 fastcall,则去掉这一行
子 eax,110b
添加 eax,2
ret
$ nasm -f elf foobar.s
$ cc -m32 asmtest.c foobar.o
$ ./a.out
数量:30
26
(虽然-f elf
对于 Windows 来说不正确。也许-f win32
吧?而且由于 Windows 的愚蠢,您可能不得不_foobar
在程序集中使用该名称。)