pimpl idiom обычно используется для того, чтобы разрешить изменение кода в динамически связанных библиотеках без нарушения совместимости с ABI и перекомпиляции всего кода, который зависит от библиотеки.
В большинстве объяснений я вижу, что добавление новой переменной частного члена изменяет смещения открытых и закрытых членов класса. Это имеет смысл для меня. Я не понимаю, как на практике это фактически разрушает зависимые библиотеки.
Я много читал в файлах ELF и о том, как работает динамическая компоновка, но я все еще не вижу, как изменение размера класса в общей библиотеке могло бы сломать вещи.
например. Вот тестовое приложение (a.out), которое я написал, которое использует код (Interface::some_method
) из тестовой общей библиотеки (libInterface.so):
[email protected]:~/pimpl$ objdump -d -j .text a.out
08048874 <main>:
...
8048891: e8 b2 fe ff ff call 8048748 <[email protected]>
В вызове some_method
используется таблица процедур (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>
который затем переходит в глобальную таблицу смещения (GOT), где содержится адрес 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 ....
И вот тогда динамический компоновщик работает своей магией и просматривает все символы, содержащиеся в общих библиотеках в LD_LIBRARY_PATH, находит Interface::some_method
в libInterface.so и загружает свой код в GOT, поэтому последующие вызовы на some_method
, код в GOT на самом деле является сегментом кода из общей библиотеки.
Или что-то в этом роде.
Но, учитывая вышеизложенное, я до сих пор не понимаю, как здесь используется общий размер класса lib или его смещения метода. Насколько я могу судить, описанные выше шаги не зависят от размера класса. Похоже, что только символ имя метода в библиотеке включен в a.out. Любые изменения в размере класса должны быть разрешены во время выполнения, когда компоновщик загружает код в GOT, нет?
Что мне здесь не хватает?