From 0541e4bdaa3c4b979088adc637ffc9f39b903bf3 Mon Sep 17 00:00:00 2001 From: Swaroop Sridhar Date: Mon, 20 Apr 2020 22:04:13 -0700 Subject: [PATCH] Single-File: Run from Bundle This change implements: * Runtime changes necessary to load assemblies directly from the bundle: * Design notes about [Load from Bundle](https://github.com/dotnet/designs/blob/master/accepted/2020/single-file/design.md#peimage-loader) * Most of these changes are directly from https://github.com/dotnet/coreclr/pull/26504 and https://github.com/dotnet/coreclr/pull/26904 * Hostpolicy change to not add bundled assemblies to TPA list: * Design notes about [Dependency Resolution](https://github.com/dotnet/designs/blob/master/accepted/2020/single-file/design.md#dependency-resolution) * TBD (separately) items: Fix for hammer servicing #36031 Fixes #32822 --- src/coreclr/src/binder/assemblybinder.cpp | 114 ++++++++++++++---- .../src/binder/coreclrbindercommon.cpp | 1 + src/coreclr/src/binder/inc/assembly.hpp | 21 ++-- src/coreclr/src/binder/inc/assemblybinder.hpp | 4 +- .../src/dlls/mscoree/unixinterface.cpp | 22 +++- src/coreclr/src/inc/bundle.h | 62 ++++++++++ src/coreclr/src/pal/inc/pal.h | 6 +- src/coreclr/src/pal/src/include/pal/map.hpp | 3 +- src/coreclr/src/pal/src/include/pal/module.h | 3 +- src/coreclr/src/pal/src/loader/module.cpp | 12 +- src/coreclr/src/pal/src/map/map.cpp | 25 ++-- src/coreclr/src/vm/CMakeLists.txt | 1 + src/coreclr/src/vm/appdomain.cpp | 10 +- src/coreclr/src/vm/appdomain.hpp | 1 + src/coreclr/src/vm/assemblynative.cpp | 8 +- src/coreclr/src/vm/bundle.cpp | 93 ++++++++++++++ src/coreclr/src/vm/coreassemblyspec.cpp | 16 +-- src/coreclr/src/vm/crossgen/CMakeLists.txt | 1 + src/coreclr/src/vm/pefile.cpp | 7 +- src/coreclr/src/vm/peimage.cpp | 47 ++++---- src/coreclr/src/vm/peimage.h | 15 ++- src/coreclr/src/vm/peimage.inl | 39 +++++- src/coreclr/src/vm/peimagelayout.cpp | 64 +++++++--- src/coreclr/src/vm/peimagelayout.h | 1 + .../corehost/cli/bundle/file_entry.cpp | 3 +- src/installer/corehost/cli/bundle/runner.cpp | 9 +- src/installer/corehost/cli/bundle/runner.h | 15 ++- src/installer/corehost/cli/deps_entry.cpp | 18 ++- src/installer/corehost/cli/deps_entry.h | 4 +- .../corehost/cli/hostpolicy/deps_resolver.cpp | 59 ++++++--- .../corehost/cli/hostpolicy/deps_resolver.h | 3 +- .../cli/hostpolicy/hostpolicy_context.cpp | 23 ++-- .../BundleExtractToSpecificPath.cs | 3 +- .../BundledAppWithSubDirs.cs | 6 +- .../Helpers/BundleHelper.cs | 11 +- .../BundleAndRun.cs | 17 +-- .../BundleLegacy.cs | 4 +- 37 files changed, 558 insertions(+), 193 deletions(-) create mode 100644 src/coreclr/src/inc/bundle.h create mode 100644 src/coreclr/src/vm/bundle.cpp diff --git a/src/coreclr/src/binder/assemblybinder.cpp b/src/coreclr/src/binder/assemblybinder.cpp index 784b91260ee75..18d66a8b198f5 100644 --- a/src/coreclr/src/binder/assemblybinder.cpp +++ b/src/coreclr/src/binder/assemblybinder.cpp @@ -14,7 +14,6 @@ #include "assemblybinder.hpp" #include "assemblyname.hpp" - #include "assembly.hpp" #include "applicationcontext.hpp" #include "bindertracing.h" @@ -380,23 +379,29 @@ namespace BINDER_SPACE _ASSERTE(ppSystemAssembly != NULL); - StackSString sCoreLibDir(systemDirectory); ReleaseHolder pSystemAssembly; + StackSString sCoreLibDir(systemDirectory); if (!sCoreLibDir.EndsWith(DIRECTORY_SEPARATOR_CHAR_W)) { sCoreLibDir.Append(DIRECTORY_SEPARATOR_CHAR_W); } - StackSString sCoreLib; + StackSString sCoreLib(sCoreLibDir); + StackSString coreLibName(CoreLibName_IL_W); + sCoreLib.Append(coreLibName); // At run-time, System.Private.CoreLib.dll is expected to be the NI image. - sCoreLib = sCoreLibDir; - sCoreLib.Append(CoreLibName_IL_W); + // System.Private.CoreLib.dll is expected to be found at one of the following locations: + // * Non-single-file app: In systemDirectory, beside coreclr.dll + // * Framework-dependent single-file app: In system directory, beside coreclr.dll + // * Self-contained single-file app: Within the single-file bundle. IF_FAIL_GO(AssemblyBinder::GetAssembly(sCoreLib, - TRUE /* fIsInGAC */, - fBindToNativeImage, - &pSystemAssembly)); + TRUE /* fIsInGAC */, + fBindToNativeImage, + &pSystemAssembly, + NULL /* szMDAssemblyPath */, + Bundle::ProbeAppBundle(coreLibName, /*pathIsBundleRelative */ true))); *ppSystemAssembly = pSystemAssembly.Extract(); @@ -418,25 +423,32 @@ namespace BINDER_SPACE _ASSERTE(ppSystemAssembly != NULL); - StackSString sMscorlibSatellite(systemDirectory); - ReleaseHolder pSystemAssembly; + // Satellite assembly's relative path + StackSString relativePath; // append culture name if (!cultureName.IsEmpty()) { - CombinePath(sMscorlibSatellite, cultureName, sMscorlibSatellite); + CombinePath(relativePath, cultureName, relativePath); } // append satellite assembly's simple name - CombinePath(sMscorlibSatellite, simpleName, sMscorlibSatellite); + CombinePath(relativePath, simpleName, relativePath); // append extension - sMscorlibSatellite.Append(W(".dll")); + relativePath.Append(W(".dll")); + + // Satellite assembly's absolute path + StackSString sMscorlibSatellite(systemDirectory); + CombinePath(sMscorlibSatellite, relativePath, sMscorlibSatellite); + ReleaseHolder pSystemAssembly; IF_FAIL_GO(AssemblyBinder::GetAssembly(sMscorlibSatellite, TRUE /* fIsInGAC */, FALSE /* fExplicitBindToNativeImage */, - &pSystemAssembly)); + &pSystemAssembly, + NULL /* szMDAssemblyPath */, + Bundle::ProbeAppBundle(relativePath, /*pathIsBundleRelative */ true))); *ppSystemAssembly = pSystemAssembly.Extract(); @@ -553,7 +565,9 @@ namespace BINDER_SPACE // specified. Generally only NGEN PDB generation has // this TRUE. fExplicitBindToNativeImage, - &pAssembly)); + &pAssembly, + NULL /* szMDAssemblyPath */, + Bundle::ProbeAppBundle(assemblyPath))); AssemblyName *pAssemblyName; pAssemblyName = pAssembly->GetAssemblyName(); @@ -850,6 +864,13 @@ namespace BINDER_SPACE /* * BindByTpaList is the entry-point for the custom binding algorithm in CoreCLR. + * + * The search for assemblies will proceed in the following order: + * + * If this application is a single-file bundle, the meta-data contained in the bundle + * will be probed to find the requested assembly. If the assembly is not found, + * The list of platform assemblies (TPAs) are considered next. + * * Platform assemblies are specified as a list of files. This list is the only set of * assemblies that we will load as platform. They can be specified as IL or NIs. * @@ -903,11 +924,53 @@ namespace BINDER_SPACE } else { + ReleaseHolder pTPAAssembly; + SString& simpleName = pRequestedAssemblyName->GetSimpleName(); + + // Is assembly in the bundle? + // Single-file bundle contents take precedence over TPA. + // The list of bundled assemblies is contained in the bundle manifest, and NOT in the TPA. + // Therefore the bundle is first probed using the assembly's simple name. + // If found, the assembly is loaded from the bundle. + if (Bundle::AppIsBundle()) + { + SString candidates[] = { W(".dll"), W(".ni.dll") }; + + // Loop through the binding paths looking for a matching assembly + for (int i = 0; i < 2; i++) + { + SString assemblyFileName(simpleName); + assemblyFileName.Append(candidates[i]); + + SString assemblyFilePath(Bundle::AppBundle->BasePath()); + assemblyFilePath.Append(assemblyFileName); + + hr = GetAssembly(assemblyFilePath, + TRUE, // fIsInGAC + FALSE, // fExplicitBindToNativeImage + &pTPAAssembly, + NULL, // szMDAssemblyPath + Bundle::ProbeAppBundle(assemblyFileName, /* pathIsBundleRelative */ true)); + + if (hr != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) + { + // Any other error is fatal + IF_FAIL_GO(hr); + + if (TestCandidateRefMatchesDef(pRequestedAssemblyName, pTPAAssembly->GetAssemblyName(), true /*tpaListAssembly*/)) + { + // We have found the requested assembly match in the bundle with validation of the full-qualified name. + // Bind to it. + pBindResult->SetResult(pTPAAssembly); + GO_WITH_HRESULT(S_OK); + } + } + } + } + // Is assembly on TPA list? - SString &simpleName = pRequestedAssemblyName->GetSimpleName(); SimpleNameToFileNameMap * tpaMap = pApplicationContext->GetTpaList(); const SimpleNameToFileNameMapEntry *pTpaEntry = tpaMap->LookupPtr(simpleName.GetUnicode()); - ReleaseHolder pTPAAssembly; if (pTpaEntry != nullptr) { if (pTpaEntry->m_wszNIFileName != nullptr) @@ -1020,20 +1083,21 @@ namespace BINDER_SPACE } /* static */ - HRESULT AssemblyBinder::GetAssembly(SString &assemblyPath, - BOOL fIsInGAC, + HRESULT AssemblyBinder::GetAssembly(SString &assemblyPath, + BOOL fIsInGAC, // When binding to the native image, should we // assume assemblyPath explicitly specifies that // NI? (If not, infer the path to the NI // implicitly.) - BOOL fExplicitBindToNativeImage, + BOOL fExplicitBindToNativeImage, - Assembly **ppAssembly, + Assembly **ppAssembly, // If assemblyPath refers to a native image without metadata, // szMDAssemblyPath gives the alternative file to get metadata. - LPCTSTR szMDAssemblyPath) + LPCTSTR szMDAssemblyPath, + BundleFileLocation bundleFileLocation) { HRESULT hr = S_OK; @@ -1053,7 +1117,7 @@ namespace BINDER_SPACE { LPCTSTR szAssemblyPath = const_cast(assemblyPath.GetUnicode()); - hr = BinderAcquirePEImage(szAssemblyPath, &pPEImage, &pNativePEImage, fExplicitBindToNativeImage); + hr = BinderAcquirePEImage(szAssemblyPath, &pPEImage, &pNativePEImage, fExplicitBindToNativeImage, bundleFileLocation); IF_FAIL_GO(hr); // If we found a native image, it might be an MSIL assembly masquerading as an native image @@ -1068,7 +1132,7 @@ namespace BINDER_SPACE BinderReleasePEImage(pPEImage); BinderReleasePEImage(pNativePEImage); - hr = BinderAcquirePEImage(szAssemblyPath, &pPEImage, &pNativePEImage, false); + hr = BinderAcquirePEImage(szAssemblyPath, &pPEImage, &pNativePEImage, false, bundleFileLocation); IF_FAIL_GO(hr); } } @@ -1094,7 +1158,7 @@ namespace BINDER_SPACE } else { - hr = BinderAcquirePEImage(szMDAssemblyPath, &pPEImage, NULL, FALSE); + hr = BinderAcquirePEImage(szMDAssemblyPath, &pPEImage, NULL, FALSE, bundleFileLocation); IF_FAIL_GO(hr); hr = BinderAcquireImport(pPEImage, &pIMetaDataAssemblyImport, dwPAFlags, FALSE); diff --git a/src/coreclr/src/binder/coreclrbindercommon.cpp b/src/coreclr/src/binder/coreclrbindercommon.cpp index afd64cfc4dc58..e415b9067bb34 100644 --- a/src/coreclr/src/binder/coreclrbindercommon.cpp +++ b/src/coreclr/src/binder/coreclrbindercommon.cpp @@ -7,6 +7,7 @@ #include "assemblybinder.hpp" #include "coreclrbindercommon.h" #include "clrprivbindercoreclr.h" +#include "bundle.h" using namespace BINDER_SPACE; diff --git a/src/coreclr/src/binder/inc/assembly.hpp b/src/coreclr/src/binder/inc/assembly.hpp index 0d5500afb8203..f42cee4e62268 100644 --- a/src/coreclr/src/binder/inc/assembly.hpp +++ b/src/coreclr/src/binder/inc/assembly.hpp @@ -27,15 +27,18 @@ #include "clrprivbinderassemblyloadcontext.h" #endif // !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) -STDAPI BinderAcquirePEImage(LPCTSTR szAssemblyPath, - PEImage **ppPEImage, - PEImage **ppNativeImage, - BOOL fExplicitBindToNativeImage); - -STDAPI BinderAcquireImport(PEImage *pPEImage, - IMDInternalImport **pIMetaDataAssemblyImport, - DWORD *pdwPAFlags, - BOOL bNativeImage); +#include "bundle.h" + +STDAPI BinderAcquirePEImage(LPCTSTR szAssemblyPath, + PEImage **ppPEImage, + PEImage **ppNativeImage, + BOOL fExplicitBindToNativeImage, + BundleFileLocation bundleFileLocation); + +STDAPI BinderAcquireImport(PEImage *pPEImage, + IMDInternalImport **pIMetaDataAssemblyImport, + DWORD *pdwPAFlags, + BOOL bNativeImage); STDAPI BinderHasNativeHeader(PEImage *pPEImage, BOOL *result); diff --git a/src/coreclr/src/binder/inc/assemblybinder.hpp b/src/coreclr/src/binder/inc/assemblybinder.hpp index 1332b671edc2d..5246676b1a47f 100644 --- a/src/coreclr/src/binder/inc/assemblybinder.hpp +++ b/src/coreclr/src/binder/inc/assemblybinder.hpp @@ -18,6 +18,7 @@ #include "bindertypes.hpp" #include "bindresult.hpp" #include "coreclrbindercommon.h" +#include "bundle.h" class CLRPrivBinderAssemblyLoadContext; class CLRPrivBinderCoreCLR; @@ -54,7 +55,8 @@ namespace BINDER_SPACE /* in */ BOOL fIsInGAC, /* in */ BOOL fExplicitBindToNativeImage, /* out */ Assembly **ppAssembly, - /* in */ LPCTSTR szMDAssemblyPath = NULL); + /* in */ LPCTSTR szMDAssemblyPath = NULL, + /* in */ BundleFileLocation bundleFileLocation = BundleFileLocation::Invalid()); #if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) static HRESULT BindUsingHostAssemblyResolver (/* in */ INT_PTR pManagedAssemblyLoadContextToBindWithin, diff --git a/src/coreclr/src/dlls/mscoree/unixinterface.cpp b/src/coreclr/src/dlls/mscoree/unixinterface.cpp index f54e7c7be0604..ca4d452a8edae 100644 --- a/src/coreclr/src/dlls/mscoree/unixinterface.cpp +++ b/src/coreclr/src/dlls/mscoree/unixinterface.cpp @@ -18,6 +18,7 @@ #ifdef FEATURE_GDBJIT #include "../../vm/gdbjithelpers.h" #endif // FEATURE_GDBJIT +#include "bundle.h" #define ASSERTE_ALL_BUILDS(expr) _ASSERTE_ALL_BUILDS(__FILE__, (expr)) @@ -112,7 +113,8 @@ static void ConvertConfigPropertiesToUnicode( const char** propertyValues, int propertyCount, LPCWSTR** propertyKeysWRef, - LPCWSTR** propertyValuesWRef) + LPCWSTR** propertyValuesWRef, + BundleProbe** bundleProbe) { LPCWSTR* propertyKeysW = new (nothrow) LPCWSTR[propertyCount]; ASSERTE_ALL_BUILDS(propertyKeysW != nullptr); @@ -124,6 +126,13 @@ static void ConvertConfigPropertiesToUnicode( { propertyKeysW[propertyIndex] = StringToUnicode(propertyKeys[propertyIndex]); propertyValuesW[propertyIndex] = StringToUnicode(propertyValues[propertyIndex]); + + if (strcmp(propertyKeys[propertyIndex], "BUNDLE_PROBE") == 0) + { + // If this application is a single-file bundle, the bundle-probe callback + // is passed in as the value of "BUNDLE_PROBE" property (encoded as a string). + *bundleProbe = (BundleProbe*)_wcstoui64(propertyValuesW[propertyIndex], nullptr, 0); + } } *propertyKeysWRef = propertyKeysW; @@ -183,12 +192,21 @@ int coreclr_initialize( LPCWSTR* propertyKeysW; LPCWSTR* propertyValuesW; + BundleProbe* bundleProbe = nullptr; + ConvertConfigPropertiesToUnicode( propertyKeys, propertyValues, propertyCount, &propertyKeysW, - &propertyValuesW); + &propertyValuesW, + &bundleProbe); + + if (bundleProbe != nullptr) + { + static Bundle bundle(StringToUnicode(exePath), bundleProbe); + Bundle::AppBundle = &bundle; + } // This will take ownership of propertyKeysWTemp and propertyValuesWTemp Configuration::InitializeConfigurationKnobs(propertyCount, propertyKeysW, propertyValuesW); diff --git a/src/coreclr/src/inc/bundle.h b/src/coreclr/src/inc/bundle.h new file mode 100644 index 0000000000000..00fc186ab9ff0 --- /dev/null +++ b/src/coreclr/src/inc/bundle.h @@ -0,0 +1,62 @@ +// 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. + +/***************************************************************************** + ** ** + ** bundle.h - Information about applications bundled as a single-file ** + ** ** + *****************************************************************************/ + +#ifndef _BUNDLE_H_ +#define _BUNDLE_H_ + +#include + +class Bundle; + +struct BundleFileLocation +{ + INT64 Size; + INT64 Offset; + + BundleFileLocation() + { + LIMITED_METHOD_CONTRACT; + + Size = 0; + Offset = 0; + } + + static BundleFileLocation Invalid() { LIMITED_METHOD_CONTRACT; return BundleFileLocation(); } + + const SString &Path() const; + + bool IsValid() const { LIMITED_METHOD_CONTRACT; return Offset != 0; } +}; + +typedef bool(__stdcall BundleProbe)(LPCWSTR, INT64*, INT64*); + +class Bundle +{ +public: + Bundle(LPCWSTR bundlePath, BundleProbe *probe); + BundleFileLocation Probe(LPCWSTR path, bool pathIsBundleRelative = false) const; + + const SString &Path() const { LIMITED_METHOD_CONTRACT; return m_path; } + const SString &BasePath() const { LIMITED_METHOD_CONTRACT; return m_basePath; } + + static Bundle* AppBundle; // The BundleInfo for the current app, initialized by coreclr_initialize. + static bool AppIsBundle() { LIMITED_METHOD_CONTRACT; return AppBundle != nullptr; } + static BundleFileLocation ProbeAppBundle(LPCWSTR path, bool pathIsBundleRelative = false); + +private: + + SString m_path; // The path to single-file executable + BundleProbe *m_probe; + + SString m_basePath; // The prefix to denote a path within the bundle +}; + +#endif // _BUNDLE_H_ +// EOF ======================================================================= diff --git a/src/coreclr/src/pal/inc/pal.h b/src/coreclr/src/pal/inc/pal.h index 861a10f842da7..7ffdc2b7ec320 100644 --- a/src/coreclr/src/pal/inc/pal.h +++ b/src/coreclr/src/pal/inc/pal.h @@ -47,6 +47,9 @@ Module Name: #include #include #include +#include +#include +#include #endif #ifdef __cplusplus @@ -2543,6 +2546,7 @@ Abstract Parameters: IN hFile - The file to load + IN offset - offset within hFile where the PE "file" is located Return value: A valid base address if successful. @@ -2551,7 +2555,7 @@ Return value: PALIMPORT PVOID PALAPI -PAL_LOADLoadPEFile(HANDLE hFile); +PAL_LOADLoadPEFile(HANDLE hFile, size_t offset); /*++ PAL_LOADUnloadPEFile diff --git a/src/coreclr/src/pal/src/include/pal/map.hpp b/src/coreclr/src/pal/src/include/pal/map.hpp index eba0d8844bb27..b8c584f83c526 100644 --- a/src/coreclr/src/pal/src/include/pal/map.hpp +++ b/src/coreclr/src/pal/src/include/pal/map.hpp @@ -79,13 +79,14 @@ extern "C" Parameters: IN hFile - file to map + IN offset - offset within hFile where the PE "file" is located Return value: non-NULL - the base address of the mapped image NULL - error, with last error set. --*/ - void * MAPMapPEFile(HANDLE hFile); + void* MAPMapPEFile(HANDLE hFile, off_t offset); /*++ Function : diff --git a/src/coreclr/src/pal/src/include/pal/module.h b/src/coreclr/src/pal/src/include/pal/module.h index bb409b8fcd449..6a5e080d499d2 100644 --- a/src/coreclr/src/pal/src/include/pal/module.h +++ b/src/coreclr/src/pal/src/include/pal/module.h @@ -145,12 +145,13 @@ Abstract Parameters: IN hFile - The file to load + IN offset - offset within hFile where the PE "file" is located Return value: A valid base address if successful. 0 if failure --*/ -void * PAL_LOADLoadPEFile(HANDLE hFile); +void* PAL_LOADLoadPEFile(HANDLE hFile, size_t offset); /*++ PAL_LOADUnloadPEFile diff --git a/src/coreclr/src/pal/src/loader/module.cpp b/src/coreclr/src/pal/src/loader/module.cpp index 8418ef776d4be..65168978ba743 100644 --- a/src/coreclr/src/pal/src/loader/module.cpp +++ b/src/coreclr/src/pal/src/loader/module.cpp @@ -752,6 +752,7 @@ PAL_UnregisterModule( Parameters: IN hFile - file to map + IN offset - offset within hFile where the PE "file" is located Return value: non-NULL - the base address of the mapped image @@ -759,11 +760,11 @@ Return value: --*/ PVOID PALAPI -PAL_LOADLoadPEFile(HANDLE hFile) +PAL_LOADLoadPEFile(HANDLE hFile, size_t offset) { - ENTRY("PAL_LOADLoadPEFile (hFile=%p)\n", hFile); + ENTRY("PAL_LOADLoadPEFile (hFile=%p, offset=%zx)\n", hFile, offset); - void * loadedBase = MAPMapPEFile(hFile); + void* loadedBase = MAPMapPEFile(hFile, offset); #ifdef _DEBUG if (loadedBase != nullptr) @@ -775,7 +776,7 @@ PAL_LOADLoadPEFile(HANDLE hFile) { TRACE("Forcing failure of PE file map, and retry\n"); PAL_LOADUnloadPEFile(loadedBase); // unload it - loadedBase = MAPMapPEFile(hFile); // load it again + loadedBase = MAPMapPEFile(hFile, offset); // load it again } free(envVar); @@ -1547,7 +1548,8 @@ static MODSTRUCT *LOADAddModule(NATIVE_LIBRARY_HANDLE dl_handle, LPCSTR libraryN { /* found the handle. increment the refcount and return the existing module structure */ - TRACE("Found matching module %p for module name %s\n", module, libraryNameOrPath); + TRACE("Found matching module %p for module name %s\n", module, + (libraryNameOrPath != nullptr) ? libraryNameOrPath : "nullptr"); if (module->refcount != -1) { diff --git a/src/coreclr/src/pal/src/map/map.cpp b/src/coreclr/src/pal/src/map/map.cpp index cac736460bb3f..90936b19fd916 100644 --- a/src/coreclr/src/pal/src/map/map.cpp +++ b/src/coreclr/src/pal/src/map/map.cpp @@ -1088,6 +1088,8 @@ CorUnix::InternalMapViewOfFile( CFileMappingImmutableData *pImmutableData = NULL; CFileMappingProcessLocalData *pProcessLocalData = NULL; IDataLock *pProcessLocalDataLock = NULL; + INT64 offset = ((INT64)dwFileOffsetHigh << 32) | (INT64)dwFileOffsetLow; + #if ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS PMAPPED_VIEW_LIST pReusedMapping = NULL; #endif @@ -1102,9 +1104,9 @@ CorUnix::InternalMapViewOfFile( goto InternalMapViewOfFileExit; } - if ( 0 != dwFileOffsetHigh || 0 != dwFileOffsetLow ) + if (offset < 0) { - ASSERT( "dwFileOffsetHigh and dwFileOffsetLow are always 0.\n" ); + ASSERT("dwFileOffsetHigh | dwFileOffsetLow should be non-negative.\n"); palError = ERROR_INVALID_PARAMETER; goto InternalMapViewOfFileExit; } @@ -1182,7 +1184,7 @@ CorUnix::InternalMapViewOfFile( PROT_READ|PROT_WRITE, flags, pProcessLocalData->UnixFd, - 0 + offset ); } else @@ -1205,7 +1207,7 @@ CorUnix::InternalMapViewOfFile( prot, flags, pProcessLocalData->UnixFd, - 0 + offset ); #if ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS @@ -2210,13 +2212,14 @@ MAPmmapAndRecord( Parameters: IN hFile - file to map + IN offset - offset within hFile where the PE "file" is located Return value: non-NULL - the base address of the mapped image NULL - error, with last error set. --*/ -void * MAPMapPEFile(HANDLE hFile) +void * MAPMapPEFile(HANDLE hFile, off_t offset) { PAL_ERROR palError = 0; IPalObject *pFileObject = NULL; @@ -2231,7 +2234,7 @@ void * MAPMapPEFile(HANDLE hFile) char* envVar; #endif - ENTRY("MAPMapPEFile (hFile=%p)\n", hFile); + ENTRY("MAPMapPEFile (hFile=%p offset=%zx)\n", hFile, offset); //Step 0: Verify values, find internal pal data structures. if (INVALID_HANDLE_VALUE == hFile) @@ -2270,13 +2273,13 @@ void * MAPMapPEFile(HANDLE hFile) //Step 1: Read the PE headers and reserve enough space for the whole image somewhere. IMAGE_DOS_HEADER dosHeader; IMAGE_NT_HEADERS ntHeader; - if (sizeof(dosHeader) != pread(fd, &dosHeader, sizeof(dosHeader), 0)) + if (sizeof(dosHeader) != pread(fd, &dosHeader, sizeof(dosHeader), offset)) { palError = FILEGetLastErrorFromErrno(); ERROR_(LOADER)( "reading dos header failed\n" ); goto done; } - if (sizeof(ntHeader) != pread(fd, &ntHeader, sizeof(ntHeader), dosHeader.e_lfanew)) + if (sizeof(ntHeader) != pread(fd, &ntHeader, sizeof(ntHeader), offset + dosHeader.e_lfanew)) { palError = FILEGetLastErrorFromErrno(); goto done; @@ -2418,7 +2421,7 @@ void * MAPMapPEFile(HANDLE hFile) //first, map the PE header to the first page in the image. Get pointers to the section headers palError = MAPmmapAndRecord(pFileObject, loadedBase, - loadedBase, headerSize, PROT_READ, MAP_FILE|MAP_PRIVATE|MAP_FIXED, fd, 0, + loadedBase, headerSize, PROT_READ, MAP_FILE|MAP_PRIVATE|MAP_FIXED, fd, offset, (void**)&loadedHeader); if (NO_ERROR != palError) { @@ -2511,7 +2514,7 @@ void * MAPMapPEFile(HANDLE hFile) prot, MAP_FILE|MAP_PRIVATE|MAP_FIXED, fd, - currentHeader.PointerToRawData, + offset + currentHeader.PointerToRawData, §ionData); if (NO_ERROR != palError) { @@ -2541,7 +2544,7 @@ void * MAPMapPEFile(HANDLE hFile) palError = MAPRecordMapping(pFileObject, loadedBase, prevSectionEnd, - (char*)imageEnd - (char*)prevSectionEnd, + offset + (char*)imageEnd - (char*)prevSectionEnd, PROT_NONE); if (NO_ERROR != palError) { diff --git a/src/coreclr/src/vm/CMakeLists.txt b/src/coreclr/src/vm/CMakeLists.txt index e11e58d777251..c04d5f6219e03 100644 --- a/src/coreclr/src/vm/CMakeLists.txt +++ b/src/coreclr/src/vm/CMakeLists.txt @@ -42,6 +42,7 @@ set(VM_SOURCES_DAC_AND_WKS_COMMON assemblyloadcontext.cpp baseassemblyspec.cpp binder.cpp + bundle.cpp castcache.cpp callcounting.cpp ceeload.cpp diff --git a/src/coreclr/src/vm/appdomain.cpp b/src/coreclr/src/vm/appdomain.cpp index 95cd9174b9db2..7681e64a99cef 100644 --- a/src/coreclr/src/vm/appdomain.cpp +++ b/src/coreclr/src/vm/appdomain.cpp @@ -1761,7 +1761,7 @@ void SystemDomain::Init() sizeof(MethodDesc), sizeof(FieldDesc), sizeof(Module) - )); + )); #endif // _DEBUG // The base domain is initialized in SystemDomain::Attach() @@ -1775,7 +1775,7 @@ void SystemDomain::Init() m_pSystemAssembly = NULL; DWORD size = 0; - + AppDomain* pAppDomain = ::GetAppDomain(); // Get the install directory so we can find mscorlib hr = GetInternalSystemDirectory(NULL, &size); @@ -1783,19 +1783,19 @@ void SystemDomain::Init() ThrowHR(hr); // GetInternalSystemDirectory returns a size, including the null! - WCHAR *buffer = m_SystemDirectory.OpenUnicodeBuffer(size-1); + WCHAR* buffer = m_SystemDirectory.OpenUnicodeBuffer(size - 1); IfFailThrow(GetInternalSystemDirectory(buffer, &size)); m_SystemDirectory.CloseBuffer(); m_SystemDirectory.Normalize(); // At this point m_SystemDirectory should already be canonicalized - m_BaseLibrary.Append(m_SystemDirectory); if (!m_BaseLibrary.EndsWith(DIRECTORY_SEPARATOR_CHAR_W)) { m_BaseLibrary.Append(DIRECTORY_SEPARATOR_CHAR_W); } + m_BaseLibrary.Append(g_pwBaseLibrary); m_BaseLibrary.Normalize(); @@ -1825,7 +1825,7 @@ void SystemDomain::Init() #ifdef _DEBUG BOOL fPause = EEConfig::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_PauseOnLoad, FALSE); - while(fPause) + while (fPause) { ClrSleepEx(20, TRUE); } diff --git a/src/coreclr/src/vm/appdomain.hpp b/src/coreclr/src/vm/appdomain.hpp index f017b42c73a2a..ece967214cc74 100644 --- a/src/coreclr/src/vm/appdomain.hpp +++ b/src/coreclr/src/vm/appdomain.hpp @@ -29,6 +29,7 @@ #include "gchandleutilities.h" #include "../binder/inc/applicationcontext.hpp" #include "rejit.h" +#include "bundle.h" #ifdef FEATURE_MULTICOREJIT #include "multicorejit.h" diff --git a/src/coreclr/src/vm/assemblynative.cpp b/src/coreclr/src/vm/assemblynative.cpp index b9cec28e3c5a2..0cb1dba31c3c4 100644 --- a/src/coreclr/src/vm/assemblynative.cpp +++ b/src/coreclr/src/vm/assemblynative.cpp @@ -239,7 +239,9 @@ void QCALLTYPE AssemblyNative::LoadFromPath(INT_PTR ptrNativeAssemblyLoadContext if (pwzILPath != NULL) { - pILImage = PEImage::OpenImage(pwzILPath); + pILImage = PEImage::OpenImage(pwzILPath, + MDInternalImport_Default, + Bundle::ProbeAppBundle(pwzILPath)); // Need to verify that this is a valid CLR assembly. if (!pILImage->CheckILFormat()) @@ -257,7 +259,9 @@ void QCALLTYPE AssemblyNative::LoadFromPath(INT_PTR ptrNativeAssemblyLoadContext // Form the PEImage for the NI assembly, if specified if (pwzNIPath != NULL) { - pNIImage = PEImage::OpenImage(pwzNIPath, MDInternalImport_TrustedNativeImage); + pNIImage = PEImage::OpenImage(pwzNIPath, + MDInternalImport_TrustedNativeImage, + Bundle::ProbeAppBundle(pwzNIPath)); if (pNIImage->HasReadyToRunHeader()) { diff --git a/src/coreclr/src/vm/bundle.cpp b/src/coreclr/src/vm/bundle.cpp new file mode 100644 index 0000000000000..e0d03155a56a1 --- /dev/null +++ b/src/coreclr/src/vm/bundle.cpp @@ -0,0 +1,93 @@ +// 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. +//***************************************************************************** +// Bundle.cpp +// +// Helpers to access meta-data stored in single-file bundles +// +//***************************************************************************** + +#include "common.h" +#include "bundle.h" +#include +#include +#include + +Bundle *Bundle::AppBundle = nullptr; + +const SString &BundleFileLocation::Path() const +{ + LIMITED_METHOD_CONTRACT; + + // Currently, there is only one bundle -- the bundle for the main App. + // Therefore, obtain the path from the global AppBundle. + // If there is more than one bundle in one application (ex: single file plugins) + // the BundlePath may be stored in the BundleFileLocation structure. + + _ASSERTE(IsValid()); + _ASSERTE(Bundle::AppBundle != nullptr); + + return Bundle::AppBundle->Path(); +} + +Bundle::Bundle(LPCWSTR bundlePath, BundleProbe *probe) +{ + STANDARD_VM_CONTRACT; + + _ASSERTE(probe != nullptr); + + m_path.Set(bundlePath); + m_probe = probe; + + // The bundle-base path is the directory containing the single-file bundle. + // When the Probe() function searches within the bundle, it masks out the basePath from the assembly-path (if found). + + LPCWSTR pos = wcsrchr(bundlePath, DIRECTORY_SEPARATOR_CHAR_W); + _ASSERTE(pos != nullptr); + size_t baseLen = pos - bundlePath + 1; // Include DIRECTORY_SEPARATOR_CHAR_W in m_basePath + m_basePath.Set(bundlePath, (COUNT_T)baseLen); +} + +BundleFileLocation Bundle::Probe(LPCWSTR path, bool pathIsBundleRelative) const +{ + STANDARD_VM_CONTRACT; + + BundleFileLocation loc; + + // Skip over m_base_path, if any. For example: + // Bundle.Probe("lib.dll") => m_probe("lib.dll") + // Bundle.Probe("path/to/exe/lib.dll") => m_probe("lib.dll") + // Bundle.Probe("path/to/exe/and/some/more/lib.dll") => m_probe("and/some/more/lib.dll") + + if (!pathIsBundleRelative) + { + size_t baseLen = m_basePath.GetCount(); + +#ifdef TARGET_UNIX + if (wcsncmp(m_basePath, path, baseLen) == 0) +#else + if (_wcsnicmp(m_basePath, path, baseLen) == 0) +#endif // TARGET_UNIX + { + path += baseLen; // m_basePath includes count for DIRECTORY_SEPARATOR_CHAR_W + } + else + { + // This is not a file within the bundle + return loc; + } + } + + m_probe(path, &loc.Offset, &loc.Size); + + return loc; +} + +BundleFileLocation Bundle::ProbeAppBundle(LPCWSTR path, bool pathIsBundleRelative) +{ + STANDARD_VM_CONTRACT; + + return AppIsBundle() ? AppBundle->Probe(path, pathIsBundleRelative) : BundleFileLocation::Invalid(); +} + diff --git a/src/coreclr/src/vm/coreassemblyspec.cpp b/src/coreclr/src/vm/coreassemblyspec.cpp index 99ae244064758..d2fc56ad064d9 100644 --- a/src/coreclr/src/vm/coreassemblyspec.cpp +++ b/src/coreclr/src/vm/coreassemblyspec.cpp @@ -19,7 +19,7 @@ #include "domainfile.h" #include "holder.h" #include "../binder/inc/assemblybinder.hpp" - +#include "bundle.h" #include "strongnameinternal.h" #include "strongnameholders.h" @@ -174,10 +174,11 @@ VOID AssemblySpec::Bind(AppDomain *pAppDomain, } -STDAPI BinderAcquirePEImage(LPCWSTR wszAssemblyPath, - PEImage **ppPEImage, - PEImage **ppNativeImage, - BOOL fExplicitBindToNativeImage) +STDAPI BinderAcquirePEImage(LPCWSTR wszAssemblyPath, + PEImage **ppPEImage, + PEImage **ppNativeImage, + BOOL fExplicitBindToNativeImage, + BundleFileLocation bundleFileLocation) { HRESULT hr = S_OK; @@ -187,12 +188,13 @@ STDAPI BinderAcquirePEImage(LPCWSTR wszAssemblyPath, { PEImageHolder pImage = NULL; PEImageHolder pNativeImage = NULL; + AppDomain* pDomain = ::GetAppDomain(); // DEAD ? ? #ifdef FEATURE_PREJIT // fExplicitBindToNativeImage is set on Phone when we bind to a list of native images and have no IL on device for an assembly if (fExplicitBindToNativeImage) { - pNativeImage = PEImage::OpenImage(wszAssemblyPath, MDInternalImport_TrustedNativeImage); + pNativeImage = PEImage::OpenImage(wszAssemblyPath, MDInternalImport_TrustedNativeImage, bundleFileLocation); // Make sure that the IL image can be opened if the native image is not available. hr=pNativeImage->TryOpenFile(); @@ -204,7 +206,7 @@ STDAPI BinderAcquirePEImage(LPCWSTR wszAssemblyPath, else #endif { - pImage = PEImage::OpenImage(wszAssemblyPath, MDInternalImport_Default); + pImage = PEImage::OpenImage(wszAssemblyPath, MDInternalImport_Default, bundleFileLocation); // Make sure that the IL image can be opened if the native image is not available. hr=pImage->TryOpenFile(); diff --git a/src/coreclr/src/vm/crossgen/CMakeLists.txt b/src/coreclr/src/vm/crossgen/CMakeLists.txt index 77f46314b9bed..b532a9ecdc3cb 100644 --- a/src/coreclr/src/vm/crossgen/CMakeLists.txt +++ b/src/coreclr/src/vm/crossgen/CMakeLists.txt @@ -6,6 +6,7 @@ set(VM_CROSSGEN_SOURCES ../assemblyspec.cpp ../baseassemblyspec.cpp ../binder.cpp + ../bundle.cpp ../castcache.cpp ../ceeload.cpp ../ceemain.cpp diff --git a/src/coreclr/src/vm/pefile.cpp b/src/coreclr/src/vm/pefile.cpp index 7da14c95cf3a8..ae9c966cb7983 100644 --- a/src/coreclr/src/vm/pefile.cpp +++ b/src/coreclr/src/vm/pefile.cpp @@ -295,12 +295,15 @@ void PEFile::LoadLibrary(BOOL allowNativeSkip/*=TRUE*/) // if allowNativeSkip==F if (GetILimage()->IsFile()) { #ifdef TARGET_UNIX - if (GetILimage()->IsILOnly()) + bool loadILImage = GetILimage()->IsILOnly(); +#else // TARGET_UNIX + bool loadILImage = GetILimage()->IsILOnly() && GetILimage()->IsInBundle(); +#endif // TARGET_UNIX + if (loadILImage) { GetILimage()->Load(); } else -#endif // TARGET_UNIX { GetILimage()->LoadFromMapped(); } diff --git a/src/coreclr/src/vm/peimage.cpp b/src/coreclr/src/vm/peimage.cpp index 7dc580fe8f8d8..c07facfa37269 100644 --- a/src/coreclr/src/vm/peimage.cpp +++ b/src/coreclr/src/vm/peimage.cpp @@ -973,10 +973,7 @@ PTR_PEImageLayout PEImage::GetLayoutInternal(DWORD imageLayoutMask,DWORD flags) BOOL bIsFlatLayoutSuitable = ((imageLayoutMask & PEImageLayout::LAYOUT_FLAT) != 0); #if !defined(TARGET_UNIX) - if (bIsMappedLayoutSuitable) - { - bIsFlatLayoutSuitable = FALSE; - } + bIsFlatLayoutSuitable = IsInBundle() || !bIsMappedLayoutSuitable; #endif // !TARGET_UNIX _ASSERTE(bIsMappedLayoutSuitable || bIsFlatLayoutSuitable); @@ -1183,7 +1180,14 @@ void PEImage::Load() } #ifdef TARGET_UNIX - if (m_pLayouts[IMAGE_FLAT] != NULL + bool canUseLoadedFlat = true; +#else + bool canUseLoadedFlat = IsInBundle(); +#endif // TARGET_UNIX + + + if (canUseLoadedFlat + && m_pLayouts[IMAGE_FLAT] != NULL && m_pLayouts[IMAGE_FLAT]->CheckILOnlyFormat() && !m_pLayouts[IMAGE_FLAT]->HasWriteableSections()) { @@ -1200,7 +1204,6 @@ void PEImage::Load() SetLayout(IMAGE_LOADED, m_pLayouts[IMAGE_FLAT]); } else -#endif // TARGET_UNIX { if(!IsFile()) { @@ -1328,19 +1331,19 @@ HANDLE PEImage::GetFileHandle() { ErrorModeHolder mode(SEM_NOOPENFILEERRORBOX|SEM_FAILCRITICALERRORS); - m_hFile=WszCreateFile((LPCWSTR) m_path, - GENERIC_READ, - FILE_SHARE_READ|FILE_SHARE_DELETE, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - NULL); + m_hFile=WszCreateFile((LPCWSTR) GetPathToLoad(), + GENERIC_READ, + FILE_SHARE_READ|FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); } if (m_hFile == INVALID_HANDLE_VALUE) { #if !defined(DACCESS_COMPILE) - EEFileLoadException::Throw(m_path, HRESULT_FROM_WIN32(GetLastError())); + EEFileLoadException::Throw(GetPathToLoad(), HRESULT_FROM_WIN32(GetLastError())); #else // defined(DACCESS_COMPILE) ThrowLastError(); #endif // !defined(DACCESS_COMPILE) @@ -1374,14 +1377,14 @@ HRESULT PEImage::TryOpenFile() if (m_hFile!=INVALID_HANDLE_VALUE) return S_OK; { - ErrorModeHolder mode(SEM_NOOPENFILEERRORBOX|SEM_FAILCRITICALERRORS); - m_hFile=WszCreateFile((LPCWSTR) m_path, - GENERIC_READ, - FILE_SHARE_READ|FILE_SHARE_DELETE, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - NULL); + ErrorModeHolder mode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS); + m_hFile=WszCreateFile((LPCWSTR)GetPathToLoad(), + GENERIC_READ, + FILE_SHARE_READ|FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); } if (m_hFile != INVALID_HANDLE_VALUE) return S_OK; diff --git a/src/coreclr/src/vm/peimage.h b/src/coreclr/src/vm/peimage.h index 1edf05d8ec217..2a9b0b76ac10a 100644 --- a/src/coreclr/src/vm/peimage.h +++ b/src/coreclr/src/vm/peimage.h @@ -19,6 +19,7 @@ #include "peimagelayout.h" #include "sstring.h" #include "holder.h" +#include class SimpleRWLock; // -------------------------------------------------------------------------------- @@ -102,7 +103,8 @@ class PEImage #endif // !TARGET_UNIX static PTR_PEImage OpenImage( LPCWSTR pPath, - MDInternalImportFlags flags = MDInternalImport_Default); + MDInternalImportFlags flags = MDInternalImport_Default, + BundleFileLocation bundleFileLocation = BundleFileLocation::Invalid()); // clones the image with new flags (this is pretty much about cached / noncached difference) @@ -146,8 +148,13 @@ class PEImage // Accessors const SString &GetPath(); + const SString& GetPathToLoad(); BOOL IsFile(); + BOOL IsInBundle() const; HANDLE GetFileHandle(); + INT64 GetOffset() const; + INT64 GetSize() const; + void SetFileHandle(HANDLE hFile); HRESULT TryOpenFile(); @@ -237,7 +244,7 @@ class PEImage // Private routines // ------------------------------------------------------------ - void Init(LPCWSTR pPath); + void Init(LPCWSTR pPath, BundleFileLocation bundleFileLocation); void Init(IStream* pStream, UINT64 uStreamAsmId, DWORD dwModuleId, BOOL resourceFile); @@ -273,6 +280,10 @@ class PEImage SString m_path; LONG m_refCount; + BundleFileLocation m_bundleFileLocation; // If this image is located within a single-file bundle, + // the location within the bundle. If m_bundleFileLocation is vaild, + // it takes precedence over m_path for loading. + // This variable will have the data of module name. // It is only used by DAC to remap fusion loaded modules back to // disk IL. This really is a workaround. The real fix is for fusion loader diff --git a/src/coreclr/src/vm/peimage.inl b/src/coreclr/src/vm/peimage.inl index 03d8bc229ce05..eebae16e15f7a 100644 --- a/src/coreclr/src/vm/peimage.inl +++ b/src/coreclr/src/vm/peimage.inl @@ -33,6 +33,33 @@ inline const SString &PEImage::GetPath() return m_path; } +inline const SString& PEImage::GetPathToLoad() +{ + LIMITED_METHOD_DAC_CONTRACT; + + return IsInBundle() ? m_bundleFileLocation.Path() : m_path; +} + +inline INT64 PEImage::GetOffset() const +{ + LIMITED_METHOD_CONTRACT; + + return m_bundleFileLocation.Offset; +} + +inline BOOL PEImage::IsInBundle() const +{ + LIMITED_METHOD_CONTRACT; + + return m_bundleFileLocation.IsValid(); +} + +inline INT64 PEImage::GetSize() const +{ + LIMITED_METHOD_CONTRACT; + return m_bundleFileLocation.Size; +} + inline void PEImage::SetModuleFileNameHintForDAC() { LIMITED_METHOD_DAC_CONTRACT; @@ -71,7 +98,7 @@ inline BOOL PEImage::IsFile() { WRAPPER_NO_CONTRACT; - return !m_path.IsEmpty(); + return !GetPathToLoad().IsEmpty(); } #ifndef DACCESS_COMPILE @@ -433,7 +460,7 @@ inline CHECK PEImage::CheckFormat() CHECK_OK; } -inline void PEImage::Init(LPCWSTR pPath) +inline void PEImage::Init(LPCWSTR pPath, BundleFileLocation bundleFileLocation) { CONTRACTL { @@ -442,8 +469,10 @@ inline void PEImage::Init(LPCWSTR pPath) MODE_ANY; } CONTRACTL_END; + m_path = pPath; m_path.Normalize(); + m_bundleFileLocation = bundleFileLocation; SetModuleFileNameHintForDAC(); } #ifndef DACCESS_COMPILE @@ -475,14 +504,14 @@ inline PTR_PEImage PEImage::FindByPath(LPCWSTR pPath) } /* static */ -inline PTR_PEImage PEImage::OpenImage(LPCWSTR pPath, MDInternalImportFlags flags /* = MDInternalImport_Default */) +inline PTR_PEImage PEImage::OpenImage(LPCWSTR pPath, MDInternalImportFlags flags /* = MDInternalImport_Default */, BundleFileLocation bundleFileLocation) { BOOL fUseCache = !((flags & MDInternalImport_NoCache) == MDInternalImport_NoCache); if (!fUseCache) { PEImageHolder pImage(new PEImage); - pImage->Init(pPath); + pImage->Init(pPath, bundleFileLocation); return dac_cast(pImage.Extract()); } @@ -504,7 +533,7 @@ inline PTR_PEImage PEImage::OpenImage(LPCWSTR pPath, MDInternalImportFlags flags if (flags & MDInternalImport_TrustedNativeImage) pImage->SetIsTrustedNativeImage(); #endif - pImage->Init(pPath); + pImage->Init(pPath, bundleFileLocation); pImage->AddToHashMap(); return dac_cast(pImage.Extract()); diff --git a/src/coreclr/src/vm/peimagelayout.cpp b/src/coreclr/src/vm/peimagelayout.cpp index 79739716da430..33032108e7e3e 100644 --- a/src/coreclr/src/vm/peimagelayout.cpp +++ b/src/coreclr/src/vm/peimagelayout.cpp @@ -40,6 +40,17 @@ PEImageLayout* PEImageLayout::LoadFromFlat(PEImageLayout* pflatimage) return new ConvertedImageLayout(pflatimage); } +PEImageLayout* PEImageLayout::LoadConverted(PEImage* pOwner) +{ + STANDARD_VM_CONTRACT; + + PEImageLayoutHolder pFlat(new FlatImageLayout(pOwner)); + if (!pFlat->CheckFormat()) + ThrowHR(COR_E_BADIMAGEFORMAT); + + return new ConvertedImageLayout(pFlat); +} + PEImageLayout* PEImageLayout::Load(PEImage* pOwner, BOOL bNTSafeLoad, BOOL bThrowOnError) { STANDARD_VM_CONTRACT; @@ -47,6 +58,11 @@ PEImageLayout* PEImageLayout::Load(PEImage* pOwner, BOOL bNTSafeLoad, BOOL bThro #if defined(CROSSGEN_COMPILE) || defined(TARGET_UNIX) return PEImageLayout::Map(pOwner); #else + if (pOwner->IsInBundle()) + { + return PEImageLayout::LoadConverted(pOwner); + } + PEImageLayoutHolder pAlloc(new LoadedImageLayout(pOwner,bNTSafeLoad,bThrowOnError)); if (pAlloc->GetBase()==NULL) return NULL; @@ -83,11 +99,7 @@ PEImageLayout* PEImageLayout::Map(PEImage* pOwner) if (pAlloc->GetBase()==NULL) { //cross-platform or a bad image - PEImageLayoutHolder pFlat(new FlatImageLayout(pOwner)); - if (!pFlat->CheckFormat()) - ThrowHR(COR_E_BADIMAGEFORMAT); - - pAlloc=new ConvertedImageLayout(pFlat); + pAlloc = LoadConverted(pOwner); } else if(!pAlloc->CheckFormat()) @@ -393,8 +405,8 @@ ConvertedImageLayout::ConvertedImageLayout(PEImageLayout* source) m_FileMap.Assign(WszCreateFileMapping(INVALID_HANDLE_VALUE, NULL, - PAGE_READWRITE, 0, - source->GetVirtualSize(), NULL)); + PAGE_READWRITE, 0, + source->GetVirtualSize(), NULL)); if (m_FileMap == NULL) ThrowLastError(); @@ -428,6 +440,8 @@ MappedImageLayout::MappedImageLayout(PEImage* pOwner) m_pOwner=pOwner; HANDLE hFile = pOwner->GetFileHandle(); + INT64 offset = pOwner->GetOffset(); + INT64 size = pOwner->GetSize(); // If mapping was requested, try to do SEC_IMAGE mapping LOG((LF_LOADER, LL_INFO100, "PEImage: Opening OS mapped %S (hFile %p)\n", (LPCWSTR) GetPath(), hFile)); @@ -459,21 +473,24 @@ MappedImageLayout::MappedImageLayout(PEImage* pOwner) ThrowWin32(dwLastError); } -#endif // CROSSGEN_COMPILE +#endif // !CROSSGEN_COMPILE return; } + DWORD offsetLowPart = (DWORD)offset; + DWORD offsetHighPart = (DWORD)(offset >> 32); + #ifdef _DEBUG // Force relocs by occuping the preferred base while the actual mapping is performed CLRMapViewHolder forceRelocs; if (PEDecoder::GetForceRelocs()) { - forceRelocs.Assign(CLRMapViewOfFile(m_FileMap, 0, 0, 0, 0)); + forceRelocs.Assign(CLRMapViewOfFile(m_FileMap, 0, offsetHighPart, offsetLowPart, (SIZE_T)size)); } #endif // _DEBUG - m_FileView.Assign(CLRMapViewOfFile(m_FileMap, 0, 0, 0, 0)); + m_FileView.Assign(CLRMapViewOfFile(m_FileMap, 0, offsetHighPart, offsetLowPart, (SIZE_T)size)); if (m_FileView == NULL) ThrowLastError(); IfFailThrow(Init((void *) m_FileView)); @@ -502,7 +519,7 @@ MappedImageLayout::MappedImageLayout(PEImage* pOwner) } } else -#endif +#endif // CROSSGEN_COMPILE if (!IsNativeMachineFormat() && !IsI386()) { //can't rely on the image @@ -529,7 +546,7 @@ MappedImageLayout::MappedImageLayout(PEImage* pOwner) #else //!TARGET_UNIX #ifndef CROSSGEN_COMPILE - m_LoadedFile = PAL_LOADLoadPEFile(hFile); + m_LoadedFile = PAL_LOADLoadPEFile(hFile, offset); if (m_LoadedFile == NULL) { @@ -612,13 +629,19 @@ FlatImageLayout::FlatImageLayout(PEImage* pOwner) m_pOwner=pOwner; HANDLE hFile = pOwner->GetFileHandle(); + INT64 offset = pOwner->GetOffset(); + INT64 size = pOwner->GetSize(); LOG((LF_LOADER, LL_INFO100, "PEImage: Opening flat %S\n", (LPCWSTR) GetPath())); - COUNT_T size = SafeGetFileSize(hFile, NULL); - if (size == 0xffffffff && GetLastError() != NOERROR) + // If a size is not specified, load the whole file + if (size == 0) { - ThrowLastError(); + size = SafeGetFileSize(hFile, NULL); + if (size == 0xffffffff && GetLastError() != NOERROR) + { + ThrowLastError(); + } } // It's okay if resource files are length zero @@ -628,11 +651,16 @@ FlatImageLayout::FlatImageLayout(PEImage* pOwner) if (m_FileMap == NULL) ThrowLastError(); - m_FileView.Assign(CLRMapViewOfFile(m_FileMap, FILE_MAP_READ, 0, 0, 0)); + //DWORD lowPart = (DWORD)offset; + //DWORD highPart = (DWORD)(offset >> 32); + char *addr = (char*)CLRMapViewOfFile(m_FileMap, FILE_MAP_READ, 0, 0, 0); + addr += offset; + m_FileView.Assign((LPVOID)addr); + if (m_FileView == NULL) ThrowLastError(); } - Init(m_FileView, size); + Init(m_FileView, (COUNT_T)size); } NativeImageLayout::NativeImageLayout(LPCWSTR fullPath) @@ -655,7 +683,7 @@ NativeImageLayout::NativeImageLayout(LPCWSTR fullPath) ThrowLastError(); } - loadedImage = PAL_LOADLoadPEFile(fileHandle); + loadedImage = PAL_LOADLoadPEFile(fileHandle, 0); } #else loadedImage = CLRLoadLibraryEx(fullPath, NULL, GetLoadWithAlteredSearchPathFlag()); diff --git a/src/coreclr/src/vm/peimagelayout.h b/src/coreclr/src/vm/peimagelayout.h index 9ae14c4b74e54..bc846c54d6c74 100644 --- a/src/coreclr/src/vm/peimagelayout.h +++ b/src/coreclr/src/vm/peimagelayout.h @@ -54,6 +54,7 @@ class PEImageLayout : public PEDecoder static PEImageLayout* LoadFromFlat(PEImageLayout* pflatimage); static PEImageLayout* Load(PEImage* pOwner, BOOL bNTSafeLoad, BOOL bThrowOnError = TRUE); static PEImageLayout* LoadFlat(PEImage* pOwner); + static PEImageLayout* LoadConverted(PEImage* pOwner); static PEImageLayout* LoadNative(LPCWSTR fullPath); static PEImageLayout* Map(PEImage* pOwner); #endif diff --git a/src/installer/corehost/cli/bundle/file_entry.cpp b/src/installer/corehost/cli/bundle/file_entry.cpp index 775117fb9a1f6..25e2cd01ce3fc 100644 --- a/src/installer/corehost/cli/bundle/file_entry.cpp +++ b/src/installer/corehost/cli/bundle/file_entry.cpp @@ -38,10 +38,9 @@ bool file_entry_t::needs_extraction() const { switch (m_type) { - // Once the runtime can load assemblies from bundle, - // file_type_t::assembly should be in this category case file_type_t::deps_json: case file_type_t::runtime_config_json: + case file_type_t::assembly: return false; default: diff --git a/src/installer/corehost/cli/bundle/runner.cpp b/src/installer/corehost/cli/bundle/runner.cpp index 19fc8bcd47576..dec93009e4683 100644 --- a/src/installer/corehost/cli/bundle/runner.cpp +++ b/src/installer/corehost/cli/bundle/runner.cpp @@ -79,8 +79,7 @@ bool runner_t::probe(const pal::string_t& relative_path, int64_t* offset, int64_ return true; } - -bool runner_t::locate(const pal::string_t& relative_path, pal::string_t& full_path) const +bool runner_t::locate(const pal::string_t& relative_path, pal::string_t& full_path, bool& extracted_to_disk) const { const bundle::file_entry_t* entry = probe(relative_path); @@ -90,11 +89,9 @@ bool runner_t::locate(const pal::string_t& relative_path, pal::string_t& full_pa return false; } - // Currently, all files except deps.json and runtimeconfig.json are extracted to disk. - // The json files are not queried by the host using this method. - assert(entry->needs_extraction()); + extracted_to_disk = entry->needs_extraction(); + full_path.assign(extracted_to_disk ? extraction_path() : base_path()); - full_path.assign(extraction_path()); append_path(&full_path, relative_path.c_str()); return true; diff --git a/src/installer/corehost/cli/bundle/runner.h b/src/installer/corehost/cli/bundle/runner.h index 39c6b6b1190b0..f632814df8f81 100644 --- a/src/installer/corehost/cli/bundle/runner.h +++ b/src/installer/corehost/cli/bundle/runner.h @@ -21,26 +21,31 @@ namespace bundle { public: runner_t(const pal::char_t* bundle_path, - const pal::char_t *app_path, - int64_t header_offset) + const pal::char_t* app_path, + int64_t header_offset) : info_t(bundle_path, app_path, header_offset) {} const pal::string_t& extraction_path() const { return m_extraction_path; } bool probe(const pal::string_t& relative_path, int64_t* offset, int64_t* size) const; - bool locate(const pal::string_t& relative_path, pal::string_t& full_path) const; + const file_entry_t* probe(const pal::string_t& relative_path) const; + bool locate(const pal::string_t& relative_path, pal::string_t& full_path, bool& extracted_to_disk) const; + bool locate(const pal::string_t& relative_path, pal::string_t& full_path) const + { + bool extracted_to_disk; + return locate(relative_path, full_path, extracted_to_disk); + } static StatusCode process_manifest_and_extract() { return ((runner_t*) the_app)->extract(); } - + static const runner_t* app() { return (const runner_t*)the_app; } private: StatusCode extract(); - const file_entry_t* probe(const pal::string_t& relative_path) const; manifest_t m_manifest; pal::string_t m_extraction_path; diff --git a/src/installer/corehost/cli/deps_entry.cpp b/src/installer/corehost/cli/deps_entry.cpp index 449403fa038e2..85c845047da4c 100644 --- a/src/installer/corehost/cli/deps_entry.cpp +++ b/src/installer/corehost/cli/deps_entry.cpp @@ -38,11 +38,12 @@ static pal::string_t normalize_dir_separator(const pal::string_t& path) // Returns: // If the file exists in the path relative to the "base" directory within the // single-file or on disk. -bool deps_entry_t::to_path(const pal::string_t& base, const pal::string_t& ietf_dir, bool look_in_base, bool look_in_bundle, pal::string_t* str) const +bool deps_entry_t::to_path(const pal::string_t& base, const pal::string_t& ietf_dir, bool look_in_base, bool look_in_bundle, pal::string_t* str, bool &loaded_from_bundle) const { pal::string_t& candidate = *str; candidate.clear(); + loaded_from_bundle = false; // Base directory must be present to obtain full path if (base.empty()) @@ -67,9 +68,11 @@ bool deps_entry_t::to_path(const pal::string_t& base, const pal::string_t& ietf_ { // If sub_path is found in the single-file bundle, // app::locate() will set candidate to the full-path to the assembly extracted out to disk. - if (app->locate(sub_path, candidate)) + bool extracted_to_disk = false; + if (app->locate(sub_path, candidate, extracted_to_disk)) { - trace::verbose(_X(" %s found in bundle [%s]"), sub_path.c_str(), candidate.c_str()); + loaded_from_bundle = !extracted_to_disk; + trace::verbose(_X(" %s found in bundle [%s] %s"), sub_path.c_str(), candidate.c_str(), extracted_to_disk ? _X("(extracted)") : _X("")); return true; } else @@ -112,7 +115,7 @@ bool deps_entry_t::to_path(const pal::string_t& base, const pal::string_t& ietf_ // Returns: // If the file exists in the path relative to the "base" directory. // -bool deps_entry_t::to_dir_path(const pal::string_t& base, bool look_in_bundle, pal::string_t* str) const +bool deps_entry_t::to_dir_path(const pal::string_t& base, bool look_in_bundle, pal::string_t* str, bool& loaded_from_bundle) const { pal::string_t ietf_dir; @@ -134,7 +137,7 @@ bool deps_entry_t::to_dir_path(const pal::string_t& base, bool look_in_bundle, p base.c_str(), ietf_dir.c_str(), asset.name.c_str()); } - return to_path(base, ietf_dir, true, look_in_bundle, str); + return to_path(base, ietf_dir, true, look_in_bundle, str, loaded_from_bundle); } // ----------------------------------------------------------------------------- @@ -150,7 +153,10 @@ bool deps_entry_t::to_dir_path(const pal::string_t& base, bool look_in_bundle, p // bool deps_entry_t::to_rel_path(const pal::string_t& base, bool look_in_bundle, pal::string_t* str) const { - return to_path(base, _X(""), false, look_in_bundle, str); + bool loaded_from_bundle; + bool result = to_path(base, _X(""), false, look_in_bundle, str, loaded_from_bundle); + assert(!loaded_from_bundle); + return result; } // ----------------------------------------------------------------------------- diff --git a/src/installer/corehost/cli/deps_entry.h b/src/installer/corehost/cli/deps_entry.h index 2c66b1f055273..a8e994bef5b21 100644 --- a/src/installer/corehost/cli/deps_entry.h +++ b/src/installer/corehost/cli/deps_entry.h @@ -53,7 +53,7 @@ struct deps_entry_t bool is_rid_specific; // Given a "base" dir, yield the file path within this directory or single-file bundle. - bool to_dir_path(const pal::string_t& base, bool look_in_bundle, pal::string_t* str) const; + bool to_dir_path(const pal::string_t& base, bool look_in_bundle, pal::string_t* str, bool& loaded_from_bundle) const; // Given a "base" dir, yield the relative path in the package layout. bool to_rel_path(const pal::string_t& base, bool look_in_bundle, pal::string_t* str) const; @@ -64,7 +64,7 @@ struct deps_entry_t private: // Given a "base" dir, yield the filepath within this directory or relative to this directory based on "look_in_base" // Returns a path within the single-file bundle, or a file on disk, - bool to_path(const pal::string_t& base, const pal::string_t& ietf_code, bool look_in_base, bool look_in_bundle, pal::string_t* str) const; + bool to_path(const pal::string_t& base, const pal::string_t& ietf_code, bool look_in_base, bool look_in_bundle, pal::string_t* str, bool & loaded_from_bundle) const; }; #endif // __DEPS_ENTRY_H_ diff --git a/src/installer/corehost/cli/hostpolicy/deps_resolver.cpp b/src/installer/corehost/cli/hostpolicy/deps_resolver.cpp index 21060eab4bdfb..5a3d5a0fc612d 100644 --- a/src/installer/corehost/cli/hostpolicy/deps_resolver.cpp +++ b/src/installer/corehost/cli/hostpolicy/deps_resolver.cpp @@ -281,10 +281,13 @@ void deps_resolver_t::setup_additional_probes(const std::vector& * -- When servicing directories are looked up, look up only if the deps file marks the entry as serviceable. * -- When a deps json based probe is performed, the deps entry's package name and version must match. * -- When looking into a published dir, for rid specific assets lookup rid split folders; for non-rid assets lookup the layout dir. + * The path to the resolved file is returned in candidate out parameter + * If the candidate is embedded within the single-file bundle (rather than an actual file on disk), found_in_bundle will be set to true. */ -bool deps_resolver_t::probe_deps_entry(const deps_entry_t& entry, const pal::string_t& deps_dir, int fx_level, pal::string_t* candidate) +bool deps_resolver_t::probe_deps_entry(const deps_entry_t& entry, const pal::string_t& deps_dir, int fx_level, pal::string_t* candidate, bool & loaded_from_bundle) { candidate->clear(); + loaded_from_bundle = false; for (const auto& config : m_probes) { @@ -316,8 +319,9 @@ bool deps_resolver_t::probe_deps_entry(const deps_entry_t& entry, const pal::str // If the deps json has the package name and version, then someone has already done rid selection and // put the right asset in the dir. So checking just package name and version would suffice. // No need to check further for the exact asset relative sub path. - if (config.probe_deps_json->has_package(entry.library_name, entry.library_version) && entry.to_dir_path(probe_dir, false, candidate)) + if (config.probe_deps_json->has_package(entry.library_name, entry.library_version) && entry.to_dir_path(probe_dir, false, candidate, loaded_from_bundle)) { + assert(!loaded_from_bundle); trace::verbose(_X(" Probed deps json and matched '%s'"), candidate->c_str()); return true; } @@ -343,7 +347,7 @@ bool deps_resolver_t::probe_deps_entry(const deps_entry_t& entry, const pal::str else { // Non-rid assets, lookup in the published dir. - if (entry.to_dir_path(deps_dir, true, candidate)) + if (entry.to_dir_path(deps_dir, true, candidate, loaded_from_bundle)) { trace::verbose(_X(" Probed deps dir and matched '%s'"), candidate->c_str()); return true; @@ -437,10 +441,17 @@ bool deps_resolver_t::resolve_tpa_list( name_to_resolved_asset_map_t::iterator existing = items.find(entry.asset.name); if (existing == items.end()) { - if (probe_deps_entry(entry, deps_dir, fx_level, &resolved_path)) + bool loaded_from_bundle = false; + if (probe_deps_entry(entry, deps_dir, fx_level, &resolved_path, loaded_from_bundle)) { - deps_resolved_asset_t resolved_asset(entry.asset, resolved_path); - add_tpa_asset(resolved_asset, &items); + // Assemblies loaded directly from the bundle are not added to the TPA list. + // The runtime directly probes the bundle-manifest using a host-callback. + if (!loaded_from_bundle) + { + deps_resolved_asset_t resolved_asset(entry.asset, resolved_path); + add_tpa_asset(resolved_asset, &items); + } + return true; } @@ -468,7 +479,8 @@ bool deps_resolver_t::resolve_tpa_list( if (entry.asset.assembly_version > existing_entry->asset.assembly_version || (entry.asset.assembly_version == existing_entry->asset.assembly_version && entry.asset.file_version >= existing_entry->asset.file_version)) { - if (probe_deps_entry(entry, deps_dir, fx_level, &resolved_path)) + bool loaded_from_bundle = false; + if (probe_deps_entry(entry, deps_dir, fx_level, &resolved_path, loaded_from_bundle)) { // If the path is the same, then no need to replace if (resolved_path != existing_entry->resolved_path) @@ -480,9 +492,12 @@ bool deps_resolver_t::resolve_tpa_list( existing_entry = nullptr; items.erase(existing); - deps_asset_t asset(entry.asset.name, entry.asset.relative_path, entry.asset.assembly_version, entry.asset.file_version); - deps_resolved_asset_t resolved_asset(asset, resolved_path); - add_tpa_asset(resolved_asset, &items); + if (!loaded_from_bundle) + { + deps_asset_t asset(entry.asset.name, entry.asset.relative_path, entry.asset.assembly_version, entry.asset.file_version); + deps_resolved_asset_t resolved_asset(asset, resolved_path); + add_tpa_asset(resolved_asset, &items); + } } } else if (fx_level != 0) @@ -505,9 +520,17 @@ bool deps_resolver_t::resolve_tpa_list( // First add managed assembly to the TPA. // TODO: Remove: the deps should contain the managed DLL. // Workaround for: csc.deps.json doesn't have the csc.dll - deps_asset_t asset(get_filename_without_ext(m_managed_app), get_filename(m_managed_app), version_t(), version_t()); - deps_resolved_asset_t resolved_asset(asset, m_managed_app); - add_tpa_asset(resolved_asset, &items); + + // If this is a single-file bundle, app.dll is expected to be within the bundle, unless it is explicitly excluded from the bundle. + // In all other cases, add its path to the TPA list. + pal::string_t managed_app_name = get_filename(m_managed_app); + if (!bundle::info_t::is_single_file_bundle() || + bundle::runner_t::app()->probe(managed_app_name) == nullptr) + { + deps_asset_t asset(get_filename_without_ext(m_managed_app), managed_app_name, version_t(), version_t()); + deps_resolved_asset_t resolved_asset(asset, m_managed_app); + add_tpa_asset(resolved_asset, &items); + } // Add the app's entries const auto& deps_entries = get_deps().get_entries(deps_entry_t::asset_types::runtime); @@ -783,10 +806,14 @@ bool deps_resolver_t::resolve_probe_dirs( trace::verbose(_X("Processing native/culture for deps entry [%s, %s, %s]"), entry.library_name.c_str(), entry.library_version.c_str(), entry.asset.relative_path.c_str()); - if (probe_deps_entry(entry, deps_dir, fx_level, &candidate)) + bool loaded_from_bundle = false; + if (probe_deps_entry(entry, deps_dir, fx_level, &candidate, loaded_from_bundle)) { - init_known_entry_path(entry, candidate); - add_unique_path(asset_type, action(candidate), &items, output, &non_serviced, core_servicing); + if (!loaded_from_bundle) + { + init_known_entry_path(entry, candidate); + add_unique_path(asset_type, action(candidate), &items, output, &non_serviced, core_servicing); + } } else { diff --git a/src/installer/corehost/cli/hostpolicy/deps_resolver.h b/src/installer/corehost/cli/hostpolicy/deps_resolver.h index cb033f5c6d698..8471d94b44c12 100644 --- a/src/installer/corehost/cli/hostpolicy/deps_resolver.h +++ b/src/installer/corehost/cli/hostpolicy/deps_resolver.h @@ -228,7 +228,8 @@ class deps_resolver_t const deps_entry_t& entry, const pal::string_t& deps_dir, int fx_level, - pal::string_t* candidate); + pal::string_t* candidate, + bool &loaded_from_bundle); fx_definition_vector_t& m_fx_definitions; diff --git a/src/installer/corehost/cli/hostpolicy/hostpolicy_context.cpp b/src/installer/corehost/cli/hostpolicy/hostpolicy_context.cpp index 5a0a32394e9a1..ed6801342c465 100644 --- a/src/installer/corehost/cli/hostpolicy/hostpolicy_context.cpp +++ b/src/installer/corehost/cli/hostpolicy/hostpolicy_context.cpp @@ -113,17 +113,24 @@ int hostpolicy_context_t::initialize(hostpolicy_init_t &hostpolicy_init, const a // Get path in which CoreCLR is present. clr_dir = get_directory(clr_path); + // If this is a self-contained single-file bundle, + // System.Private.CoreLib.dll is expected to be within the bundle, unless it is explicitly excluded from the bundle. + // In all other cases, // System.Private.CoreLib.dll is expected to be next to CoreCLR.dll - add its path to the TPA list. - pal::string_t corelib_path = clr_dir; - append_path(&corelib_path, CORELIB_NAME); - - // Append CoreLib path - if (!probe_paths.tpa.empty() && probe_paths.tpa.back() != PATH_SEPARATOR) + if (!bundle::info_t::is_single_file_bundle() || + bundle::runner_t::app()->probe(CORELIB_NAME) == nullptr) { - probe_paths.tpa.push_back(PATH_SEPARATOR); - } + pal::string_t corelib_path = clr_dir; + append_path(&corelib_path, CORELIB_NAME); + + // Append CoreLib path + if (!probe_paths.tpa.empty() && probe_paths.tpa.back() != PATH_SEPARATOR) + { + probe_paths.tpa.push_back(PATH_SEPARATOR); + } - probe_paths.tpa.append(corelib_path); + probe_paths.tpa.append(corelib_path); + } const fx_definition_vector_t &fx_definitions = resolver.get_fx_definitions(); diff --git a/src/installer/test/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleExtractToSpecificPath.cs b/src/installer/test/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleExtractToSpecificPath.cs index 26dbc3a00ef83..8f9e97415285b 100644 --- a/src/installer/test/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleExtractToSpecificPath.cs +++ b/src/installer/test/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleExtractToSpecificPath.cs @@ -30,7 +30,7 @@ private void Bundle_Extraction_To_Specific_Path_Succeeds() // Publish the bundle string singleFile; - Bundler bundler = BundleHelper.BundleApp(fixture, out singleFile); + Bundler bundler = BundleHelper.BundleApp(fixture, out singleFile, options: BundleOptions.BundleNativeBinaries); // Verify expected files in the bundle directory var bundleDir = BundleHelper.GetBundleDir(fixture); @@ -157,7 +157,6 @@ private void Bundle_extraction_can_recover_missing_files() extractDir.Should().HaveFiles(extractedFiles); } - public class SharedTestState : IDisposable { public TestProjectFixture TestFixture { get; set; } diff --git a/src/installer/test/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundledAppWithSubDirs.cs b/src/installer/test/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundledAppWithSubDirs.cs index 33e05b5c0504d..ff15ee6fb80f1 100644 --- a/src/installer/test/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundledAppWithSubDirs.cs +++ b/src/installer/test/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundledAppWithSubDirs.cs @@ -32,9 +32,7 @@ private void RunTheApp(string path) .HaveStdOutContaining("Wow! We now say hello to the big world and you."); } - // BundleOptions.BundleNativeBinaries: Test when the payload data files are unbundled, and beside the single-file app. - // BundleOptions.BundleAllContent: Test when the payload data files are bundled and extracted to temporary directory. - // Once the runtime can load assemblies from the bundle, BundleOptions.None can be used in place of BundleOptions.BundleNativeBinaries. + [InlineData(BundleOptions.None)] [InlineData(BundleOptions.BundleNativeBinaries)] [InlineData(BundleOptions.BundleAllContent)] [Theory] @@ -50,6 +48,7 @@ public void Bundled_Framework_dependent_App_Run_Succeeds(BundleOptions options) RunTheApp(singleFile); } + [InlineData(BundleOptions.None)] [InlineData(BundleOptions.BundleNativeBinaries)] [InlineData(BundleOptions.BundleAllContent)] [Theory] @@ -65,6 +64,7 @@ public void Bundled_Self_Contained_App_Run_Succeeds(BundleOptions options) RunTheApp(singleFile); } + [InlineData(BundleOptions.None)] [InlineData(BundleOptions.BundleNativeBinaries)] [InlineData(BundleOptions.BundleAllContent)] [Theory] diff --git a/src/installer/test/Microsoft.NET.HostModel.Tests/Helpers/BundleHelper.cs b/src/installer/test/Microsoft.NET.HostModel.Tests/Helpers/BundleHelper.cs index c5f7717943676..def3a46956399 100644 --- a/src/installer/test/Microsoft.NET.HostModel.Tests/Helpers/BundleHelper.cs +++ b/src/installer/test/Microsoft.NET.HostModel.Tests/Helpers/BundleHelper.cs @@ -53,8 +53,7 @@ public static string[] GetBundledFiles(TestProjectFixture fixture) public static string[] GetExtractedFiles(TestProjectFixture fixture) { - string appBaseName = GetAppBaseName(fixture); - return new string[] { $"{appBaseName}.dll" }; + return new string[] { Path.GetFileName(fixture.TestProject.CoreClrDll) }; } public static string[] GetFilesNeverExtracted(TestProjectFixture fixture) @@ -62,6 +61,7 @@ public static string[] GetFilesNeverExtracted(TestProjectFixture fixture) string appBaseName = GetAppBaseName(fixture); return new string[] { $"{appBaseName}.deps.json", $"{appBaseName}.runtimeconfig.json", + $"{appBaseName}.dll", Path.GetFileName(fixture.TestProject.HostFxrDll), Path.GetFileName(fixture.TestProject.HostPolicyDll) }; } @@ -139,7 +139,7 @@ public static string GenerateBundle(Bundler bundler, string sourceDir, string ou // which may not (yet) be available in the SDK. public static Bundler BundleApp(TestProjectFixture fixture, out string singleFile, - BundleOptions options = BundleOptions.BundleNativeBinaries, + BundleOptions options = BundleOptions.None, Version targetFrameworkVersion = null, bool copyExcludedFiles = true) { @@ -153,11 +153,8 @@ public static Bundler BundleApp(TestProjectFixture fixture, return bundler; } - // The defaut option for Bundling apps is BundleOptions.BundleNativeBinaries - // Until CoreCLR runtime can learn how to process assemblies from the bundle. - // This is because CoreCLR expects System.Private.Corelib.dll to be beside CoreCLR.dll public static string BundleApp(TestProjectFixture fixture, - BundleOptions options = BundleOptions.BundleNativeBinaries, + BundleOptions options = BundleOptions.None, Version targetFrameworkVersion = null) { string singleFile; diff --git a/src/installer/test/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.Bundle.Tests/BundleAndRun.cs b/src/installer/test/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.Bundle.Tests/BundleAndRun.cs index e4949b47750da..bf9ff14cf5b0f 100644 --- a/src/installer/test/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.Bundle.Tests/BundleAndRun.cs +++ b/src/installer/test/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.Bundle.Tests/BundleAndRun.cs @@ -32,7 +32,7 @@ private void RunTheApp(string path) .HaveStdOutContaining("Wow! We now say hello to the big world and you."); } - private void BundleRun(TestProjectFixture fixture, string publishPath, string singleFileDir) + private void BundleRun(TestProjectFixture fixture, string publishPath) { var hostName = BundleHelper.GetHostName(fixture); @@ -56,33 +56,24 @@ private string RelativePath(string path) public void TestWithAbsolutePaths() { var fixture = sharedTestState.TestFixture.Copy(); - string publishDir = BundleHelper.GetPublishPath(fixture); - string outputDir = BundleHelper.GetBundleDir(fixture).FullName; - - BundleRun(fixture, publishDir, outputDir); + BundleRun(fixture, publishDir); } [Fact] public void TestWithRelativePaths() { var fixture = sharedTestState.TestFixture.Copy(); - string publishDir = RelativePath(BundleHelper.GetPublishPath(fixture)); - string outputDir = RelativePath(BundleHelper.GetBundleDir(fixture).FullName); - - BundleRun(fixture, publishDir, outputDir); + BundleRun(fixture, publishDir); } [Fact] public void TestWithRelativePathsDirSeparator() { var fixture = sharedTestState.TestFixture.Copy(); - string publishDir = RelativePath(BundleHelper.GetPublishPath(fixture)) + Path.DirectorySeparatorChar; - string outputDir = RelativePath(BundleHelper.GetBundleDir(fixture).FullName) + Path.DirectorySeparatorChar; - - BundleRun(fixture, publishDir, outputDir); + BundleRun(fixture, publishDir); } public class SharedTestState : IDisposable diff --git a/src/installer/test/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.Bundle.Tests/BundleLegacy.cs b/src/installer/test/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.Bundle.Tests/BundleLegacy.cs index 7b9d6863607d2..1690be766e33a 100644 --- a/src/installer/test/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.Bundle.Tests/BundleLegacy.cs +++ b/src/installer/test/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.Bundle.Tests/BundleLegacy.cs @@ -27,8 +27,7 @@ public void TestNetCoreApp3xApp(int minorVersion) { var fixture = (minorVersion == 0) ? sharedTestState.TestFixture30.Copy() : sharedTestState.TestFixture31.Copy(); - // Targetting netcoreap3.x implies BundleOption.BundleAllContent - var singleFile = BundleHelper.BundleApp(fixture, BundleOptions.None, new Version(3, minorVersion)); + var singleFile = BundleHelper.BundleApp(fixture, targetFrameworkVersion: new Version(3, minorVersion)); Command.Create(singleFile) .CaptureStdErr() @@ -50,7 +49,6 @@ private static TestProjectFixture CreatePublishedFixture(string netCoreAppFramew return fixture; } - public class SharedTestState : IDisposable { public TestProjectFixture TestFixture30 { get; set; }