TL; DR: Ho scritto un'estensione DLL che corregge il bug. Disponibile here.
Il problema
per capire il problema, abbiamo prima bisogno di capire che WinDbg è fondamentalmente solo un frontend per di Windows Symbolic Debugger Engine di Microsoft, implementata all'interno dbgeng.dll
. Altri frontend includono la riga di comando kd.exe
(debugger kernel) e cdb.exe
(debugger in modalità utente).
Il motore implementa tutto ciò che ci aspettiamo da un debugger: lavorare con file di simboli, leggere e scrivere memoria e registri, impostare breakpoitns, ecc. Il motore espone quindi tutte queste funzionalità attraverso interfacce COM-like (implementano IUnknown ma sono componenti non registrati). Questo ci consente, ad esempio, di scrivere il nostro debugger (come ha fatto this person).
Armati di questa conoscenza, ora possiamo formulare un'ipotesi su come WinDbg ottiene i valori dei registri sul computer di destinazione.
Il motore espone l'interfaccia IDebugRegisters
per la manipolazione dei registri. Questa interfaccia dichiara il metodo GetValues
per il recupero dei valori di più registri in una volta. Ma come fa WinDbg a sapere quanti registri ci sono? Ecco perché abbiamo il metodo GetNumberRegisters
.
Così, per recuperare i valori di tutti i registri sul bersaglio, dovremo fare qualcosa di simile:
- chiamata
IDebugRegisters::GetNumberRegisters
per ottenere il numero totale di registri.
- chiamata
IDebugRegisters::GetValues
con il parametro Count
impostata sul numero totale di registri, il parametro Indices
impostato NULL
, e il parametro Start
impostato 0
.
Un piccolo problema, tuttavia: la seconda chiamata ha esito negativo con E_INVALIDARG
.
Ehm, mi scusi? Come può fallire? Particolarmente sconcertante è la documentazione per questo valore di ritorno:
Il valore dell'indice di uno dei registri è maggiore del numero di registri sulla macchina di destinazione.
Ma ti ho appena chiesto quanti registri ci sono, quindi come può essere fuori portata? Va bene, continuiamo a leggere la documentazione in ogni caso, forse qualcosa sarà chiaro:
Se il valore di ritorno non è S_OK, alcuni dei registri ancora potrebbe essere stato letto. Se la destinazione non era accessibile, il tipo di reso è E_UNEXPECTED e Valori è invariato; altrimenti, i valori conterranno i risultati parziali ei registri che non è stato possibile leggere avranno il tipo DEBUG_VALUE_INVALID.
(sottolineatura mia.)
Aha! Quindi forse il motore non poteva leggere uno dei registri! Ma quale? Risulta che il motore soffoca sul registro xcr0
. Dal manuale il Intel 64 e IA-32 Architetture dello sviluppatore di software:
registro di controllo esteso XCR0
contiene una bitmap stato-componente che specifica i componenti dello stato di utente che il software ha permesso la XSAVE
set di funzionalità per la gestione. Se il bit corrispondente a un componente di stato è chiaro in XCR0
, le istruzioni nel set di funzionalità XSAVE
non funzioneranno su quel componente di stato, indipendentemente dal valore della maschera di istruzioni.
Va bene, in modo che il registro controlla il funzionamento dell'istruzione XSAVE
, che salva lo stato di funzionalità estese della CPU (come XMM e AVX). Secondo l'ultimo commento sulla pagina this, questa istruzione richiede supporto dal sistema operativo. Sebbene il commento affermi che Windows 7 (è quello su cui era in esecuzione la VM su cui stavo testando) supporta questa istruzione, sembra che il problema in questione sia comunque correlato all'OS, poiché quando l'obiettivo è Windows 8, tutto funziona correttamente.
Davvero, non è chiaro se il bug è all'interno del motore di debugger, che riporta più registri di quello che può recuperare i valori, o all'interno di WinDbg, che si rifiuta di mostrare i valori affatto se il motore non riesce a produrre tutto di loro.
La soluzione
Potremmo, ovviamente, stringere i denti e basta usare una vecchia versione di WinDbg per il debug di vecchie versioni di Windows. Ma dov'è la sfida in questo?
Invece, vi presento uno debugger extension che risolve questo problema. Lo fa agganciando (con l'aiuto di this library) i metodi del motore di debugger rilevanti e restituendo S_OK
se l'unico registro che ha avuto esito negativo è stato xcr0
. Altrimenti, si propaga l'errore. L'estensione supporta lo scaricamento di runtime, quindi se si verificano problemi è sempre possibile disabilitare i ganci.
Questo è tutto, buon divertimento!
dovresti postarlo su quel thread. –