[英]gcc with intel x86-32 bit assembly : accessing C function arguments
我正在執行操作系統實施工作。
下面是代碼:
//generate software interrupt
void generate_interrupt(int n) {
asm("mov al, byte ptr [n]");
asm("mov byte ptr [genint+1], al");
asm("jmp genint");
asm("genint:");
asm("int 0");
}
我在gcc中使用-masm=intel
選項編譯以上代碼。 另外,這不是生成軟件中斷的完整代碼。
我的問題是我收到n undefined
錯誤,我該如何解決,請幫忙?
使用GCC時,即使使用Intel匯編語法,也必須使用GCC樣式的擴展asm
來訪問用C聲明的變量。 直接將C變量名稱寫入程序集插入的功能是MSVC的一項功能,GCC不會復制該功能。
對於這樣的結構,使用單個裝配體插入件(而不是連續多個插入件)也很重要。 GCC可以並且將相對於周圍的代碼,包括相對於其他程序集插入,重新排列程序集插入,除非您采取特定步驟阻止它。
這種特殊的結構應該寫成
void generate_interrupt(unsigned char n)
{
asm ("mov byte ptr [1f+1], %0\n\t"
"jmp 1f\n"
"1:\n\t"
"int 0"
: /* no outputs */ : "r" (n));
}
請注意,我已經刪除了初始mov
和所有涉及到A寄存器的要求,而是告訴GCC使用"r"
輸入約束將n
加載到任何方便的寄存器中。 最好在程序集插入中做的盡可能少,並且將寄存器的選擇留給編譯器。
我還將n
的類型更改為unsigned char
以匹配INT指令的實際要求,並且我使用的是1f
局部標簽語法,因此,如果將generate_interrupt
用作內聯函數,則此語法可以正常工作。
說了這么多,我懇請您找到不涉及自修改代碼的操作系統實現策略。 好吧,除非您打算從自我修改中獲得更多使用 ,否則。
這不是您有關將參數傳遞給內聯匯編的特定問題的答案(請參閱@zwol的答案)。 對於該特定任務,這不必要地使用了自我修改代碼。
使用自修改代碼的另一種方法是創建一個C宏,該宏生成所需的特定中斷。 一種技巧是您需要一個將數字轉換為字符串的宏。 字符串化宏非常常見,並在GCC文檔中進行了記錄 。
您可以創建一個看起來像這樣的宏GENERATE_INTERRUPT
:
#define STRINGIZE_INTERNAL(s) #s
#define STRINGIZE(s) STRINGIZE_INTERNAL(s)
#define GENERATE_INTERRUPT(n) asm ("int " STRINGIZE(n));
STRINGIZE
將采用數字值並將其轉換為字符串。 GENERATE_INTERRUPT
只是獲取數字,將其轉換為字符串,然后將其附加到INT
指令的末尾。
您可以這樣使用它:
GENERATE_INTERRUPT(0);
GENERATE_INTERRUPT(3);
GENERATE_INTERRUPT(255);
生成的指令應類似於:
int 0x0
int3
int 0xff
如果您需要調用僅在運行時才知道的中斷,則可以創建一張中斷調用表(使用int
指令),然后創建一個ret
。 然后, generate_interrupt
會簡單地從堆棧中檢索中斷號,計算表中可以找到特定int
的位置並jmp
到該位置。
在下面的代碼中,我得到GNU匯編器以生成256個中斷調用的表,每個中斷調用后都使用.rept
指令進行ret
。 每個代碼片段可容納4個字節。 結果代碼生成和generate_interrupt
函數如下所示:
/* We use GNU assembly to create a table of interrupt calls followed by a ret
* using the .rept directive. 256 entries (0 to 255) are generated.
* generate_interrupt is a simple function that takes the interrupt number
* as a parameter, computes the offset in the interrupt table and jumps to it.
* The specific interrupted needed will be called followed by a RET to return
* back from the function */
extern void generate_interrupt(unsigned char int_no);
asm (".pushsection .text\n\t"
/* Generate the table of interrupt calls */
".align 4\n"
"int_jmp_table:\n\t"
"intno=0\n\t"
".rept 256\n\t"
"\tint intno\n\t"
"\tret\n\t"
"\t.align 4\n\t"
"\tintno=intno+1\n\t"
".endr\n\t"
/* generate_interrupt function */
".global generate_interrupt\n" /* Give this function global visibility */
"generate_interrupt:\n\t"
#ifdef __x86_64__
"movzx edi, dil\n\t" /* Zero extend int_no (in DIL) across RDI */
"lea rax, int_jmp_table[rip]\n\t" /* Get base of interrupt jmp table */
"lea rax, [rax+rdi*4]\n\t" /* Add table base to offset = jmp address */
"jmp rax\n\t" /* Do sepcified interrupt */
#else
"movzx eax, byte ptr 4[esp]\n\t" /* Get Zero extend int_no (arg1 on stack) */
"lea eax, int_jmp_table[eax*4]\n\t" /* Compute jump address */
"jmp eax\n\t" /* Do specified interrupt */
#endif
".popsection");
int main()
{
generate_interrupt (0);
generate_interrupt (3);
generate_interrupt (255);
}
如果要查看目標文件中生成的代碼,您會發現中斷調用表( int_jmp_table
)類似於以下內容:
00000000 <int_jmp_table>:
0: cd 00 int 0x0
2: c3 ret
3: 90 nop
4: cd 01 int 0x1
6: c3 ret
7: 90 nop
8: cd 02 int 0x2
a: c3 ret
b: 90 nop
c: cc int3
d: c3 ret
e: 66 90 xchg ax,ax
10: cd 04 int 0x4
12: c3 ret
13: 90 nop
...
[snip]
因為我使用.align 4
每個條目都被填充為4個字節。 這使jmp
的地址計算更加容易。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.