151 lines
5.6 KiB
C#
151 lines
5.6 KiB
C#
/*
|
|
Copyright (C) 2014-2019 de4dot@gmail.com
|
|
|
|
This file is part of dnSpy
|
|
|
|
dnSpy is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
dnSpy is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with dnSpy. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
using System;
|
|
using System.ComponentModel.Composition;
|
|
using System.IO;
|
|
using dnlib.DotNet;
|
|
using dnSpy.Contracts.Debugger;
|
|
using dnSpy.Contracts.Debugger.DotNet.Metadata;
|
|
using dnSpy.Contracts.Documents;
|
|
using dnSpy.Contracts.Metadata;
|
|
|
|
namespace dnSpy.Debugger.DotNet.Metadata {
|
|
[Export(typeof(DbgMetadataService))]
|
|
sealed class DbgMetadataServiceImpl : DbgMetadataService {
|
|
readonly DbgModuleIdProviderService dbgModuleIdProviderService;
|
|
readonly DbgInMemoryModuleService dbgInMemoryModuleService;
|
|
readonly DsDocumentProvider dsDocumentProvider;
|
|
readonly IDsDocumentService documentService;
|
|
readonly DebuggerSettings debuggerSettings;
|
|
|
|
bool UseMemoryModules => debuggerSettings.UseMemoryModules;
|
|
|
|
[ImportingConstructor]
|
|
DbgMetadataServiceImpl(DbgModuleIdProviderService dbgModuleIdProviderService, DbgInMemoryModuleService dbgInMemoryModuleService, DsDocumentProvider dsDocumentProvider, IDsDocumentService documentService, DebuggerSettings debuggerSettings) {
|
|
this.dbgModuleIdProviderService = dbgModuleIdProviderService;
|
|
this.dbgInMemoryModuleService = dbgInMemoryModuleService;
|
|
this.dsDocumentProvider = dsDocumentProvider;
|
|
this.documentService = documentService;
|
|
this.debuggerSettings = debuggerSettings;
|
|
}
|
|
|
|
public override ModuleDef? TryGetMetadata(DbgModule module, DbgLoadModuleOptions options) {
|
|
if (module is null)
|
|
throw new ArgumentNullException(nameof(module));
|
|
|
|
if (UseMemoryModules || module.IsDynamic || module.IsInMemory || (options & DbgLoadModuleOptions.ForceMemory) != 0)
|
|
return dbgInMemoryModuleService.LoadModule(module);
|
|
|
|
var mod = dbgInMemoryModuleService.FindModule(module);
|
|
if (mod is not null)
|
|
return mod;
|
|
|
|
var id = dbgModuleIdProviderService.GetModuleId(module);
|
|
if (id is not null)
|
|
return TryGetMetadata(id.Value, options);
|
|
|
|
return null;
|
|
}
|
|
|
|
ModuleDef? LoadNonDiskFile(ModuleId moduleId, DbgLoadModuleOptions options) {
|
|
if (UseMemoryModules || moduleId.IsDynamic || moduleId.IsInMemory || (options & DbgLoadModuleOptions.ForceMemory) != 0) {
|
|
var module = dbgModuleIdProviderService.GetModule(moduleId);
|
|
if (module is not null)
|
|
return dbgInMemoryModuleService.LoadModule(module);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
// Priority order: 1) active in-memory/dynamic module, 2) active module, 3) other module
|
|
ModuleDef? LoadExisting(ModuleId moduleId) {
|
|
ModuleDef? foundModule = null;
|
|
ModuleDef? activeModule = null;
|
|
foreach (var info in dsDocumentProvider.DocumentInfos) {
|
|
if (info.Id != moduleId)
|
|
continue;
|
|
if (info.IsActive) {
|
|
if (info.Document is MemoryModuleDefDocument || info.Document is DynamicModuleDefDocument)
|
|
return info.Document.ModuleDef;
|
|
activeModule ??= info.Document.ModuleDef;
|
|
}
|
|
else
|
|
foundModule ??= info.Document.ModuleDef;
|
|
}
|
|
return activeModule ?? foundModule;
|
|
}
|
|
|
|
public override ModuleDef? TryGetMetadata(ModuleId moduleId, DbgLoadModuleOptions options) {
|
|
var mod = LoadNonDiskFile(moduleId, options) ?? LoadExisting(moduleId);
|
|
if (mod is not null)
|
|
return mod;
|
|
|
|
if (moduleId.IsDynamic || moduleId.IsInMemory)
|
|
return null;
|
|
|
|
string moduleFilename = moduleId.ModuleName;
|
|
if (!File.Exists(moduleFilename))
|
|
return null;
|
|
var asmFilename = GetAssemblyFilename(moduleFilename, moduleId.AssemblyFullName, moduleId.ModuleNameOnly);
|
|
|
|
bool isAutoLoaded = (options & DbgLoadModuleOptions.AutoLoaded) != 0;
|
|
if (!string2.IsNullOrEmpty(asmFilename)) {
|
|
var document = documentService.TryGetOrCreate(DsDocumentInfo.CreateDocument(asmFilename), isAutoLoaded);
|
|
if (document is null)
|
|
document = documentService.Resolve(new AssemblyNameInfo(moduleId.AssemblyFullName), null);
|
|
if (document is not null) {
|
|
// Common case is a single-file assembly or first module of a multifile assembly
|
|
if (asmFilename.Equals(moduleFilename, StringComparison.OrdinalIgnoreCase))
|
|
return document.ModuleDef;
|
|
|
|
foreach (var child in document.Children) {
|
|
if (child.Filename.Equals(moduleFilename, StringComparison.OrdinalIgnoreCase))
|
|
return child.ModuleDef;
|
|
}
|
|
}
|
|
}
|
|
|
|
return documentService.TryGetOrCreate(DsDocumentInfo.CreateDocument(moduleFilename), isAutoLoaded)?.ModuleDef;
|
|
}
|
|
|
|
static string? GetAssemblyFilename(string moduleFilename, string assemblyFullName, bool moduleNameOnly) {
|
|
if (string2.IsNullOrEmpty(moduleFilename) || (string2.IsNullOrEmpty(assemblyFullName) && !moduleNameOnly))
|
|
return null;
|
|
if (moduleNameOnly)
|
|
return moduleFilename;
|
|
try {
|
|
var asm = new AssemblyNameInfo(assemblyFullName);
|
|
if (Path.GetFileName(asm.Name) != asm.Name || asm.Name.Length == 0)
|
|
return null;
|
|
var filenameNoExt = Path.Combine(Path.GetDirectoryName(moduleFilename)!, asm.Name);
|
|
var dllFilename = filenameNoExt + ".dll";
|
|
if (File.Exists(dllFilename))
|
|
return dllFilename;
|
|
var exeFilename = filenameNoExt + ".exe";
|
|
if (File.Exists(exeFilename))
|
|
return exeFilename;
|
|
}
|
|
catch {
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
}
|