[Visual C++ CRT] Wer braucht __initmbctable()?

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Antworten
Benutzeravatar
Krishty
Establishment
Beiträge: 8245
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

[Visual C++ CRT] Wer braucht __initmbctable()?

Beitrag von Krishty »

Hi,

Auf meinem Feldzug gegen die CRT-Windmühlen kloppe ich mich gerade mit __initmbctable().

(Die Funktion wird bei mir automatisch während des Prozessbeginns ausgeführt. Offenbar ist sie dafür verantwortlich, eine Konvertierungstabelle anzulegen, damit Visual C++ 2012s CRT diese in ihren Multi-Byte-Character-Funktionen verwenden kann. Problematisch ist, dass diese Funktion Thread-Local Storage anfordert, der nicht existiert. Weil ich die CRT nicht initialisiere. Und garnicht initialisieren will.)

Wie finde ich nun raus, wer dafür verantwortlich ist, dass die Funktion bei Prozessbegin ausgeführt wird? Ich binde die CRT statisch ein, und irgendein CRT-Symbol muss ja dafür sorgen, dass der Linker sie nicht als unreferenziert wegschmeißt!


————


Nachtrag: Definiert ist die Funktion in mbctype.c. Dort befindet sich auch:

    _CRTALLOC(".CRT$XIC") static _PIFV pinit = __initmbctable;

für alle, denen die Syntax fremd ist: Das sorgt dafür, dass der Linker die Funktion in den Bereich, der für C-Initialisierungsfunktionen vorgesehen ist, einträgt.

Der Punkt ist: Der Linker würde das AFAIK (liege ich hier falsch?) einfach verwerfen, falls nichts aus dieser Übersetzungseinheit referenziert würde. Irgendeine Funktion, die ich benutze, ruft also direkt oder indirekt eine Funktion auf, die ebenfalls in mbctype.c liegt, oder benutzt ein Datensymbol daraus. Wie finde ich raus, wer der Verursacher ist?

Alle CRT-Funktionen, die ich einbinde:

    __report_rangecheckfailure
   
    _RTC_CheckStackVars
    _RTC_Check_2_to_1
    _RTC_Check_4_to_1
    _RTC_Check_4_to_2
    _RTC_Check_8_to_1
    _RTC_Check_8_to_2
    _RTC_Check_8_to_4
    _RTC_InitBase
    _RTC_Shutdown
   
    __GSHandlerCheck
    __GSHandlerCheck_EH
   
    __chkstk
   
    __security_cookie
    __security_check_cookie
   
    const type_info::`vftable'
   
    _fltused
   
    acos
    asin
    atan2
    cos
    exp
    log
    pow
    sin
    tan
   
    memmove
    memcmp
   
    __CxxFrameHandler3


Noch einer: WTF wird da alles eingebunden?! __isa_available_init()?! „Ööööh sind wir *echt* auf x64“ oder was?! __initstdio()?! Wozu?
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
j.klugmann
Establishment
Beiträge: 201
Registriert: 07.07.2010, 13:00
Kontaktdaten:

Re: [Visual C++ CRT] Wer braucht __initmbctable()?

Beitrag von j.klugmann »

So, nachdem der letzte Post meinem mangelnden Schlaf zum Opfer gefallen ist, hier mal das Update.

Die Funktion __initmbctable wird tatsächlich von einem Datensegment in der .rdata Sektion referenziert. Zusammen mit ein paar anderen interessanten Funktionen:
__xi_a:
.rdata: dd 0
.rdata: dd 0
.rdata: dd 0
.rdata: dd 0
.rdata: dd offset ___onexitinit
.rdata: dd offset ___isa_available_init
.rdata: dd offset ___initmbctable
.rdata: dd offset ___initstdio
.rdata: dd offset ___CxxSetUnhandledExceptionFilter
__xi_z:
.rdata: db 0

Markiert wird das Ende dieser Liste von einer Variable namens __xi_z und einem Nullbyte. Die cinit Funktion ruft _initterm_e mit genau diesen Argumenten auf:

Code: Alles auswählen

_initterm_e( __xi_a, __xi_z );
_initterm_e macht ungefähr folgendes:

Code: Alles auswählen

typedef int (__cdecl *_PIFV)(void);
int _cdecl _initterm_e( PIFV beginPtr, PIFV endPtr ) {
      int ret( 0 );

     while( ret == 0 && endPtr > beginPtr ) {
          if ( *beginPtr != 0 ) {
               ret = (**beginPtr)();
          }
          ++beginPtr;
      }

      return ret;
}  
D.h. letztendlich wird einfach eine Liste von Funktionen initialisiert. __xi_a und __xi_z sind in der CRT definiert und bilden explizit den Bereich der Initializer dar. Anscheinend reicht diese Definition bereits dafür, dass der Linker sie auch auf jedenfall mit ins Executable legt.

Der Header, der üblicherweise die Sections der CRT kontrolliert, heißt sect_attribs.h.

Code: Alles auswählen

...
#pragma section(".CRTMP$XCA",long,read)
#pragma section(".CRTMP$XCZ",long,read)
#pragma section(".CRTMP$XIA",long,read)
...
#pragma section(".CRT$XDZ",long,read)
#pragma section(".CRT$XIA",long,read)
....

#define _CRTALLOC(x) __declspec(allocate(x))
Da ist das _CRTALLOC, welches dieses __initmbctable referenzieren kann. Allerdings bekommt man das ohne weiteres aus der LIB nicht mehr heraus.

Eine Möglichtkeit einzelne Object-Files aus einer LIB heraus zu editieren, ist das kleine LIB.exe-Tool, welches beim SDK dabei liegt. lib /extract:fullpath/object.obj /out:newlib.lib libcmt.lib. Bei mir fliegt danach alles in die Luft, weil viele Symbole nicht mehr gefunden werden. Allerdings wird man das mit diesem Tool die Größe der CRT auf ein Minimum beschränken können. Man muss nur genug von der CRT wegschneiden. Alternativ kann man innerhalb des spezifischen Object-Files Relocations als auch Daten anpassen, damit die Funktion nicht mehr referenziert wird. Das ist aber noch frickeliger.

Nachtrag: Afaik kannst du nichts machen, abgesehen von CRT wegschmeißen oder soweit mit lib.exe zerhachseln, bis nur noch das in der Library ist, was du unbedingt brauchst.

Referenzen:
http://msdn.microsoft.com/en-us/library ... 71%29.aspx

Ab in die Heia.
Imaging-Software und bald auch Middleware: http://fd-imaging.com
j.klugmann
Establishment
Beiträge: 201
Registriert: 07.07.2010, 13:00
Kontaktdaten:

Re: [Visual C++ CRT] Wer braucht __initmbctable()?

Beitrag von j.klugmann »

So, gestern habe ich mir primär die 32bit libcmt angeguckt, da ich nicht die passenden 64bit-kompatiblen Tools auf meinem Heimrechner hatte. Ich schaue mir gerade die libcmt für 64-bit Builds an. Wie man sich denken kann, ist dieses Object-File komplett anders aufgebaut. Die ersten 8 Byte des Objekt-Files ist die relative Adresse zur bekannten __initmbctable. Eine weitere Referenz auf diese Funktion findet sich einem COMDAT-Datensatz im .pdata Segment am Ende der Datei.

Allerdings lässt sich auch hier recht wenig ohne grundlegendes Reverse-Engineering machen. Das kostet etwas Zeit.
Imaging-Software und bald auch Middleware: http://fd-imaging.com
Benutzeravatar
Krishty
Establishment
Beiträge: 8245
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [Visual C++ CRT] Wer braucht __initmbctable()?

Beitrag von Krishty »

j.klugmann hat geschrieben:D.h. letztendlich wird einfach eine Liste von Funktionen initialisiert. __xi_a und __xi_z sind in der CRT definiert und bilden explizit den Bereich der Initializer dar.
Japp; die ist bereits in meiner Hand und falls ich garantieren könnte, dass die Reihenfolge immer identisch ist, könnte ich die CRT-Initialisierung einfach überspringen. Das würde nur nicht viel bringen, weil ich auch den tatsächlichen Maschinentext raus haben will.
j.klugmann hat geschrieben:Anscheinend reicht diese Definition bereits dafür, dass der Linker sie auch auf jedenfall mit ins Executable legt.
DAS ist der Punkt, mit dem ich hadere. Falls ich das richtig verstanden habe, ist der Unterschied, ob man Visual C++ mit statischen Bibliotheken füttert oder direkt mit Quelltext, dass im Fall von statischen Bibliotheken nur Symbole eingebunden werden, die in Übersetzungseinheiten liegen, die auch tatsächlich referenziert werden. (Bei direktem Quelltext landet alles in der Exe; egal, ob referenziert oder nicht.) Das Verhalten, das ich mit __initmbctable() beobachte, passt also eher dazu, die CRT direkt aus dem Quelltext ins Projekt zu kompilieren, als zu einer statischen Bibliothek.
Eine Möglichtkeit einzelne Object-Files aus einer LIB heraus zu editieren, ist das kleine LIB.exe-Tool, welches beim SDK dabei liegt. lib /extract:fullpath/object.obj /out:newlib.lib libcmt.lib. Bei mir fliegt danach alles in die Luft, weil viele Symbole nicht mehr gefunden werden. Allerdings wird man das mit diesem Tool die Größe der CRT auf ein Minimum beschränken können. Man muss nur genug von der CRT wegschneiden. Alternativ kann man innerhalb des spezifischen Object-Files Relocations als auch Daten anpassen, damit die Funktion nicht mehr referenziert wird. Das ist aber noch frickeliger.

Nachtrag: Afaik kannst du nichts machen, abgesehen von CRT wegschmeißen oder soweit mit lib.exe zerhachseln, bis nur noch das in der Library ist, was du unbedingt brauchst.
Dankesehr. Ich denke, dann schmeiße ich die CRT komplett weg und baue mir den pisseligen Rest, den ich noch brauche, lieber selber :(
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Antworten