2016-04-15 21 views
7

Voglio verificare la verifica che l'assembly fornito abbia un file pdb valido vicino e che i checksum memorizzati nel pdb corrispondano ai checksum di origine.Verificare che il file pdb corrisponda alla fonte

Il controllo veniva chiamato come unit test sul nostro server CI per impedire la pubblicazione dei file binari se la configurazione del server di build era interrotta (ad esempio, per creare il codice era stato utilizzato un ramo git diverso).

Se possibile, deve accettare MethodInfo come parametro di input. Voglio fare un doppio controllo e verificare che le linee di origine per un metodo siano quelle stimate.

La prima parte, la lettura dell'origine per il metodo specifico è piuttosto semplice. Ecco a sample.

La parte difficile è il controllo dei checksum di tutti i documenti nel PDB. Lo standard System.Diagnostics.SymbolStore.SymDocument.GetCheckSum() genera NotImplementedException quindi ho sicuramente bisogno di usare qualcos'altro.

Qualche suggerimento?

UPD. Cercando di chiarire :)

  1. Mi interessa il controllo checksum che i file di origine sono corrispondenti ai checksum memorizzate nel file PDB, la prima parte (controllando che PDB corrisponde binario) è fatto già.

  2. Sto cercando alcune API per questa attività in quanto non voglio analizzare l'output della console. Inoltre, preferirei non installare alcun SDK aggiuntivo sui nostri server CI, poiché ci vorrà più tempo per dimostrare che non è stato rotto nulla.

+1

Microsoft ha CCI open source che contiene il parser di simboli di codice sorgente aperto, e dovresti usarlo per controllare gli interni dei file di simboli. –

+0

Beh, non è quello obsoleto? Ci sono anche Microsoft.DiaSymReader (in realtà, ci sono due versioni, Native e Portable) e System.Reflection.Pacchetti di metadati e c'è anche Mono.Cecil. Quale scegliere? * confuso * – Sinix

+0

Il codice PDB di Mono.Cecil proviene direttamente da Microsoft CCI. Gli altri non ho controllato, ma dovresti sapere che il formato PDB è stato modificato per un lungo periodo e non hai bisogno di alcuni ultimi bit. –

risposta

5

Se è necessario un modo programmatico per farlo, è possibile utilizzare Microsoft.DiaSymReaderPackage (versione 1.0.7.0), ed è necessario dichiarare un paio di altre interfacce non gestite. Il checksum viene calcolato facilmente dai byte dei file utilizzando uno dei due algoritmi: SHA1 o MD5.

Ecco un'applicazione di esempio console che visualizza le informazioni sulla propria fonte:

class Program 
    { 
     static void Main(string[] args) 
     { 
      ValidateChecksums(Path.GetFullPath("ConsoleApplication1.exe")); 
     } 

     // this needs a reference to Microsoft.DiaSymReader 
     // or redefine its interfaces manually from here https://github.com/dotnet/roslyn/tree/master/src/Dependencies/Microsoft.DiaSymReader 
     public static void ValidateChecksums(string filePath) 
     { 
      if (filePath == null) 
       throw new ArgumentNullException(nameof(filePath)); 

      var dispenser = (IMetaDataDispenser)new CorMetaDataDispenser(); 
      var import = dispenser.OpenScope(filePath, 0, typeof(IMetaDataImport).GUID); 

      var binder = (ISymUnmanagedBinder)new CorSymBinder_SxS(); 
      ISymUnmanagedReader reader; 
      binder.GetReaderForFile(import, filePath, null, out reader); 

      int count; 
      reader.GetDocuments(0, out count, null); 
      if (count > 0) 
      { 
       var docs = new ISymUnmanagedDocument[count]; 
       reader.GetDocuments(count, out count, docs); 
       foreach (var d in docs) 
       { 
        var doc = new SymDocument(d); 
        Console.WriteLine(doc.Url); 

        if (doc.Checksum.SequenceEqual(doc.ComputeChecksum())) 
        { 
         Console.WriteLine(" checksum is valid."); 
        } 
        else 
        { 
         Console.WriteLine(" checksum is not valid."); 
        } 
       } 
      } 
     } 
    } 

E una classe di supporto del campione, interfacce native e coclassi.

public class SymDocument 
    { 
     // guids are from corsym.h 
     public static readonly Guid CorSym_SourceHash_MD5 = new Guid(0x406ea660, 0x64cf, 0x4c82, 0xb6, 0xf0, 0x42, 0xd4, 0x81, 0x72, 0xa7, 0x99); 
     public static readonly Guid CorSym_SourceHash_SHA1 = new Guid(0xff1816ec, 0xaa5e, 0x4d10, 0x87, 0xf7, 0x6f, 0x49, 0x63, 0x83, 0x34, 0x60); 

     public SymDocument(ISymUnmanagedDocument doc) 
     { 
      if (doc == null) 
       throw new ArgumentNullException(nameof(doc)); 

      int len; 
      doc.GetUrl(0, out len, null); 
      if (len > 0) 
      { 
       var urlChars = new char[len]; 
       doc.GetUrl(len, out len, urlChars); 
       Url = new string(urlChars, 0, len - 1); 
      } 

      doc.GetChecksum(0, out len, null); 
      if (len > 0) 
      { 
       Checksum = new byte[len]; 
       doc.GetChecksum(len, out len, Checksum); 
      } 

      Guid id = Guid.Empty; 
      doc.GetChecksumAlgorithmId(ref id); 
      ChecksumAlgorithmId = id; 
     } 

     public string Url { get; private set; } 
     public byte[] Checksum { get; private set; } 
     public Guid ChecksumAlgorithmId { get; private set; } 

     public byte[] ComputeChecksum() 
     { 
      HashAlgorithm algo; 
      if (ChecksumAlgorithmId == CorSym_SourceHash_MD5) 
      { 
       algo = MD5.Create(); 
      } 
      else if (ChecksumAlgorithmId == CorSym_SourceHash_SHA1) 
      { 
       algo = SHA1.Create(); 
      } 
      else 
       throw new NotSupportedException(); 

      try 
      { 
       return algo.ComputeHash(File.ReadAllBytes(Url)); 
      } 
      finally 
      { 
       algo.Dispose(); 
      } 
     } 
    } 

    [ComImport, Guid("0A29FF9E-7F9C-4437-8B11-F424491E3931")] 
    internal class CorSymBinder_SxS // from corsym.h 
    { 
    } 

    [ComImport, Guid("E5CB7A31-7512-11d2-89CE-0080C792E5D8")] 
    internal class CorMetaDataDispenser // from cor.h 
    { 
    } 

    [Guid("7DAC8207-D3AE-4c75-9B67-92801A497D44"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 
    internal interface IMetaDataImport // from cor.h 
    { 
     // we don't need to use what's inside 
    } 

    [Guid("809c652e-7396-11d2-9771-00a0c9b4d50c"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 
    internal interface IMetaDataDispenser // from cor.h 
    { 
     void _VtblGap0_1(); // skip 1 method 
     IMetaDataImport OpenScope([MarshalAs(UnmanagedType.LPWStr)] string szScope, int dwOpenFlags, [MarshalAs(UnmanagedType.LPStruct)] Guid riid); 
    } 
2

È possibile leggere le età e GUID dal PDB utilizzando Microsoft CCI o Mono.Cecil, e leggere le stesse informazioni dal RSDS resource in un file PE.

Se l'età e il GUID corrispondono, considerare che i file PDB e PE corrispondono effettivamente.

La partita può essere ulteriormente verificata con lo strumento symchk.

Problemi correlati