ASM内联疑问

时间:2011-08-21 15:49:13

标签: gcc assembly

我试图理解Linux中内联汇编程序的一些内容。我正在使用以下功能:

void test_func(Word32 *var){
   asm( " addl %0, %%eax" : : "m"(var) );
   return;
}

它生成以下汇编代码:

.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

它将var mem地址与eax寄存器值相加而不是var值。

有没有办法告诉addl指令使用var值代替var mem地址而不将var mem地址复制到寄存器?

此致

4 个答案:

答案 0 :(得分:4)

是的,因为你给他的var是地址。给他* var。

像:

void test_func(Word32 *var){
   asm( " addl %0, %%eax" : : "m"(*var) );
   return;
}

我不记得确切,但你应该用“r”代替“m”?

内存操作数并不意味着它将从该地址获取值。它只是一个指针

答案 1 :(得分:3)

它将var mem地址与eax寄存器值相加而不是var值。

是的,gcc内联汇编的语法非常神秘。从GCC Inline Assembly HOWTO "m"中的相关部分释义大致为您提供了C变量的内存位置。

当您只想要一个可以写入或读取的地址时,就会使用它。注意我说了 C 变量的位置,所以%0被设置为Word32 *var的地址 - 你有一个指向指针的指针。内联汇编块的AC转换看起来像EAX += *(&var),因为您可以说"m"约束隐式地获取C变量的地址并为您提供一个地址表达式,然后将其添加到{{1} }}。

有没有办法告诉addl指令使用var值而不是var mem地址而不将var mem地址复制到寄存器?

这取决于你的意思。你需要从堆栈中获取%eax,所以有人必须取消引用内存(参见@Bo Perssons的答案),但你不必在内联汇编中进行此操作

约束必须是var(如@fazo建议的那样)。这将为您提供"m"(*var)指向的值的内存位置,而不是指向它的内存位置。

现在生成的代码:

var

这有点可疑,但这是可以理解的,因为你忘了告诉GCC你已经破坏了(没有在输入/输出列表中进行修改)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 。修复了%eax生成的问题:

asm("addl %0, %%eax" : : "m"(*var) : "%eax" )

这个的情况下哪个更好或更正确,但记住它总是一个好习惯。请参阅clobber list部分,并特别注意 movl 8(%ebp), %edx addl (%edx), %eax clobber以获取内联汇编的高级用法。

即使你不想(明确地)将内存地址加载到寄存器中,我也会简单地介绍它。 将约束从"memory"更改为"m"几乎可以正常工作,相关部分会更改为(如果我们在clobber列表中包含"r"):

%eax

这几乎是正确的,我们已将指针值 movl 8(%ebp), %edx addl %edx, %eax 加载到寄存器中,但现在我们必须指定自己从内存加载。更改代码以匹配约束(通常不合适,我只是为了完整性而显示):

var

给出:

asm("addl (%0), %%eax" : : "r"(var) : "%eax" );

movl 8(%ebp), %edx addl (%edx), %eax 相同。

答案 2 :(得分:1)

不,x86处理器的寻址模式没有间接的两个级别。

您必须首先从内存地址加载指针,然后从指针间接加载。

答案 3 :(得分:0)

尝试

void test_func(Word32 *var){
    asm( " mov %0, %%edx; \
    addl (%%edx), %%eax" : : "m"(var) );    

    return;
}