El pimpl idiom se usa comúnmente para permitir el cambio de código en bibliotecas vinculadas dinámicamente sin romper la compatibilidad ABI y tener que volver a compilar todo el código que depende de la biblioteca.¿Cómo la adición de una variable de miembro privado rompe la compatibilidad de C++ ABI?
La mayoría de los explanations Veo mencionar que agregar una nueva variable de miembro privado cambia las compensaciones de los miembros públicos y privados de la clase. Eso tiene sentido para mí. Lo que no entiendo es cómo en la práctica esto realmente rompe las bibliotecas dependientes.
He leído mucho sobre archivos ELF y cómo funcionan realmente los enlaces dinámicos, pero todavía no veo cómo cambiar el tamaño de clase en la biblioteca compartida podría romper las cosas.
E.g. Aquí es una aplicación de prueba (a.out) escribí que utiliza código (Interface::some_method
) a partir de una biblioteca compartida de prueba (libInterface.so):
[email protected]:~/pimpl$ objdump -d -j .text a.out
08048874 <main>:
...
8048891: e8 b2 fe ff ff call 8048748 <[email protected]>
La llamada a some_method
utiliza la tabla de vinculación procesal (PLT):
[email protected]:~/pimpl$ objdump -d -j .plt a.out
08048748 <[email protected]>:
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>
que posteriormente va a la Tabla Global Offset (GOT) donde está contenida la dirección 0x804a01c:
[email protected]:~/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 ....
Y entonces aquí es donde el enlazador dinámico wo muestra su magia y examina todos los símbolos contenidos en las bibliotecas compartidas en LD_LIBRARY_PATH, encuentra Interface::some_method
en libInterface.so y carga su código en GOT, por lo que en llamadas posteriores al some_method
, el código en GOT es en realidad el segmento de código del archivo compartido biblioteca.
O algo así.
Pero dado lo anterior, todavía no entiendo cómo el tamaño de clase de lib compartido o sus desplazamientos de método entran en juego aquí. Por lo que puedo decir, los pasos anteriores son independientes del tamaño de la clase. Parece que solo el símbolo del método en la biblioteca está incluido en a.out. Cualquier cambio en el tamaño de clase debería resolverse en tiempo de ejecución cuando el vinculador carga el código en el GOT, ¿no?
¿Qué me falta aquí?
Aha! Lo tengo. De hecho, mirando un poco más en el desmontaje antes de que se llame al ctor de Interface, puedo ver que asigna el espacio (en este caso 4bytes) para el objeto. – adg