[英]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.