2016-04-15 26 views
7

特定のアセンブリの近くに有効なpdbファイルがあり、pdbに格納されているチェックサムがソースチェックサムと一致することを確認するためにチェックを書きます。pdbファイルがソースと一致することを確認

ビルドサーバーの設定が壊れている場合(たとえば、コードをビルドするために別のgitブランチを使用した場合など)、公開バイナリを防ぐために、このチェックはCIサーバー上で単体テストとして呼び出す必要がありました。

可能であれば、入力パラメータとしてMethodInfoを受け入れる必要があります。ダブルチェックを行い、メソッドのソース行が推定されていることを確認します。

最初の部分は、特定のメソッドのソースを読むのはかなり簡単です。ここにはa sampleがあります。

難しい部分は、PDB内のすべてのドキュメントのチェックサムをチェックしています。標準System.Diagnostics.SymbolStore.SymDocument.GetCheckSum()NotImplementedExceptionを投げるので、私は間違いなく他のものを使う必要があります。

提案がありますか?

UPD。明確化しようとすると:)

  1. 私はそのソースファイルチェックサムは、PDBファイルに保存されているチェックサムに一致しているを確認するに興味がある、最初の部分(そのPDBをチェックするには、バイナリ一致する)はすでに行われています。

  2. 私はコンソール出力を解析したくないため、このタスクのAPIを探しています。また、CIサーバーに追加のSDKをインストールしたくないので、何も壊れていないことを証明するのに時間がかかります。

+1

マイクロソフトでは、オープンソースのシンボルパーサーを含むCCIオープンソースを使用しています。これを使用してシンボルファイルの内部をチェックする必要があります。 –

+0

それはもう古くはありませんか? Microsoft.DiaSymReader(実際には、NativeとPortableの2つのバージョンがあります)とSystem.Reflectionもあります。メタデータパッケージとMono.Cecilもあります。どちらを選択するのですか? *混乱しています* – Sinix

+0

Mono.CecilのPDBコードはMicrosoft CCIから直接提供されます。他には私がチェックしなかったが、あなたはPDBフォーマットが長い間変更されており、最新のビットが必要ないことを知っておくべきである。 –

答えて

5

プログラミング方法が必要な場合は、Microsoft.DiaSymReaderPackage(バージョン1.0.7.0)を使用できます。また、いくつかの他のアンマネージインターフェイスも宣言する必要があります。チェックサムは、SHA1またはMD5という2つのアルゴリズムのいずれかを使用してファイルバイトから簡単に計算されます。

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."); 
        } 
       } 
      } 
     } 
    } 

とサンプルヘルパークラス、ネイティブインターフェイスとコクラス:ここ

は、独自のソースに関する情報を表示するサンプルコンソールアプリケーションです。

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

あなたはMicrosoft CCIまたはMono.Cecilを使用してPDBから年齢とGUIDを読み、PEファイルにRSDS resourceから同じ情報を読み取ることができます。

年齢とGUIDが一致する場合は、PDBとPEファイルが実際に一致すると見なします。

symchkツールでさらに一致を確認することができます。

関連する問題