Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use PEImage API get export symbols for single file apps #2674

Merged
merged 7 commits into from
Oct 18, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@

using Microsoft.Diagnostics.Runtime;
using Microsoft.Diagnostics.Runtime.Utilities;
using Microsoft.FileFormats;
using Microsoft.FileFormats.ELF;
using Microsoft.FileFormats.MachO;
using Microsoft.FileFormats.PE;
using System;
using System.Collections.Immutable;
using System.Diagnostics;
using System.IO;
using System.Reflection.PortableExecutable;
using System.Runtime.InteropServices;
using FileVersionInfo = Microsoft.Diagnostics.Runtime.Utilities.FileVersionInfo;
Expand All @@ -18,7 +21,7 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
/// <summary>
/// Module base implementation
/// </summary>
public abstract class Module : IModule, IDisposable
public abstract class Module : IModule, IExportSymbols, IDisposable
{
[Flags]
public enum Flags : byte
Expand Down Expand Up @@ -58,6 +61,7 @@ public Module(ITarget target)
ServiceProvider.RemoveService(typeof(ELFFile));
ServiceProvider.RemoveService(typeof(PEReader));
});
ServiceProvider.AddService<IExportSymbols>(this);
}

public void Dispose()
Expand Down Expand Up @@ -163,6 +167,57 @@ public ImmutableArray<byte> BuildId

#endregion

#region IExportSymbols

bool IExportSymbols.TryGetSymbolAddress(string name, out ulong address)
{
if (Target.OperatingSystem == OSPlatform.Windows)
{
Stream stream = Target.Services.GetService<IMemoryService>().CreateMemoryStream(ImageBase, ImageSize);
PEFile image = new(new StreamAddressSpace(stream), isDataSourceVirtualAddressSpace: true);
if (image.IsValid())
{
if (image.TryGetExportSymbol(name, out ulong offset))
{
address = ImageBase + offset;
return true;
}
address = 0;
return false;
}
}
else if (Target.OperatingSystem == OSPlatform.Linux)
mikem8361 marked this conversation as resolved.
Show resolved Hide resolved
{
try
{
Stream stream = Target.Services.GetService<IMemoryService>().CreateMemoryStream(ImageBase, ImageSize);
ElfFile elfFile = new(stream, position: ImageBase, leaveOpen: false, isVirtual: true);
hoyosjs marked this conversation as resolved.
Show resolved Hide resolved
if (elfFile.Header.IsValid)
{
if (elfFile.TryGetExportSymbol(name, out ulong offset))
{
address = ImageBase + offset;
return true;
}
address = 0;
return false;
}
}
catch (InvalidDataException)
{
}
}
return TryGetSymbolAddressInner(name, out address);
}

protected virtual bool TryGetSymbolAddressInner(string name, out ulong address)
{
address = 0;
return false;
}

#endregion

protected VersionData GetVersion()
{
VersionData versionData = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
/// </summary>
public class ModuleServiceFromDataReader : ModuleService
{
class ModuleFromDataReader : Module, IExportSymbols
class ModuleFromDataReader : Module
{
// This is what clrmd returns for non-PE modules that don't have a timestamp
private const uint InvalidTimeStamp = 0;
Expand All @@ -38,10 +38,6 @@ public ModuleFromDataReader(ModuleServiceFromDataReader moduleService, IExportRe
_imageSize = imageSize;
_exportReader = exportReader;
ModuleIndex = moduleIndex;
if (exportReader is not null)
{
ServiceProvider.AddService<IExportSymbols>(this);
}
}

#region IModule
Expand Down Expand Up @@ -97,28 +93,16 @@ public override string VersionString

#endregion

#region IExportSymbols

public bool TryGetSymbolAddress(string name, out ulong address)
protected override bool TryGetSymbolAddressInner(string name, out ulong address)
{
if (_exportReader is not null)
{
// Some exceptions are escaping from the clrmd ELF dump reader. This will be
// fixed in a clrmd update.
try
{
return _exportReader.TryGetSymbolAddress(ImageBase, name, out address);
}
catch (IOException)
{
}
return _exportReader.TryGetSymbolAddress(ImageBase, name, out address);
hoyosjs marked this conversation as resolved.
Show resolved Hide resolved
}
address = 0;
return false;
}

#endregion

protected override ModuleService ModuleService => _moduleService;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
public class RuntimeService : IRuntimeService, IDataReader, IExportReader
{
private readonly ITarget _target;
private readonly bool _exportReaderEnabled;
private readonly IDisposable _onFlushEvent;
private DataTarget _dataTarget;
private List<Runtime> _runtimes;
Expand All @@ -33,8 +32,6 @@ public class RuntimeService : IRuntimeService, IDataReader, IExportReader
public RuntimeService(ITarget target)
{
_target = target;
// TODO - mikem 4/30/21 - remove when dbgeng services doesn't take so long looking up exports (attempts to load PDBs).
_exportReaderEnabled = target.Host.HostType != HostType.DbgEng || Environment.GetEnvironmentVariable("DOTNET_ENABLE_SOS_SINGLEFILE") == "1";
_onFlushEvent = target.OnFlushEvent.Register(() => {
if (_runtimes is not null && _runtimes.Count == 0)
{
Expand Down Expand Up @@ -219,20 +216,17 @@ ulong IMemoryReader.ReadPointer(ulong address)

bool IExportReader.TryGetSymbolAddress(ulong baseAddress, string name, out ulong offset)
{
if (_exportReaderEnabled)
try
{
try
{
IExportSymbols exportSymbols = ModuleService.GetModuleFromBaseAddress(baseAddress).Services.GetService<IExportSymbols>();
if (exportSymbols is not null)
{
return exportSymbols.TryGetSymbolAddress(name, out offset);
}
}
catch (DiagnosticsException)
IExportSymbols exportSymbols = ModuleService.GetModuleFromBaseAddress(baseAddress).Services.GetService<IExportSymbols>();
if (exportSymbols is not null)
{
return exportSymbols.TryGetSymbolAddress(name, out offset);
}
}
catch (DiagnosticsException)
{
}
offset = 0;
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ public class SymbolService : ISymbolService
/// <summary>
/// Symbol server URLs
/// </summary>
public const string MsdlSymbolServer = "http://msdl.microsoft.com/download/symbols/";
public const string SymwebSymbolServer = "http://symweb.corp.microsoft.com/";
public const string MsdlSymbolServer = "https://msdl.microsoft.com/download/symbols/";
public const string SymwebSymbolServer = "https://symweb.corp.microsoft.com/";

private readonly IHost _host;
private string _defaultSymbolCache;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,12 +165,12 @@ protected virtual string GetDebuggeeBinaryDirPath(string debuggeeProjectDirPath,

protected static string GetDebuggeeBinaryDllPath(TestConfiguration config, string debuggeeBinaryDirPath, string debuggeeName)
{
return config.IsNETCore ? Path.Combine(debuggeeBinaryDirPath, debuggeeName + (config.PublishSingleFile ? "" : ".dll")) : null;
return config.IsNETCore ? Path.Combine(debuggeeBinaryDirPath, debuggeeName + ".dll") : null;
}

protected static string GetDebuggeeBinaryExePath(TestConfiguration config, string debuggeeBinaryDirPath, string debuggeeName)
{
return config.IsDesktop ? Path.Combine(debuggeeBinaryDirPath, debuggeeName + ".exe") : null;
return config.IsDesktop || config.PublishSingleFile ? Path.Combine(debuggeeBinaryDirPath, debuggeeName + (OS.Kind == OSKind.Windows ? ".exe" : "")) : null;
}

protected static string GetLogPath(TestConfiguration config, string framework, string runtime, string debuggeeName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ private static Dictionary<string,string> GetBuildProperties(TestConfiguration co
{
Debug.Assert(runtimeIdentifier != null);
buildProperties.Add("PublishSingleFile", "true");
buildProperties.Add("SelfContained", "true");
}
string debugType = config.DebugType;
if (debugType == null)
Expand Down Expand Up @@ -78,6 +79,7 @@ public override DotNetBuildDebuggeeTestStep ConfigureDotNetBuildDebuggeeTask(Tes
GetDebuggeeNativeLibDirPath(config, debuggeeName),
GetBuildProperties(config, runtimeIdentifier),
runtimeIdentifier,
config.BuildProjectFramework,
config.LinkerPackageVersion,
debuggeeName,
debuggeeSolutionDirPath,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public CsprojBuildDebuggeeTestStep(string dotnetToolPath,
string debuggeeNativeLibDirPath,
Dictionary<string,string> buildProperties,
string runtimeIdentifier,
string runtimeFramework,
string linkerPackageVersion,
string debuggeeName,
string debuggeeSolutionDirPath,
Expand All @@ -54,6 +55,7 @@ public CsprojBuildDebuggeeTestStep(string dotnetToolPath,
{
BuildProperties = buildProperties;
RuntimeIdentifier = runtimeIdentifier;
RuntimeFramework = runtimeFramework;
DebuggeeName = debuggeeName;
ProjectTemplateFileName = debuggeeName + ".csproj";
LinkerPackageVersion = linkerPackageVersion;
Expand All @@ -64,6 +66,7 @@ public CsprojBuildDebuggeeTestStep(string dotnetToolPath,
/// </summary>
public IDictionary<string,string> BuildProperties { get; }
public string RuntimeIdentifier { get; }
public string RuntimeFramework { get; }
public string DebuggeeName { get; }
public string LinkerPackageVersion { get; }
public override string ProjectTemplateFileName { get; }
Expand All @@ -73,7 +76,7 @@ protected override async Task Restore(ITestOutputHelper output)
string extraArgs = "";
if (RuntimeIdentifier != null)
{
extraArgs = " --runtime " + RuntimeIdentifier;
extraArgs += " --runtime " + RuntimeIdentifier;
}
foreach (var prop in BuildProperties)
{
Expand All @@ -85,9 +88,14 @@ protected override async Task Restore(ITestOutputHelper output)
protected override async Task Build(ITestOutputHelper output)
{
string publishArgs = "publish";
if (RuntimeFramework != null)
{
publishArgs += " --framework " + RuntimeFramework;
}
if (RuntimeIdentifier != null)
{
publishArgs += " --runtime " + RuntimeIdentifier;
publishArgs += " --self-contained true";
}
foreach (var prop in BuildProperties)
{
Expand Down
15 changes: 10 additions & 5 deletions src/SOS/SOS.Extensions/ModuleServiceFromDebuggerServices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Microsoft.Diagnostics.Runtime.Utilities;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
mikem8361 marked this conversation as resolved.
Show resolved Hide resolved
using System.Runtime.InteropServices;

namespace SOS.Extensions
Expand All @@ -17,7 +18,7 @@ namespace SOS.Extensions
/// </summary>
internal class ModuleServiceFromDebuggerServices : ModuleService
{
class ModuleFromDebuggerServices : Module, IExportSymbols, IModuleSymbols
class ModuleFromDebuggerServices : Module, IModuleSymbols
{
// This is what dbgeng/IDebuggerServices returns for non-PE modules that don't have a timestamp
private const uint InvalidTimeStamp = 0xFFFFFFFE;
Expand All @@ -44,7 +45,6 @@ public ModuleFromDebuggerServices(
IndexFileSize = indexTimeStamp == InvalidTimeStamp ? null : indexFileSize;
IndexTimeStamp = indexTimeStamp == InvalidTimeStamp ? null : indexTimeStamp;

ServiceProvider.AddService<IExportSymbols>(this);
ServiceProvider.AddService<IModuleSymbols>(this);
}

Expand Down Expand Up @@ -110,20 +110,25 @@ public override string VersionString

#endregion

#region IExportSymbols/IModuleSymbols
#region IModuleSymbols

public bool TryGetSymbolName(ulong address, out string symbol, out ulong displacement)
bool IModuleSymbols.TryGetSymbolName(ulong address, out string symbol, out ulong displacement)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't _debuggerServices in this case WinDBG? Is this the slow path for the export case?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I see you're making this the classic symbol lookup path, and not the module export symbol one. Makes sense that you need the explicit interface implementation. Do you know which one will it bind to if I call it on the object, and not the interface? Or will it just not bind?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code really didn't change for the IModuleSymbols interface (IExportSymbols interface is now implemented by the base Module class with the TryGetSymbolAddressInner helper). Both interfaces should be accessible via the IModule.Services provider and shouldn't be accessed through casting IModule to IModuleSymbols.

I'm not sure I understand your last question about binding.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IExportSymbols (through the base class Module) and IModuleSymbols in this class here both define TryGetSymbolName. I was wondering what happens if in the scope of a member of the class you try to call the method, what happens. If I have something that implements an interface, it's not obvious you need to call the provider. This is not blocking this though.

{
return _moduleService._debuggerServices.GetSymbolByOffset(ModuleIndex, address, out symbol, out displacement) == HResult.S_OK;
}

public bool TryGetSymbolAddress(string name, out ulong address)
bool IModuleSymbols.TryGetSymbolAddress(string name, out ulong address)
{
return _moduleService._debuggerServices.GetOffsetBySymbol(ModuleIndex, name, out address) == HResult.S_OK;
}

#endregion

protected override bool TryGetSymbolAddressInner(string name, out ulong address)
{
return _moduleService._debuggerServices.GetOffsetBySymbol(ModuleIndex, name, out address) == HResult.S_OK;
}

protected override ModuleService ModuleService => _moduleService;
}

Expand Down
5 changes: 3 additions & 2 deletions src/SOS/SOS.Hosting/SOSLibrary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ private delegate int SOSCommandDelegate(

[UnmanagedFunctionPointer(CallingConvention.Winapi)]
private delegate int SOSInitializeDelegate(
IntPtr IHost);
IntPtr IHost,
IntPtr IDebuggerServices);

[UnmanagedFunctionPointer(CallingConvention.Winapi)]
private delegate void SOSUninitializeDelegate();
Expand Down Expand Up @@ -120,7 +121,7 @@ private void Initialize()
{
throw new EntryPointNotFoundException($"Can not find SOS module initialization function: {SOSInitialize}");
}
int result = initializeFunc(_hostWrapper.IHost);
int result = initializeFunc(_hostWrapper.IHost, IntPtr.Zero);
if (result != 0)
{
throw new InvalidOperationException($"SOS initialization FAILED 0x{result:X8}");
Expand Down
Loading