------------------------------------------------------------------------
r70491 | mitalia | 2016-01-25 11:07:31 +0100 (lun, 25 gen 2016) | 2 lines
Patch binaria a libmingwex.a per evitare la chiamata a getenv ad ogni printf (vedi
http://stackoverflow.com/q/13970675/214671); per dettagli, vedi README.printf_binary_patch
------------------------------------------------------------------------
La build originale di questo MinGW è ancora affetta dal bug descritto a
http://stackoverflow.com/q/13970675/214671.
Per mantenere compatibilità sia con MSVC e con glibc in MinGW hanno introdotto una variabile d'ambiente (PRINTF_EXPONENT_DIGITS) che modifica il numero di cifre decimali da stampare di default per il formato "%f"; purtroppo, viene effettuato il lookup tramite getenv ad ogni invocazione di printf e affini, per cui le funzioni di famiglia printf diventano inaccettabilmente lente (~8x per environment"normali" e stringhe di formato semplici).
Dato che il nostro software fa uso abbastanza massiccio di printf, per ovviare all'inconveniente senza ricompilare in blocco il MinGW (cosa non banale) ho patchato direttamente i binari della libreria standard.
IMPORTANTE: ho patchato *solo* la libreria statica, le dll fornite a corredo con il MinGW hanno ancora il comportamento originario.
La funzione in causa è __mingw_pformat in libmingwex.a, in particolare a 0x1699:
codice:
1687: 31 c0 xor eax,eax
1689: 66 89 44 24 50 mov WORD PTR [esp+0x50],ax
168e: 8b 84 24 88 00 00 00 mov eax,DWORD PTR [esp+0x88]
1695: 89 44 24 58 mov DWORD PTR [esp+0x58],eax
1699: e8 00 00 00 00 call 169e <___mingw_pformat+0x8e>
169e: 85 c0 test eax,eax
16a0: 74 10 je 16b2 <___mingw_pformat+0xa2>
16a2: 0f be 10 movsx edx,BYTE PTR [eax]
16a5: b8 02 00 00 00 mov eax,0x2
16aa: 83 ea 30 sub edx,0x30
16ad: 83 fa 02 cmp edx,0x2
16b0: 76 0d jbe 16bf <___mingw_pformat+0xaf>
16b2: e8 00 00 00 00 call 16b7 <___mingw_pformat+0xa7>
16b7: 83 e0 01 and eax,0x1
La call in questione nella libreria ha come target l'offset 0, ma è un placeholder che viene riempito dal linker con l'indirizzo del trampolino per la getenv (motivo per cui non si può semplicemente asfaltare con dei nop, il linker li sovrascriverebbe).
La modifica effettuata è:
codice:
1687: 31 c0 xor eax,eax
1689: 66 89 44 24 50 mov WORD PTR [esp+0x50],ax
168e: 8b 84 24 88 00 00 00 mov eax,DWORD PTR [esp+0x88]
1695: 89 44 24 58 mov DWORD PTR [esp+0x58],eax
1699: b8 00 00 00 00 mov eax,0x0
169e: 85 c0 test eax,eax
16a0: 75 10 jne 16b2 <___mingw_pformat+0xa2>
16a2: 0f be 10 movsx edx,BYTE PTR [eax]
16a5: b8 02 00 00 00 mov eax,0x2
16aa: 83 ea 30 sub edx,0x30
16ad: 83 fa 02 cmp edx,0x2
16b0: 76 0d jbe 16bf <___mingw_pformat+0xaf>
16b2: e8 00 00 00 00 call 16b7 <___mingw_pformat+0xa7>
16b7: 83 e0 01 and eax,0x1
La call a 0x1699 diventa una mov (per cui in eax va a finire l'indirizzo del trampolino della getenv, sicuramente diverso da zero); la je a 0x16a0 invece diventa una jne. In questa maniera, attiviamo sempre il percorso di codice relativo al caso in cui getenv restituisce NULL (che è quello che di fatto verrebbe attivato sempre). L'unica differenza è che in questo caso eax resta diverso da zero, ma viene immediatamente sovrascritto dalla call a 0x16b2 (a cui punta la jne), quindi non è un problema.