diff --git a/src/coreclr/src/inc/CrstTypes.def b/src/coreclr/src/inc/CrstTypes.def index 28d638bfc345e..58a5fe2daf529 100644 --- a/src/coreclr/src/inc/CrstTypes.def +++ b/src/coreclr/src/inc/CrstTypes.def @@ -702,3 +702,6 @@ End Crst NativeImageEagerFixups End + +Crst NativeImageLoad +End diff --git a/src/coreclr/src/inc/clrconfigvalues.h b/src/coreclr/src/inc/clrconfigvalues.h index 9b03b075a6c75..267dd0827ae73 100644 --- a/src/coreclr/src/inc/clrconfigvalues.h +++ b/src/coreclr/src/inc/clrconfigvalues.h @@ -484,6 +484,8 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_PartialNGen, W("PartialNGen"), (DWORD)-1, "Gen CONFIG_DWORD_INFO(INTERNAL_NoASLRForNgen, W("NoASLRForNgen"), 0, "Turn off IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE bit in generated ngen images. Makes nidump output repeatable from run to run.") +RETAIL_CONFIG_STRING_INFO_EX(INTERNAL_NativeImageSearchPaths, W("NativeImageSearchPaths"), "Extra search paths for native composite R2R images", CLRConfig::EEConfig_default) + #ifdef CROSSGEN_COMPILE RETAIL_CONFIG_DWORD_INFO(INTERNAL_CrossGenAssumeInputSigned, W("CrossGenAssumeInputSigned"), 1, "CrossGen should assume that its input assemblies will be signed before deployment") #endif diff --git a/src/coreclr/src/inc/crsttypes.h b/src/coreclr/src/inc/crsttypes.h index fa26d3ef17d78..15ff9d8bfc550 100644 --- a/src/coreclr/src/inc/crsttypes.h +++ b/src/coreclr/src/inc/crsttypes.h @@ -108,67 +108,68 @@ enum CrstType CrstNativeBinderInit = 89, CrstNativeImageCache = 90, CrstNativeImageEagerFixups = 91, - CrstNls = 92, - CrstNotifyGdb = 93, - CrstObjectList = 94, - CrstOnEventManager = 95, - CrstPatchEntryPoint = 96, - CrstPEImage = 97, - CrstPEImagePDBStream = 98, - CrstPendingTypeLoadEntry = 99, - CrstPinHandle = 100, - CrstPinnedByrefValidation = 101, - CrstProfilerGCRefDataFreeList = 102, - CrstProfilingAPIStatus = 103, - CrstPublisherCertificate = 104, - CrstRCWCache = 105, - CrstRCWCleanupList = 106, - CrstRCWRefCache = 107, - CrstReadyToRunEntryPointToMethodDescMap = 108, - CrstReDacl = 109, - CrstReflection = 110, - CrstReJITGlobalRequest = 111, - CrstRemoting = 112, - CrstRetThunkCache = 113, - CrstRWLock = 114, - CrstSavedExceptionInfo = 115, - CrstSaveModuleProfileData = 116, - CrstSecurityStackwalkCache = 117, - CrstSharedAssemblyCreate = 118, - CrstSigConvert = 119, - CrstSingleUseLock = 120, - CrstSpecialStatics = 121, - CrstSqmManager = 122, - CrstStackSampler = 123, - CrstStressLog = 124, - CrstStrongName = 125, - CrstStubCache = 126, - CrstStubDispatchCache = 127, - CrstStubUnwindInfoHeapSegments = 128, - CrstSyncBlockCache = 129, - CrstSyncHashLock = 130, - CrstSystemBaseDomain = 131, - CrstSystemDomain = 132, - CrstSystemDomainDelayedUnloadList = 133, - CrstThreadIdDispenser = 134, - CrstThreadpoolEventCache = 135, - CrstThreadpoolTimerQueue = 136, - CrstThreadpoolWaitThreads = 137, - CrstThreadpoolWorker = 138, - CrstThreadStaticDataHashTable = 139, - CrstThreadStore = 140, - CrstTieredCompilation = 141, - CrstTPMethodTable = 142, - CrstTypeEquivalenceMap = 143, - CrstTypeIDMap = 144, - CrstUMEntryThunkCache = 145, - CrstUMThunkHash = 146, - CrstUniqueStack = 147, - CrstUnresolvedClassLock = 148, - CrstUnwindInfoTableLock = 149, - CrstVSDIndirectionCellLock = 150, - CrstWrapperTemplate = 151, - kNumberOfCrstTypes = 152 + CrstNativeImageLoad = 92, + CrstNls = 93, + CrstNotifyGdb = 94, + CrstObjectList = 95, + CrstOnEventManager = 96, + CrstPatchEntryPoint = 97, + CrstPEImage = 98, + CrstPEImagePDBStream = 99, + CrstPendingTypeLoadEntry = 100, + CrstPinHandle = 101, + CrstPinnedByrefValidation = 102, + CrstProfilerGCRefDataFreeList = 103, + CrstProfilingAPIStatus = 104, + CrstPublisherCertificate = 105, + CrstRCWCache = 106, + CrstRCWCleanupList = 107, + CrstRCWRefCache = 108, + CrstReadyToRunEntryPointToMethodDescMap = 109, + CrstReDacl = 110, + CrstReflection = 111, + CrstReJITGlobalRequest = 112, + CrstRemoting = 113, + CrstRetThunkCache = 114, + CrstRWLock = 115, + CrstSavedExceptionInfo = 116, + CrstSaveModuleProfileData = 117, + CrstSecurityStackwalkCache = 118, + CrstSharedAssemblyCreate = 119, + CrstSigConvert = 120, + CrstSingleUseLock = 121, + CrstSpecialStatics = 122, + CrstSqmManager = 123, + CrstStackSampler = 124, + CrstStressLog = 125, + CrstStrongName = 126, + CrstStubCache = 127, + CrstStubDispatchCache = 128, + CrstStubUnwindInfoHeapSegments = 129, + CrstSyncBlockCache = 130, + CrstSyncHashLock = 131, + CrstSystemBaseDomain = 132, + CrstSystemDomain = 133, + CrstSystemDomainDelayedUnloadList = 134, + CrstThreadIdDispenser = 135, + CrstThreadpoolEventCache = 136, + CrstThreadpoolTimerQueue = 137, + CrstThreadpoolWaitThreads = 138, + CrstThreadpoolWorker = 139, + CrstThreadStaticDataHashTable = 140, + CrstThreadStore = 141, + CrstTieredCompilation = 142, + CrstTPMethodTable = 143, + CrstTypeEquivalenceMap = 144, + CrstTypeIDMap = 145, + CrstUMEntryThunkCache = 146, + CrstUMThunkHash = 147, + CrstUniqueStack = 148, + CrstUnresolvedClassLock = 149, + CrstUnwindInfoTableLock = 150, + CrstVSDIndirectionCellLock = 151, + CrstWrapperTemplate = 152, + kNumberOfCrstTypes = 153 }; #endif // __CRST_TYPES_INCLUDED @@ -271,6 +272,7 @@ int g_rgCrstLevelMap[] = -1, // CrstNativeBinderInit -1, // CrstNativeImageCache 0, // CrstNativeImageEagerFixups + 0, // CrstNativeImageLoad 0, // CrstNls 0, // CrstNotifyGdb 2, // CrstObjectList @@ -428,6 +430,7 @@ LPCSTR g_rgCrstNameMap[] = "CrstNativeBinderInit", "CrstNativeImageCache", "CrstNativeImageEagerFixups", + "CrstNativeImageLoad", "CrstNls", "CrstNotifyGdb", "CrstObjectList", diff --git a/src/coreclr/src/inc/shash.h b/src/coreclr/src/inc/shash.h index 58d08d4a67d7a..8c1aeedbf0ae8 100644 --- a/src/coreclr/src/inc/shash.h +++ b/src/coreclr/src/inc/shash.h @@ -1028,23 +1028,7 @@ class EMPTY_BASES_DECL MapSHash : public SHash< TRAITS > PARENT::Add(KeyValuePair(key, value)); } - BOOL Lookup(KEY key, VALUE* pValue) const - { - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - PRECONDITION(key != (KEY)0); - } - CONTRACTL_END; - - const KeyValuePair *pRet = PARENT::LookupPtr(key); - if (pRet == NULL) - return FALSE; - - *pValue = pRet->Value(); - return TRUE; - } + BOOL Lookup(KEY key, VALUE* pValue) const; }; template diff --git a/src/coreclr/src/inc/shash.inl b/src/coreclr/src/inc/shash.inl index d0d6e1a6b9a94..cdca15e3d8192 100644 --- a/src/coreclr/src/inc/shash.inl +++ b/src/coreclr/src/inc/shash.inl @@ -947,5 +947,23 @@ bool SHash::CheckAddInPhases( // 4. addCall's destructor will take care of any required cleanup. } +template +BOOL MapSHash::Lookup(KEY key, VALUE* pValue) const +{ + CONTRACTL + { + NOTHROW_UNLESS_TRAITS_THROWS; + GC_NOTRIGGER; + PRECONDITION(key != (KEY)0); + } + CONTRACTL_END; + + const KeyValuePair *pRet = PARENT::LookupPtr(key); + if (pRet == NULL) + return FALSE; + + *pValue = pRet->Value(); + return TRUE; +} #endif // _SHASH_INL_ diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs index bd192ca1429d6..5216e851df785 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs @@ -222,6 +222,8 @@ public sealed class ReadyToRunCodegenCompilation : Compilation /// private readonly IEnumerable _inputFiles; + private readonly string _compositeRootPath; + private bool _resilient; private int _parallelism; @@ -243,6 +245,7 @@ internal ReadyToRunCodegenCompilation( Logger logger, DevirtualizationManager devirtualizationManager, IEnumerable inputFiles, + string compositeRootPath, InstructionSetSupport instructionSetSupport, bool resilient, bool generateMapFile, @@ -268,6 +271,7 @@ internal ReadyToRunCodegenCompilation( SymbolNodeFactory = new ReadyToRunSymbolNodeFactory(nodeFactory); _corInfoImpls = new ConditionalWeakTable(); _inputFiles = inputFiles; + _compositeRootPath = compositeRootPath; CompilationModuleGroup = (ReadyToRunCompilationModuleGroupBase)nodeFactory.CompilationModuleGroup; // Generate baseline support specification for InstructionSetSupport. This will prevent usage of the generated @@ -281,7 +285,7 @@ internal ReadyToRunCodegenCompilation( _fileLayoutOptimizer = new ReadyToRunFileLayoutOptimizer(methodLayoutAlgorithm, fileLayoutAlgorithm, profileData, _nodeFactory); } - + private readonly static string s_folderUpPrefix = ".." + Path.DirectorySeparatorChar; public override void Compile(string outputFile) { @@ -299,13 +303,19 @@ public override void Compile(string outputFile) if (moduleGroup.IsCompositeBuildMode) { // In composite mode with standalone MSIL we rewrite all input MSIL assemblies to the - // output folder, adding a format R2R header to them with forwarding information to + // output folder, adding a formal R2R header to them with forwarding information to // the composite executable. string outputDirectory = Path.GetDirectoryName(outputFile); string ownerExecutableName = Path.GetFileName(outputFile); foreach (string inputFile in _inputFiles) { - string standaloneMsilOutputFile = Path.Combine(outputDirectory, Path.GetFileName(inputFile)); + string relativeMsilPath = Path.GetRelativePath(_compositeRootPath, inputFile); + if (relativeMsilPath.StartsWith(s_folderUpPrefix)) + { + // Input file not in the composite root, emit to root output folder + relativeMsilPath = Path.GetFileName(inputFile); + } + string standaloneMsilOutputFile = Path.Combine(outputDirectory, relativeMsilPath); RewriteComponentFile(inputFile: inputFile, outputFile: standaloneMsilOutputFile, ownerExecutableName: ownerExecutableName); } } @@ -316,6 +326,8 @@ private void RewriteComponentFile(string inputFile, string outputFile, string ow { EcmaModule inputModule = NodeFactory.TypeSystemContext.GetModuleFromPath(inputFile); + Directory.CreateDirectory(Path.GetDirectoryName(outputFile)); + CopiedCorHeaderNode copiedCorHeader = new CopiedCorHeaderNode(inputModule); DebugDirectoryNode debugDirectory = new DebugDirectoryNode(inputModule, outputFile); NodeFactory componentFactory = new NodeFactory( diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs index 7cc5d3daa0fa1..e03372d9b2ac5 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs @@ -20,6 +20,7 @@ namespace ILCompiler public sealed class ReadyToRunCodegenCompilationBuilder : CompilationBuilder { private readonly IEnumerable _inputFiles; + private readonly string _compositeRootPath; private bool _ibcTuning; private bool _resilient; private bool _generateMapFile; @@ -38,10 +39,15 @@ public sealed class ReadyToRunCodegenCompilationBuilder : CompilationBuilder private KeyValuePair[] _ryujitOptions = Array.Empty>(); private ILProvider _ilProvider = new ReadyToRunILProvider(); - public ReadyToRunCodegenCompilationBuilder(CompilerTypeSystemContext context, ReadyToRunCompilationModuleGroupBase group, IEnumerable inputFiles) + public ReadyToRunCodegenCompilationBuilder( + CompilerTypeSystemContext context, + ReadyToRunCompilationModuleGroupBase group, + IEnumerable inputFiles, + string compositeRootPath) : base(context, group, new CoreRTNameMangler()) { _inputFiles = inputFiles; + _compositeRootPath = compositeRootPath; // R2R field layout needs compilation group information ((ReadyToRunCompilerContext)context).SetCompilationGroup(group); @@ -224,6 +230,7 @@ public override ICompilation ToCompilation() _logger, new DependencyAnalysis.ReadyToRun.DevirtualizationManager(_compilationGroup), _inputFiles, + _compositeRootPath, _instructionSetSupport, _resilient, _generateMapFile, diff --git a/src/coreclr/src/tools/crossgen2/crossgen2/CommandLineOptions.cs b/src/coreclr/src/tools/crossgen2/crossgen2/CommandLineOptions.cs index 721ea2e507126..c0070ab8b9ecd 100644 --- a/src/coreclr/src/tools/crossgen2/crossgen2/CommandLineOptions.cs +++ b/src/coreclr/src/tools/crossgen2/crossgen2/CommandLineOptions.cs @@ -16,6 +16,8 @@ public class CommandLineOptions public string[] Reference { get; set; } public string InstructionSet { get; set; } public FileInfo OutputFilePath { get; set; } + + public DirectoryInfo CompositeRootPath { get; set; } public bool Optimize { get; set; } public bool OptimizeSpace { get; set; } public bool OptimizeTime { get; set; } @@ -92,6 +94,10 @@ public static Command RootCommand() { Argument = new Argument() }, + new Option(new[] { "--compositerootpath", "--crp" }, SR.CompositeRootPath) + { + Argument = new Argument() + }, new Option(new[] { "--optimize", "-O" }, SR.EnableOptimizationsOption) { Argument = new Argument() diff --git a/src/coreclr/src/tools/crossgen2/crossgen2/Program.cs b/src/coreclr/src/tools/crossgen2/crossgen2/Program.cs index 5e77aea2235c9..e1df88322d2a4 100644 --- a/src/coreclr/src/tools/crossgen2/crossgen2/Program.cs +++ b/src/coreclr/src/tools/crossgen2/crossgen2/Program.cs @@ -291,6 +291,8 @@ private int Run() var targetDetails = new TargetDetails(_targetArchitecture, _targetOS, TargetAbi.CoreRT, instructionSetSupport.GetVectorTSimdVector()); CompilerTypeSystemContext typeSystemContext = new ReadyToRunCompilerContext(targetDetails, genericsMode); + string compositeRootPath = _commandLineOptions.CompositeRootPath?.FullName; + // // TODO: To support our pre-compiled test tree, allow input files that aren't managed assemblies since // some tests contain a mixture of both managed and native binaries. @@ -311,6 +313,10 @@ private int Run() allInputFilePaths.Add(inputFile.Key, inputFile.Value); inputFilePaths.Add(inputFile.Key, inputFile.Value); referenceableModules.Add(module); + if (compositeRootPath == null) + { + compositeRootPath = Path.GetDirectoryName(inputFile.Value); + } } catch (TypeSystemException.BadImageFormatException) { @@ -329,6 +335,10 @@ private int Run() allInputFilePaths.Add(unrootedInputFile.Key, unrootedInputFile.Value); unrootedInputFilePaths.Add(unrootedInputFile.Key, unrootedInputFile.Value); referenceableModules.Add(module); + if (compositeRootPath == null) + { + compositeRootPath = Path.GetDirectoryName(unrootedInputFile.Value); + } } } catch (TypeSystemException.BadImageFormatException) @@ -490,7 +500,7 @@ private int Run() // ReadyToRunCodegenCompilationBuilder builder = new ReadyToRunCodegenCompilationBuilder( - typeSystemContext, compilationGroup, allInputFilePaths.Values); + typeSystemContext, compilationGroup, allInputFilePaths.Values, compositeRootPath); string compilationUnitPrefix = ""; builder.UseCompilationUnitPrefix(compilationUnitPrefix); diff --git a/src/coreclr/src/tools/crossgen2/crossgen2/Properties/Resources.resx b/src/coreclr/src/tools/crossgen2/crossgen2/Properties/Resources.resx index e9504c5e5e60c..3c1f25a1bfacf 100644 --- a/src/coreclr/src/tools/crossgen2/crossgen2/Properties/Resources.resx +++ b/src/coreclr/src/tools/crossgen2/crossgen2/Properties/Resources.resx @@ -273,4 +273,7 @@ Layout non-method contents of file using profile driven optimization assuming the layout algorithm specified. The default value is DefaultSort, which indicates that complex layout is disabled + + Logical root folder for composite builds; defaults to directory of 1st input file. + \ No newline at end of file diff --git a/src/coreclr/src/vm/appdomain.cpp b/src/coreclr/src/vm/appdomain.cpp index 87c9283d7ff6c..1feaacbc2a74e 100644 --- a/src/coreclr/src/vm/appdomain.cpp +++ b/src/coreclr/src/vm/appdomain.cpp @@ -2262,7 +2262,6 @@ AppDomain::AppDomain() #ifdef FEATURE_PREJIT m_pDomainFileWithNativeImageList = NULL; #endif - } // AppDomain::AppDomain AppDomain::~AppDomain() @@ -2360,6 +2359,8 @@ void AppDomain::Init() m_tieredCompilationManager.Init(); #endif #endif // CROSSGEN_COMPILE + + m_nativeImageLoadCrst.Init(CrstNativeImageLoad); } // AppDomain::Init @@ -6075,6 +6076,32 @@ PTR_DomainAssembly AppDomain::FindAssembly(PTR_ICLRPrivAssembly pHostAssembly) } } +#ifndef DACCESS_COMPILE +// Return native image for a given composite image file name, NULL when not found. +PTR_NativeImage AppDomain::GetNativeImage(LPCUTF8 simpleFileName) +{ + CrstHolder ch(&m_nativeImageLoadCrst); + PTR_NativeImage pExistingImage; + if (m_nativeImageMap.Lookup(simpleFileName, &pExistingImage)) + { + return pExistingImage; + } + return nullptr; +} + +PTR_NativeImage AppDomain::SetNativeImage(LPCUTF8 simpleFileName, PTR_NativeImage pNativeImage) +{ + CrstHolder ch(&m_nativeImageLoadCrst); + PTR_NativeImage pExistingImage; + if (m_nativeImageMap.Lookup(simpleFileName, &pExistingImage)) + { + return pExistingImage; + } + m_nativeImageMap.Add(simpleFileName, pNativeImage); + return nullptr; +} +#endif//DACCESS_COMPILE + #if !defined(DACCESS_COMPILE) && defined(FEATURE_NATIVE_IMAGE_GENERATION) void ZapperSetBindingPaths(ICorCompilationDomain *pDomain, SString &trustedPlatformAssemblies, SString &platformResourceRoots, SString &appPaths, SString &appNiPaths) diff --git a/src/coreclr/src/vm/appdomain.hpp b/src/coreclr/src/vm/appdomain.hpp index 6b1393fa2d7c2..def9b2f025f9b 100644 --- a/src/coreclr/src/vm/appdomain.hpp +++ b/src/coreclr/src/vm/appdomain.hpp @@ -1559,6 +1559,10 @@ class AppDomain : public BaseDomain OBJECTREF GetRawExposedObject() { LIMITED_METHOD_CONTRACT; return NULL; } OBJECTHANDLE GetRawExposedObjectHandleForDebugger() { LIMITED_METHOD_DAC_CONTRACT; return NULL; } +#ifndef DACCESS_COMPILE + PTR_NativeImage GetNativeImage(LPCUTF8 compositeFileName); + PTR_NativeImage SetNativeImage(LPCUTF8 compositeFileName, PTR_NativeImage pNativeImage); +#endif // DACCESS_COMPILE //**************************************************************************************** @@ -2318,6 +2322,12 @@ class AppDomain : public BaseDomain // Hash table that maps a clsid to a type PtrHashMap m_clsidHash; +#ifndef DACCESS_COMPILE + // Map of loaded composite native images indexed by base load addresses + CrstExplicitInit m_nativeImageLoadCrst; + MapSHash m_nativeImageMap; +#endif + #ifdef FEATURE_COMINTEROP // this cache stores the RCWs in this domain RCWCache *m_pRCWCache; diff --git a/src/coreclr/src/vm/assemblyloadcontext.cpp b/src/coreclr/src/vm/assemblyloadcontext.cpp index d5adfffbd0943..fa226ddfadea2 100644 --- a/src/coreclr/src/vm/assemblyloadcontext.cpp +++ b/src/coreclr/src/vm/assemblyloadcontext.cpp @@ -25,48 +25,6 @@ NativeImage *AssemblyLoadContext::LoadNativeImage(Module *componentModule, LPCUT AssemblyLoadContext *loadContext = componentModule->GetFile()->GetAssemblyLoadContext(); PTR_LoaderAllocator moduleLoaderAllocator = componentModule->GetLoaderAllocator(); - int nativeImageCount = m_nativeImages.GetCount(); -#ifndef FEATURE_CASE_SENSITIVE_FILESYSTEM - SString nativeImageNameString; - nativeImageNameString.SetUTF8(nativeImageName); -#endif - for (int nativeImageIndex = 0; nativeImageIndex < nativeImageCount; nativeImageIndex++) - { - NativeImage *nativeImage = m_nativeImages[nativeImageIndex]; - LPCUTF8 existingImageFileName = nativeImage->GetFileName(); -#ifdef FEATURE_CASE_SENSITIVE_FILESYSTEM - bool match = (strcmp(nativeImageName, existingImageFileName) == 0); -#else - bool match = SString(SString::Utf8Literal, existingImageFileName).EqualsCaseInsensitive(nativeImageNameString); -#endif - if (match) - { - return nativeImage; - } - } - - SString path = componentModule->GetPath(); - SString::Iterator lastPathSeparatorIter = path.End(); - size_t pathDirLength = 0; - if (PEAssembly::FindLastPathSeparator(path, lastPathSeparatorIter)) - { - pathDirLength = (lastPathSeparatorIter - path.Begin()) + 1; - } - - SString compositeImageFileName(SString::Utf8, nativeImageName); - SString fullPath; - fullPath.Set(path, path.Begin(), (COUNT_T)pathDirLength); - fullPath += compositeImageFileName; - - AllocMemTracker amTracker; - NativeImage *nativeImage = NativeImage::Open(fullPath, nativeImageName, moduleLoaderAllocator, &amTracker); - if (nativeImage != NULL) - { - m_nativeImages.Append(nativeImage); - amTracker.SuppressRelease(); - return nativeImage; - } - - return NULL; + return NativeImage::Open(componentModule, nativeImageName, loadContext, moduleLoaderAllocator); } #endif diff --git a/src/coreclr/src/vm/assemblyloadcontext.h b/src/coreclr/src/vm/assemblyloadcontext.h index dd6d958f1ce2c..b18ecf6249fc5 100644 --- a/src/coreclr/src/vm/assemblyloadcontext.h +++ b/src/coreclr/src/vm/assemblyloadcontext.h @@ -23,9 +23,6 @@ class AssemblyLoadContext : public IUnknownCommon m_nativeImages; }; #endif diff --git a/src/coreclr/src/vm/nativeimage.cpp b/src/coreclr/src/vm/nativeimage.cpp index e181bfb06446f..d321deb9ce35a 100644 --- a/src/coreclr/src/vm/nativeimage.cpp +++ b/src/coreclr/src/vm/nativeimage.cpp @@ -20,7 +20,7 @@ BOOL AssemblyNameIndexHashTraits::Equals(LPCUTF8 a, LPCUTF8 b) { WRAPPER_NO_CONTRACT; - return SString(SString::Utf8Literal, a).Compare(SString(SString::Utf8Literal, b)) == 0; + return SString(SString::Utf8Literal, a).CompareCaseInsensitive(SString(SString::Utf8Literal, b)) == 0; } AssemblyNameIndexHashTraits::count_t AssemblyNameIndexHashTraits::Hash(LPCUTF8 s) @@ -30,7 +30,21 @@ AssemblyNameIndexHashTraits::count_t AssemblyNameIndexHashTraits::Hash(LPCUTF8 s return SString(SString::Utf8Literal, s).HashCaseInsensitive(); } -NativeImage::NativeImage(PEImageLayout *pImageLayout, LPCUTF8 imageFileName) +BOOL NativeImageIndexTraits::Equals(LPCUTF8 a, LPCUTF8 b) +{ + WRAPPER_NO_CONTRACT; + + return SString(SString::Utf8Literal, a).CompareCaseInsensitive(SString(SString::Utf8Literal, b)) == 0; +} + +NativeImageIndexTraits::count_t NativeImageIndexTraits::Hash(LPCUTF8 a) +{ + WRAPPER_NO_CONTRACT; + + return SString(SString::Utf8Literal, a).HashCaseInsensitive(); +} + +NativeImage::NativeImage(AssemblyLoadContext *pAssemblyLoadContext, PEImageLayout *pImageLayout, LPCUTF8 imageFileName) : m_eagerFixupsLock(CrstNativeImageEagerFixups) { CONTRACTL @@ -42,6 +56,7 @@ NativeImage::NativeImage(PEImageLayout *pImageLayout, LPCUTF8 imageFileName) } CONTRACTL_END; + m_pAssemblyLoadContext = pAssemblyLoadContext; m_pImageLayout = pImageLayout; m_fileName = imageFileName; m_eagerFixupsHaveRun = false; @@ -96,14 +111,90 @@ NativeImage::~NativeImage() #ifndef DACCESS_COMPILE NativeImage *NativeImage::Open( - LPCWSTR fullPath, + Module *componentModule, LPCUTF8 nativeImageFileName, - LoaderAllocator *pLoaderAllocator, - AllocMemTracker *pamTracker) + AssemblyLoadContext *pAssemblyLoadContext, + LoaderAllocator *pLoaderAllocator) { STANDARD_VM_CONTRACT; - NewHolder peLoadedImage = PEImageLayout::LoadNative(fullPath); + NativeImage *pExistingImage = AppDomain::GetCurrentDomain()->GetNativeImage(nativeImageFileName); + if (pExistingImage != nullptr) + { + return pExistingImage->GetAssemblyLoadContext() == pAssemblyLoadContext ? pExistingImage : nullptr; + } + + SString path = componentModule->GetPath(); + SString::Iterator lastPathSeparatorIter = path.End(); + size_t pathDirLength = 0; + if (PEAssembly::FindLastPathSeparator(path, lastPathSeparatorIter)) + { + pathDirLength = (lastPathSeparatorIter - path.Begin()) + 1; + } + + SString compositeImageFileName(SString::Utf8, nativeImageFileName); + SString fullPath; + fullPath.Set(path, path.Begin(), (COUNT_T)pathDirLength); + fullPath += compositeImageFileName; + LPWSTR searchPathsConfig; + IfFailThrow(CLRConfig::GetConfigValue(CLRConfig::INTERNAL_NativeImageSearchPaths, &searchPathsConfig)); + + NewHolder peLoadedImage; + + EX_TRY + { + peLoadedImage = PEImageLayout::LoadNative(fullPath); + } + EX_CATCH + { + SString searchPaths(searchPathsConfig); + SString::CIterator start = searchPaths.Begin(); + while (start != searchPaths.End()) + { + SString::CIterator end = start; + if (!searchPaths.Find(end, PATH_SEPARATOR_CHAR_W)) + { + end = searchPaths.End(); + } + fullPath.Set(searchPaths, start, (COUNT_T)(end - start)); + + if (end != searchPaths.End()) + { + // Skip path separator character + ++end; + } + start = end; + + if (fullPath.GetCount() == 0) + { + continue; + } + + fullPath.Append(DIRECTORY_SEPARATOR_CHAR_W); + fullPath += compositeImageFileName; + + EX_TRY + { + peLoadedImage = PEImageLayout::LoadNative(fullPath); + break; + } + EX_CATCH + { + } + EX_END_CATCH(SwallowAllExceptions) + } + } + EX_END_CATCH(SwallowAllExceptions) + + if (peLoadedImage.IsNull()) + { + // Failed to locate the native composite R2R image + LOG((LF_LOADER, LL_ALWAYS, "LOADER: failed to load native image '%s' for component assembly '%S' using search paths: '%S'\n", + nativeImageFileName, + path.GetUnicode(), + searchPathsConfig != nullptr ? searchPathsConfig : W(""))); + RaiseFailFastException(nullptr, nullptr, 0); + } READYTORUN_HEADER *pHeader = (READYTORUN_HEADER *)peLoadedImage->GetExport("RTR_HEADER"); if (pHeader == NULL) @@ -118,9 +209,18 @@ NativeImage *NativeImage::Open( { COMPlusThrowHR(COR_E_BADIMAGEFORMAT); } - NewHolder image = new NativeImage(peLoadedImage.Extract(), nativeImageFileName); - image->Initialize(pHeader, pLoaderAllocator, pamTracker); - return image.Extract(); + NewHolder image = new NativeImage(pAssemblyLoadContext, peLoadedImage.Extract(), nativeImageFileName); + AllocMemTracker amTracker; + image->Initialize(pHeader, pLoaderAllocator, &amTracker); + pExistingImage = AppDomain::GetCurrentDomain()->SetNativeImage(nativeImageFileName, image); + if (pExistingImage == nullptr) + { + // No pre-existing image, new image has been stored in the map + amTracker.SuppressRelease(); + return image.Extract(); + } + // Return pre-existing image if it was loaded into the same ALC, null otherwise + return (pExistingImage->GetAssemblyLoadContext() == pAssemblyLoadContext ? pExistingImage : nullptr); } #endif diff --git a/src/coreclr/src/vm/nativeimage.h b/src/coreclr/src/vm/nativeimage.h index 323a509ecbc83..f40ef694ab7c5 100644 --- a/src/coreclr/src/vm/nativeimage.h +++ b/src/coreclr/src/vm/nativeimage.h @@ -38,6 +38,25 @@ class AssemblyNameIndexHashTraits : public NoRemoveSHashTraits< DefaultSHashTrai static count_t Hash(LPCUTF8 a); }; +typedef DPTR(class NativeImage) PTR_NativeImage; + +class NativeImageIndexTraits : public NoRemoveSHashTraits> +{ +public: + // Similar to BaseAssemblySpec::CompareStrings, we're using temporary SStrings that throw + // for case-insensitive UTF8 assembly name comparisons. + static const bool s_NoThrow = false; + + static LPCUTF8 GetKey(const KeyValuePair& e) { return e.Key(); } + static BOOL Equals(LPCUTF8 a, LPCUTF8 b); + static count_t Hash(LPCUTF8 a); + + static KeyValuePair Null() { LIMITED_METHOD_CONTRACT; return KeyValuePair(nullptr, PTR_NativeImage(nullptr)); } + static KeyValuePair Deleted() { LIMITED_METHOD_CONTRACT; return KeyValuePair(nullptr, PTR_NativeImage(nullptr)); } + static bool IsNull(const KeyValuePair& e) { LIMITED_METHOD_CONTRACT; return e.Key() == nullptr; } + static bool IsDeleted(const KeyValuePair& e) { return e.Key() == nullptr; } +}; + class AssemblyLoadContext; class ReadyToRunInfo; class PEFile; @@ -58,7 +77,8 @@ class NativeImage private: // Points to the OwnerCompositeExecutable section content within the component MSIL module LPCUTF8 m_fileName; - + + AssemblyLoadContext *m_pAssemblyLoadContext; ReadyToRunInfo *m_pReadyToRunInfo; IMDInternalImport *m_pManifestMetadata; PEImageLayout *m_pImageLayout; @@ -73,7 +93,7 @@ class NativeImage bool m_eagerFixupsHaveRun; private: - NativeImage(PEImageLayout *peImageLayout, LPCUTF8 imageFileName); + NativeImage(AssemblyLoadContext *pAssemblyLoadContext, PEImageLayout *peImageLayout, LPCUTF8 imageFileName); protected: void Initialize(READYTORUN_HEADER *header, LoaderAllocator *loaderAllocator, AllocMemTracker *pamTracker); @@ -82,10 +102,10 @@ class NativeImage ~NativeImage(); static NativeImage *Open( - LPCWSTR fullPath, + Module *componentModule, LPCUTF8 nativeImageFileName, - LoaderAllocator *pLoaderAllocator, - AllocMemTracker *pamTracker); + AssemblyLoadContext *pAssemblyLoadContext, + LoaderAllocator *pLoaderAllocator); Crst *EagerFixupsLock() { return &m_eagerFixupsLock; } bool EagerFixupsHaveRun() const { return m_eagerFixupsHaveRun; } @@ -97,6 +117,7 @@ class NativeImage IMDInternalImport *GetManifestMetadata() const { return m_pManifestMetadata; } uint32_t GetManifestAssemblyCount() const { return m_manifestAssemblyCount; } PTR_Assembly *GetManifestMetadataAssemblyRefMap() { return m_pNativeMetadataAssemblyRefMap; } + AssemblyLoadContext *GetAssemblyLoadContext() const { return m_pAssemblyLoadContext; } Assembly *LoadManifestAssembly(uint32_t rowid); diff --git a/src/coreclr/src/vm/readytoruninfo.h b/src/coreclr/src/vm/readytoruninfo.h index f4b62b0d48fcf..d86786042aca2 100644 --- a/src/coreclr/src/vm/readytoruninfo.h +++ b/src/coreclr/src/vm/readytoruninfo.h @@ -45,7 +45,6 @@ class ReadyToRunCoreInfo typedef DPTR(class ReadyToRunInfo) PTR_ReadyToRunInfo; typedef DPTR(class ReadyToRunCoreInfo) PTR_ReadyToRunCoreInfo; -typedef DPTR(class NativeImage) PTR_NativeImage; class ReadyToRunInfo { diff --git a/src/coreclr/tests/issues.targets b/src/coreclr/tests/issues.targets index a9845c7331b03..ec9ec72cfda35 100644 --- a/src/coreclr/tests/issues.targets +++ b/src/coreclr/tests/issues.targets @@ -618,6 +618,9 @@ Needs Triage + + https://github.com/dotnet/runtime/issues/37990 + diff --git a/src/coreclr/tests/src/readytorun/multifolder/FolderA/FileA.cs b/src/coreclr/tests/src/readytorun/multifolder/FolderA/FileA.cs new file mode 100644 index 0000000000000..b93c5d239ad76 --- /dev/null +++ b/src/coreclr/tests/src/readytorun/multifolder/FolderA/FileA.cs @@ -0,0 +1,10 @@ +// 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; + +public class ClassA +{ + public static string Property => "ClassA.Property"; +} diff --git a/src/coreclr/tests/src/readytorun/multifolder/FolderA/FolderA.csproj b/src/coreclr/tests/src/readytorun/multifolder/FolderA/FolderA.csproj new file mode 100644 index 0000000000000..5e9b19ed4efde --- /dev/null +++ b/src/coreclr/tests/src/readytorun/multifolder/FolderA/FolderA.csproj @@ -0,0 +1,11 @@ + + + Library + BuildOnly + false + + + + + + diff --git a/src/coreclr/tests/src/readytorun/multifolder/FolderB/FileB.cs b/src/coreclr/tests/src/readytorun/multifolder/FolderB/FileB.cs new file mode 100644 index 0000000000000..41a28aa33bc79 --- /dev/null +++ b/src/coreclr/tests/src/readytorun/multifolder/FolderB/FileB.cs @@ -0,0 +1,10 @@ +// 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; + +public class ClassB +{ + public static string Property => "ClassB.Property"; +} diff --git a/src/coreclr/tests/src/readytorun/multifolder/FolderB/FolderB.csproj b/src/coreclr/tests/src/readytorun/multifolder/FolderB/FolderB.csproj new file mode 100644 index 0000000000000..7a6e040103a52 --- /dev/null +++ b/src/coreclr/tests/src/readytorun/multifolder/FolderB/FolderB.csproj @@ -0,0 +1,11 @@ + + + Library + BuildOnly + false + + + + + + diff --git a/src/coreclr/tests/src/readytorun/multifolder/Root.cs b/src/coreclr/tests/src/readytorun/multifolder/Root.cs new file mode 100644 index 0000000000000..f53a1c73dba73 --- /dev/null +++ b/src/coreclr/tests/src/readytorun/multifolder/Root.cs @@ -0,0 +1,38 @@ +// 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.IO; +using System.Reflection; +using System.Runtime.Loader; + +public class RootClass +{ + public static int Main() + { + string currentFolder = Path.GetDirectoryName(typeof(RootClass).Assembly.Location); + string folderAPath = Path.Combine(currentFolder, "..", "FolderA", "FolderA", "FolderA.dll"); + Console.WriteLine("Loading FolderA: {0}", folderAPath); + Assembly folderA = AssemblyLoadContext.Default.LoadFromAssemblyPath(folderAPath); + Type classA = folderA.GetType("ClassA"); + object resultA = classA.GetProperty("Property").GetValue(null); + if (resultA != "ClassA.Property") + { + Console.WriteLine("ClassA.Property returned: '{0}'", resultA); + return 101; + } + + string folderBPath = Path.Combine(currentFolder, "..", "FolderB", "FolderB", "FolderB.dll"); + Console.WriteLine("Loading FolderB: {0}", folderBPath); + Assembly folderB = AssemblyLoadContext.Default.LoadFromAssemblyPath(folderBPath); + Type classB = folderB.GetType("ClassB"); + object resultB = classB.GetProperty("Property").GetValue(null); + if (resultB != "ClassB.Property") + { + Console.WriteLine("ClassB.Property returned: '{0}'", resultB); + return 102; + } + return 100; + } +} diff --git a/src/coreclr/tests/src/readytorun/multifolder/multifolder.csproj b/src/coreclr/tests/src/readytorun/multifolder/multifolder.csproj new file mode 100644 index 0000000000000..9c81a0173f696 --- /dev/null +++ b/src/coreclr/tests/src/readytorun/multifolder/multifolder.csproj @@ -0,0 +1,104 @@ + + + exe + BuildAndRun + false + 0 + + + true + + + false + + + + + + + + + + + + + >%__ResponseFile% + echo -r:%21CORE_ROOT%21\System.*.dll>>%__ResponseFile% + echo -r:%21CORE_ROOT%21\Microsoft.*.dll>>%__ResponseFile% + echo -r:%21CORE_ROOT%21\mscorlib.dll>>%__ResponseFile% + echo -r:%21CORE_ROOT%21\netstandard.dll>>%__ResponseFile% + echo -o:%scriptPath%\multifolder-composite.dll>>%__ResponseFile% + echo --crp:%scriptPath%\..>>%__ResponseFile% + echo --targetarch:$(TargetArchitecture)>>%__ResponseFile% + echo %scriptPath%\multifolder.dll>>%__ResponseFile% + echo %scriptPath%\..\FolderA\FolderA\FolderA.dll>>%__ResponseFile% + echo %scriptPath%\..\FolderB\FolderB\FolderB.dll>>%__ResponseFile% + + set __Command=%_DebuggerFullPath% + REM Tests run locally need __TestDotNetCmd (set by runtest.py) or a compatible 5.0 dotnet runtime in the path + if defined __TestDotNetCmd ( + set __Command=!__Command! "!__TestDotNetCmd!" + ) else ( + set __Command=!__Command! "dotnet" + ) + set __Command=%__Command% "%CORE_ROOT%\crossgen2\crossgen2.dll" + set __Command=%__Command% @"%__ResponseFile%" + + echo Response file: %__ResponseFile% + type %__ResponseFile% + echo ------------------ + echo Running crossgen2: %__Command% + %__Command% + + dir %scriptPath% + dir %scriptPath%\multifolder + + set ExePath=%scriptPath%\multifolder\multifolder.dll + set COMPlus_NativeImageSearchPaths=%scriptPath% + +]]> + >$__ResponseFile + echo -r:$CORE_ROOT/System.*.dll>>$__ResponseFile + echo -r:$CORE_ROOT/Microsoft.*.dll>>$__ResponseFile + echo -r:$CORE_ROOT/mscorlib.dll>>$__ResponseFile + echo -r:$CORE_ROOT/netstandard.dll>>$__ResponseFile + echo -o:$__OutputDir/multifolder-composite.dll>>$__ResponseFile + echo --crp:$__OutputDir/..>>$__ResponseFile + echo --targetarch:$(TargetArchitecture)>>$__ResponseFile + echo $__OutputDir/multifolder.dll>>$__ResponseFile + echo $__OutputDir/../FolderA/FolderA/FolderA.dll>>$__ResponseFile + echo $__OutputDir/../FolderB/FolderB/FolderB.dll>>$__ResponseFile + + __Command=$_DebuggerFullPath + # Tests run locally need __TestDotNetCmd (set by runtest.py) or a compatible 5.0 dotnet runtime in the path + if [ ! -z ${__TestDotNetCmd+x} ] %3B then + __Command+=" $__TestDotNetCmd" + else + __Command+=" dotnet" + fi + __Command+=" $CORE_ROOT/crossgen2/crossgen2.dll" + __Command+=" @$__ResponseFile" + + echo Response file: $__ResponseFile + cat $__ResponseFile + echo ------------------ + echo Running crossgen2: $__Command + $__Command + + ExePath=$__OutputDir/multifolder/multifolder.dll + export COMPlus_NativeImageSearchPaths=$__OutputDir + +]]> + +