RTLD_LOCAL и dynamic_cast в Linux - PullRequest
4 голосов
/ 19 мая 2011

У нас есть плагин, который состоит из нескольких общих библиотек в нашем приложении, которые мы должны обновить во время работы приложения.По соображениям производительности мы загружаем и начинаем использовать новый плагин перед выгрузкой старого плагина, и только когда все потоки завершены с использованием старого плагина, мы выгружаем его.Поскольку библиотеки нового плагина и старого плагина имеют одинаковые символы, мы dlopen() с RTLD_LOCAL.Если новый плагин не вызовет из внутренних функций случайно символы из старого плагина.

Одна библиотека плагина делает dynamic_cast() объекту, который был создан другой библиотекой плагина.Это работает в HP-UX, AIX, Solaris и Windows, но не в Linux.Насколько я понимаю, это потому, что все эти ОС (компиляторы) используют имя класса для сравнения типов (в dynamic_cast()), но Linux использует адреса строк имен для этого сравнения (для повышения производительности), и поскольку каждая библиотекаимеет свой собственный объект type_info (так как он был загружен с RTLD_LOCAL), адреса разные, и поэтому равные типы, кажется, не равны dynamic_cast().

Есть ли способ сделать один изследующее:

  • Сделать так, чтобы загружались только объекты type_info, как если бы было указано RTLD_GLOBAL.
  • Заставить компилятор использовать сравнение имен классов вместо type_info адресов для сравнениямежду типами.

?Компилятор, который мы используем:

$ icpc -V
Intel(R) C++ Intel(R) 64 Compiler XE for applications running on Intel(R) 64, Version 12.0.0.084 Build 20101006
Copyright (C) 1985-2010 Intel Corporation.  All rights reserved.

Ответы [ 2 ]

4 голосов
/ 24 мая 2011

Хорошо, мы наконец обошли проблему.

Мы добавили к классам, которые мы хотим dynamic_cast() две статические функции:

static MyClass* doNew();
static MyClass* doDynCast(MyBase*);

Они были реализованы в файле cpp, который содержал объекты new, dynamic_cast() и type_info в одной и той же библиотеке и, таким образом, заставлял dynamic_cast() обойти проблему.

Этого решения было достаточно для нашего конкретного случая, но если у кого-то есть более общее решение, оно будет приветствоваться.

Другой найденный нами вариант - поместить всю реализацию класса в файл cpp, чтобы символ typeinfo присутствовал только в одной библиотеке, а все остальные библиотеки ссылаются только на него. Это приводит к успешному dynamic_cast().

2 голосов
/ 25 мая 2011

К сожалению, поскольку структуры type_info являются слабыми символами, локальными для библиотеки, которая их создает, сделать работу dynamic_cast нелегко. Вы можете попытаться манипулировать, где создается экземпляр класса vtable (и type_info); в GCC это можно сделать, обеспечив первую не встроенную функцию в классе (в порядке определения) , определенную только в общей общей библиотеке зависимостей. Если в вашем классе нет не встроенных функций, создайте фиктивную функцию, чтобы заставить это поколение произойти. Обратите внимание, что я не проверял это и поэтому не могу гарантировать, что это будет работать. Кроме того, это зависит от компилятора; Я не знаю, что делает компилятор Intel.

Конечно, вы могли бы реализовать свой собственный альтернативный механизм динамического приведения, используя вместо этого имена классов; Есть ряд библиотек, которые делают это, например, Qt's qobject_cast .

...