#include "assemblybinder.hpp"
#include "assemblyname.hpp"
-
#include "assembly.hpp"
#include "applicationcontext.hpp"
#include "bindertracing.h"
_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();
_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();
// specified. Generally only NGEN PDB generation has
// this TRUE.
fExplicitBindToNativeImage,
- &pAssembly));
+ &pAssembly,
+ NULL /* szMDAssemblyPath */,
+ Bundle::ProbeAppBundle(assemblyPath)));
AssemblyName *pAssemblyName;
pAssemblyName = pAssembly->GetAssemblyName();
/*
* 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.
*
}
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)
}
/* 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;
{
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
BinderReleasePEImage(pPEImage);
BinderReleasePEImage(pNativePEImage);
- hr = BinderAcquirePEImage(szAssemblyPath, &pPEImage, &pNativePEImage, false);
+ hr = BinderAcquirePEImage(szAssemblyPath, &pPEImage, &pNativePEImage, false, bundleFileLocation);
IF_FAIL_GO(hr);
}
}
}
else
{
- hr = BinderAcquirePEImage(szMDAssemblyPath, &pPEImage, NULL, FALSE);
+ hr = BinderAcquirePEImage(szMDAssemblyPath, &pPEImage, NULL, FALSE, bundleFileLocation);
IF_FAIL_GO(hr);
hr = BinderAcquireImport(pPEImage, &pIMetaDataAssemblyImport, dwPAFlags, FALSE);
#include "assemblybinder.hpp"
#include "coreclrbindercommon.h"
#include "clrprivbindercoreclr.h"
+#include "bundle.h"
using namespace BINDER_SPACE;
#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);
#include "bindertypes.hpp"
#include "bindresult.hpp"
#include "coreclrbindercommon.h"
+#include "bundle.h"
class CLRPrivBinderAssemblyLoadContext;
class CLRPrivBinderCoreCLR;
/* 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,
#ifdef FEATURE_GDBJIT
#include "../../vm/gdbjithelpers.h"
#endif // FEATURE_GDBJIT
+#include "bundle.h"
#define ASSERTE_ALL_BUILDS(expr) _ASSERTE_ALL_BUILDS(__FILE__, (expr))
const char** propertyValues,
int propertyCount,
LPCWSTR** propertyKeysWRef,
- LPCWSTR** propertyValuesWRef)
+ LPCWSTR** propertyValuesWRef,
+ BundleProbe** bundleProbe)
{
LPCWSTR* propertyKeysW = new (nothrow) LPCWSTR[propertyCount];
ASSERTE_ALL_BUILDS(propertyKeysW != nullptr);
{
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;
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);
--- /dev/null
+// 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 =======================================================================
#include <string.h>
#include <errno.h>
#include <ctype.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
#endif
#ifdef __cplusplus
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.
PALIMPORT
PVOID
PALAPI
-PAL_LOADLoadPEFile(HANDLE hFile);
+PAL_LOADLoadPEFile(HANDLE hFile, size_t offset);
/*++
PAL_LOADUnloadPEFile
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 :
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
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
--*/
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)
{
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);
{
/* 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)
{
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
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;
}
PROT_READ|PROT_WRITE,
flags,
pProcessLocalData->UnixFd,
- 0
+ offset
);
}
else
prot,
flags,
pProcessLocalData->UnixFd,
- 0
+ offset
);
#if ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
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;
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)
//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;
//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)
{
prot,
MAP_FILE|MAP_PRIVATE|MAP_FIXED,
fd,
- currentHeader.PointerToRawData,
+ offset + currentHeader.PointerToRawData,
§ionData);
if (NO_ERROR != palError)
{
palError = MAPRecordMapping(pFileObject,
loadedBase,
prevSectionEnd,
- (char*)imageEnd - (char*)prevSectionEnd,
+ offset + (char*)imageEnd - (char*)prevSectionEnd,
PROT_NONE);
if (NO_ERROR != palError)
{
assemblyloadcontext.cpp
baseassemblyspec.cpp
binder.cpp
+ bundle.cpp
castcache.cpp
callcounting.cpp
ceeload.cpp
sizeof(MethodDesc),
sizeof(FieldDesc),
sizeof(Module)
- ));
+ ));
#endif // _DEBUG
// The base domain is initialized in SystemDomain::Attach()
m_pSystemAssembly = NULL;
DWORD size = 0;
-
+ AppDomain* pAppDomain = ::GetAppDomain();
// Get the install directory so we can find mscorlib
hr = GetInternalSystemDirectory(NULL, &size);
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();
#ifdef _DEBUG
BOOL fPause = EEConfig::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_PauseOnLoad, FALSE);
- while(fPause)
+ while (fPause)
{
ClrSleepEx(20, TRUE);
}
#include "gchandleutilities.h"
#include "../binder/inc/applicationcontext.hpp"
#include "rejit.h"
+#include "bundle.h"
#ifdef FEATURE_MULTICOREJIT
#include "multicorejit.h"
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())
// 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())
{
--- /dev/null
+// 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();
+}
+
#include "domainfile.h"
#include "holder.h"
#include "../binder/inc/assemblybinder.hpp"
-
+#include "bundle.h"
#include "strongnameinternal.h"
#include "strongnameholders.h"
}
-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;
{
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();
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();
../assemblyspec.cpp
../baseassemblyspec.cpp
../binder.cpp
+ ../bundle.cpp
../castcache.cpp
../ceeload.cpp
../ceemain.cpp
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();
}
BOOL bIsFlatLayoutSuitable = ((imageLayoutMask & PEImageLayout::LAYOUT_FLAT) != 0);
#if !defined(TARGET_UNIX)
- if (bIsMappedLayoutSuitable)
- {
- bIsFlatLayoutSuitable = FALSE;
- }
+ bIsFlatLayoutSuitable = IsInBundle() || !bIsMappedLayoutSuitable;
#endif // !TARGET_UNIX
_ASSERTE(bIsMappedLayoutSuitable || bIsFlatLayoutSuitable);
}
#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())
{
SetLayout(IMAGE_LOADED, m_pLayouts[IMAGE_FLAT]);
}
else
-#endif // TARGET_UNIX
{
if(!IsFile())
{
{
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)
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;
#include "peimagelayout.h"
#include "sstring.h"
#include "holder.h"
+#include <bundle.h>
class SimpleRWLock;
// --------------------------------------------------------------------------------
#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)
// 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();
// Private routines
// ------------------------------------------------------------
- void Init(LPCWSTR pPath);
+ void Init(LPCWSTR pPath, BundleFileLocation bundleFileLocation);
void Init(IStream* pStream, UINT64 uStreamAsmId,
DWORD dwModuleId, BOOL resourceFile);
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
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;
{
WRAPPER_NO_CONTRACT;
- return !m_path.IsEmpty();
+ return !GetPathToLoad().IsEmpty();
}
#ifndef DACCESS_COMPILE
CHECK_OK;
}
-inline void PEImage::Init(LPCWSTR pPath)
+inline void PEImage::Init(LPCWSTR pPath, BundleFileLocation bundleFileLocation)
{
CONTRACTL
{
MODE_ANY;
}
CONTRACTL_END;
+
m_path = pPath;
m_path.Normalize();
+ m_bundleFileLocation = bundleFileLocation;
SetModuleFileNameHintForDAC();
}
#ifndef DACCESS_COMPILE
}
/* 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());
}
if (flags & MDInternalImport_TrustedNativeImage)
pImage->SetIsTrustedNativeImage();
#endif
- pImage->Init(pPath);
+ pImage->Init(pPath, bundleFileLocation);
pImage->AddToHashMap();
return dac_cast<PTR_PEImage>(pImage.Extract());
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;
#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;
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())
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();
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));
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));
}
}
else
-#endif
+#endif // CROSSGEN_COMPILE
if (!IsNativeMachineFormat() && !IsI386())
{
//can't rely on the image
#else //!TARGET_UNIX
#ifndef CROSSGEN_COMPILE
- m_LoadedFile = PAL_LOADLoadPEFile(hFile);
+ m_LoadedFile = PAL_LOADLoadPEFile(hFile, offset);
if (m_LoadedFile == NULL)
{
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
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)
ThrowLastError();
}
- loadedImage = PAL_LOADLoadPEFile(fileHandle);
+ loadedImage = PAL_LOADLoadPEFile(fileHandle, 0);
}
#else
loadedImage = CLRLoadLibraryEx(fullPath, NULL, GetLoadWithAlteredSearchPathFlag());
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
{
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:
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);
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;
{
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;
// 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())
{
// 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
// 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;
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);
}
// -----------------------------------------------------------------------------
//
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;
}
// -----------------------------------------------------------------------------
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;
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_
* -- 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)
{
// 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;
}
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;
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;
}
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)
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)
// 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);
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
{
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;
// 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();
// 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);
extractDir.Should().HaveFiles(extractedFiles);
}
-
public class SharedTestState : IDisposable
{
public TestProjectFixture TestFixture { get; set; }
.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]
RunTheApp(singleFile);
}
+ [InlineData(BundleOptions.None)]
[InlineData(BundleOptions.BundleNativeBinaries)]
[InlineData(BundleOptions.BundleAllContent)]
[Theory]
RunTheApp(singleFile);
}
+ [InlineData(BundleOptions.None)]
[InlineData(BundleOptions.BundleNativeBinaries)]
[InlineData(BundleOptions.BundleAllContent)]
[Theory]
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)
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) };
}
// 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)
{
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;
.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);
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
{
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()
return fixture;
}
-
public class SharedTestState : IDisposable
{
public TestProjectFixture TestFixture30 { get; set; }