是否可以显式调用名称损坏的函数?
Is it possible to explicitly call a name mangled function?
假设我有一些类似
的东西
struct Foo {
void goo() {printf("Test");}
}
external void _ZN3Foo3gooEv(Foo *f);
int main() {
Foo f;
_ZN3Foo3gooEv(&f);
}
是否可以通过此处函数的名称损坏版本调用 Foo::goo()?
编辑:
澄清一下,这只是一个实验,看看是否可以显式调用名称错位的函数。这里没有进一步的目标。
我以为所有的成员函数基本上都是把this指针作为第一个参数的。
我知道这不会 link,但我不明白为什么。我认为名称重整发生在编译时,当 linker 运行时它会解析对名称重整函数的调用。 (这就是为什么我认为如果我们将 _ZN3Foo3gooEv 保留为外部,它会转到符号 table 进行查找)。
我是不是误会了什么?
可以,但有一些注意事项。
您要么必须以生成代码的方式使用成员函数,要么使其不是内联的,并且您的错位定义应该是 extern "C"
以防止“双重错位”。例如:
#include <cstdio>
struct Foo {
const char* message;
void goo();
};
void Foo::goo() {
std::printf("%s", this->message);
}
extern "C" void _ZN3Foo3gooEv(Foo *f);
int main() {
Foo f{ "Test" };
_ZN3Foo3gooEv(&f);
}
将在 gcc 中运行良好且稳定。
这是可行的,因为成员函数的调用约定等同于大多数系统上自由函数的默认调用约定。 this
被传递给成员函数,就好像它是第一个参数一样,显式参数占用后面的 arg-passing 槽。 (注册 and/or 堆栈)。我相信这对于 x86-64、至少 ARM 32 位和 64 位以及 Windows.
以外的 32 位 x86 是正确的
clang 似乎特别支持这个用例:当 gcc 假装 _ZN3Foo3gooEv
和 Foo::goo
在 mangling 之后是两个独立的实体(并且因此不能被替换和内联)。
使用 MSVC,您可以做类似的事情。但是,在 windows 上的 x86-32 代码中,使用调用约定 __thiscall
,而不是将 this
指针作为第一个参数传递,而是将其与其他参数一起传递到 ECX 寄存器中在堆栈上。如果用 clang 或 gcc 交叉编译 x86-32,你可以使用 [[gnu::thiscall]]
(__attribute__((thiscall))
)。 (如果只有一个 arg,fastcall
类似,但是有 2 个 arg 会传递寄存器中的前 2 个,而不仅仅是前 1 个)。
但确实没有理由这样做。它只能被视为编译器扩展(因为它使用 _Capital
符号),如果您需要一种从 C 调用这些函数的方法,请使用您在 C++ 翻译单元中定义的助手 void Foo_goo(struct Foo*)
.它还可以调用私有成员函数,但您已经可以使用模板特化以 standards-compliant 方式执行此操作。
假设我有一些类似
的东西struct Foo {
void goo() {printf("Test");}
}
external void _ZN3Foo3gooEv(Foo *f);
int main() {
Foo f;
_ZN3Foo3gooEv(&f);
}
是否可以通过此处函数的名称损坏版本调用 Foo::goo()?
编辑:
澄清一下,这只是一个实验,看看是否可以显式调用名称错位的函数。这里没有进一步的目标。
我以为所有的成员函数基本上都是把this指针作为第一个参数的。
我知道这不会 link,但我不明白为什么。我认为名称重整发生在编译时,当 linker 运行时它会解析对名称重整函数的调用。 (这就是为什么我认为如果我们将 _ZN3Foo3gooEv 保留为外部,它会转到符号 table 进行查找)。
我是不是误会了什么?
可以,但有一些注意事项。
您要么必须以生成代码的方式使用成员函数,要么使其不是内联的,并且您的错位定义应该是 extern "C"
以防止“双重错位”。例如:
#include <cstdio>
struct Foo {
const char* message;
void goo();
};
void Foo::goo() {
std::printf("%s", this->message);
}
extern "C" void _ZN3Foo3gooEv(Foo *f);
int main() {
Foo f{ "Test" };
_ZN3Foo3gooEv(&f);
}
将在 gcc 中运行良好且稳定。
这是可行的,因为成员函数的调用约定等同于大多数系统上自由函数的默认调用约定。 this
被传递给成员函数,就好像它是第一个参数一样,显式参数占用后面的 arg-passing 槽。 (注册 and/or 堆栈)。我相信这对于 x86-64、至少 ARM 32 位和 64 位以及 Windows.
clang 似乎特别支持这个用例:当 gcc 假装 _ZN3Foo3gooEv
和 Foo::goo
在 mangling 之后是两个独立的实体(并且因此不能被替换和内联)。
使用 MSVC,您可以做类似的事情。但是,在 windows 上的 x86-32 代码中,使用调用约定 __thiscall
,而不是将 this
指针作为第一个参数传递,而是将其与其他参数一起传递到 ECX 寄存器中在堆栈上。如果用 clang 或 gcc 交叉编译 x86-32,你可以使用 [[gnu::thiscall]]
(__attribute__((thiscall))
)。 (如果只有一个 arg,fastcall
类似,但是有 2 个 arg 会传递寄存器中的前 2 个,而不仅仅是前 1 个)。
但确实没有理由这样做。它只能被视为编译器扩展(因为它使用 _Capital
符号),如果您需要一种从 C 调用这些函数的方法,请使用您在 C++ 翻译单元中定义的助手 void Foo_goo(struct Foo*)
.它还可以调用私有成员函数,但您已经可以使用模板特化以 standards-compliant 方式执行此操作。