如何在gcc内联汇编中使用全局变量

8

我尝试像这样使用内联汇编来操作全局变量,但编译器报错,显示saved_sp未定义。

__asm__ __volatile__ (
        "movq saved_sp, %rsp\n\t" );

saved_sp 是全局声明为 static long saved_sp 的(针对一个文件)。我在这里犯了什么错误?


1
你不需要通过扩展的 asm 参数传递它吗? - Mysticial
怎么做?我不想在这里使用任何约束。 - MetallicPriest
1
那我不知道。我总是使用约束来完成它。 - Mysticial
您可能需要指定您的 gcc 版本。在我的Ubuntu上,gcc 4.4.4 对您的代码很满意。 - NPE
我建议您包含一个简短但完整的示例,该示例无法编译。 - NPE
显示剩余2条评论
3个回答

6
如果使用static修饰符时出现“undefined reference to `saved_sp'”(这实际上是一个链接器错误,而不是编译器错误),但是当没有使用static修饰符时运行良好,那么很可能编译器已经决定你的源文件中未使用saved_sp,因此决定完全从传递给汇编程序的已编译代码中省略它。
编译器不理解asm块内部的汇编代码;它只是将其粘贴到生成的汇编代码中。因此,它不知道asm块引用saved_sp,如果C代码中没有其他内容读取它,那么编译器就有权决定它完全未被使用 - 尤其是如果启用了任何优化选项。
你可以通过添加used属性(请参阅gcc变量属性文档,该文档在页面中部,例如:documentation of variable attributes)告诉gccsaved_sp被某些无法看到的东西使用,因此防止它选择放弃它。
static long __attribute__((used)) saved_sp;

这是一个完整的示例:
$ cat test.c
#ifdef FIXED
static long __attribute__((used)) saved_sp;
#else
static long saved_sp;
#endif

int main(void)
{
  __asm__ __volatile__ (
        "movq saved_sp, %rsp\n\t" );
}

$ gcc -m64 -o test test.c
$ gcc -m64 -O1 -o test test.c
/tmp/ccATLdiQ.o: In function `main':
test.c:(.text+0x4): undefined reference to `saved_sp'
collect2: ld returned 1 exit status
$ gcc -m64 -DFIXED -O1 -o test test.c
$ 

这是来自一个使用gcc 4.4.5的32位Debian squeeze系统,这是我手头最接近的系统;-m64在你的系统上可能是不必要的。


2

最好使用输入和输出参数:

__asm__ __volatile__ ( 
    "movq %0, %%rsp\n\t"
    : : "r"(saved_sp) : "memory"
);

在汇编阶段,有时会存在一些不是符号的变量(例如堆栈变量或寄存器)。此外,您需要清空整个内存,以确保在将saved_sp存储在RSP之后,没有堆栈变量保留在寄存器中。


本地变量的地址不会逃逸函数,因此不必考虑内存破坏;编译器知道没有其他线程可能引用它。像上下文切换函数这样的操作RSP需要使用手写函数进行,而不是使用内联汇编,除非您可以接受目前编译为工作汇编代码的代码。 - Peter Cordes

0

正如我在评论中所指出的,以下代码可以在64位Ubuntu上使用gcc 4.4.4编译(并生成正确的机器码):

long saved_sp;

int main() {
  __asm__ __volatile__ (
        "movq saved_sp, %rsp\n\t" );
}

也许问题完全可能是其他原因(例如缺少#include,导致saved_sp实际上未定义?编辑:既然你说它是static,那么这不太可能。)


实际上我使用了静态长整型saved_sp。如果我去掉static,它可以正常工作,但是为什么会这样呢?为什么编译器不喜欢这里的static? - MetallicPriest
@MetallicPriest:我似乎无法重现这个问题,使用或不使用 static 均如此。 - NPE
全局变量应该像 saved_sp(%rip) 这样进行引用,而不是使用32位绝对地址寻址模式。此外,在这里缺少一个 "memory" 污点或 "m" 源操作数来告诉编译器该全局变量在此处被读取。 (在这种情况下,Antti 是正确的,因为我们正在修改 RSP 本身,尽管地址未逃逸的本地变量不必考虑内存污点。) - Peter Cordes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接