【问题标题】:How to manually mangle names in Visual C++?如何在 Visual C++ 中手动修改名称?
【发布时间】:2016-06-20 15:30:47
【问题描述】:

如果我在 .c 中有一个函数,比如

void foo(int c, char v);

...在我的 .obj 中,这变成了一个名为

的符号
_foo

...根据 C 名称修改规则。如果我在 .cpp 文件中有类似的函数,则根据编译器特定的名称修改规则,这完全是另外一回事。 msvc 12 会给我们这个:

?foo@@YAXHD@Z

如果我在 .cpp 文件中有该函数 foo 并且我希望它使用 C 名称修改规则(假设我可以不重载),我们可以将其声明为

extern "C" void foo(int c, char v);

...在这种情况下,我们又回到了过去

_foo

...在 .obj 符号表中。

我的问题是,是否可以反过来?如果我想用一个 C 函数来模拟 C++ 名称修改,使用 gcc 会很容易,因为 gcc 的名称修改规则只使用标识符友好的字符,因此 foo 的修改名称变为 _ZN3fooEic,我们可以轻松编写

void ZN3fooEic(int c, char v);

回到微软编译器领域,我显然无法创建名称是完全无效标识符的函数

void ?foo@@YAXHD@Z(int c, char v);

...但我仍然希望该函数在 .obj 符号表中显示该符号名称。

有什么想法吗?我查看了 Visual C++ 支持的编译指示,但没有发现任何有用的东西。

【问题讨论】:

  • @mustafagonul 我不想破坏 - 我想破坏。
  • 即使你能做到这一点,你也必须以某种方式匹配 ABI。你在这棵树上叫错了树。为此使用 extern "C" 并接受限制。
  • @mustafagonul 你不太明白这一点。我知道损坏和未损坏的名称是什么。我想在 C 文件中伪造 C++ 名称修改。
  • 任何名称修改将保持一致的假设也不是一个好的假设。你只是在要求人们在几年内恨你。
  • ... 更不用说众所周知微软会不时更改 VSC++ 的 ABI。顺便说一句,它不完全是cdecl,而是cdecl 的扩展。我的意思是,C++ ABI 需要处理名称修改、异常、虚拟表以及所有这些时髦的东西。

标签: c++ c visual-c++ gcc linker


【解决方案1】:

您可以使用__identifier

#include <stdio.h>

#pragma warning(suppress: 4483)
extern "C" void __cdecl __identifier("?foo@@YAXHD@Z")(int c, char v)
{
    printf("%d %c\n", c, v);
}

void __cdecl foo(int, char);

int main()
{
    foo(10, 'x');
}

【讨论】:

  • 您有任何理由相信这确实有效吗?我刚试了一下,它只接受C++关键字,不接受任意字符串。
  • @TurkeyMan 它有效。 #pragma warning(suppress: 4483) 是必需的。如果你忽略它,你会得到warning C4483: syntax error: expected C++ keyword,默认情况下这是一个错误。
  • @JamesMcNellis 这正是我想要的。尽管 KemyLand 的方法很有创意,但我想我会将公认的答案转换为这个答案(当然,同时对这两个出色的答案进行投票)。
  • 作为参考,在 GCC 中你可以 use the asm keyword ... 尽管最近的 GCC 版本直接允许在 C 中使用普通 C++ 规则的“重载”函数。
  • 另外,这个答案看起来像 C++ 代码? (extern "C" 并且没有文件内标识符)
【解决方案2】:

你是对的。这不是(直接)可能的(注意:永远不要相信 VSC++)。但是,如果你真的需要这个,有一个很好的解决方法。首先,在C++文件中...

extern "C" int proxy(int i, char c);

int foo(int i, char c)
{
    return proxy(i, c);
}

那么,在C文件中……

int proxy(int i, char c)
{
    // Do whatever you wanna do here
}

根本不需要输入任何错误的名称,您现在可以调用foo 函数,它实际上只是C 函数proxy 的一个包装器。从 C++ 的角度来看,这会给您带来与 proxy 实际上是 foo 相同的效果。这里唯一的惩罚当然是一个快速的“n”脏函数调用。如果 ABI 允许,并且编译器足够智能,则可以将其替换为单个 JMP x86 指令。

另一种方法是在 C 中编写函数 foo,然后使用 MinGW 的 objcopy 来重命名符号...

$ objcopy --redefine-sym "foo=?foo@@YAXHD@Z" foobar.obj

我不确定仅使用 VSC++ 工具是否可行。无论如何,这将是非常不稳定、不可移植和hacky。

【讨论】:

  • 这不是我想要的,原因有很多与函数序言有关,但它可能是唯一的解决方案。
  • @TedMiddleton:查看编辑。实际上还有另一种解决方案。
  • 在 mingw 中使用 objcopy 实际上听起来是一个不错的解决方案。谢谢!
  • 请参阅James McNellis's answer 了解 Visual C++ 中的正确方法(更简单)。 James 是 VC++ 编译器团队的一员。
【解决方案3】:

您可以使用 .DEF 文件使其工作。 在 foo.cpp 中定义你的函数:

void foo(int c, char v) { ... } 

然后将def file 传递给链接器,如下所示:

LIBRARY mylib
EXPORTS
   ?foo@@YAXHD@Z=_foo

免责声明:未经测试,我可能会遗漏一些细节。

【讨论】:

  • 我目前只能访问MSVC2008,这个解决方案不起作用。基本上,链接器将 def 文件中的“@”解释为特殊字符,因此导出的符号只有“?foo”。也许这在更新版本的编译器中有所改变。
  • 我实际上也在 MSVC2013 上尝试过这个,但它不起作用 - 正如你所说,它在第一个 '@' 字符处截断。您可能认为这是因为链接器将 @ 字符与序数字段混淆了,但事实并非如此 - 当我有一个像“?foo@@YAXHD@Z=_foo @24”这样的符号时,我得到了?foo 但它仍然得到正确的序数,24。
猜你喜欢
  • 2011-06-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-22
  • 2012-11-03
  • 2021-04-11
  • 2017-11-28
  • 1970-01-01
相关资源
最近更新 更多