【发布时间】:2016-05-02 17:54:59
【问题描述】:
我在 C 中手动构建 vtable。当从 DLL 导出时,它们会在其重定位表中生成大量条目。
示例objdump 输出:
Virtual Address: 00002000 Chunk size 24 (0x18) Number of fixups 8
reloc 0 offset 0 [2000] HIGHLOW
reloc 1 offset 4 [2004] HIGHLOW
reloc 2 offset 8 [2008] HIGHLOW
reloc 3 offset c [200c] HIGHLOW
reloc 4 offset 10 [2010] HIGHLOW
reloc 5 offset 14 [2014] HIGHLOW
reloc 6 offset 18 [2018] HIGHLOW
reloc 7 offset 1c [201c] HIGHLOW
有没有办法摆脱它们,或者它们是 Windows 上的唯一方法?
以下是我目前的发现:
- 在 Visual Studio 的
link中,有一个选项/FIXED(这正是我想要的) - 有this tuturial,但大部分似乎仅适用于Linux 下的
gcc - 我可以在没有
-shared的情况下构建DLL,而是设置--image-base
最后一个确实有效(没有生成 .reloc 部分),但我认为这是一个极其丑陋的 hack,因为它实际上不再是 DLL。
澄清:
我的印象是,这个问题之所以被否决,是因为人们发现搬迁是件好事。 我承认,总的来说他们很好,但我有一个非常具体的目标。我想展示如何在 O(1) 中实现使用 vtable 的动态多态性,如下所示:
struct IDerived {
union {
IBaseA asBaseA;
struct {
int (*foo)(Derived this); // inherited from BaseA
...
};
};
union {
IBaseB asBaseB;
struct {
int (*bar)(Derived this); // inherited from BaseB
...
};
};
int (*baz)(Derived this);
...
};
struct Derived {
const IDerived *iface;
void *data;
};
extern void doSthWithBaseB(BaseB x);
void doSthWithDerived(Derived x) {
x.iface->foo(x);
doSthWithBaseB((BaseB){ &x.iface->asBaseB, x.data }) // high-level cast
}
由于“高级转换”只涉及指针算术,所以这是 O(1)(尤其是不像 Java 中那样进行线性搜索)。
现在回到重定位:无论成本有多低,它恰好是 O(n),因为每个类中的每个方法都需要更新。叹息。
tl;dr
微软的/FIXED 是否有 GCC 的挂件?如果不是,要在 PE 中设置哪些标志来实现所需的行为?
【问题讨论】:
-
为什么投反对票?请在评论中解释,以便我改进问题。
-
您假设这些不是必需的。你为什么这么认为?您如何检查您的假设是否正确?
-
@KubaOber 我认为这就是
-pie所做的,创建一个“与位置无关的可执行文件”。在我看来,这不适用于 .rdata 部分中的函数指针。但这正是我的问题(我已经对其进行了编辑以使其更清楚)。 -
为什么要摆脱搬迁?在 Windows 目标上,
-pie选项在很大程度上被忽略了。它与您想要的完全相反,它告诉链接器为可执行文件(.EXE)创建重定位。通常 Windows 上的可执行文件不可重定位,重定位允许它像 DLL 一样重定位。通常 DLL 确实有重定位,Visual Studio 选项/FIXED创建没有它们的 DLL,防止 DLL 被重定位。如果某些内容已经位于必须加载 DLL 的地址,这将阻止 Windows 加载 DLL。 -
几个选项:(1.) 静态构建(如果许可证允许)- 不需要 PIC。 (2.) 使用 -fvisibility=hidden 编译,然后标记需要使用属性可见性默认值显式导出的符号(我忘记了语法)。 (3.) 标记您不想导出的符号,隐藏属性可见性。或 (4.) 使用链接器脚本和映射文件
标签: c gcc mingw portable-executable relocation