تخطيط VTABLE غير صحيح للفئة التي تم تصديرها بواسطة DLL: طلب التوضيح بخصوص الرؤوس والبناء القابل للتطبيق

StackOverflow https://stackoverflow.com/questions/3453402

سؤال

على الرغم من أن المشكلة في متناول اليد تم حلها, ، لقد جعلني أشعر بالارتباك قليلاً فيما يتعلق بالبيانات المستخدمة لإنشاء VTABLES لفئة وأين يتم تخزين تخطيط VTABLE. إذا كان بإمكان أي شخص تقديم توضيح أو يوجهني نحو بعض المعلومات التي قد تشبع فضولي ، فسأقدر ذلك كثيرًا.

خلفية

  1. مشروعان منفصلان VC6.0: واحد لـ exe ، أحدهما لـ DLL.
  2. يحتوي التطبيق على ملفات .lib و .dll و .h من إصدارات مشروع DLL.
  3. عندما يكون إصدار جديد جاهزًا ، فإن ملفات .lib و .dll و .h نسخ في مشروع EXE من مشروع DLL.
  4. لقد ورثت هذا المخطط.

مشكلة:

قمت مؤخرًا بإجراء تغييرات على DLL ونسخها على .lib و .dll لكن نسيت نسخ ملفات الرأس. كان هناك بعض التغييرات الهرمية ، ونتيجة لذلك ، فإن VTABLE لفئة معينة (نسميها InternalClass) قد تغير.

exe يفعل لا تستدعي مباشرة أي طرق InternalClass. بدلاً من ذلك ، ينشئ مثيلًا لفئة مختلفة (نسميها InterfaceClass) التي تغلف مؤشر إلى InternalClass كائن ويستدعي طرق مختلفة ضد هذا المؤشر.

في وقت التشغيل ، المكالمات من الداخل InterfaceClass طرق ل InternalClass كانت الأساليب في الواقع تستدعي طرق خاطئة (بمعنى آخر InterfaceClass سوف تستدعي InternalClass::A و InternalClass::B سوف يعمل بالفعل). أنظر إلى ASM, ، اتضح أن الإصلاح أو thunk (أنا آسف إذا كان هذا هو المصطلحات الخاطئة!) كان يستخدم الصحيح عوض في vtable. لكن, يحتوي VTABLE نفسه على قائمة المؤشرات التي تتوقعها من قديم ملفات الرأس.

السؤال الفعلي:

أدركت خطأي ، ونسخت ملفات الرأس ، وأعيد ترجمة ، وكان كل شيء على ما يرام. لكن, ، لقد تركت مع سؤال واحد مزعج:

متى يتم تحديد تخطيط VTABLES وما هي المعلومات المستخدمة لإنشاء VTABLE لهذه الفئات في وقت التشغيل؟ هذا هو ، يبدو لي أن مناسب يجب تجميع VTABLE عندما تم تجميع DLL بحيث يمكن استخدام الإزاحات ، وما إلى ذلك للمكالمات من InterfaceClass إلى InternalClass. لماذا ، إذن ، تم استخدام vtable عفا عليها الزمن في وقت التشغيل؟ هو التصميم ايضا تم تحديده بشكل منفصل عندما يتم تجميع EXE باستخدام الرؤوس التي لديها؟

لست متأكدًا مما إذا كان هذا واضحًا على الإطلاق. اسمحوا لي أن أعرف ما إذا كان ما أطلبه هو معقد للغاية. شكرًا!

هل كانت مفيدة؟

المحلول

حسنًا ، تفاصيل التنفيذ الثقيلة لمجمول مترجم يبلغ من العمر 12 عامًا. عند استخدام __declspec (dllexport) على الفصل ، يقوم الرابط بتصدير أعضاء الفصل. ليس vtable. يتم إعادة بنائها في عميل الفصل عندما يقوم المترجم بتوزيع إعلان الفصل في ملف الرأس. يملأ الرابط أن VTABLE المحلي مع عناوين الأعضاء المصدرة.

هل سيعمل بشكل أفضل إذا قام المترجم ببساطة بتصدير VTABLE من DLL؟ ربما ، لكن تصدير تفاصيل التنفيذ هذه هشة للغاية. أتصور أن الخوف من الطلاء في زاوية لا يمكنك الخروج منه بسبب التوافق المتخلف حافزًا قويًا.

نصائح أخرى

الطريقة التي تصف بها تفاعل هاتين الفئتين ليست واضحة تمامًا بتفاصيل مهمة للغاية: هل ما يسمى ما يسمى InternalClass يجري تصديرها من DLL أم لا؟ أو لوضع السؤال بطريقة مختلفة: هل يستخدم الرمز في ملفات الرأس المنسوخة مؤشرات ل InternalClass أو الكل معرفة تلك الفئة مخبأة على نحو مقلد داخل DLL؟

إذا كان هذا الفصل المعني مخفيًا تمامًا داخل DLL ، فأنا لست متأكدًا من كيفية حدوث هذه المشكلة. ولكن من ما تصفه ، يبدو الأمر كما لو InternalClass يتعرض من قبل DLL.

إذا كانت تلك الرؤوس التي يتم نسخها تحتوي على رمز مشابه في مفهوم هذا:

class InterfaceClass
{
    InternalClass* Internal;

    void DoSomething()
    {
        Internal->DoSomething();
    }
};

ثم InternalClass يجب أن يتعرض ، وإلا فإن مشروع EXE سيفشل في تجميع الرأس. (لذلك ليس داخليًا حقًا.)

وإذا كان هذا هو الحال ، فإنه يفسر مشكلتك بسهولة. يتم بناء exe ضد إعلان مختلف عن InternalClass تم بناء DLL مع. وهذا يعني أن كل يجمع نسخته الخاصة من الجدول الافتراضي ، وتلك الإصدارات مختلفة.

ثم في وقت التشغيل ، يتم تسليم رمز EXE كائن تم إنشاؤه من داخل DLL ، وبالتالي مع مؤشر VTABLE الذي يشير إلى إصدار DLL من VTABLE. يحاول رمز EXE بعد ذلك العمل من خلال هذا المؤشر القابل للتطبيق كما لو كان يشير إلى إصدار exe من VTABLE. ومن الواضح أن هذا يسبب مشاكل.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top