Seite 1 von 1

[Visual C++ x64] Konstruktor via Funktionszeiger aufrufen

Verfasst: 17.03.2013, 18:59
von Krishty
Hi,

In Fortsetzung von Artificial Minds alter Frage speziell für Visual Studio 2010/2012 x64:

Ich kenne die Adresse eines Kopierk’tors; habe seinen Parameter; und den Ort, an dem ich eine Kopie erzeugen will. Wie rufe ich den Kopierk'tor auf?

Zunächst einmal ist bei der Signatur zu beachten: this als unsichtbarer erster Parameter; Referenz auf das Original als zweiter Parameter. Unter Visual C++ geben außerdem alle Konstruktoren implizit einen Zeiger auf die neue Instanz als Rückgabewert zurück:

    typedef void * (CopyConstructor)(void * pThis, void const * pThat);
(Die Referenz als Zeiger zu übergeben ist legitim, da Referenzen in Visual C++’ ABI als Zeiger realisiert werden.)

Den Kopierk'tor aufzurufen wird allerdings den Stack zerschießen, da der Aufruf keiner bekannten Calling Convention folgt:

    myCopyCtor(pDestination, pSource);
    // Ab hier ist alles kaputt!


Die x86-CRT bietet Quelltext für einen vernünftigen Aufruf:

    __declspec(naked) void __stdcall _CallMemberFunction1(
        void *pthis,   // Value for 'this' pointer
        void *pmfn,    // Pointer to the member function
        void *pthat    // Value of 1st parameter (type assumes copy ctor)
    ) {
        __asm {
            pop    eax        // Save return address
            pop    ecx        // Get 'this'
            xchg   [esp],eax  // Get function address, stash return address
            jmp    eax        // jump to the function (function will return to caller of this func)
        }
    }

(Die 1 am Namensende steht dafür, dass dieser Kopierk'tor einen Parameter gibt. Es gibt scheinbar auch welche mit zweien.)

Leider ist für die x64-CRT kein entsprechender Quelltext mitgeliefert. Was der Compiler erzeugt, ist:

    mov    rdx,[pSource]
    mov    rcx,qword ptr [pDestination]
    call  qword ptr [myCopyCtor]
    jmp    (Ende)


Aber Inline Assembly gibt es unter Visual C++ x64 nicht.

Und was mache ich jetzt um aufzurufen?

Re: [Visual C++ x64] Konstruktor via Funktionszeiger aufrufe

Verfasst: 17.03.2013, 19:04
von Artificial Mind
Krishty hat geschrieben: Aber Inline Assembly gibt es unter Visual C++ x64 nicht.
Kannst du dann nicht einfach eine eigene Datei für die Assembler-Teile anlegen und daraus aufrufen?
Also nicht Inline Assembler, sondern ganz normale Assembler-Module.

Re: [Visual C++ x64] Konstruktor via Funktionszeiger aufrufe

Verfasst: 17.03.2013, 19:32
von Krishty
Ich habe ehrlich gesagt Angst vor Prolog, Epilog, und Frame Unwinding. Da die Funktion kein Leaf ist, müsste das ja dann von Hand gemacht werden …

… ich suche gerade, ob ich einen Sprung zu einem Funktionszeiger nicht auch mit Intrinsics hinbekomme. Ich begreife auch nicht, wo der Fehler liegt, weil die Maschinenbefehle des normalen typedef-Aufrufs sehr ähnlich aussehen:

    mov    rdx,qword ptr [pSource]
    mov    rcx,qword ptr [pDestination]
    call   qword ptr [myCopyCtor]


Nachtrag: Das Problem ist, dass der K’tor RCX überschreibt. Hmm.

Re: [Visual C++ x64] Konstruktor via Funktionszeiger aufrufe

Verfasst: 17.03.2013, 20:34
von dot
Ich komm leider nicht umhin zu fragen: Wofür genau brauchst du das? xD

Re: [Visual C++ x64] Konstruktor via Funktionszeiger aufrufe

Verfasst: 17.03.2013, 20:43
von Krishty
http://zfx.info/viewtopic.php?f=9&t=307&p=36840#p36840

Wird eine User-Type-Ausnahme geworfen und by-value gefangen, muss das Objekt kopiert werden. Dafür gibt mir der Compiler einen Zeiger auf den Kopierk'tor und ein paar Zusatzinformationen wie die Größe des Objekts.

Re: [Visual C++ x64] Konstruktor via Funktionszeiger aufrufe

Verfasst: 17.03.2013, 20:52
von dot
Notlösung: Bytecode in Array packen, nach void (*)() casten und aufrufen :>

Re: [Visual C++ x64] Konstruktor via Funktionszeiger aufrufe

Verfasst: 17.03.2013, 20:53
von Krishty
Sieht so aus, als ob ich irgendwo zwischen der 4. und 6. Indirektion einen Zeiger vertauscht hätte. Jedenfalls kann ich auch alles krachen lassen, wenn ich den K'tor nicht aufrufe, sondern einfach memset() auf meinen Zielbereich. Yeah. Ich melde mich zurück, sobald es was Neues gibt.