我试图了解Linux中内联汇编程序的一些内容.我正在使用以下功能:
void test_func(Word32 *var){
asm( " addl %0, %%eax" : : "m"(var) );
return;
}
Run Code Online (Sandbox Code Playgroud)
它生成以下汇编代码:
.globl test_func
.type test_func, @function
test_func:
pushl %ebp
movl %esp, %ebp
#APP
# 336 "opers.c" 1
addl 8(%ebp), %eax
# 0 "" 2
#NO_APP
popl %ebp
ret
.size test_func, .-test_func
Run Code Online (Sandbox Code Playgroud)
它将var mem地址与eax寄存器值相加而不是var值.
有没有办法告诉addl指令使用var值而不是var mem地址而不将var mem地址复制到寄存器?
问候
它将 var mem 地址与 eax 寄存器值相加,而不是 var 值。
是的,gcc 内联汇编的语法非常晦涩难懂。根据GCC 内联汇编 HOWTO 中的相关部分进行解释,"m"可以粗略地了解 C 变量的内存位置。
当您只需要一个可以写入或读取的地址时,您可以使用它。请注意,我说的是C变量的位置,因此%0设置为地址Word32 *var- 您有一个指向指针的指针。内联汇编块的 C 翻译可能看起来像这样EAX += *(&var),因为您可以说"m"约束隐式采用 C 变量的地址并为您提供一个地址表达式,然后将其添加到%eax.
有没有办法告诉 addl 指令使用 var 值而不是 var mem 地址而不将 var mem 地址复制到寄存器?
这取决于你的意思。您需要var从堆栈中获取,因此有人必须取消引用内存(请参阅@Bo Perssons 答案),但您不必在内联汇编中执行此操作
约束需要是"m"(*var)(正如@fazo 建议的那样)。这将为您提供指向 的值的var内存位置,而不是指向它的内存位置。
现在生成的代码是:
test_func:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
#APP
# 2 "test.c" 1
addl (%eax), %eax
# 0 "" 2
#NO_APP
popl %ebp
ret
Run Code Online (Sandbox Code Playgroud)
这有点可疑,但这是可以理解的,因为你忘记告诉 GCC 你已经破坏了(在输入/输出列表中没有修改)%eax。修复后asm("addl %0, %%eax" : : "m"(*var) : "%eax" )会生成:
movl 8(%ebp), %edx
addl (%edx), %eax
Run Code Online (Sandbox Code Playgroud)
在这种情况下,这并没有更好或更正确,但这始终是一个值得记住的好习惯。请参阅有关clobber 列表的部分,并特别注意"memory"clobber 以了解内联汇编的高级用法。
即使您不想(明确地)将内存地址加载到寄存器中,我也会简要介绍一下它。将约束从 更改为"m"几乎"r"似乎有效,相关部分更改为(如果我们包含%eax在破坏列表中):
movl 8(%ebp), %edx
addl %edx, %eax
Run Code Online (Sandbox Code Playgroud)
这几乎是正确的,我们已将指针值加载var到寄存器中,但现在我们必须指定自己是从内存加载的。更改代码以匹配约束(通常是不可取的,我只是为了完整性而显示它):
asm("addl (%0), %%eax" : : "r"(var) : "%eax" );
Run Code Online (Sandbox Code Playgroud)
给出:
movl 8(%ebp), %edx
addl (%edx), %eax
Run Code Online (Sandbox Code Playgroud)
与 相同"m"。