// 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;
}
}
}