Visual C++-CRT ersetzen
Verfasst: 08.05.2016, 00:49
Es gibt im Netz viel zu wenig Infos dazu, also steuere ich mal alles bei, was mir so bei /NODEFAULTLIB (ohne Microsoft-CRT kompilieren) begegnet.
1. Alle CRT-Ersatzfunktionen müssen in eine eigene .cpp ohne globale Optimierungen. Nicht aus kosmetischen Gründen, sondern weil viele CRT-Funktionen als Intrinsics realisiert sind:
MyStruct x = { 0 };
ersetzt Visual C++ durch
MyStruct x;
memset(&x, 0, sizeof(x));
und der Optimizer optimiert bei kleinen structs den Funktionsaufruf weg und nutzt stattdessen direkt ein Nullregister oder so. Sowas ist nur möglich, wenn der Compiler das Verhalten einer Funktion fest eingebaut hat, und das passiert bei CRT-Funktionen oft. Wenn solche Intrinsics ersetzt werden sollen, bringt das den Optimizer durcheinander, darum kann man sie nur in einer gesonderten .cpp ohne globale Optimierung ersetzen und hat keinen Einfluss auf die Aufrufe, die geinlinet oder wegoptimiert werden.
2. new löst, selbst als Placement new, eine Referenz zu delete und type_info aus.
Sogar, wenn Exceptions und Run-Time Type Information deaktiviert sind (was mit Placement new legitimer Anwendungsfall ist). Darum muss man immer einen eigenen
void __cdecl operator delete(void *, size_t)
zur Verfügung stellen, den man halt leer macht oder so. Fragt mich bitte nicht, wo der zweite Parameter herkommt. Das weiß wohl nur Microsofts Compiler-Team. Ebenso müsst ihr
class type_info {
~type_info() { }
};
zur Verfügung stellen, weil new aus irgendeinem Grund die Virtual Function Table von type_info braucht und die nunmal aus dem Destruktor besteht. Beachtet, dass die obige Version Speicher verliert, falls ihr type_info tatsächlich nutzt und auf Namen, Hash, o.ä. zugreift. In dem Fall müsst ihr einen vollwertigen Destruktor gemäß der Dokumentation irgendwo auf einer Kaffee-bekleckerten, vollgekritzelten College-Block-Rückseite aus der MSDN entwickeln. Im Debugger *irgendeines* Visual-C++-Projekts
(type_info*)0
eingeben stellt euch ebenfalls das Layout zur Verfügung.
3. Dieses delete und type_info müssen in der selben .cpp definiert sein, in der ihr auch euer new definiert ist.
… oder alternativ in jeder .cpp, in der new vorkommt. Ihr erkennt das Muster dahinter, den Master Plan des Compilerbaus! … oder? Sonst kriegt ihr so schöne Meldungen wie
error LNK2016: absolute symbol '@comp.id' used as target of REL32 relocation in section 8D
oder
fatal error C1001: An internal error has occurred in the compiler.
4. Ihr dürft Run-Time Type nur deaktivieren, wenn type_info in der selben Datei wie new definiert ist.
Denn sonst werden eure Definitionen einfach ignoriert und es gibt
error LNK2001: unresolved external symbol "const type_info::`vftable'"
Fragt nicht.
Mehr trage ich nach, wenn mir gerade danach ist.
1. Alle CRT-Ersatzfunktionen müssen in eine eigene .cpp ohne globale Optimierungen. Nicht aus kosmetischen Gründen, sondern weil viele CRT-Funktionen als Intrinsics realisiert sind:
MyStruct x = { 0 };
ersetzt Visual C++ durch
MyStruct x;
memset(&x, 0, sizeof(x));
und der Optimizer optimiert bei kleinen structs den Funktionsaufruf weg und nutzt stattdessen direkt ein Nullregister oder so. Sowas ist nur möglich, wenn der Compiler das Verhalten einer Funktion fest eingebaut hat, und das passiert bei CRT-Funktionen oft. Wenn solche Intrinsics ersetzt werden sollen, bringt das den Optimizer durcheinander, darum kann man sie nur in einer gesonderten .cpp ohne globale Optimierung ersetzen und hat keinen Einfluss auf die Aufrufe, die geinlinet oder wegoptimiert werden.
2. new löst, selbst als Placement new, eine Referenz zu delete und type_info aus.
Sogar, wenn Exceptions und Run-Time Type Information deaktiviert sind (was mit Placement new legitimer Anwendungsfall ist). Darum muss man immer einen eigenen
void __cdecl operator delete(void *, size_t)
zur Verfügung stellen, den man halt leer macht oder so. Fragt mich bitte nicht, wo der zweite Parameter herkommt. Das weiß wohl nur Microsofts Compiler-Team. Ebenso müsst ihr
class type_info {
~type_info() { }
};
zur Verfügung stellen, weil new aus irgendeinem Grund die Virtual Function Table von type_info braucht und die nunmal aus dem Destruktor besteht. Beachtet, dass die obige Version Speicher verliert, falls ihr type_info tatsächlich nutzt und auf Namen, Hash, o.ä. zugreift. In dem Fall müsst ihr einen vollwertigen Destruktor gemäß der Dokumentation irgendwo auf einer Kaffee-bekleckerten, vollgekritzelten College-Block-Rückseite aus der MSDN entwickeln. Im Debugger *irgendeines* Visual-C++-Projekts
(type_info*)0
eingeben stellt euch ebenfalls das Layout zur Verfügung.
3. Dieses delete und type_info müssen in der selben .cpp definiert sein, in der ihr auch euer new definiert ist.
… oder alternativ in jeder .cpp, in der new vorkommt. Ihr erkennt das Muster dahinter, den Master Plan des Compilerbaus! … oder? Sonst kriegt ihr so schöne Meldungen wie
error LNK2016: absolute symbol '@comp.id' used as target of REL32 relocation in section 8D
oder
fatal error C1001: An internal error has occurred in the compiler.
4. Ihr dürft Run-Time Type nur deaktivieren, wenn type_info in der selben Datei wie new definiert ist.
Denn sonst werden eure Definitionen einfach ignoriert und es gibt
error LNK2001: unresolved external symbol "const type_info::`vftable'"
Fragt nicht.
Mehr trage ich nach, wenn mir gerade danach ist.