循环内内联汇编

发布于 2024-08-18 09:30:39 字数 846 浏览 4 评论 0原文

我在一个项目中大量使用内联汇编,在该项目中我需要在编译时调用具有未知数量参数的函数,而我自己设法让它工作,有时,在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 循环中,因此避免它不是一个选择。

我还尝试使用内联汇编词“易失性”,但没有任何改变。

I use inline assembly massively in a project where I need to call functions with an unknown number of arguments at compile time and while I manage myself to get it to work, sometimes, in linux (in windows I don't recall having that problem) strange things like this happen:

If I have something like

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

It works.

If I have

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

and I guarantee with my life that someVar is holding the value 1 it throws segmentation fault.

Also if I have

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

it works but

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

does not.

Also, and also strangely, I can say that while in some functions I don't have problems doing that in others I have, all in the same object.

If someone can point me to some information that can clear up what's the problem there, I would appreciate.

Beware that I really have to push the arguments in a for loop so avoiding it is not an option.

I also tried using the inline assembly word "volatile" but nothing changed.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

巨坚强 2024-08-25 09:30:39

我无法理解问题是什么,但尝试使用与

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

...

相同的清晰asm代码编写代码退出:

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

I can't understand what's the problem but try to write code using clear asm code same as

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

...

exit:

Also try to make "var" value as static may it help too.

浮云落日 2024-08-25 09:30:39

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

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

Examine the disassembly. The most likely cause is that i and/or the variables holding the end value are being refetched from a fixed offset on the stack at each iteration of the for loop, and your push offsets the stack pointer from where the compiler expected it to be and so causes the wrong values to be fetched.

You could attempt various workarounds (e.g. declaring the local variables register), but unfortunately there is no good way to guarantee correct behaviour in C/C++ in this case. To avoid the problem, implement the loop yourself, as oivoodoo suggests.

忘东忘西忘不掉你 2024-08-25 09:30:39

这是我的心理调试工作:

ithis很可能存储在堆栈上,并且在386及更高版本上,机器代码可以引用esp - 直接相对内存位置,因此编译器很可能会生成诸如

mov eax,[esp+8]

this 的值获取到 eax 寄存器之类的指令。问题在于您的 push 操作会扰乱堆栈指针,因此这些硬编码访问将在第一次迭代后访问(越来越多)错误的内存位置。

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

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

警告:编译器不希望您弄乱堆栈——它希望它始终知道堆栈顶部在哪里。如果您确实想动态地将内容推送到堆栈上,我建议完全用汇编语言编写循环本身,如 oivoodoo 已完成。

Here's my psychic debugging effort:

i and this are most likely stored on the stack, and on the 386 and up, machine code can refer to esp-relative memory locations directly, so the compiler may well produce instructions like

mov eax,[esp+8]

to get the value of this into the eax register. The problem is that your push operations mess with the stack pointer, so these hard coded accesses will access (increasingly) wrong memory locations after the first iteration.

Most likely, the simpler loop forms without this->someVar are optimised more thoroughly by the compiler and result in machine code that uses only registers and no esp-relative accesses, meaning they continue to work fine.

Once upon a time, all memory accesses to local variables and arguments were done via the ebp register, which is not changed by your inline assembly code. If you can find a compiler switch to force the use of ebp instead of esp, this may solve your problem.

Warning: the compiler does not expect you to mess with the stack -- it expects that it knows at all times where the top-of-stack is. If you really want to dynamically push things on the stack, I would suggest writing the loop itself completely in assembly language as oivoodoo has done.

维持三分热 2024-08-25 09:30:39

首先,可能发生的情况是 Linux 下的 gcc 使用堆栈指针来索引局部变量,而不是使用堆栈帧指针。这是一项优化,允许 gcc 使用帧指针(x86 下的 BP)作为另一个通用寄存器,并避免大量设置帧的代码。框架本质上就是属于局部函数的 SP 和 BP 之间的区域。我敢打赌,如果您包含对 alloca 的调用以及传递给该函数的大小,那么一切都会变得更好,因为它会强制编译器不进行此优化。

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

可以搞乱这些的情况很少见,通常是在上下文切换代码(从一个线程或进程更改为另一个线程或进程)之类的情况下。

First, what is probably happening is that gcc under linux is using the stack pointer to index your local variables rather than using the stack frame pointer. This is an optimization that allows gcc to use the frame pointer (BP under x86) as another general purpose register and avoid lots of code that sets up frames. Frames are essentially just the area between SP and BP that belong to the local function. I'll bet that if you included a call to alloca with a size that you passed into this function it would all get better because it would force the compiler to not do this optimization.

That being said, the bug is really in your code. Unless you really know what you're doing you should never exit an inline asm with the stack pointer different than what it was when you entered the inline asm. Compilers almost always think that they exclusively own the stack pointer. They depend on it staying the same so that they can use it to find where they have stored variables.
You should also stay away from the frame pointer (BP).

The times when it is ok to mess with those are rare and usually for things like context switching code (changing from one thread or process to another).

浅笑轻吟梦一曲 2024-08-25 09:30:39

如果您知道参数数量的限制,则可以使用该数量的参数通过单个函数调用来调用它,从而将实际参数对齐到末尾。

警告:x86_64 abi 使用寄存器来存储某些参数,这也会破坏此代码和您的代码。

If you know the limit to the number of args, you can just call it with a single function call with that number of arguments, aligning your actual arguments up to the end.

Warning: the x86_64 abi uses register for some parameters, which breaks this and your code too.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文