3

我在一个项目中大量使用内联汇编,在该项目中我需要在编译时调用具有未知数量参数的函数,而我自己管理自己让它工作,有时,在 linux 中(在 Windows 中我不记得有这个问题)像这样奇怪的事情发生了:

如果我有类似的东西

for(int i = 1; i >= 0; i--)
   asm("push %0"::"m"(someArray[i]));

有用。

如果我有

for(int i = this->someVar; i >= 0; i--)
   asm("push %0"::"m"(someArray[i]));

我用我的生命保证 someVar 保持值 1 它会引发分段错误。

另外,如果我有

int x = 1;
for(int i = x; i >= 0; i--)
   asm("push %0"::"m"(someArray[i]));

它有效,但

int x = this->someVar;
for(int i = x; i >= 0; i--)
    asm("push %0"::"m"(someArray[i]));

才不是。

另外,奇怪的是,我可以说,虽然在某些功能中我没有问题,但在我拥有的其他功能中,都在同一个对象中。

如果有人可以向我指出一些可以解决问题所在的信息,我将不胜感激。

请注意,我确实必须在 for 循环中推送参数,因此避免它不是一种选择。

我也尝试使用内联汇编词“volatile”,但没有任何改变。

4

5 回答 5

4

我不明白问题出在哪里,但尝试使用相同的清晰 asm 代码编写代码

asm{
   loop1:
     mov ax, this->var
     ...
     dec ax
     cmp ax, 0
     je exit
     jmp loop1
}

...

出口:

也尝试将“var”值设为静态可能也有帮助。

于 2010-01-13T10:21:43.183 回答
4

检查拆卸。最可能的原因是i和/或保存最终值的变量在循环的每次迭代中从堆栈上的固定偏移量重新获取for,并且您push将堆栈指针从编译器预期的位置偏移,因此导致要获取的错误值。

您可以尝试各种变通方法(例如声明局部变量register),但不幸的是,在这种情况下,没有好的方法可以保证C/C++ 中的正确行为。为避免该问题,请按照 oivoodoo 的建议自行实现循环。

于 2010-01-13T10:25:36.623 回答
3

这是我的心理调试工作:

i并且this最有可能存储在堆栈上,并且在 386 及更高版本上,机器代码可以esp直接引用 -relative 内存位置,因此编译器很可能会生成如下指令

mov eax,[esp+8]

将 的值this放入eax寄存器。问题是您的push操作与堆栈指针混淆,因此这些硬编码访问将在第一次迭代后访问(越来越多)错误的内存位置。

最有可能的this->someVar是,编译器更彻底地优化了更简单的循环形式,并导致机器代码仅使用寄存器而没有esp-relative 访问,这意味着它们可以继续正常工作。

曾几何时,对局部变量和参数的所有内存访问都是通过ebp寄存器完成的,这不会被您的内联汇编代码更改。如果您可以找到一个编译器开关来强制使用 ofebp而不是esp,这可能会解决您的问题。

警告:编译器不希望你弄乱堆栈——它希望它始终知道堆栈顶部的位置。如果您真的想在堆栈上动态推送内容,我建议您完全用汇编语言编写循环本身,就像oivoodoo所做的那样。

于 2010-01-13T10:26:35.793 回答
1

首先,可能发生的事情是 linux 下的 gcc 正在使用堆栈指针来索引您的局部变量,而不是使用堆栈帧指针。这是一个优化,允许 gcc 使用帧指针(x86 下的 BP)作为另一个通用寄存器,并避免大量设置帧的代码。帧本质上只是属于局部功能的 SP 和 BP 之间的区域。我敢打赌,如果你包含一个对 alloca 的调用,并且你传递给这个函数的大小会变得更好,因为它会强制编译器不做这个优化。

话虽如此,错误确实在您的代码中。除非你真的知道你在做什么,否则你永远不应该退出一个内联汇编,它的堆栈指针与你进入内联汇编时的堆栈指针不同。编译器几乎总是认为他们完全拥有堆栈指针。他们依赖于它保持不变,以便他们可以使用它来找到他们存储变量的位置。您还应该远离帧指针 (BP)。

可以处理这些问题的情况很少见,通常用于上下文切换代码(从一个线程或进程更改为另一个)之类的事情。

于 2010-03-26T19:14:07.960 回答
0

如果您知道 args 数量的限制,您可以使用具有该数量参数的单个函数调用来调用它,将您的实际参数对齐到最后。

警告:x86_64 abi 对某些参数使用寄存器,这也破坏了这个和你的代码。

于 2010-01-13T10:40:23.447 回答