我在一个项目中大量使用内联汇编,我需要在编译时调用具有未知数量参数的函数,同时我自己管理它,有时候,在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”,但没有改变。
答案 0 :(得分:4)
我无法理解问题是什么,但尝试使用与
相同的清晰asm代码编写代码asm{
loop1:
mov ax, this->var
...
dec ax
cmp ax, 0
je exit
jmp loop1
}
...
出口:
同时尝试将“var”值设为静态也可能有所帮助。
答案 1 :(得分:4)
检查反汇编。最可能的原因是i
和/或保持结束值的变量在for
循环的每次迭代时从堆栈上的固定偏移量中被重新获取,并且push
偏移量堆栈指针从编译器预期的位置开始,因此导致获取错误的值。
您可以尝试各种变通方法(例如声明局部变量register
),但遗憾的是,在这种情况下,保证在C / C ++中的正确行为没有好办法。为避免这个问题,请自行实施循环,如oivoodoo所暗示的那样。
答案 2 :(得分:3)
这是我的心理调试工作:
i
和this
很可能存储在堆栈中,而在386及更高版本上,机器代码可以直接引用esp
- 相对内存位置,因此编译器很可能制作像
mov eax,[esp+8]
将this
的值输入eax
寄存器。问题是你的push
操作混乱了堆栈指针,因此这些硬编码访问将在第一次迭代后访问(越来越多)错误的内存位置。
最有可能的是,没有this->someVar
的更简单的循环形式被编译器更彻底地优化,并导致机器代码仅使用寄存器而没有esp
- 相对访问,这意味着它们继续正常工作。
曾几何时,所有对局部变量和参数的内存访问都是通过ebp
寄存器完成的,该寄存器不会被内联汇编代码更改。如果你能找到一个强制使用ebp
代替esp
的编译器开关,这可能会解决你的问题。
警告:编译器不希望你弄乱堆栈 - 它希望它始终知道栈顶的位置。如果你真的想在堆栈上动态地推送东西,我建议用汇编语言完全编写循环本身oivoodoo。
答案 3 :(得分:1)
首先,可能发生的是linux下的gcc使用堆栈指针索引局部变量而不是使用堆栈帧指针。这是一个优化,允许gcc使用帧指针(x86下的BP)作为另一个通用寄存器,并避免大量设置帧的代码。帧基本上只是属于本地函数的SP和BP之间的区域。我敢打赌,如果你包含一个调用alloca的大小,你传入这个函数它会变得更好,因为它会迫使编译器不进行这种优化。
话虽如此,该错误确实存在于您的代码中。除非你真的知道你在做什么,否则你永远不应该退出内联asm,其堆栈指针与你输入内联asm时的不同。编译器几乎总是认为它们只拥有堆栈指针。它们依赖于它保持不变,以便它们可以使用它来查找存储变量的位置。 你也应该远离帧指针(BP)。
可以捣乱的时间很少见,通常用于上下文切换代码(从一个线程或进程更改为另一个)。
答案 4 :(得分:0)
如果您知道args数量的限制,您可以使用具有该数量的参数的单个函数调用来调用它,将您的实际参数对齐到最后。
警告:x86_64 abi使用寄存器来处理某些参数,这会打破这个和你的代码。