CPP, Zugriff bzw. überladen von "->" Member Access Operators

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Antworten
Walker
Beiträge: 41
Registriert: 28.07.2017, 08:58
Alter Benutzername: Walker

CPP, Zugriff bzw. überladen von "->" Member Access Operators

Beitrag von Walker »

ich kämpfe mit dem Überladen von "->" . Hintergrund dafür ist das ich mitbekommen möchte wer in dem sehr vielen Legacy-Code (virtuelle) Methoden aufruft. Ich habe nur Zugriff auf die Basisklasse. Die Basisklasse ist gut 20 Jahre alt , also um Designen alter Strukturen ist nicht möglich.

Ich hätte erwartet das alle Situationen einen Log auswerfen, aber nur zwei der Zugriffe loggen.:

Code: Alles auswählen

	pOB->a = 22; 
	pOB->b = 345;
	pOB->c = 22345;
        pOB->Logtest();//Log access
       (*pOB).a = 3;
       (*pOB)->b = 7; //C access 

Hier das Beispiel:
https://godbolt.org/z/rsvT3v5s6

Code: Alles auswählen

#include <iostream>
#include <cstdio>
using namespace std;

class COperTest
{
public:
	long a,b;
    static long c;

	COperTest()
	{
		a=5;
		b=7;
	}
	COperTest & operator *()
	{
		printf("A access %d\n",__LINE__);
		return *this;
	}
	COperTest * operator->() const
	{
		printf("B access %d\n",__LINE__);
		return  const_cast<COperTest*>(this);
	}
	COperTest * operator->()
	{
		printf("C access %d\n",__LINE__);
		return this;
	}
    void Logtest()
    {
        printf("Log access %d\n",__LINE__);
    }

};

long COperTest::c = 33;


void Test()
{
    printf("Test %d\n",__LINE__); //Test 
	COperTest op;
	COperTest *pOB = &op;
	pOB->a = 22; 
	pOB->b = 345;
	pOB->c = 22345;
    pOB->Logtest();//Log access
    (*pOB).a = 3;
    (*pOB)->b = 7; //C access 


}



int main()
{
    cout<<"Hello World" <<endl;
    Test();
    printf("\n\nReady\n");
    return 0;
}
Über sachdienliche Hinweise würde ich mich freuen.
DerAlbi
Establishment
Beiträge: 269
Registriert: 20.05.2011, 05:37

Re: CPP, Zugriff bzw. überladen von "->" Member Access Operators

Beitrag von DerAlbi »

Ich weiß, du willst eine Programmier-Lösung, aber: setze einen Daten-Breakpoint auf die vtable der Klasse...?
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: CPP, Zugriff bzw. überladen von "->" Member Access Operators

Beitrag von Krishty »

Funktionieren Daten-Breakpoints auch für bloße Zugriffe, statt nur für Veränderung?!
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
DerAlbi
Establishment
Beiträge: 269
Registriert: 20.05.2011, 05:37

Re: CPP, Zugriff bzw. überladen von "->" Member Access Operators

Beitrag von DerAlbi »

Wie sau. Das ist Hardware-Unterstützt von der CPU. Für den GDB gibt es z.B. "rwatch" für lesend-zugriff und "awatch" für r/w-zugriff. Andere Debugger können das sicher auch.
Man muss nur die vtable-Adresse rausfinden, das sagt einem aber auch der Debugger. Wenn man wirklich ein Log haben will, könnte man die vtable hacken/umleiten und von der injizierten Funktion aus die Originalfunktion aufrufen, nachdem man fein geloggt hat.
Man hätte dann sogar direkt den Call-Ursprung irgendwo in den Registern stehen (oder im Stack?) - wie man die Code-Adresse in Datei und Zeilennummer umwandelt.... hmmh.
Und wie man in read-only-Speicher rumschreibt... weiß hier sicher jemand, stimmts, Krishty?
Mirror
Establishment
Beiträge: 248
Registriert: 25.08.2019, 05:00
Alter Benutzername: gdsWizard
Kontaktdaten:

Re: CPP, Zugriff bzw. überladen von "->" Member Access Operators

Beitrag von Mirror »

Ich glaube das Verhalten ist korrekt. Probiere mal den "->" global zu überladen. Ich habe es zwar nicht ausprobiert, aber einen Versuch wäre es wert.
Hat den StormWizard 1.0 und 2.0 verbrochen. http://www.mirrorcad.com
Alexander Kornrumpf
Moderator
Beiträge: 2106
Registriert: 25.02.2009, 13:37

Re: CPP, Zugriff bzw. überladen von "->" Member Access Operators

Beitrag von Alexander Kornrumpf »

Mirror hat geschrieben: 07.10.2021, 05:49 Ich glaube das Verhalten ist korrekt.
Es ist zumindest in sich logisch.

Nur mal kurz für das gemeinsame Verständnis, die Zeile die tatsächlich loggt ruft operator -> auf COperTest auf, die anderen Zeilen rufen es auf COperTest* auf. Überladen ist nur ersteres. Korrekt?
Mirror
Establishment
Beiträge: 248
Registriert: 25.08.2019, 05:00
Alter Benutzername: gdsWizard
Kontaktdaten:

Re: CPP, Zugriff bzw. überladen von "->" Member Access Operators

Beitrag von Mirror »

Alexander Kornrumpf hat geschrieben:Überladen ist nur ersteres.
Genau das habe ich gemeint. Mit überladen für den globalen Scope meine ich so wie man einen Vektor negiert.

COperTest * operator -> ( COperTest *pObject ) // oder ( CoperTest * & rObject )
{
return pObject;
}

Ist frei von der Leber geschrieben, kann also noch Buggy sein !!! Aber als Ansatzpunkt ...
Hat den StormWizard 1.0 und 2.0 verbrochen. http://www.mirrorcad.com
Walker
Beiträge: 41
Registriert: 28.07.2017, 08:58
Alter Benutzername: Walker

Re: CPP, Zugriff bzw. überladen von "->" Member Access Operators

Beitrag von Walker »

Mirror hat geschrieben: 07.10.2021, 08:22
Alexander Kornrumpf hat geschrieben:Überladen ist nur ersteres.
Genau das habe ich gemeint. Mit überladen für den globalen Scope meine ich so wie man einen Vektor negiert.

COperTest * operator -> ( COperTest *pObject ) // oder ( CoperTest * & rObject )
{
return pObject;
}

Ist frei von der Leber geschrieben, kann also noch Buggy sein !!! Aber als Ansatzpunkt ...
Grob ist die Fehlermeldung bei beiden Varianten:
error: overloaded 'operator->' must have at least one parameter of class or enumeration type
Mirror
Establishment
Beiträge: 248
Registriert: 25.08.2019, 05:00
Alter Benutzername: gdsWizard
Kontaktdaten:

Re: CPP, Zugriff bzw. überladen von "->" Member Access Operators

Beitrag von Mirror »

Habe es eben auch ausprobiert. Bei mir meckert er "operator -> muss ein nicht statischer Member sein". Geht also nicht. Naja, wäre zumindest im Bereich des möglichen gewesen
Hat den StormWizard 1.0 und 2.0 verbrochen. http://www.mirrorcad.com
Benutzeravatar
Schrompf
Moderator
Beiträge: 4838
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: CPP, Zugriff bzw. überladen von "->" Member Access Operators

Beitrag von Schrompf »

Wenn ich das Godbolt-Beispiel richtig lese, fehlt Dir der 'B'-Text? Und der kann doch gar nicht kommen, weil der im const-Overload des Pfeil-Operators kommt. Und der wird nicht aufgerufen, Du machst -> nur auf nicht-konstanten Objekten.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Walker
Beiträge: 41
Registriert: 28.07.2017, 08:58
Alter Benutzername: Walker

Re: CPP, Zugriff bzw. überladen von "->" Member Access Operators

Beitrag von Walker »

Im Prinzip überlade ich den "->" Operator oben für die Klasse COperTest so das folgendes dann geht:
COperTest xyz;
xyz->a = 5;

Das würde auch den Zugriff loggen. Aber der Build-In oder Internal Operator -> des Pointers von COperTest kann ich wohl nicht überladen.
Benutzeravatar
Schrompf
Moderator
Beiträge: 4838
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: CPP, Zugriff bzw. überladen von "->" Member Access Operators

Beitrag von Schrompf »

Ahso. Stimmt.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: CPP, Zugriff bzw. überladen von "->" Member Access Operators

Beitrag von Krishty »

DerAlbi hat geschrieben: 07.10.2021, 02:07Wie sau. Das ist Hardware-Unterstützt von der CPU. Für den GDB gibt es z.B. "rwatch" für lesend-zugriff und "awatch" für r/w-zugriff. Andere Debugger können das sicher auch.
Geil! Wieder was gelernt.
Man muss nur die vtable-Adresse rausfinden, das sagt einem aber auch der Debugger. Wenn man wirklich ein Log haben will, könnte man die vtable hacken/umleiten und von der injizierten Funktion aus die Originalfunktion aufrufen, nachdem man fein geloggt hat.
Du musst aber die vtables aller abgeleiteten Klassen umleiten. Schließlich geht der Aufruf nicht durch den Basis-vtable.
Man hätte dann sogar direkt den Call-Ursprung irgendwo in den Registern stehen (oder im Stack?) - wie man die Code-Adresse in Datei und Zeilennummer umwandelt.... hmmh.
Falls wir von Windows sprechen, sind das bloß ein paar Zeilen, ja. Das Stack Layout ist ab Windows x64 fest definiert und Backtrace wird vom Kernel angeboten; Umwandlung der Adressen in Funktionsnamen und Zeilennummern von der DbgHelp-API.
Und wie man in read-only-Speicher rumschreibt... weiß hier sicher jemand, stimmts, Krishty?
Wäre einfacher, das readonly-Segment im Linker temporär auf R/W umzuleiten :P
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Lord Delvin
Establishment
Beiträge: 574
Registriert: 05.07.2003, 11:17

Re: CPP, Zugriff bzw. überladen von "->" Member Access Operators

Beitrag von Lord Delvin »

Ist die vtable nicht immer bei *this?
Dürfte aber nicht für Mehrfachvererbung gehen.

Kann dir das die IDE nicht einfach zeigen? Oder hast du kein Projekt mit einer IDE, die die callees einer Funktion findet? Oder sind das einfach zu viele uninteressante?
Ich habe öfter mal so Probleme, wo man zwar 50-60 Callees anschauen muss, es aber trotzdem schneller ist, als live zu schauen, weil du bei letzterem das Problem hast, dass du ja irgendwie sicherstellen musst, dass du dabei an allen Callees vorbei gekommen bist.
XML/JSON/EMF in schnell: OGSS
Keine Lust mehr auf C++? Versuche Tyr: Get & Get started
joggel

Re: CPP, Zugriff bzw. überladen von "->" Member Access Operators

Beitrag von joggel »

@injizierte Funktion, vtable hacken und co.
Mal ne blöde Frage.
Ich habe bestimmt nicht alles verstanden, aber bezüglich dieser Aussage:
Man muss nur die vtable-Adresse rausfinden, das sagt einem aber auch der Debugger. Wenn man wirklich ein Log haben will, könnte man die vtable hacken/umleiten und von der injizierten Funktion aus die Originalfunktion aufrufen, nachdem man fein geloggt hat.
Ich denke hier ist debugging gemeint..
Würde sowas auch funktionieren, wenn ich sowas im meinem Programm selbst implementiere?
Also, ich habe eine Instanz einer Klasse und will in einer bestimmten Funktion ein Logging haben ohne den SourceCode der Klasse anzufassen. Also...quasi hacken. So könnte man ja zB prima Logger verwenden können.

Ich rede jetzt aber nicht von einer separaten EXE, sondern sozusagen eine extra Funktionalität im selben C++-Projekt
Also... ich hoffe, es konnte verstanden werden was ich meine?

Würde das irgendwie funktionieren? Gibt es evtl dazu irgendwelche Links?
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: CPP, Zugriff bzw. überladen von "->" Member Access Operators

Beitrag von Krishty »

Lord Delvin hat geschrieben: 07.10.2021, 15:46Ist die vtable nicht immer bei *this?
Dürfte aber nicht für Mehrfachvererbung gehen.
Nein, bei this sitzt nur ein Zeiger auf die vtable. a) hinge sonst die Konstruktionsgeschwindigkeit einer Instanz von der Anzahl ihrer virtuellen Funktionen ab; b) möchte man die vtables möglichst in read-only-Speicher sehen, weil C++ keinen Use Case hat, in dem man sie zur Laufzeit verändern können sollte.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: CPP, Zugriff bzw. überladen von "->" Member Access Operators

Beitrag von Krishty »

joggel hat geschrieben: 08.10.2021, 08:29Würde das irgendwie funktionieren? Gibt es evtl dazu irgendwelche Links?
Ja; beim Cheaten in Spielen dürfte das regelmäßig gemacht werden.

Hier gibt's Beispiel-Code; habe aber nicht seine Qualität bewertet: https://stackoverflow.com/questions/314 ... t-function
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Alexander Kornrumpf
Moderator
Beiträge: 2106
Registriert: 25.02.2009, 13:37

Re: CPP, Zugriff bzw. überladen von "->" Member Access Operators

Beitrag von Alexander Kornrumpf »

joggel hat geschrieben: 08.10.2021, 08:29 @injizierte Funktion, vtable hacken und co.
Mal ne blöde Frage.
Ich habe bestimmt nicht alles verstanden, aber bezüglich dieser Aussage:
Man muss nur die vtable-Adresse rausfinden, das sagt einem aber auch der Debugger. Wenn man wirklich ein Log haben will, könnte man die vtable hacken/umleiten und von der injizierten Funktion aus die Originalfunktion aufrufen, nachdem man fein geloggt hat.
Ich denke hier ist debugging gemeint..
Würde sowas auch funktionieren, wenn ich sowas im meinem Programm selbst implementiere?
Also, ich habe eine Instanz einer Klasse und will in einer bestimmten Funktion ein Logging haben ohne den SourceCode der Klasse anzufassen. Also...quasi hacken. So könnte man ja zB prima Logger verwenden können.

Ich rede jetzt aber nicht von einer separaten EXE, sondern sozusagen eine extra Funktionalität im selben C++-Projekt
Also... ich hoffe, es konnte verstanden werden was ich meine?

Würde das irgendwie funktionieren? Gibt es evtl dazu irgendwelche Links?
Die Sache ist ja die, wenn du diese Funktionalität per Design haben willst und nicht im Nachhinein als Workaround in Legacy, dann würdest du es nicht über die vtable machen, sondern mit irgendwas was die Sprache tatsächlich unterstützt.

Eine Möglichkeit das in C++ zu realisieren, die ziemlich nahe an der ersten Idee von Walker ist, wäre einen smart-pointer anzubieten, der loggen kann, denke ich.
joggel

Re: CPP, Zugriff bzw. überladen von "->" Member Access Operators

Beitrag von joggel »

Ja, ich weiß.
Ich fragte auch nur eher interessehalber :)
DerAlbi
Establishment
Beiträge: 269
Registriert: 20.05.2011, 05:37

Re: CPP, Zugriff bzw. überladen von "->" Member Access Operators

Beitrag von DerAlbi »

Also das vtable-hacking ist von meiner Seite aus schon so gedacht, dass man das selbst implementiert.
1) Du lässt dir einfach die Adresse einer virtuellen Funktion geben
2) Du musst den Funktionspointer selbst devirtualisieren (Member-Function-Pointer auf virtuelle funktionen sind irgendwie nicht die Funktion selbst und irgendwie auch doppelt so groß wie ein normaler Pointer - dafür gibts aber Lösungen, die man googeln kann)
3) Du suchst nach dem Pointer in den vtables und ersetzt ihn durch eine loggende Funktion mit gleicher Signatur und Aufrufkonvention (this-call?)
4) Du loggst und reichst die Parameter an die Originalfunktion weiter.

Das ist eine Code-Lösung, die im nachhinein in gewissen grenzen geht. Scheitern tut man, wenn aufgrund von Optimierung der Compiler gewisse Calls direkt devirtualisiert, aber wenn man die Optimierung runterschraubt, sollte das klappen.

Ich kann nicht sagen, dass das alles sehr einfach ist. Das ist weder Portabel noch elegant - viel mehr "interessant". Aber wenn es einmal läuft, ist es für den Zweck sicher ok. Man muss halt am Anfang viel mit dem Debugger arbeiten und einige Crashs in kauf nehmen.. aber ich denke, dass man dabei eine ganze Menge lernt.
joggel

Re: CPP, Zugriff bzw. überladen von "->" Member Access Operators

Beitrag von joggel »

Wäre definitiv mal interessant das auszuprobieren...
Benutzeravatar
Lord Delvin
Establishment
Beiträge: 574
Registriert: 05.07.2003, 11:17

Re: CPP, Zugriff bzw. überladen von "->" Member Access Operators

Beitrag von Lord Delvin »

Krishty hat geschrieben: 08.10.2021, 08:44
Lord Delvin hat geschrieben: 07.10.2021, 15:46Ist die vtable nicht immer bei *this?
Dürfte aber nicht für Mehrfachvererbung gehen.
Nein, bei this sitzt nur ein Zeiger auf die vtable. a) hinge sonst die Konstruktionsgeschwindigkeit einer Instanz von der Anzahl ihrer virtuellen Funktionen ab; b) möchte man die vtables möglichst in read-only-Speicher sehen, weil C++ keinen Use Case hat, in dem man sie zur Laufzeit verändern können sollte.
So meinte ich das nicht.
Eher bei Offset 0 des real repräsentierenden Structs, d.h. mit load(this) bekommst du den VTable Pointer.
Also zumindest die für die aktuelle Sicht auf das Objekt, oder ist Mehrfachvererbung nicht mehr mit this-Pointer-Adjustment?
XML/JSON/EMF in schnell: OGSS
Keine Lust mehr auf C++? Versuche Tyr: Get & Get started
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: CPP, Zugriff bzw. überladen von "->" Member Access Operators

Beitrag von Krishty »

Du hast recht; ich habe den Deref-Operator übersehen! Ja; Mehrfachvererbung verändert noch immer this. AFAIK werden dafür extra Thunks erzeugt. Die vftable zeigt dann also noch nicht einmal auf die finale Funktion, sondern nur auf den Thunk, der this verändert und dann auf die Zielfunktion weiterleitet. In dem Fall funktioniert das Überschreiben der vtable also nur mit großem Aufwand …
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
DerAlbi
Establishment
Beiträge: 269
Registriert: 20.05.2011, 05:37

Re: CPP, Zugriff bzw. überladen von "->" Member Access Operators

Beitrag von DerAlbi »

Neeeeein, es ist nicht soooohh kompliziert. Du musst eh händisch devirtualisieren und dafür gibt es pro Compiler Google-Code.
Beim Devirtualisieren musst wird man eh aus der vtable lesen und wenn man sich den Code anschaut, kann man in dem Moment, wo man den finalen Pointer liest, auch einfach direkt einen anderen finalen Pointer hinschreiben.
Das Devirtualisieren ist vermutlich auch recht einfach reverse-enigneerbar via godbolt, indem man mit minimaler Optimierung einen virtuellen Member-Function-Pointer-Call auslöst. Das wird nicht mehr als ein paar zeilen ASM generieren die man in C++ hinschreiben muss.
Hat man einmal den finalen Pointer, kann man einfach alle vtables (das ganze Datensegment dafür) durchsuchen und entsprechend alle Matches umleiten.
Antworten