سؤال

مقدمة

اسمحوا لي أن أعتذر مقدمًا عن السؤال الطويل. إنه قصير بقدر ما أستطيع أن أصنعه ، وهو ، للأسف ، ليس قصيرًا جدًا.

نصب

لقد حددت واجهتين ، 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 (انظر الكود أدناه). هذا الحل يعمل في كلتا الحالتين:

  1. روابط التطبيق الرئيسية مقابل المضيف SO.
  2. يقوم التطبيق الرئيسي بتحميل المضيف حتى باستخدام 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));
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top