添加私有成员变量如何破坏C ++ ABI兼容性? [英] How does adding a private member variable break C++ ABI compatibility?

查看:93
本文介绍了添加私有成员变量如何破坏C ++ ABI兼容性?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

pimpl习惯用法通常用于允许在动态链接库中更改代码而不会破坏ABI兼容性,并且重新编译所有依赖库的代码.

The pimpl idiom is commonly used in order to allow changing code in dynamically linked libraries without breaking ABI compatibility and having to recompile all the code that depends on the library.

大多数说明 我看到提到添加一个新的私有成员变量会更改公共和私有成员的偏移量.班级.这对我来说很有意义.我不明白的是,实际上这是如何打破依赖库的.

Most of the explanations I see mention that adding a new private member variable changes the offsets of public and private members in the class. That makes sense to me. What I don't understand is how in practice this actually breaks the dependent libraries.

我已经阅读了很多有关ELF文件的内容,以及动态链接的实际工作方式,但是我仍然看不到如何更改共享库中的类大小会破坏事情.

I've done a lot of reading on ELF files and how dynamic linking actually works, but I still don't see how changing the class size in the shared lib would break things.

例如这是我编写的一个测试应用程序(a.out),它使用来自测试共享库(libInterface.so)的代码(Interface::some_method):

E.g. Here is a test application (a.out) I wrote that uses code (Interface::some_method) from a test shared library (libInterface.so):

aguthrie@ana:~/pimpl$ objdump -d -j .text a.out 
08048874 <main>:
...
 8048891:   e8 b2 fe ff ff          call   8048748 <_ZN9Interface11some_methodEv@plt>

some_method的调用使用过程链接表(PLT):

The call to some_method uses the Procedural Linkage Table (PLT):

aguthrie@ana:~/pimpl$ objdump -d -j .plt a.out 

08048748 <_ZN9Interface11some_methodEv@plt>:
 8048748:   ff 25 1c a0 04 08       jmp    *0x804a01c
 804874e:   68 38 00 00 00          push   $0x38
 8048753:   e9 70 ff ff ff          jmp    80486c8 <_init+0x30>

随后进入包含地址0x804a01c的全局偏移表(GOT):

which subsequently goes to the Global Offset Table (GOT) where address 0x804a01c is contained:

aguthrie@ana:~/pimpl$ readelf -x 24 a.out 

Hex dump of section '.got.plt':
  0x08049ff4 089f0408 00000000 00000000 de860408 ................
  0x0804a004 ee860408 fe860408 0e870408 1e870408 ................
  0x0804a014 2e870408 3e870408 4e870408 5e870408 ....>...N...^...
  0x0804a024 6e870408 7e870408 8e870408 9e870408 n...~...........
  0x0804a034 ae870408                            ....

然后这是动态链接器发挥作用的地方,并浏览LD_LIBRARY_PATH中共享库中包含的所有符号,在libInterface.so中找到Interface::some_method并将其代码加载到GOT中,以便随后调用,GOT中的代码实际上就是共享库中的代码段.

And then this is where the dynamic linker works its magic and looks through all the symbols contained in the shared libs in LD_LIBRARY_PATH, finds Interface::some_method in libInterface.so and loads its code into the GOT so on subsequent calls to some_method, the code in the GOT is actually the code segment from the shared library.

或者类似的东西.

但是鉴于以上所述,我仍然不了解共享库的类大小或其方法偏移量如何在这里发挥作用.据我所知,上述步骤与班级人数无关.看起来好像库中方法的符号名称包含在a.out中.类大小的任何变化都应该在链接程序将代码加载到GOT时在运行时解决,对吗?

But given the above, I still don't understand how the shared lib's class size or its method offsets come into play here. As far as I can tell, the steps above are agnostic to the class size. It looks like only the symbol name of the method in the library is included in a.out. Any changes in class size should just be resolved at runtime when the linker loads the code into the GOT, no?

我在这里想念什么?

推荐答案

主要问题是,当您分配类的新实例(在堆栈上或通过new)时,调用代码需要知道物体的大小.如果以后更改对象的大小(通过添加私有成员),则会增加所需的大小;但是您的呼叫者仍在使用旧大小.因此,您最终没有分配足够的空间来容纳该对象,并且该对象的构造函数随后破坏了堆栈(或堆),因为它假定它具有足够的空间.

The main problem is that, when you allocate a new instance of a class (either on the stack, or via new), the calling code needs to know the size of the object. If you later change the size of the object (by adding a private member), this increases the size needed; however your callers are still using the old size. So you end up not allocating enough space to hold the object, and the object's constructor then proceeds to corrupt the stack (or heap), because it assumes it has enough space.

此外,如果您有任何内联成员函数,则它们的代码(包括成员变量的偏移量)可能会内联到调用代码中.如果在末尾以外的任何地方添加私有成员,则这些偏移量将是不正确的,还会导致内存损坏(注意:即使在末尾添加,大小仍然不匹配也是一个问题).

Additionally, if you have any inline member functions, their code (including offsets to member variables) may be inlined into the calling code. If you add private members anywhere other than the end, these offsets will be incorrect, also leading to memory corruption (note: even if you add to the end, the size mismatch is still a problem).

这篇关于添加私有成员变量如何破坏C ++ ABI兼容性?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆