Layout vtable errato per la classe esportata dalla DLL:richiesta di chiarimenti riguardanti le intestazioni e la costruzione di vtable

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

Domanda

Sebbene il problema in questione sia risolto, mi lascia un po' confuso su quali dati vengono utilizzati per costruire i vtables per una classe e dove è archiviato il layout per il vtable.Se qualcuno potesse darmi chiarimenti o indicarmi qualche informazione che possa saziare la mia curiosità, gli sarei molto grato.

Sfondo

  1. Due progetti VC6.0 separati:uno per un exe, uno per una dll.
  2. L'applicazione contiene file .lib, .dll e .h dalle versioni del progetto dll.
  3. Quando una nuova versione è pronta, i file .lib, .dll e .h lo sono copiato nel progetto exe dal progetto dll.
  4. Ho ereditato questo schema.

Problema:

Recentemente ho apportato modifiche alla DLL e ho copiato i file .lib e .dll ma ho dimenticato di copiare i file di intestazione.C'erano stati alcuni cambiamenti nella gerarchia e, di conseguenza, la vtable per una classe particolare (chiamiamola InternalClass) è cambiato.

L'exe lo fa non invocare direttamente alcun metodo su InternalClass.Invece, crea un'istanza di una classe diversa (chiamatela InterfaceClass) che incapsula un puntatore a an InternalClass oggetto e invoca vari metodi contro quel puntatore.

In fase di esecuzione, chiamate effettuate dall'interno InterfaceClass metodi a InternalClass i metodi stavano effettivamente invocando il metodo metodi sbagliati (cioè. InterfaceClass invocherebbe InternalClass::A E InternalClass::B correrebbe davvero).Guardando il asm, risulta che la correzione o il thunk (mi scuso se questo è il gergo sbagliato!) utilizzava il corretto compensare nella vtable. Tuttavia, la vtable stessa conteneva l'elenco di puntatori che ti aspetteresti da vecchio file di intestazione.

Domanda reale:

Mi sono reso conto del mio errore, ho copiato i file di intestazione, ho ricompilato e tutto è andato bene. Tuttavia, mi rimane una domanda fastidiosa:

Quando viene determinato il layout per le vtables e quali informazioni vengono utilizzate per costruire la vtable per queste classi in fase di runtime?Cioè, mi sembra che il corretto vtable deve essere stato assemblato quando la DLL è stata compilata in modo che gli offset, ecc. potessero essere utilizzati per le chiamate da InterfaceClass A InternalClass.Perché, allora, è stata utilizzata una vtable obsoleta in fase di runtime?È la disposizione Anche determinato separatamente quando l'exe viene compilato utilizzando le intestazioni che ha?

Non sono sicuro che tutto ciò sia chiaro. Fammi sapere se quello che chiedo è troppo contorto.Grazie!

È stato utile?

Soluzione

Hmm, dettagli di implementazione pesanti di un compilatore di 12 anni.Quando usi __declspec(dllexport) su una classe, il linker esporta i membri della classe.Non il vtable.Questo viene ricostruito nel client della classe quando il compilatore analizza la dichiarazione della classe nel file di intestazione.Il linker compila la vtable locale con gli indirizzi dei membri esportati.

Funzionerebbe meglio se il compilatore esportasse semplicemente il vtable dalla DLL?Forse, ma esportare tali dettagli di implementazione è terribilmente fragile.La paura di metterti in un angolo da cui non puoi uscire a causa della retrocompatibilità è una forte motivazione, immagino.

Altri suggerimenti

Il modo in cui descrivi l'interazione di queste due classi non è del tutto chiaro su un dettaglio molto importante:Il tuo è il tuo nome? InternalClass viene esportato dalla DLL o no?O per porre la domanda in un modo diverso:Il codice in questi file di intestazione copiati utilizza i puntatori a InternalClass o è Tutto conoscenza di quella classe nascosta esclusivamente all'interno della DLL?

Se la classe in questione è effettivamente completamente nascosta nella DLL, non sono sicuro di come possa verificarsi questo problema.Ma da quello che descrivi, sembra davvero così InternalClass viene esposto dalla dll.

Se le intestazioni che vengono copiate contengono codice simile nel concetto a questo:

class InterfaceClass
{
    InternalClass* Internal;

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

Poi il InternalClass deve essere esposto, altrimenti il ​​progetto dell'exe non riuscirebbe a compilare l'intestazione.(Quindi non è proprio interno, dopo tutto.)

E se è così, allora spiega facilmente il tuo problema.L'exe viene creato rispetto a una diversa dichiarazione di InternalClass rispetto a quello con cui è stata creata la dll.Ciò significa che ognuno compila la propria versione della tabella virtuale e tali versioni sono diverse.

Quindi, in fase di esecuzione, al codice exe viene consegnato un oggetto creato dall'interno della DLL, e quindi con il suo puntatore vtable che punta alla versione della DLL di vtable.Il codice exe tenta quindi di utilizzare il puntatore vtable come se puntasse alla versione exe di vtable.E ovviamente questo causa problemi.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top