Single-File: Run from Bundle
authorSwaroop Sridhar <swaroop.sridhar@microsoft.com>
Tue, 21 Apr 2020 05:04:13 +0000 (22:04 -0700)
committerSwaroop Sridhar <swaroop.sridhar@microsoft.com>
Mon, 18 May 2020 07:53:47 +0000 (00:53 -0700)
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

37 files changed:
src/coreclr/src/binder/assemblybinder.cpp
src/coreclr/src/binder/coreclrbindercommon.cpp
src/coreclr/src/binder/inc/assembly.hpp
src/coreclr/src/binder/inc/assemblybinder.hpp
src/coreclr/src/dlls/mscoree/unixinterface.cpp
src/coreclr/src/inc/bundle.h [new file with mode: 0644]
src/coreclr/src/pal/inc/pal.h
src/coreclr/src/pal/src/include/pal/map.hpp
src/coreclr/src/pal/src/include/pal/module.h
src/coreclr/src/pal/src/loader/module.cpp
src/coreclr/src/pal/src/map/map.cpp
src/coreclr/src/vm/CMakeLists.txt
src/coreclr/src/vm/appdomain.cpp
src/coreclr/src/vm/appdomain.hpp
src/coreclr/src/vm/assemblynative.cpp
src/coreclr/src/vm/bundle.cpp [new file with mode: 0644]
src/coreclr/src/vm/coreassemblyspec.cpp
src/coreclr/src/vm/crossgen/CMakeLists.txt
src/coreclr/src/vm/pefile.cpp
src/coreclr/src/vm/peimage.cpp
src/coreclr/src/vm/peimage.h
src/coreclr/src/vm/peimage.inl
src/coreclr/src/vm/peimagelayout.cpp
src/coreclr/src/vm/peimagelayout.h
src/installer/corehost/cli/bundle/file_entry.cpp
src/installer/corehost/cli/bundle/runner.cpp
src/installer/corehost/cli/bundle/runner.h
src/installer/corehost/cli/deps_entry.cpp
src/installer/corehost/cli/deps_entry.h
src/installer/corehost/cli/hostpolicy/deps_resolver.cpp
src/installer/corehost/cli/hostpolicy/deps_resolver.h
src/installer/corehost/cli/hostpolicy/hostpolicy_context.cpp
src/installer/test/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleExtractToSpecificPath.cs
src/installer/test/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundledAppWithSubDirs.cs
src/installer/test/Microsoft.NET.HostModel.Tests/Helpers/BundleHelper.cs
src/installer/test/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.Bundle.Tests/BundleAndRun.cs
src/installer/test/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.Bundle.Tests/BundleLegacy.cs

index 784b912..18d66a8 100644 (file)
@@ -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<Assembly> 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<Assembly> 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<Assembly> 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<Assembly> 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<Assembly> 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<LPCTSTR>(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);
index afd64cf..e415b90 100644 (file)
@@ -7,6 +7,7 @@
 #include "assemblybinder.hpp"
 #include "coreclrbindercommon.h"
 #include "clrprivbindercoreclr.h"
+#include "bundle.h"
 
 using namespace BINDER_SPACE;
 
index 0d5500a..f42cee4 100644 (file)
 #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);
index 1332b67..5246676 100644 (file)
@@ -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,
index f54e7c7..ca4d452 100644 (file)
@@ -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 (file)
index 0000000..00fc186
--- /dev/null
@@ -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 <sstring.h>
+
+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 =======================================================================
index 861a10f..7ffdc2b 100644 (file)
@@ -47,6 +47,9 @@ Abstract:
 #include <string.h>
 #include <errno.h>
 #include <ctype.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
 #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
index eba0d88..b8c584f 100644 (file)
@@ -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 :
index bb409b8..6a5e080 100644 (file)
@@ -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
index 8418ef7..6516897 100644 (file)
@@ -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)
             {
index cac7364..90936b1 100644 (file)
@@ -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,
                         &sectionData);
         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)
         {
index e11e58d..c04d5f6 100644 (file)
@@ -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
index 95cd917..7681e64 100644 (file)
@@ -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);
     }
index f017b42..ece9672 100644 (file)
@@ -29,6 +29,7 @@
 #include "gchandleutilities.h"
 #include "../binder/inc/applicationcontext.hpp"
 #include "rejit.h"
+#include "bundle.h"
 
 #ifdef FEATURE_MULTICOREJIT
 #include "multicorejit.h"
index b9cec28..0cb1dba 100644 (file)
@@ -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 (file)
index 0000000..e0d0315
--- /dev/null
@@ -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 <utilcode.h>
+#include <corhost.h>
+#include <sstring.h>
+
+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();
+}
+
index 99ae244..d2fc56a 100644 (file)
@@ -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();
index 77f4631..b532a9e 100644 (file)
@@ -6,6 +6,7 @@ set(VM_CROSSGEN_SOURCES
   ../assemblyspec.cpp
   ../baseassemblyspec.cpp
   ../binder.cpp
+  ../bundle.cpp
   ../castcache.cpp
   ../ceeload.cpp
   ../ceemain.cpp
index 7da14c9..ae9c966 100644 (file)
@@ -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();
                 }
index 7dc580f..c07facf 100644 (file)
@@ -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;
index 1edf05d..2a9b0b7 100644 (file)
@@ -19,6 +19,7 @@
 #include "peimagelayout.h"
 #include "sstring.h"
 #include "holder.h"
+#include <bundle.h>
 
 class SimpleRWLock;
 // --------------------------------------------------------------------------------
@@ -102,7 +103,8 @@ public:
 #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 @@ public:
 
     // 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 @@ private:
     // 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 @@ private:
     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
index 03d8bc2..eebae16 100644 (file)
@@ -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<PTR_PEImage>(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<PTR_PEImage>(pImage.Extract());
index 7973971..3303210 100644 (file)
@@ -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());
index 9ae14c4..bc846c5 100644 (file)
@@ -54,6 +54,7 @@ public:
     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
index 775117f..25e2cd0 100644 (file)
@@ -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:
index 19fc8bc..dec9300 100644 (file)
@@ -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;
index 39c6b6b..f632814 100644 (file)
@@ -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;
index 449403f..85c8450 100644 (file)
@@ -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;
 }
 
 // -----------------------------------------------------------------------------
index 2c66b1f..a8e994b 100644 (file)
@@ -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_
index 21060ea..5a3d5a0 100644 (file)
@@ -281,10 +281,13 @@ void deps_resolver_t::setup_additional_probes(const std::vector<pal::string_t>&
  *   -- 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
         {
index cb033f5..8471d94 100644 (file)
@@ -228,7 +228,8 @@ private:
         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;
 
index 5a0a323..ed68013 100644 (file)
@@ -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();
 
index 26dbc3a..8f9e974 100644 (file)
@@ -30,7 +30,7 @@ namespace AppHost.Bundle.Tests
 
             // 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 @@ namespace AppHost.Bundle.Tests
             extractDir.Should().HaveFiles(extractedFiles);
         }
 
-
         public class SharedTestState : IDisposable
         {
             public TestProjectFixture TestFixture { get; set; }
index 33e05b5..ff15ee6 100644 (file)
@@ -32,9 +32,7 @@ namespace AppHost.Bundle.Tests
                 .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 @@ namespace AppHost.Bundle.Tests
             RunTheApp(singleFile);
         }
 
+        [InlineData(BundleOptions.None)]
         [InlineData(BundleOptions.BundleNativeBinaries)]
         [InlineData(BundleOptions.BundleAllContent)]
         [Theory]
@@ -65,6 +64,7 @@ namespace AppHost.Bundle.Tests
             RunTheApp(singleFile);
         }
 
+        [InlineData(BundleOptions.None)]
         [InlineData(BundleOptions.BundleNativeBinaries)]
         [InlineData(BundleOptions.BundleAllContent)]
         [Theory]
index c5f7717..def3a46 100644 (file)
@@ -53,8 +53,7 @@ namespace BundleTests.Helpers
 
         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 @@ namespace BundleTests.Helpers
             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 @@ namespace BundleTests.Helpers
         // 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 @@ namespace BundleTests.Helpers
             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;
index e4949b4..bf9ff14 100644 (file)
@@ -32,7 +32,7 @@ namespace Microsoft.NET.HostModel.Tests
                 .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 @@ namespace Microsoft.NET.HostModel.Tests
         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
index 7b9d686..1690be7 100644 (file)
@@ -27,8 +27,7 @@ namespace Microsoft.NET.HostModel.Tests
         {
             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 @@ namespace Microsoft.NET.HostModel.Tests
             return fixture;
         }
 
-
         public class SharedTestState : IDisposable
         {
             public TestProjectFixture TestFixture30 { get; set; }