[英]Strange 'asm' operand has impossible constraints error
我正在尝试编译一个简单的C程序(Win7 32位,Mingw32 Shell和GCC 5.3.0)。 C代码是这样的:
#include <stdio.h>
#include <stdlib.h>
#define _set_tssldt_desc(n,addr,type) \
__asm__ ("movw $104,%1\n\t" \
:\
:"a" (addr),\
"m" (*(n)),\
"m" (*(n+2)),\
"m" (*(n+4)),\
"m" (*(n+5)),\
"m" (*(n+6)),\
"m" (*(n+7))\
)
#define set_tss_desc(n,addr) _set_tssldt_desc(((char *) (n)),addr,"0x89")
char *n;
char *addr;
int main(void) {
char *n = (char *)malloc(100*sizeof(int));
char *addr = (char *)malloc(100*sizeof(int));
set_tss_desc(n, addr);
free(n);
free(addr);
return 0;
}
_set_tssldt_desc(n,addr,type)
是一个宏,其主体是汇编代码。 set_tss_desc(n,addr)
是另一个非常类似于_set_tssldt_desc(n,addr,type)
宏。 在主函数中调用set_tss_desc(n,addr)
宏。
当我尝试编译此代码时,编译器向我显示以下错误:
$ gcc test.c
test.c: In function 'main':
test.c:5:1: error: 'asm' operand has impossible constraints
__asm__ ("movw $104,%1\n\t" \
^
test.c:16:30: note: in expansion of macro '_set_tssldt_desc'
#define set_tss_desc(n,addr) _set_tssldt_desc(((char *) (n)),addr,"0x89")
^
test.c:25:3: note: in expansion of macro 'set_tss_desc'
set_tss_desc(n, addr);
^
奇怪的是,如果我在主函数中注释调用,指出代码已成功编译。
int main(void) {
char *n = (char *)malloc(100*sizeof(int));
char *addr = (char *)malloc(100*sizeof(int));
//I comment it out and code compiled.
//set_tss_desc(n, addr);
free(n);
free(addr);
return 0;
}
或者,如果我在汇编代码的输出部分删除了一些变量,它也会被编译。
#include <stdio.h>
#include <stdlib.h>
#define _set_tssldt_desc(n,addr,type) \
__asm__ ("movw $104,%1\n\t" \
:\
:"a" (addr),\
"m" (*(n)),\
"m" (*(n+2)),\
"m" (*(n+4)),\
"m" (*(n+5)),\
"m" (*(n+6))\
)
//I DELETE "m" (*(n+7)) , code compiled
#define set_tss_desc(n,addr) _set_tssldt_desc(((char *) (n)),addr,"0x89")
char *n;
char *addr;
int main(void) {
char *n = (char *)malloc(100*sizeof(int));
char *addr = (char *)malloc(100*sizeof(int));
set_tss_desc(n, addr);
free(n);
free(addr);
return 0;
}
有人可以向我解释这是为什么以及如何解决此问题?
就像@MichealPetch所说的那样 ,您正在以错误的方式进行处理。 如果您要为lgdt
设置操作数,请在C中进行操作,并且仅对lgdt
指令本身使用inline-asm。 请参阅内联汇编标签Wiki和x86标签Wiki。
相关文章:用于弄乱Intel描述符表的C结构/联合: 如何在编译/链接时使用地址进行计算? 。 (该问题想将表生成为静态数据,因此询问在编译时将地址分为低半/高半)。
另外: 用基本内核实现GDT,以进行某些C + asm GDT操作。 也许不是,因为那里的答案只是说问题中的代码有问题,而没有详细的解决方法。
链接器错误设置使用内联汇编程序通过LGDT指令加载GDT寄存器来加载GDT寄存器,这是Michael Petch的答案,其中有些链接提供了更多指南/教程。
即使正确的解决方法是https://gcc.gnu.org/wiki/DontUseInlineAsm ,回答特定问题仍然有用。
启用优化后,编译效果良好。
使用-O0
,gcc不会注意到或利用以下事实:操作数彼此之间都是小的常量偏移量,并且可以在偏移量寻址模式下使用相同的基址寄存器。 它希望将指向每个输入存储器操作数的指针放入单独的寄存器中,但用完了寄存器。 使用-O1
或更高版本,CSE可以满足您的期望。
您可以在简化的示例中看到这一点,对最后3个内存操作数进行注释,然后将asm字符串更改为在所有操作数中都包含一个asm注释。 从Godbolt编译器资源管理器上的gcc5.3 -O0 -m32
:
#define _set_tssldt_desc(n,addr,type) \
__asm__ ("movw $104,%1\n\t" \
"#operands: %0, %1, %2, %3\n" \
...
void simple_wrapper(char *n, char *addr) {
set_tss_desc(n, addr);
}
pushl %ebp
movl %esp, %ebp
pushl %ebx
movl 8(%ebp), %eax
leal 2(%eax), %ecx
movl 8(%ebp), %eax
leal 4(%eax), %ebx
movl 12(%ebp), %eax
movl 8(%ebp), %edx
#APP # your inline-asm code
movw $104,(%edx)
#operands: %eax, (%edx), (%ecx), (%ebx)
#NO_APP
nop # no idea why the compiler inserted a literal NOP here (not .p2align)
popl %ebx
popl %ebp
ret
但是启用优化后,您将获得
simple_wrapper:
movl 4(%esp), %edx
movl 8(%esp), %eax
#APP
movw $104,(%edx)
#operands: %eax, (%edx), 2(%edx), 4(%edx)
#NO_APP
ret
请注意,后面的操作数如何使用base + disp寻址模式。
您的约束是完全落后的。 您正在写内存,告诉编译器是输入操作数。 它将假定该内存未由asm
语句修改,因此,如果从C中加载该内存,则可能会将该负载移至asm
。 等可能的破损。
如果您使用了"=m"
输出操作数,那么此代码将是正确的(但与让编译器为您执行此操作相比,效率仍然较低)。
您可能已经编写了asm来对单个内存输入操作数进行偏移,但是随后您需要做一些事情来告诉编译器有关asm
语句读取的内存; 例如"=m" (*(struct {char a; char x[];} *) n)
告诉您您编写了从n
开始的整个对象。 (请参阅此答案 )。
AT&T语法x86内存操作数始终是可偏移的,因此您可以使用2 + %[nbase]
代替单独的操作数
asm("movw $104, %[nbase]\n\t"
"movw $123, 2 + %[nbase]\n\t"
: [nbase] "=m" (*(struct {char a; char x[];} *) n)
: [addr] "ri" (addr)
);
气体会警告约2 + (%ebx)
或最终导致的危险,但这没关系。
对您写入的每个位置使用单独的内存输出操作数,可以避免告诉编译器您写入哪个内存的任何问题。 但是你搞错了:你已经告诉编译器,你的代码没有使用n+1
,而实际上你是在使用movw $104
来存储2个从n
开始的字节。 所以应该是一个uint16_t
内存操作数。 如果听起来很复杂, 请https://gcc.gnu.org/wiki/DontUseInlineAsm 。 就像Michael所说的那样,用struct
在C中完成此部分,并且仅对需要它的单个指令使用内联asm。
使用更少的更广泛的存储指令显然会更有效率。 IDK接下来您打算做什么,但是任何相邻的常量都应该合并到32位存储中,例如mov $(104 + 0x1234 << 16), %[n0]
或类似内容。 同样, https://gcc.gnu.org/wiki/DontUseInlineAsm 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.