【发布时间】:2021-04-04 12:52:12
【问题描述】:
我有以下简单但令人沮丧的问题:我正在使用 g++ 编译器,但我用作示例的代码是为与 Visual Studio 一起使用而编写的。通常不是问题,但它们的内联汇编器工作方式不同,因此需要更改代码。
有问题的截图如下:
float someotherfuncname(inputvar)
{
//For example purposes. Can be arbitrary amount of math.
return 1;
}
void __declspec(naked)funcname(void)
{
__asm
{
push ecx
call someotherfuncname
pop ecx
retn
}
}
我在这个话题上没有经验,基本上我正在精心复制一个更有经验的编码员编写的神秘公式,希望能重现结果(在其他部分取得了成功,但这个让我明白了)。
据我所知,主要问题是:
-g++ asm 没有“retn”。
-"call" 使用来自周围 c++ 程序的东西。没有extendend asm,g++ asm 不能轻易使用c++ 的东西。 __declspec(naked) 阻止我使用扩展 asm。
-我不知道“retn”是做什么的,所以我不能用等效的代码来伪造它。
编辑:我正在使用 Windows。
目的是构建一个 .dll 供 OBSE 用于 TES Oblivion。我正在用 g++(可能是 MinGW)构建它。
(不)工作代码的最小草图是:在 .h 文件中(不是问题,只是为了完整性):
#ifndef MSK_B
#define MSK_B
#ifdef __cplusplus
extern "C" {
#endif
#ifdef DLL_KOMP
#define SPECK __declspec(dllexport)
#else
#define SPECK __declspec(dllexport)
#endif
bool SPECK OBSEPlugin_Query(const OBSEInterface * obse, PluginInfo * info);
bool SPECK OBSEPlugin_Load(const OBSEInterface * obse);
#ifdef __cplusplus
}
#endif
#endif // MMC_B_H
在 .cpp 文件中:注意,这里没有定义 OBSEInterface 和 PluginInfo,但它们不是必需的(只有一些骨架定义可以使编译器满意,所以我将它们省略了)。
include "[name of the .h file]"
include <fstream>
include "Windows.h"
typedef unsigned long UInt32
void SafeWrite8(UInt32 addr, UInt32 data)
{
UInt32 oldProtect;
VirtualProtect((void *)addr, 4, PAGE_EXECUTE_READWRITE, &oldProtect);
*((UInt8 *)addr) = data;
VirtualProtect((void *)addr, 4, oldProtect, &oldProtect);
}
void SafeWrite32(UInt32 addr, UInt32 data)
{
UInt32 oldProtect;
VirtualProtect((void *)addr, 4, PAGE_EXECUTE_READWRITE, &oldProtect);
*((UInt32 *)addr) = data;
VirtualProtect((void *)addr, 4, oldProtect, &oldProtect);
}
void WriteRelPaddedCall(UInt32 strtAddr, UInt32 retnAddr, UInt32 callbackAddr)
{
for (UInt32 i = strtAddr; i < retnAddr; i++) SafeWrite8(i,0x90);
WriteRelCall(strtAddr,callbackAddr);
}
extern "C" {
bool SPECK OBSEPlugin_Query(const OBSEInterface * obse, PluginInfo * info)
{
return true;
}
bool SPECK OBSEPlugin_Load(const OBSEInterface * obse)
{
UInt32 RuesDispAddrH = 0x005AB365;
UInt32 RuesDispAddrR = 0x005AB36D;
//RuestungECX is the function i posted above.
//This line (as implemented here) causes the game to crash when loading a game (or starting a new game).
WriteRelPaddedCall(RuesDispAddrH, RuesDispAddrR, (UInt32) &RuestungECX);
return true;
}
};
【问题讨论】:
-
g++ asm 没有“调用” - 是的,它是有效的 x86 助记符。为什么你认为它没有? (你的 g++ 尝试的minimal reproducible example)。
__attribute__((naked))函数可能是唯一一次在语句中使用它是个好主意,所以这段代码应该很容易移植。 (顺便说一句,retn只是普通的ret(近而不是远)。但实际上最好完全消除内联汇编并使用纯 C,而不是一些旧的 32 位堆栈参数调用约定肯定会使堆栈错位 16,因此即使您保持 32 位模式,它也不能移植到 Linux -
您在使用 g++ for Windows(如 MinGW、TDM)吗?其他人似乎建议您移植到 Linux,尽管我没有看到任何表明您正在离开 Windows 平台的信息。
-
我正在尝试为 OBSE(用于 TES Oblivion)编写一个插件。我不知道这些东西,我基本上是在尝试复制另一个有效插件的代码。如果我知道该函数需要做什么,我也许可以在纯 C 中重现它,但是,我不知道。
-
我对代码一无所知,但我假设在这里使用 ECX 是为了将 CDECL 调用按摩到 THISCALL 调用约定。
funcname您实际上在这里显示的是您实际尝试转换的功能吗? -
@CPPCPPCPPCPPCPPCPPCPPCPPCPPCPP:没关系,也许我应该把它作为答案发布。
标签: c++ assembly gcc visual-c++ inline-assembly