// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection.PortableExecutable; using System.Text; namespace R2RDump { /// /// Helper class for diffing a pair of R2R images. /// class R2RDiff { /// /// Left R2R image for the diff. /// private readonly R2RReader _leftFile; /// /// Right R2R image for the diff. /// private readonly R2RReader _rightFile; /// /// Text writer to receive diff output. /// private readonly TextWriter _writer; /// /// Store the left and right file and output writer. /// /// Left R2R file /// Right R2R file /// Output writer to receive the diff public R2RDiff(R2RReader leftFile, R2RReader rightFile, TextWriter writer) { _leftFile = leftFile; _rightFile = rightFile; _writer = writer; } /// /// Public API runs all available diff algorithms in sequence. /// public void Run() { DiffTitle(); DiffPESections(); DiffR2RSections(); DiffR2RMethods(); } /// /// Diff title shows the names of the files being compared and their lengths. /// private void DiffTitle() { _writer.WriteLine($@"Left file: {_leftFile.Filename} ({_leftFile.Image.Length} B)"); _writer.WriteLine($@"Right file: {_rightFile.Filename} ({_rightFile.Image.Length} B)"); _writer.WriteLine(); } /// /// Diff raw PE sections. /// private void DiffPESections() { ShowDiff(GetPESectionMap(_leftFile), GetPESectionMap(_rightFile), "PE sections"); } /// /// Diff R2R header sections. /// private void DiffR2RSections() { ShowDiff(GetR2RSectionMap(_leftFile), GetR2RSectionMap(_rightFile), "R2R sections"); } /// /// Diff the R2R method maps. /// private void DiffR2RMethods() { ShowDiff(GetR2RMethodMap(_leftFile), GetR2RMethodMap(_rightFile), "R2R methods"); } /// /// Show a difference summary between the sets of "left objects" and "right objects". /// /// Dictionary of left object sizes keyed by their names /// Dictionary of right object sizes keyed by their names /// Logical name of the diffing operation to display in the header line private void ShowDiff(Dictionary leftObjects, Dictionary rightObjects, string diffName) { HashSet allKeys = new HashSet(leftObjects.Keys); allKeys.UnionWith(rightObjects.Keys); string title = $@" LEFT_SIZE RIGHT_SIZE DIFF {diffName} ({allKeys.Count} ELEMENTS)"; _writer.WriteLine(title); _writer.WriteLine(new string('-', title.Length)); int leftTotal = 0; int rightTotal = 0; foreach (string key in allKeys) { int leftSize; bool inLeft = leftObjects.TryGetValue(key, out leftSize); int rightSize; bool inRight = rightObjects.TryGetValue(key, out rightSize); leftTotal += leftSize; rightTotal += rightSize; StringBuilder line = new StringBuilder(); if (inLeft) { line.AppendFormat("{0,10}", leftSize); } else { line.Append(' ', 10); } if (inRight) { line.AppendFormat("{0,11}", rightSize); } else { line.Append(' ', 11); } if (leftSize != rightSize) { line.AppendFormat("{0,11}", rightSize - leftSize); } else { line.Append(' ', 11); } line.Append(" "); line.Append(key); _writer.WriteLine(line); } _writer.WriteLine($@"{leftTotal,10} {rightTotal,10} {(rightTotal - leftTotal),10} "); _writer.WriteLine(); } /// /// Read the PE file section map for a given R2R image. /// /// R2R image to scan /// private Dictionary GetPESectionMap(R2RReader reader) { Dictionary sectionMap = new Dictionary(); foreach (SectionHeader sectionHeader in reader.PEReader.PEHeaders.SectionHeaders) { sectionMap.Add(sectionHeader.Name, sectionHeader.SizeOfRawData); } return sectionMap; } /// /// Read the R2R header section map for a given R2R image. /// /// R2R image to scan /// private Dictionary GetR2RSectionMap(R2RReader reader) { Dictionary sectionMap = new Dictionary(); foreach (KeyValuePair typeAndSection in reader.R2RHeader.Sections) { string name = typeAndSection.Key.ToString(); sectionMap.Add(name, typeAndSection.Value.Size); } return sectionMap; } /// /// Read the R2R method map for a given R2R image. /// /// R2R image to scan /// private Dictionary GetR2RMethodMap(R2RReader reader) { Dictionary methodMap = new Dictionary(); foreach (R2RMethod method in reader.R2RMethods) { int size = method.RuntimeFunctions.Sum(rf => rf.Size); methodMap.Add(method.SignatureString, size); } return methodMap; } } }