يفشل Dynamic_cast عند استخدامه مع dlopen/dlsym
-
23-09-2019 - |
سؤال
مقدمة
اسمحوا لي أن أعتذر مقدمًا عن السؤال الطويل. إنه قصير بقدر ما أستطيع أن أصنعه ، وهو ، للأسف ، ليس قصيرًا جدًا.
نصب
لقد حددت واجهتين ، A و B:
class A // An interface
{
public:
virtual ~A() {}
virtual void whatever_A()=0;
};
class B // Another interface
{
public:
virtual ~B() {}
virtual void whatever_B()=0;
};
بعد ذلك ، لدي مكتبة مشتركة "Testc" تقوم ببناء كائنات من الفئة C ، وتنفيذ كل من A و B ، ثم تمرير المؤشرات إلى واجهة A الخاصة بهم:
class C: public A, public B
{
public:
C();
~C();
virtual void whatever_A();
virtual void whatever_B();
};
A* create()
{
return new C();
}
أخيرًا ، لدي مكتبة مشتركة ثانية "TestD" ، والتي تأخذ ملف A*
كمدخلات ، ويحاول إلقاءها على B*
, ، استخدام dynamic_cast
void process(A* a)
{
B* b = dynamic_cast<B*>(a);
if(b)
b->whatever_B();
else
printf("Failed!\n");
}
أخيرًا ، لدي تطبيق رئيسي ، يمر A*
بين المكتبات:
A* a = create();
process(a);
سؤال
إذا قمت بإنشاء طلبي الرئيسي ، وربط المكتبات "Testc" و "TestD" ، فكل شيء يعمل كما هو متوقع. ومع ذلك ، إذا قمت بتعديل التطبيق الرئيسي لعدم الارتباط مع "Testc" و "testd" ، ولكن بدلاً من ذلك قم بتحميله في وقت التشغيل باستخدام dlopen
/dlsym
, ، ثم dynamic_cast
فشل.
لا افهم لماذا. أي أدلة؟
معلومة اضافية
- تم اختباره مع GCC 4.4.1 ، libc6 2.10.1 (Ubuntu 9.10)
- رمز مثال متوفر
المحلول
لقد وجدت إجابة سؤالي هنا. كما أفهمها ، أحتاج إلى إتاحة TypeInfo في "Testc" للمكتبة "TestD". للقيام بذلك عند استخدام dlopen()
, ، هناك شيئان إضافيان يجب القيام به:
- عند ربط المكتبة ، مرر الرابط
-E
الخيار ، للتأكد من تصدير جميع الرموز إلى القابل للتنفيذ ، وليس فقط تلك التي لم يتم حلها فيها (لأنه لا يوجد شيء) - عند تحميل المكتبة مع
dlopen()
, ، أضف الRTLD_GLOBAL
الخيار ، للتأكد من تصدير الرموزtestc
متاحة أيضا لtestd
نصائح أخرى
بشكل عام ، لا يدعم GCC RTTI عبر حدود dlopen. لديّ خبرة شخصية في هذا العبث/التقطه ، لكن مشكلتك تبدو أكثر من نفس الشيء. للأسف ، أخشى أنك بحاجة إلى التمسك بالأشياء البسيطة عبر dlopen.
يجب أن أضيف إلى هذا السؤال منذ أن واجهت هذه المشكلة أيضًا.
حتى عند توفير -Wl,-E
واستخدام RTLD_GLOBAL
, ، لا يزال Dynamic_casts فشل. ومع ذلك ، المرور -Wl,-E
في ارتباط التطبيق الفعلي أيضًا ، وليس فقط في المكتبة قد تم إصلاحه.
إذا لم يكن لدى أحد أي سيطرة على مصدر التطبيق الرئيسي ، فإن -WL ، -e لا ينطبق. تمرير -wl ، -e إلى الرابط أثناء بناء الثنائيات الخاصة (المضيف So والمكونات الإضافية) لا تساعد أيضًا. في حالتي ، كان الحل الوحيد للعمل هو تحميل وتفريغ مضيفتي ، لذا من وظيفة _init للمضيف ، لذا باستخدام علامة RTLD_GLOBAL (انظر الكود أدناه). هذا الحل يعمل في كلتا الحالتين:
- روابط التطبيق الرئيسية مقابل المضيف SO.
- يقوم التطبيق الرئيسي بتحميل المضيف حتى باستخدام DLOPEN (بدون RTLD_GLOBAL).
في كلتا الحالتين ، يتعين على المرء اتباع التعليمات التي ذكرها وايكي الرؤية.
إذا قام المرء بتوصيل رموز المكون الإضافي والمضيف مرئيًا لبعضهما البعض (باستخدام #Pragma GCC Push/Pop أو السمة المقابلة) ويقوم بتحميل الإضافات (من المضيف So) باستخدام RTLD_GLOBAL 1. ستعمل أيضًا دون تحميل وتفريغ الخاص بك SO (كما ذكر الرابط الوارد أعلاه). هذا الحل يجعل 2. أيضا العمل الذي لم يكن كذلك من قبل.
// get the path to the module itself
static std::string get_module_path() {
Dl_info info;
int res = dladdr( (void*)&get_module_path, &info);
assert(res != 0); //failure...
std::string module_path(info.dli_fname);
assert(!module_path.empty()); // no name? should not happen!
return module_path;
}
void __attribute__ ((constructor)) init_module() {
std::string module = get_module_path();
// here the magic happens :)
// without this 2. fails
dlclose(dlopen(module.c_str(), RTLD_LAZY | RTLD_GLOBAL));
}