繁体   English   中英

C ++内联汇编(Intel编译器):LEA和MOV在Windows和Linux中的行为不同

[英]C++ inline assembly (Intel compiler): LEA and MOV behaving differently in Windows and Linux

我正在转换一个巨大的Windows dll以在Windows和Linux上均可使用。 该dll具有许多用于视频处理的程序集(和SS2指令)。

现在,使用Windows上的Intel ComposerXE-2011和Linux上的Intel ComposerXE-2013 SP1中包含的Intel编译器,可以在Windows和Linux上很好地编译代码。

但是,在尝试调用函数指针时,执行会在Linux中崩溃。 我在gdb中跟踪了代码,实际上函数指针没有指向所需的函数(而Windows中是)。 几乎所有其他东西都可以正常工作。

这是代码序列:

...
mov    rdi, this
lea    rdx, [rdi].m_sSomeStruct
...
lea    rax, FUNCTION_NAME                # if replaced by 'mov', works in Linux but crashes in Windows
mov    [rdx].m_pfnFunction, rax
...
call   [rdx].m_pfnFunction               # crash in Linux

哪里:

1)'this'具有结构成员m_sSomeStruct。

2)m_sSomeStruct具有成员m_pfnFunction,它是指向函数的指针。

3)FUNCTION_NAME是同一编译单元中的自由函数。

4)所有这些纯汇编函数都声明为裸函数。

5)64位环境。

最让我感到困惑的是,如果我用“ mov”指令替换应该将函数地址加载到rax的“ lea”指令,则在Linux上可以正常工作,但在Windows上会崩溃。 我在Visual Studio和gdb中都跟踪了代码,显然在Windows中“ lea”给出了正确的函数地址,而在Linux中“ mov”给出了正确的地址。

我尝试查看Intel汇编参考,但是在这里没有什么帮助(除非我没有在正确的位置查找)。

任何帮助表示赞赏。 谢谢!


编辑更多详细信息:

1)我尝试使用方括号

lea    rax, [FUNCTION_NAME]

但这并没有改变Windows和Linux中的行为。

2)我在gdb和Windows中查看了反汇编程序,似乎都给出了与我实际编写的相同的指令。 更糟糕的是,我尝试将lea / mov放在另一个位置,当我在gdb中反汇编它们时,在指令后的#号后面打印了地址(我假设这是将要发送的地址)。被存储在寄存器中)实际上是相同的,并且不是该函数的正确地址。

在gdb反汇编器中看起来像这样

lea  0xOffset1(%rip), %rax   # 0xSomeAddress
mov  0xOffset2(%rip), %rax   # 0xSomeAddress

两者(SomeAddress)相同,并且两个偏移量因lea和mov指令之间的相同差异而偏移,但是不知何故,当我在每次执行后检查寄存器的内容时​​, mov似乎输入了正确的值! !!

3)成员变量m_pfnFunction的类型为LOAD_FUNCTION,其定义为

typedef void (*LOAD_FUNCTION)(const void*, void*);

4)函数FUNCTION_NAME在.h(在命名空间内)声明为

void FUNCTION_NAME(const void* , void*);

并在.cpp中实现为

__declspec(naked) void namespace_name::FUNCTION_NAME(const void* , void*)
{
...
}

5)我尝试通过添加来关闭优化

#pragma optimize("", off)

但我仍然有同样的问题

临时而言,我怀疑链接到DLL的方式在后一种情况下有效,因为FUNCTION_NAME是实际上将被设置为函数的加载地址的内存位置。 也就是说,它是对函数的引用(或指针),而不是入口点。

我熟悉Win(而不是其他),并且我已经知道如何调用函数

(1)生成对该地址的CALL,该地址在链接时填写。 对于同一模块中的函数来说,足够正常,但是如果在链接时发现它位于不同的DLL中,则导入库是一个存根,链接器将其视为与任何正常函数相同,但仅不过是JMP [? ??]。 导入函数的地址表安排有一些字节,这些字节在将保存该地址的字段之前编码一个JMP指令。 该表在DLL加载时填充。

(2)如果编译器知道该函数将在其他DLL中,则它可以生成更有效的代码:将间接CALL编码为导入表中的地址。 (1)中显示的存根函数具有与其关联的符号名称,并且包含地址的实际字段也具有符号名称。 它们都以函数命名,但是具有不同的“装饰”。 通常,程序可能包含对两者的修正引用。

因此,我推测您使用的符号名称与一个编译器上的存根函数匹配,并且(以类似的方式工作)与另一平台上的指针匹配。 也许汇编程序会根据未声明的名称将其分配给另一个,这取决于它是否被声明为已导入,并且两个工具链上的选项有所不同。

希望能有所帮助。 我想您可以在调试器中查看运行时,看看上面的内容是否可以帮助您解释地址及其周围的内容。

在阅读了mov和lea之间的区别之后,这里的LEA指令的目的是什么? 在我看来,在Linux上,函数指针中又增加了一个间接级别。 mov指令会导致传递额外的间接级别,而在没有该额外间接的Windows上,您将使用lea

您是否有机会在Linux上使用PIC进行编译? 我可以看到添加了额外的间接层。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM