【问题标题】:How to define an exported symbol's address如何定义导出符号的地址
【发布时间】:2014-07-29 13:35:32
【问题描述】:

假设我正在编写一个 C++ 库,它必须导出一个变量、一个数据结构,例如,称为ExpData。因此,与我的库链接的程序可以访问此变量(有一个公共标头将其定义为extern void *ExpData[])。

但是,这个数据结构在内部是 C++ 类的 vtable。例如,类的名称是InternalType。于是,查看生成的汇编代码后,发现InternalType的vtable导出为_ZTV12InternalType

然后,我需要一种方法来使我的库导出变量ExpData 解析为与_ZTV12InternalType 相同的地址,这样,当外部程序读取我的库的ExpData 变量时,它实际上正在读取@987654329 @的vtable。

澄清一下,InternalType 的 vtable 汇编代码是:

    .type   _ZTV12InternalType,@object # @_ZTV12InternalType
    .section    .data.rel.ro._ZTV12InternalType,"aGw",@progbits,_ZTV12InternalType,comdat
    .weak   _ZTV12InternalType
    .align  16
_ZTV12InternalType:
    .quad   0
    .quad   _ZTI12InternalType
    .quad   _ZN12InternalType8vMethodXEi
    .size   _ZTV12InternalType, 24

所以,我需要一种方法来实现这一点(或其他具有相同效果的方法):

    .type   ExpData,@object
    .globl  ExpData

    .type   _ZTV12InternalType,@object
    .section    .data.rel.ro._ZTV12InternalType,"aGw",@progbits,_ZTV12InternalType,comdat
    .weak   _ZTV12InternalType
    .align  16
_ZTV12InternalType:
ExpData:
    .quad   0
    .quad   _ZTI12InternalType
    .quad   _ZN12InternalType8vMethodXEi
    .size   _ZTV12InternalType, 24

在C++端有可能吗?

P.S.:我知道我不应该依赖与实现相关的细节,例如名称修饰和 C++ 类内部数据,而只是考虑我的库将在非常特定的环境中运行。

编辑

我可以通过将--defsym ExpData=_ZTV12InternalType 传递给链接器来解决我的问题。但是,我不想将实现细节附加到外部资源。假设我决定将类的 vtable 映射为名为 InternalTypeVTable 的 C 结构。所以,我可以将ExpData 声明为InternalTypeVTable ExpData;。如果我只需要更改源文件,而不是生成文件和链接器脚本,那就太好了。

【问题讨论】:

  • 你不使用标题吗?
  • @ColeJohnson 是的,但是标头应该声明变量,而不是定义它。所以,我需要一种方法来将我的变量定义为编译器生成结构的入口点。
  • 所以你想做一个粗略的反射形式就是我所看到的?你的问题对我来说并不完全清楚。
  • @ColeJohnson 这不完全是反射。它可以是任何编译器生成的数据,而不仅仅是 C++ 类。如果我有编译器生成的结构的符号名称,我需要一种将变量与其关联的方法。我需要用相同的地址解决它们。
  • 您应该使用ld 链接器脚本在特定地址手动生成符号。

标签: c++ c gcc assembly linker


【解决方案1】:

GCC 的 __attribute__ ((alias())) 正是我需要的原因。

如果我声明 ExpData 喜欢:

void *ExpData[0] __attribute__ ((alias("_ZTV12InternalType")));

在我的库的源文件中,我得到以下汇编代码:

    .globl  ExpData
ExpData = _ZTV12InternalType

因此,导出的符号ExpData 引用的内存地址与_ZTV12InternalType 相同。

【讨论】:

  • 是的,但为什么要void**? vtable 不是指针,它更像是一个数组。
  • @n.m.是的,vtable 实际上类似于数组。但是,如果我要将它声明为一个数组,我应该定义它的长度。因为我不知道它的确切长度,所以我决定将它声明为一个指针,这样我就可以像访问数组一样访问 vtable 条目(ExpData[0]ExpData[1]ExpData[2]、. ..)。所以,其实没关系。
  • “我决定将它声明为一个指针,这样我就可以像访问数组一样访问 vtable 条目”。你不能。 ExpData驻留在与vtable的第一个元素相同的地址,初始化为0。它不包含vtable的第一个元素的地址.它包含 0。所以 ExpData[0] 将是一个空指针取消引用。您可以从程序集文件中提取 vtable 大小。你看到三个quad 声明了吗?它们本质上就是构成 vtable 的三个指针。
  • @n.m.你是绝对正确的。我回答你的时候没注意。如果我使用ExpData 作为指针,尝试访问任何元素都会导致分段错误,因为存储在指针变量中的地址为NULL(如您所说,vtable 的第一个元素是0)。因此,声明ExpData 的最佳方式确实是void *ExpData[]。实际上,要定义它,我什至不需要使用确切的长度。我可以将其定义为void *ExpData[0]。这足以像访问内存区域的起点(而不是指向内存区域的指针)一样访问它。非常感谢!
  • @n.m.当我说我不知道​​数组大小时,我的意思是如果编译器决定以其他方式存储数据,它可能会改变(尽管如果编译器改变,甚至 vtable 本身也可能消失,因为它是实现细节......)
猜你喜欢
  • 2018-03-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-30
  • 2011-08-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多