diff --git a/src/binder/assemblybinder.cpp b/src/binder/assemblybinder.cpp index 2ad127652bb2..78ad94772b31 100644 --- a/src/binder/assemblybinder.cpp +++ b/src/binder/assemblybinder.cpp @@ -14,7 +14,6 @@ #include "assemblybinder.hpp" #include "assemblyname.hpp" - #include "assembly.hpp" #include "applicationcontext.hpp" #include "loadcontext.hpp" @@ -645,19 +644,23 @@ namespace BINDER_SPACE _ASSERTE(ppSystemAssembly != NULL); - StackSString sCoreLibDir(systemDirectory); ReleaseHolder pSystemAssembly; + StackSString sCoreLib; + // For normal runs, corelib is expected to be found in systemDirectory. + // For self-contained single-file bundles, corelib is expected within the bundle + // (systemDirectory is set accordingly) + + StackSString sCoreLibDir(systemDirectory); if (!sCoreLibDir.EndsWith(DIRECTORY_SEPARATOR_CHAR_W)) { sCoreLibDir.Append(DIRECTORY_SEPARATOR_CHAR_W); } - StackSString sCoreLib; - - // At run-time, System.Private.CoreLib.dll is expected to be the NI image. sCoreLib = sCoreLibDir; sCoreLib.Append(CoreLibName_IL_W); + + // At run-time, System.Private.CoreLib.dll is expected to be the NI image. BOOL fExplicitBindToNativeImage = (fBindToNativeImage == true)? TRUE:FALSE; #ifdef FEATURE_NI_BIND_FALLBACK // Some non-Windows platforms do not automatically generate the NI image as CoreLib.dll. @@ -667,7 +670,9 @@ namespace BINDER_SPACE IF_FAIL_GO(AssemblyBinder::GetAssembly(sCoreLib, TRUE /* fIsInGAC */, fExplicitBindToNativeImage, - &pSystemAssembly)); + &pSystemAssembly, + NULL /* szMDAssemblyPath */, + Bundle::ProbeAppBundle(CoreLibName_IL_W, /* pathIsBundleRelative */ true))); *ppSystemAssembly = pSystemAssembly.Extract(); @@ -708,7 +713,9 @@ namespace BINDER_SPACE IF_FAIL_GO(AssemblyBinder::GetAssembly(sMscorlibSatellite, TRUE /* fIsInGAC */, FALSE /* fExplicitBindToNativeImage */, - &pSystemAssembly)); + &pSystemAssembly, + NULL /* szMDAssemblyPath */, + Bundle::ProbeAppBundle(sMscorlibSatellite))); *ppSystemAssembly = pSystemAssembly.Extract(); @@ -873,7 +880,9 @@ namespace BINDER_SPACE // specified. Generally only NGEN PDB generation has // this TRUE. fExplicitBindToNativeImage, - &pAssembly)); + &pAssembly, + NULL /* szMDAssemblyPath */, + Bundle::ProbeAppBundle(assemblyPath))); #ifdef FEATURE_VERSIONING_LOG IF_FAIL_GO(LogAssemblyNameWhereRef(pApplicationContext, pAssembly)); #endif // FEATURE_VERSIONING_LOG @@ -1106,6 +1115,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. * @@ -1158,13 +1174,55 @@ namespace BINDER_SPACE pBindResult)); } } - else + else { - // Is assembly on TPA list? + ReleaseHolder pTPAAssembly; SString &simpleName = pRequestedAssemblyName->GetSimpleName(); + + // Is assembly in the bundle? + // Single-file bundle contents take precedence over TPA. + // The list of bundled assemblies is contained in the bundle manifest, and NOT in the TPA. + // Therefore the bundle is first probed using the assembly's simple name. + // If found, the assembly is loaded from the bundle. + if (Bundle::AppIsBundle()) + { + SString candidates[] = { W(".dll"), W(".ni.dll") }; + + // Loop through the binding paths looking for a matching assembly + for (int i = 0; i < 2; i++) + { + SString assemblyFileName(simpleName); + assemblyFileName.Append(candidates[i]); + + SString assemblyFilePath(Bundle::AppBundle->BasePath()); + assemblyFilePath.Append(assemblyFileName); + + hr = GetAssembly(assemblyFilePath, + TRUE, // fIsInGAC + FALSE, // fExplicitBindToNativeImage + &pTPAAssembly, + NULL, // szMDAssemblyPath + Bundle::ProbeAppBundle(assemblyFileName, /* pathIsBundleRelative */ true)); + + if (hr != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) + { + // Any other error is fatal + IF_FAIL_GO(hr); + + if (TestCandidateRefMatchesDef(pRequestedAssemblyName, pTPAAssembly->GetAssemblyName(), true /*tpaListAssembly*/)) + { + // We have found the requested assembly match in the bundle with validation of the full-qualified name. + // Bind to it. + pBindResult->SetResult(pTPAAssembly); + GO_WITH_HRESULT(S_OK); + } + } + } + } + + // Is assembly on TPA list? SimpleNameToFileNameMap * tpaMap = pApplicationContext->GetTpaList(); const SimpleNameToFileNameMapEntry *pTpaEntry = tpaMap->LookupPtr(simpleName.GetUnicode()); - ReleaseHolder pTPAAssembly; if (pTpaEntry != nullptr) { if (pTpaEntry->m_wszNIFileName != nullptr) @@ -1350,20 +1408,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; @@ -1387,7 +1446,7 @@ namespace BINDER_SPACE LPCTSTR szAssemblyPath = const_cast(assemblyPath.GetUnicode()); BINDER_LOG_ENTER(W("BinderAcquirePEImage")); - hr = BinderAcquirePEImage(szAssemblyPath, &pPEImage, &pNativePEImage, fExplicitBindToNativeImage); + hr = BinderAcquirePEImage(szAssemblyPath, &pPEImage, &pNativePEImage, fExplicitBindToNativeImage, bundleFileLocation); BINDER_LOG_LEAVE_HR(W("BinderAcquirePEImage"), hr); IF_FAIL_GO(hr); @@ -1404,7 +1463,7 @@ namespace BINDER_SPACE BinderReleasePEImage(pNativePEImage); BINDER_LOG_ENTER(W("BinderAcquirePEImageIL")); - hr = BinderAcquirePEImage(szAssemblyPath, &pPEImage, &pNativePEImage, false); + hr = BinderAcquirePEImage(szAssemblyPath, &pPEImage, &pNativePEImage, false, bundleFileLocation); BINDER_LOG_LEAVE_HR(W("BinderAcquirePEImageIL"), hr); IF_FAIL_GO(hr); } @@ -1434,7 +1493,7 @@ namespace BINDER_SPACE else { BINDER_LOG_ENTER(W("BinderAcquirePEImage")); - hr = BinderAcquirePEImage(szMDAssemblyPath, &pPEImage, NULL, FALSE); + hr = BinderAcquirePEImage(szMDAssemblyPath, &pPEImage, NULL, FALSE, bundleFileLocation); BINDER_LOG_LEAVE_HR(W("BinderAcquirePEImage"), hr); IF_FAIL_GO(hr); diff --git a/src/binder/coreclrbindercommon.cpp b/src/binder/coreclrbindercommon.cpp index 76cdc1e70440..90f0069f5632 100644 --- a/src/binder/coreclrbindercommon.cpp +++ b/src/binder/coreclrbindercommon.cpp @@ -8,6 +8,7 @@ #include "coreclrbindercommon.h" #include "clrprivbindercoreclr.h" #include "clrprivbinderutil.h" +#include "bundle.h" using namespace BINDER_SPACE; @@ -177,5 +178,7 @@ HRESULT CCoreCLRBinderHelper::GetAssembly(/* in */ SString &assemblyPath, return AssemblyBinder::GetAssembly(assemblyPath, fIsInGAC, fExplicitBindToNativeImage, - ppAssembly); + ppAssembly, + NULL /* szMDAssemblyPath */, + Bundle::ProbeAppBundle(assemblyPath)); } diff --git a/src/binder/inc/assembly.hpp b/src/binder/inc/assembly.hpp index 8846913059ea..0b4283dd22e5 100644 --- a/src/binder/inc/assembly.hpp +++ b/src/binder/inc/assembly.hpp @@ -26,16 +26,18 @@ #if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) #include "clrprivbinderassemblyloadcontext.h" #endif // !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) - -STDAPI BinderAcquirePEImage(LPCTSTR szAssemblyPath, - PEImage **ppPEImage, - PEImage **ppNativeImage, - BOOL fExplicitBindToNativeImage); - -STDAPI BinderAcquireImport(PEImage *pPEImage, - IMDInternalImport **pIMetaDataAssemblyImport, - DWORD *pdwPAFlags, - BOOL bNativeImage); +#include "bundle.h" + +STDAPI BinderAcquirePEImage(LPCTSTR szAssemblyPath, + PEImage **ppPEImage, + PEImage **ppNativeImage, + BOOL fExplicitBindToNativeImage, + BundleFileLocation bundleFileLocation); + +STDAPI BinderAcquireImport(PEImage *pPEImage, + IMDInternalImport **pIMetaDataAssemblyImport, + DWORD *pdwPAFlags, + BOOL bNativeImage); STDAPI BinderHasNativeHeader(PEImage *pPEImage, BOOL *result); diff --git a/src/binder/inc/assemblybinder.hpp b/src/binder/inc/assemblybinder.hpp index 957ab4dbeb09..6ec72e6a9f43 100644 --- a/src/binder/inc/assemblybinder.hpp +++ b/src/binder/inc/assemblybinder.hpp @@ -18,6 +18,7 @@ #include "bindertypes.hpp" #include "bindresult.hpp" #include "coreclrbindercommon.h" +#include "bundle.h" class CLRPrivBinderAssemblyLoadContext; class CLRPrivBinderCoreCLR; @@ -62,11 +63,12 @@ namespace BINDER_SPACE /* in */ PEImage *pNativePEImage, /* out */ Assembly **ppAssembly); - static HRESULT GetAssembly(/* in */ SString &assemblyPath, - /* in */ BOOL fIsInGAC, - /* in */ BOOL fExplicitBindToNativeImage, - /* out */ Assembly **ppAssembly, - /* in */ LPCTSTR szMDAssemblyPath = NULL); + static HRESULT GetAssembly(/* in */ SString &assemblyPath, + /* in */ BOOL fIsInGAC, + /* in */ BOOL fExplicitBindToNativeImage, + /* out */ Assembly **ppAssembly, + /* in */ LPCTSTR szMDAssemblyPath = NULL, + /* in */ BundleFileLocation bundleFileLocation = BundleFileLocation::Invalid()); #if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) static HRESULT BindUsingHostAssemblyResolver (/* in */ INT_PTR pManagedAssemblyLoadContextToBindWithin, diff --git a/src/coreclr/hosts/osxbundlerun/osxbundlerun.cpp b/src/coreclr/hosts/osxbundlerun/osxbundlerun.cpp index d4de5c764eb9..05a8917a4126 100644 --- a/src/coreclr/hosts/osxbundlerun/osxbundlerun.cpp +++ b/src/coreclr/hosts/osxbundlerun/osxbundlerun.cpp @@ -78,6 +78,7 @@ int corerun(const int argc, const char* argv[]) argv0AbsolutePath.c_str(), clrFilesAbsolutePath.c_str(), managedAssemblyAbsolutePath.c_str(), + nullptr, managedAssemblyArgc, managedAssemblyArgv); diff --git a/src/coreclr/hosts/unixcorebundle/CMakeLists.txt b/src/coreclr/hosts/unixcorebundle/CMakeLists.txt index 8fa9e0cb02cf..a29f201988ca 100644 --- a/src/coreclr/hosts/unixcorebundle/CMakeLists.txt +++ b/src/coreclr/hosts/unixcorebundle/CMakeLists.txt @@ -13,8 +13,6 @@ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") set(COREBUNDLE_SOURCES corebundle.cpp - ./bundle/dir_utils.cpp - ./bundle/extractor.cpp ./bundle/file_entry.cpp ./bundle/header.cpp ./bundle/manifest.cpp @@ -44,6 +42,20 @@ set(HEADERS ../unixcoreruncommon/coreruncommon.h ) +set(HEADERS + ./bundle/error_codes.h + ./bundle/file_entry.h + ./bundle/file_type.h + ./bundle/header.h + ./bundle/manifest.h + ./bundle/marker.h + ./bundle/pal.h + ./bundle/reader.h + ./bundle/runner.h + ./bundle/trace.h + ./bundle/utils.h +) + _add_executable(corebundle ${COREBUNDLE_SOURCES} ) diff --git a/src/coreclr/hosts/unixcorebundle/bundle/dir_utils.cpp b/src/coreclr/hosts/unixcorebundle/bundle/dir_utils.cpp deleted file mode 100644 index 47d0d6ce670a..000000000000 --- a/src/coreclr/hosts/unixcorebundle/bundle/dir_utils.cpp +++ /dev/null @@ -1,94 +0,0 @@ -// 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. - -#include "dir_utils.h" -#include "error_codes.h" -#include "utils.h" -#include "trace.h" - -using namespace bundle; - -bool dir_utils_t::has_dirs_in_path(const pal::string_t& path) -{ - return path.find_last_of(DIR_SEPARATOR) != pal::string_t::npos; -} - -void dir_utils_t::create_directory_tree(const pal::string_t &path) -{ - if (path.empty()) - { - return; - } - - if (pal::directory_exists(path)) - { - return; - } - - if (has_dirs_in_path(path)) - { - create_directory_tree(get_directory(path)); - } - - if (!pal::mkdir(path.c_str(), 0700)) // Owner - rwx - { - if (pal::directory_exists(path)) - { - // The directory was created since we last checked. - return; - } - - trace::error(_X("Failure processing application bundle.")); - trace::error(_X("Failed to create directory [%s] for extracting bundled files."), path.c_str()); - throw StatusCode::BundleExtractionIOError; - } -} - -void dir_utils_t::remove_directory_tree(const pal::string_t& path) -{ - if (path.empty()) - { - return; - } - - std::vector dirs; - pal::readdir_onlydirectories(path, &dirs); - - for (const pal::string_t &dir : dirs) - { - remove_directory_tree(dir); - } - - std::vector files; - pal::readdir(path, &files); - - for (const pal::string_t &file : files) - { - if (!pal::remove(file.c_str())) - { - trace::warning(_X("Failed to remove temporary file [%s]."), file.c_str()); - } - } - - if (!pal::rmdir(path.c_str())) - { - trace::warning(_X("Failed to remove temporary directory [%s]."), path.c_str()); - } -} - -// Fixup a path to have current platform's directory separator. -void dir_utils_t::fixup_path_separator(pal::string_t& path) -{ - const pal::char_t bundle_dir_separator = '/'; - - if (bundle_dir_separator != DIR_SEPARATOR) - { - for (size_t pos = path.find(bundle_dir_separator); - pos != pal::string_t::npos; - pos = path.find(bundle_dir_separator, pos)) - { - path[pos] = DIR_SEPARATOR; - } - } -} diff --git a/src/coreclr/hosts/unixcorebundle/bundle/dir_utils.h b/src/coreclr/hosts/unixcorebundle/bundle/dir_utils.h deleted file mode 100644 index 882415316dac..000000000000 --- a/src/coreclr/hosts/unixcorebundle/bundle/dir_utils.h +++ /dev/null @@ -1,23 +0,0 @@ -// 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. - -#ifndef __DIR_UTIL_H__ -#define __DIR_UTIL_H__ - -#include -#include "pal.h" - -namespace bundle -{ - class dir_utils_t - { - public: - static bool has_dirs_in_path(const pal::string_t &path); - static void remove_directory_tree(const pal::string_t &path); - static void create_directory_tree(const pal::string_t &path); - static void fixup_path_separator(pal::string_t& path); - }; -} - -#endif // __DIR_UTIL_H__ diff --git a/src/coreclr/hosts/unixcorebundle/bundle/extractor.cpp b/src/coreclr/hosts/unixcorebundle/bundle/extractor.cpp deleted file mode 100644 index 75cf6326114b..000000000000 --- a/src/coreclr/hosts/unixcorebundle/bundle/extractor.cpp +++ /dev/null @@ -1,185 +0,0 @@ -// 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. - -#include "extractor.h" -#include "error_codes.h" -#include "dir_utils.h" -#include "pal.h" -#include "utils.h" -#include "trace.h" - -using namespace bundle; - -// Compute the final extraction location as: -// m_extraction_dir = $DOTNET_BUNDLE_EXTRACT_BASE_DIR///... -// -// If DOTNET_BUNDLE_EXTRACT_BASE_DIR is not set in the environment, the -// base directory defaults to $TMPDIR/.net -void extractor_t::determine_extraction_dir() -{ - if (!pal::getenv(_X("DOTNET_BUNDLE_EXTRACT_BASE_DIR"), &m_extraction_dir)) - { - if (!pal::get_temp_directory(m_extraction_dir)) - { - trace::error(_X("Failure processing application bundle.")); - trace::error(_X("Failed to determine location for extracting embedded files.")); - throw StatusCode::BundleExtractionFailure; - } - - append_path(&m_extraction_dir, _X(".net")); - } - - pal::string_t host_name = strip_executable_ext(get_filename(m_bundle_path)); - append_path(&m_extraction_dir, host_name.c_str()); - append_path(&m_extraction_dir, m_bundle_id.c_str()); - - trace::info(_X("Files embedded within the bundled will be extracted to [%s] directory."), m_extraction_dir.c_str()); -} - -// Compute the working extraction location for this process, before the -// extracted files are committed to the final location -// m_working_extraction_dir = $DOTNET_BUNDLE_EXTRACT_BASE_DIR// -void extractor_t::determine_working_extraction_dir() -{ - m_working_extraction_dir = get_directory(extraction_dir()); - pal::char_t pid[32]; - pal::snwprintf(pid, 32, _X("%x"), pal::get_pid()); - append_path(&m_working_extraction_dir, pid); - - dir_utils_t::create_directory_tree(m_working_extraction_dir); - - trace::info(_X("Temporary directory used to extract bundled files is [%s]."), m_working_extraction_dir.c_str()); -} - -// Create a file to be extracted out on disk, including any intermediate sub-directories. -FILE* extractor_t::create_extraction_file(const pal::string_t& relative_path) -{ - pal::string_t file_path = m_working_extraction_dir; - append_path(&file_path, relative_path.c_str()); - - // m_working_extraction_dir is assumed to exist, - // so we only create sub-directories if relative_path contains directories - if (dir_utils_t::has_dirs_in_path(relative_path)) - { - dir_utils_t::create_directory_tree(get_directory(file_path)); - } - - FILE* file = pal::file_open(file_path.c_str(), _X("wb")); - - if (file == nullptr) - { - trace::error(_X("Failure processing application bundle.")); - trace::error(_X("Failed to open file [%s] for writing."), file_path.c_str()); - throw StatusCode::BundleExtractionIOError; - } - - return file; -} - -// Extract one file from the bundle to disk. -void extractor_t::extract(const file_entry_t &entry, reader_t &reader) -{ - FILE* file = create_extraction_file(entry.relative_path()); - reader.set_offset(entry.offset()); - size_t size = entry.size(); - - if (fwrite(reader, 1, size, file) != size) - { - trace::error(_X("Failure extracting contents of the application bundle.")); - trace::error(_X("I/O failure when writing extracted files.")); - throw StatusCode::BundleExtractionIOError; - } - - fclose(file); -} - -pal::string_t& extractor_t::extraction_dir() -{ - if (m_extraction_dir.empty()) - { - determine_extraction_dir(); - } - - return m_extraction_dir; -} - -bool extractor_t::can_reuse_extraction() -{ - // In this version, the extracted files are assumed to be - // correct by construction. - // - // Files embedded in the bundle are first extracted to m_working_extraction_dir - // Once all files are successfully extracted, the extraction location is - // committed (renamed) to m_extraction_dir. Therefore, the presence of - // m_extraction_dir means that the files are pre-extracted. - - return pal::directory_exists(extraction_dir()); -} - -void extractor_t::begin() -{ - // Files are extracted to a specific deterministic location on disk - // on first run, and are available for reuse by subsequent similar runs. - // - // The extraction should be fault tolerant with respect to: - // * Failures/crashes during extraction which result in partial-extraction - // * Race between two or more processes concurrently attempting extraction - // - // In order to solve these issues, we implement a extraction as a two-phase approach: - // 1) Files embedded in a bundle are extracted to a process-specific temporary - // extraction location (m_working_extraction_dir) - // 2) Upon successful extraction, m_working_extraction_dir is renamed to the actual - // extraction location (m_extraction_dir) - // - // This effectively creates a file-lock to protect against races and failed extractions. - - determine_working_extraction_dir(); -} - -void extractor_t::commit() -{ - // Commit files to the final extraction directory - // Retry the move operation with some wait in between the attempts. This is to workaround for possible file locking - // caused by AV software. Basically the extraction process above writes a bunch of executable files to disk - // and some AV software may decide to scan them on write. If this happens the files will be locked which blocks - // our ablity to move them. - int retry_count = 500; - while (true) - { - if (pal::rename(m_working_extraction_dir.c_str(), m_extraction_dir.c_str()) == 0) - break; - - bool should_retry = errno == EACCES; - if (can_reuse_extraction()) - { - // Another process successfully extracted the dependencies - trace::info(_X("Extraction completed by another process, aborting current extraction.")); - - dir_utils_t::remove_directory_tree(m_working_extraction_dir); - break; - } - - if (should_retry && (retry_count--) > 0) - { - trace::info(_X("Retrying extraction due to EACCES trying to rename the extraction folder to [%s]."), m_extraction_dir.c_str()); - pal::sleep(100); - continue; - } - else - { - trace::error(_X("Failure processing application bundle.")); - trace::error(_X("Failed to commit extracted files to directory [%s]."), m_extraction_dir.c_str()); - throw StatusCode::BundleExtractionFailure; - } - } -} - -void extractor_t::extract(const manifest_t& manifest, reader_t& reader) -{ - begin(); - for (const file_entry_t& entry : manifest.files) { - extract(entry, reader); - } - commit(); -} diff --git a/src/coreclr/hosts/unixcorebundle/bundle/extractor.h b/src/coreclr/hosts/unixcorebundle/bundle/extractor.h deleted file mode 100644 index eb45e2c6bfc9..000000000000 --- a/src/coreclr/hosts/unixcorebundle/bundle/extractor.h +++ /dev/null @@ -1,45 +0,0 @@ -// 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. - -#ifndef __EXTRACTOR_H__ -#define __EXTRACTOR_H__ - -#include "reader.h" -#include "manifest.h" - -namespace bundle -{ - class extractor_t - { - public: - extractor_t(const pal::string_t &bundle_id, const pal::string_t& bundle_path) - :m_extraction_dir(), m_working_extraction_dir() - { - m_bundle_id = bundle_id; - m_bundle_path = bundle_path; - } - - pal::string_t& extraction_dir(); - bool can_reuse_extraction(); - - void extract(const manifest_t &manifest, reader_t& reader); - - private: - void determine_extraction_dir(); - void determine_working_extraction_dir(); - - FILE* create_extraction_file(const pal::string_t& relative_path); - - void begin(); - void extract(const file_entry_t& entry, reader_t& reader); - void commit(); - - pal::string_t m_bundle_id; - pal::string_t m_bundle_path; - pal::string_t m_extraction_dir; - pal::string_t m_working_extraction_dir; - }; -} - -#endif // __EXTRACTOR_H__ diff --git a/src/coreclr/hosts/unixcorebundle/bundle/file_entry.cpp b/src/coreclr/hosts/unixcorebundle/bundle/file_entry.cpp index dec8fec6e3e7..7e0d4ee3f37a 100644 --- a/src/coreclr/hosts/unixcorebundle/bundle/file_entry.cpp +++ b/src/coreclr/hosts/unixcorebundle/bundle/file_entry.cpp @@ -4,7 +4,6 @@ #include "file_entry.h" #include "trace.h" -#include "dir_utils.h" #include "error_codes.h" using namespace bundle; @@ -15,6 +14,22 @@ bool file_entry_t::is_valid() const static_cast(m_type) < file_type_t::__last; } +// Fixup a path to have current platform's directory separator. +void fixup_path_separator(pal::string_t& path) +{ + const pal::char_t bundle_dir_separator = '/'; + + if (bundle_dir_separator != DIR_SEPARATOR) + { + for (size_t pos = path.find(bundle_dir_separator); + pos != pal::string_t::npos; + pos = path.find(bundle_dir_separator, pos)) + { + path[pos] = DIR_SEPARATOR; + } + } +} + file_entry_t file_entry_t::read(reader_t &reader) { // First read the fixed-sized portion of file-entry @@ -29,7 +44,7 @@ file_entry_t file_entry_t::read(reader_t &reader) } reader.read_path_string(entry.m_relative_path); - dir_utils_t::fixup_path_separator(entry.m_relative_path); + fixup_path_separator(entry.m_relative_path); return entry; } diff --git a/src/coreclr/hosts/unixcorebundle/bundle/pal.cpp b/src/coreclr/hosts/unixcorebundle/bundle/pal.cpp index 27bb3962dd1d..d94690410df3 100644 --- a/src/coreclr/hosts/unixcorebundle/bundle/pal.cpp +++ b/src/coreclr/hosts/unixcorebundle/bundle/pal.cpp @@ -5,7 +5,6 @@ #include "pal.h" #include "trace.h" -#include #include #include #include diff --git a/src/coreclr/hosts/unixcorebundle/bundle/runner.cpp b/src/coreclr/hosts/unixcorebundle/bundle/runner.cpp index ccae5ed29499..8467f6692c91 100644 --- a/src/coreclr/hosts/unixcorebundle/bundle/runner.cpp +++ b/src/coreclr/hosts/unixcorebundle/bundle/runner.cpp @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. #include -#include "extractor.h" #include "runner.h" #include "trace.h" #include "header.h" @@ -32,35 +31,33 @@ void runner_t::unmap_host() } } -// Current support for executing single-file bundles involves -// extraction of embedded files to actual files on disk. -// This method implements the file extraction functionality at startup. -StatusCode runner_t::extract() +bool runner_t::probe(const char *relative_path, int64_t *size, int64_t *offset) +{ + for (file_entry_t& entry : m_manifest.files) + { + if (strcmp(entry.relative_path().c_str(), relative_path) == 0) + { + *size = entry.size(); + *offset = entry.offset(); + return true; + } + } + return false; +} + +StatusCode runner_t::process() { try { map_host(); reader_t reader(m_bundle_map, m_bundle_length); - // Read the bundle header reader.set_offset(marker_t::header_offset()); header_t header = header_t::read(reader); - extractor_t extractor(header.bundle_id(), m_bundle_path); - m_extraction_dir = extractor.extraction_dir(); - - // Determine if embedded files are already extracted, and available for reuse - if (extractor.can_reuse_extraction()) - { - return StatusCode::Success; - } - - manifest_t manifest = manifest_t::read(reader, header.num_embedded_files()); - - extractor.extract(manifest, reader); + m_manifest = manifest_t::read(reader, header.num_embedded_files()); unmap_host(); - return StatusCode::Success; } catch (StatusCode e) diff --git a/src/coreclr/hosts/unixcorebundle/bundle/runner.h b/src/coreclr/hosts/unixcorebundle/bundle/runner.h index 633e4927364f..c90b745568cb 100644 --- a/src/coreclr/hosts/unixcorebundle/bundle/runner.h +++ b/src/coreclr/hosts/unixcorebundle/bundle/runner.h @@ -7,32 +7,32 @@ #include "error_codes.h" #include "pal.h" +#include "manifest.h" namespace bundle { class runner_t { public: - runner_t(const pal::string_t& bundle_path) + runner_t(const pal::string_t& bundle_path="") : m_bundle_path(bundle_path) , m_bundle_map(nullptr) , m_bundle_length(0) { } - StatusCode extract(); + StatusCode process(); - pal::string_t extraction_dir() - { - return m_extraction_dir; - } + bool probe (const char *relative_path, int64_t *size, int64_t *offset); + + const pal::char_t* get_bundle_path() { return m_bundle_path.c_str(); } private: void map_host(); void unmap_host(); + manifest_t m_manifest; pal::string_t m_bundle_path; - pal::string_t m_extraction_dir; int8_t* m_bundle_map; size_t m_bundle_length; }; diff --git a/src/coreclr/hosts/unixcorebundle/corebundle.cpp b/src/coreclr/hosts/unixcorebundle/corebundle.cpp index 32c26e7e5c65..bc3c73f2a230 100644 --- a/src/coreclr/hosts/unixcorebundle/corebundle.cpp +++ b/src/coreclr/hosts/unixcorebundle/corebundle.cpp @@ -15,6 +15,13 @@ #include #include +static bundle::runner_t bundle_runner; + +bool probe_bundle(const pal::char_t* path, int64_t *size, int64_t *offset) +{ + return bundle_runner.probe(path, size, offset); +} + int main(const int argc, const char* argv[]) { // Make sure we have a full path for argv[0]. @@ -31,10 +38,8 @@ int main(const int argc, const char* argv[]) return -1; } - fprintf(stdout, "Running bundle: %s\n", exe_path.c_str()); - - bundle::runner_t bundle_runner(exe_path); - StatusCode bundle_status = bundle_runner.extract(); + bundle_runner = bundle::runner_t(exe_path); + StatusCode bundle_status = bundle_runner.process(); if (bundle_status != StatusCode::Success) { @@ -42,10 +47,8 @@ int main(const int argc, const char* argv[]) return bundle_status; } - std::string root_dir = bundle_runner.extraction_dir(); + std::string root_dir = get_directory(exe_path); std::string app_path(root_dir); - app_path.push_back(DIR_SEPARATOR); - app_path.append(get_filename(exe_path.c_str())); app_path.append(".dll"); @@ -56,11 +59,11 @@ int main(const int argc, const char* argv[]) app_argv = &argv[1]; } - std::fflush(stdout); int exitCode = ExecuteManagedAssembly( exe_path.c_str(), root_dir.c_str(), app_path.c_str(), + probe_bundle, app_argc, app_argv); diff --git a/src/coreclr/hosts/unixcoreconsole/coreconsole.cpp b/src/coreclr/hosts/unixcoreconsole/coreconsole.cpp index d46f6ef5bbb6..6ec630e93360 100644 --- a/src/coreclr/hosts/unixcoreconsole/coreconsole.cpp +++ b/src/coreclr/hosts/unixcoreconsole/coreconsole.cpp @@ -148,6 +148,7 @@ int main(const int argc, const char* argv[]) entryPointExecutablePath.c_str(), clrFilesAbsolutePath.c_str(), managedAssemblyAbsolutePath, + nullptr, managedAssemblyArgc, managedAssemblyArgv); diff --git a/src/coreclr/hosts/unixcorerun/corerun.cpp b/src/coreclr/hosts/unixcorerun/corerun.cpp index 4e5c9d9839e6..288b4a802f31 100644 --- a/src/coreclr/hosts/unixcorerun/corerun.cpp +++ b/src/coreclr/hosts/unixcorerun/corerun.cpp @@ -150,6 +150,7 @@ int corerun(const int argc, const char* argv[]) argv0AbsolutePath.c_str(), clrFilesAbsolutePath.c_str(), managedAssemblyAbsolutePath.c_str(), + nullptr, managedAssemblyArgc, managedAssemblyArgv); diff --git a/src/coreclr/hosts/unixcoreruncommon/coreruncommon.cpp b/src/coreclr/hosts/unixcoreruncommon/coreruncommon.cpp index 0e8ccd583d69..df4a3292cda4 100644 --- a/src/coreclr/hosts/unixcoreruncommon/coreruncommon.cpp +++ b/src/coreclr/hosts/unixcoreruncommon/coreruncommon.cpp @@ -102,8 +102,7 @@ bool GetEntrypointExecutableAbsolutePath(std::string& entrypointExecutable) len = sizeof(path); if (sysctl(name, __arraycount(name), path, &len, NULL, 0) != -1) { - entrypointExecutable.assign(path); - result = true; + result = GetAbsolutePath(execfn, entrypointExecutable); } else { @@ -116,8 +115,7 @@ bool GetEntrypointExecutableAbsolutePath(std::string& entrypointExecutable) if (execfn) { - entrypointExecutable.assign(execfn); - result = true; + result = GetAbsolutePath(execfn, entrypointExecutable); } else #endif @@ -351,6 +349,7 @@ int ExecuteManagedAssembly( const char* currentExeAbsolutePath, const char* clrFilesAbsolutePath, const char* managedAssemblyAbsolutePath, + bool bundleProbe(const char*, int64_t*, int64_t*), int managedAssemblyArgc, const char** managedAssemblyArgv) { @@ -449,7 +448,12 @@ int ExecuteManagedAssembly( // NATIVE_DLL_SEARCH_DIRECTORIES // - The list of paths that will be probed for native DLLs called by PInvoke // + // BUNDLE_PROBE + // - If this application is a single-file bundle, the bundle-probe callback + // is passed in as the value of "BUNDLE_PROBE" property (masquarading as char *). + const char *propertyKeys[] = { + "BUNDLE_PROBE", "TRUSTED_PLATFORM_ASSEMBLIES", "APP_PATHS", "APP_NI_PATHS", @@ -461,6 +465,8 @@ int ExecuteManagedAssembly( #endif }; const char *propertyValues[] = { + // BUNDLE_PROBE + (const char*)bundleProbe, // TRUSTED_PLATFORM_ASSEMBLIES tpaList.c_str(), // APP_PATHS diff --git a/src/coreclr/hosts/unixcoreruncommon/coreruncommon.h b/src/coreclr/hosts/unixcoreruncommon/coreruncommon.h index fb7f6730b9d3..30bf12956a16 100644 --- a/src/coreclr/hosts/unixcoreruncommon/coreruncommon.h +++ b/src/coreclr/hosts/unixcoreruncommon/coreruncommon.h @@ -43,6 +43,7 @@ int ExecuteManagedAssembly( const char* currentExeAbsolutePath, const char* clrFilesAbsolutePath, const char* managedAssemblyAbsolutePath, + bool bundleProbe(const char*, int64_t*, int64_t*), int managedAssemblyArgc, const char** managedAssemblyArgv); diff --git a/src/dlls/mscoree/unixinterface.cpp b/src/dlls/mscoree/unixinterface.cpp index 67bd444ba886..c15fb5e316fb 100644 --- a/src/dlls/mscoree/unixinterface.cpp +++ b/src/dlls/mscoree/unixinterface.cpp @@ -15,6 +15,8 @@ #include #include #include +#include "bundle.h" + #ifdef FEATURE_GDBJIT #include "../../vm/gdbjithelpers.h" #endif // FEATURE_GDBJIT @@ -117,7 +119,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); @@ -127,6 +130,18 @@ static void ConvertConfigPropertiesToUnicode( for (int propertyIndex = 0; propertyIndex < propertyCount; ++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 (masquarading as char *). + // Therefore obtain the value; don't convert it to Unicode. + + *bundleProbe = (BundleProbe *)propertyValues[propertyIndex]; + propertyKeysW[propertyIndex] = W("BUNDLE_PROBE"); + propertyValuesW[propertyIndex] = W(""); + continue; + } + propertyKeysW[propertyIndex] = StringToUnicode(propertyKeys[propertyIndex]); propertyValuesW[propertyIndex] = StringToUnicode(propertyValues[propertyIndex]); } @@ -160,6 +175,7 @@ extern "C" int coreclr_create_delegate(void*, unsigned int, const char*, const c // Returns: // HRESULT indicating status of the operation. S_OK if the assembly was successfully executed // + extern "C" DLLEXPORT int coreclr_initialize( @@ -193,12 +209,20 @@ 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/inc/bundle.h b/src/inc/bundle.h new file mode 100644 index 000000000000..ec34fead90f4 --- /dev/null +++ b/src/inc/bundle.h @@ -0,0 +1,62 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/***************************************************************************** + ** ** + ** bundle.h - Information about applications bundled as a single-file ** + ** ** + *****************************************************************************/ + +#ifndef _BUNDLE_H_ +#define _BUNDLE_H_ + +#include + +class Bundle; + +struct BundleFileLocation +{ + INT64 Size; + INT64 Offset; + + BundleFileLocation() + { + LIMITED_METHOD_CONTRACT; + + Size = 0; + Offset = 0; + } + + static BundleFileLocation Invalid() { LIMITED_METHOD_CONTRACT; return BundleFileLocation(); } + + const SString &Path() const; + + bool IsValid() const { LIMITED_METHOD_CONTRACT; return Offset != 0; } +}; + +typedef bool(__stdcall BundleProbe)(LPCSTR, INT64*, INT64*); + +class Bundle +{ +public: + Bundle(LPCWSTR bundlePath, BundleProbe *probe); + BundleFileLocation Probe(LPCWSTR path, bool pathIsBundleRelative = false) const; + + const SString &Path() const { LIMITED_METHOD_CONTRACT; return m_path; } + const SString &BasePath() const { LIMITED_METHOD_CONTRACT; return m_basePath; } + + static Bundle* AppBundle; // The BundleInfo for the current app, initialized by coreclr_initialize. + static bool AppIsBundle() { LIMITED_METHOD_CONTRACT; return AppBundle != nullptr; } + static BundleFileLocation ProbeAppBundle(LPCWSTR path, bool pathIsBundleRelative = false); + +private: + + SString m_path; // The path to single-file executable + BundleProbe *m_probe; + + SString m_basePath; // The prefix to denote a path within the bundle +}; + +#endif // _BUNDLE_H_ +// EOF ======================================================================= diff --git a/src/inc/cor.h b/src/inc/cor.h index 034801b05f5b..f6970f374f15 100644 --- a/src/inc/cor.h +++ b/src/inc/cor.h @@ -125,10 +125,8 @@ interface IMetaDataAssemblyImport; interface IMetaDataEmit; interface ICeeGen; - typedef UNALIGNED void const *UVCP_CONSTANT; - // Constant for connection id and task id #define INVALID_CONNECTION_ID 0x0 #define INVALID_TASK_ID 0x0 diff --git a/src/inc/pedecoder.inl b/src/inc/pedecoder.inl index 07556b411f90..88ceadd7af65 100644 --- a/src/inc/pedecoder.inl +++ b/src/inc/pedecoder.inl @@ -166,8 +166,6 @@ inline void PEDecoder::Init(void *flatBase, COUNT_T size) m_flags = FLAG_CONTENTS; } - - inline HRESULT PEDecoder::Init(void *mappedBase, bool fixedUp /*= FALSE*/) { CONTRACTL diff --git a/src/pal/inc/pal.h b/src/pal/inc/pal.h index 6cd1012ad1a0..b7638f1168f4 100644 --- a/src/pal/inc/pal.h +++ b/src/pal/inc/pal.h @@ -46,6 +46,9 @@ Module Name: #include #include #include +#include +#include +#include #endif #ifdef __cplusplus @@ -2663,15 +2666,17 @@ 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 --*/ + PALIMPORT PVOID PALAPI -PAL_LOADLoadPEFile(HANDLE hFile); +PAL_LOADLoadPEFile(HANDLE hFile, size_t offset); /*++ PAL_LOADUnloadPEFile diff --git a/src/pal/prebuilt/inc/mscoree.h b/src/pal/prebuilt/inc/mscoree.h index ab7bbb0d0c71..270cc1f82bfb 100644 --- a/src/pal/prebuilt/inc/mscoree.h +++ b/src/pal/prebuilt/inc/mscoree.h @@ -252,7 +252,7 @@ EXTERN_C const IID IID_ICLRRuntimeHost; ICLRRuntimeHost : public IUnknown { public: - virtual HRESULT STDMETHODCALLTYPE Start( void) = 0; + virtual HRESULT STDMETHODCALLTYPE Start() = 0; virtual HRESULT STDMETHODCALLTYPE Stop( void) = 0; diff --git a/src/pal/src/include/pal/map.hpp b/src/pal/src/include/pal/map.hpp index 0709268d6daf..d76b58f22496 100644 --- a/src/pal/src/include/pal/map.hpp +++ b/src/pal/src/include/pal/map.hpp @@ -79,13 +79,14 @@ extern "C" Parameters: IN hFile - file to map + IN offset - offset within hFile where the PE "file" is located Return value: non-NULL - the base address of the mapped image NULL - error, with last error set. --*/ - void * MAPMapPEFile(HANDLE hFile); + void * MAPMapPEFile(HANDLE hFile, off_t offset); /*++ Function : diff --git a/src/pal/src/include/pal/module.h b/src/pal/src/include/pal/module.h index 66ac23834ac2..d5ff40828c8b 100644 --- a/src/pal/src/include/pal/module.h +++ b/src/pal/src/include/pal/module.h @@ -145,12 +145,13 @@ Abstract Parameters: IN hFile - The file to load + IN offset - offset within hFile where the PE "file" is located Return value: A valid base address if successful. 0 if failure --*/ -void * PAL_LOADLoadPEFile(HANDLE hFile); +void * PAL_LOADLoadPEFile(HANDLE hFile, size_t offset); /*++ PAL_LOADUnloadPEFile diff --git a/src/pal/src/loader/module.cpp b/src/pal/src/loader/module.cpp index 8760fa08207d..8e5020086728 100644 --- a/src/pal/src/loader/module.cpp +++ b/src/pal/src/loader/module.cpp @@ -757,6 +757,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 @@ -764,11 +765,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) @@ -780,7 +781,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); @@ -1601,7 +1602,8 @@ static MODSTRUCT *LOADAddModule(NATIVE_LIBRARY_HANDLE dl_handle, LPCSTR libraryN { /* found the handle. increment the refcount and return the existing module structure */ - TRACE("Found matching module %p for module name %s\n", module, libraryNameOrPath); + TRACE("Found matching module %p for module name %s\n", module, + (libraryNameOrPath != nullptr) ? libraryNameOrPath : "nullptr"); if (module->refcount != -1) { diff --git a/src/pal/src/map/map.cpp b/src/pal/src/map/map.cpp index 48ef5763c534..af3a78361460 100644 --- a/src/pal/src/map/map.cpp +++ b/src/pal/src/map/map.cpp @@ -1088,6 +1088,8 @@ CorUnix::InternalMapViewOfFile( CFileMappingImmutableData *pImmutableData = NULL; CFileMappingProcessLocalData *pProcessLocalData = NULL; IDataLock *pProcessLocalDataLock = NULL; + INT64 offset = ((INT64)dwFileOffsetHigh << 32) | (INT64)dwFileOffsetLow; + #if ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS PMAPPED_VIEW_LIST pReusedMapping = NULL; #endif @@ -1102,9 +1104,9 @@ CorUnix::InternalMapViewOfFile( goto InternalMapViewOfFileExit; } - if ( 0 != dwFileOffsetHigh || 0 != dwFileOffsetLow ) + if ( offset < 0 ) { - ASSERT( "dwFileOffsetHigh and dwFileOffsetLow are always 0.\n" ); + ASSERT( "dwFileOffsetHigh | dwFileOffsetLow should be non-negative.\n" ); palError = ERROR_INVALID_PARAMETER; goto InternalMapViewOfFileExit; } @@ -1182,7 +1184,7 @@ CorUnix::InternalMapViewOfFile( PROT_READ|PROT_WRITE, flags, pProcessLocalData->UnixFd, - 0 + offset ); } else @@ -1205,7 +1207,7 @@ CorUnix::InternalMapViewOfFile( prot, flags, pProcessLocalData->UnixFd, - 0 + offset ); #if ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS @@ -2210,13 +2212,14 @@ MAPmmapAndRecord( Parameters: IN hFile - file to map + IN offset - offset within hFile where the PE "file" is located Return value: non-NULL - the base address of the mapped image NULL - error, with last error set. --*/ -void * MAPMapPEFile(HANDLE hFile) +void * MAPMapPEFile(HANDLE hFile, off_t offset) { PAL_ERROR palError = 0; IPalObject *pFileObject = NULL; @@ -2231,7 +2234,7 @@ void * MAPMapPEFile(HANDLE hFile) char* envVar; #endif - ENTRY("MAPMapPEFile (hFile=%p)\n", hFile); + ENTRY("MAPMapPEFile (hFile=%p offset=%zx)\n", hFile, offset); //Step 0: Verify values, find internal pal data structures. if (INVALID_HANDLE_VALUE == hFile) @@ -2270,13 +2273,13 @@ void * MAPMapPEFile(HANDLE hFile) //Step 1: Read the PE headers and reserve enough space for the whole image somewhere. IMAGE_DOS_HEADER dosHeader; IMAGE_NT_HEADERS ntHeader; - if (sizeof(dosHeader) != pread(fd, &dosHeader, sizeof(dosHeader), 0)) + if (sizeof(dosHeader) != pread(fd, &dosHeader, sizeof(dosHeader), offset)) { palError = FILEGetLastErrorFromErrno(); ERROR_(LOADER)( "reading dos header failed\n" ); goto done; } - if (sizeof(ntHeader) != pread(fd, &ntHeader, sizeof(ntHeader), dosHeader.e_lfanew)) + if (sizeof(ntHeader) != pread(fd, &ntHeader, sizeof(ntHeader), offset + dosHeader.e_lfanew)) { palError = FILEGetLastErrorFromErrno(); goto done; @@ -2418,7 +2421,7 @@ void * MAPMapPEFile(HANDLE hFile) //first, map the PE header to the first page in the image. Get pointers to the section headers palError = MAPmmapAndRecord(pFileObject, loadedBase, - loadedBase, headerSize, PROT_READ, MAP_FILE|MAP_PRIVATE|MAP_FIXED, fd, 0, + loadedBase, headerSize, PROT_READ, MAP_FILE|MAP_PRIVATE|MAP_FIXED, fd, offset, (void**)&loadedHeader); if (NO_ERROR != palError) { @@ -2511,7 +2514,7 @@ void * MAPMapPEFile(HANDLE hFile) prot, MAP_FILE|MAP_PRIVATE|MAP_FIXED, fd, - currentHeader.PointerToRawData, + offset + currentHeader.PointerToRawData, §ionData); if (NO_ERROR != palError) { @@ -2541,7 +2544,7 @@ void * MAPMapPEFile(HANDLE hFile) palError = MAPRecordMapping(pFileObject, loadedBase, prevSectionEnd, - (char*)imageEnd - (char*)prevSectionEnd, + offset + (char*)imageEnd - (char*)prevSectionEnd, PROT_NONE); if (NO_ERROR != palError) { diff --git a/src/vm/CMakeLists.txt b/src/vm/CMakeLists.txt index 5ee54cb6b3d9..ae5eb3797407 100644 --- a/src/vm/CMakeLists.txt +++ b/src/vm/CMakeLists.txt @@ -40,6 +40,7 @@ set(VM_SOURCES_DAC_AND_WKS_COMMON assembly.cpp baseassemblyspec.cpp binder.cpp + bundle.cpp callcounter.cpp ceeload.cpp class.cpp diff --git a/src/vm/appdomain.cpp b/src/vm/appdomain.cpp index bf09db505831..decdf8e901e5 100644 --- a/src/vm/appdomain.cpp +++ b/src/vm/appdomain.cpp @@ -32,7 +32,6 @@ #include "comdelegate.h" #include "siginfo.hpp" #include "typekey.h" - #include "caparser.h" #include "ecall.h" #include "finalizerthread.h" @@ -1836,27 +1835,37 @@ void SystemDomain::Init() m_pSystemAssembly = NULL; DWORD size = 0; + AppDomain* pAppDomain = ::GetAppDomain(); + if (Bundle::AppIsBundle()) + { + // If we are running from a self-contained single-file bundle, the + // runtime is contained within the bundle. + m_SystemDirectory.Set(Bundle::AppBundle->BasePath()); + m_SystemDirectory.Normalize(); + } + else + { + // Get the install directory so we can find mscorlib + hr = GetInternalSystemDirectory(NULL, &size); + if (hr != HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) + ThrowHR(hr); - // Get the install directory so we can find mscorlib - hr = GetInternalSystemDirectory(NULL, &size); - if (hr != HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) - ThrowHR(hr); - - // GetInternalSystemDirectory returns a size, including the null! - 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 + // GetInternalSystemDirectory returns a size, including the null! + 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(); @@ -1886,7 +1895,7 @@ void SystemDomain::Init() #ifdef _DEBUG BOOL fPause = EEConfig::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_PauseOnLoad, FALSE); - while(fPause) + while (fPause) { ClrSleepEx(20, TRUE); } diff --git a/src/vm/appdomain.hpp b/src/vm/appdomain.hpp index dd009ba253b3..e12de2418b89 100644 --- a/src/vm/appdomain.hpp +++ b/src/vm/appdomain.hpp @@ -29,6 +29,7 @@ #include "gchandleutilities.h" #include "../binder/inc/applicationcontext.hpp" #include "rejit.h" +#include "bundle.h" #ifdef FEATURE_MULTICOREJIT #include "multicorejit.h" diff --git a/src/vm/assemblynative.cpp b/src/vm/assemblynative.cpp index 9162bfd08692..8507f68b2cc1 100644 --- a/src/vm/assemblynative.cpp +++ b/src/vm/assemblynative.cpp @@ -246,7 +246,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()) @@ -264,7 +266,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/vm/bundle.cpp b/src/vm/bundle.cpp new file mode 100644 index 000000000000..1e551d657e45 --- /dev/null +++ b/src/vm/bundle.cpp @@ -0,0 +1,115 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +//***************************************************************************** +// Bundle.cpp +// +// Helpers to access meta-data stored in single-file bundles +// +//***************************************************************************** + +#include "common.h" +#include "bundle.h" +#include +#include + +Bundle *Bundle::AppBundle = nullptr; + +static LPCSTR UnicodeToUtf8(LPCWSTR str) +{ + STANDARD_VM_CONTRACT; + + int length = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, 0, 0); + _ASSERTE(length != 0); + + LPSTR result = new (nothrow) CHAR[length]; + _ASSERTE(result != NULL); + + length = WideCharToMultiByte(CP_UTF8, 0, str, -1, result, length, 0, 0); + _ASSERTE(length != 0); + + return result; +} + +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; + + // In this prototype, the bundle-base path is simply 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). + // + // 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") + // + // This strategy obviously hides any actual files in path/to/exe from being loaded. + // + // In the final implementation, we should set base_path to a known prefix, say "#\" + // and teach the host and other parts of the runtime to expect this kind of path. + // This is related to the question of what Assembly.Location is for bundled assemblies. + + 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. + // TODO: Normalize paths + // TODO: Normalize path casing on Windows. + + if (!pathIsBundleRelative) + { + size_t baseLen = m_basePath.GetCount(); + if (wcsncmp(m_basePath, path, baseLen) == 0) + { + path += baseLen; // m_basePath includes count for DIRECTORY_SEPARATOR_CHAR_W + } + else + { + // This is not a file within the bundle + return loc; + } + } + + m_probe(UnicodeToUtf8(path), &loc.Size, &loc.Offset); + + return loc; +} + +BundleFileLocation Bundle::ProbeAppBundle(LPCWSTR path, bool pathIsBundleRelative) +{ + STANDARD_VM_CONTRACT; + + return AppIsBundle() ? AppBundle->Probe(path, pathIsBundleRelative) : BundleFileLocation::Invalid(); +} + diff --git a/src/vm/ceemain.cpp b/src/vm/ceemain.cpp index 73f9b6e9bdf6..a8751626ded6 100644 --- a/src/vm/ceemain.cpp +++ b/src/vm/ceemain.cpp @@ -274,7 +274,7 @@ HRESULT InitializeEE(COINITIEE flags) { WRAPPER_NO_CONTRACT; #ifdef FEATURE_EVENT_TRACE - if(!g_fEEComActivatedStartup) + if (!g_fEEComActivatedStartup) g_fEEOtherStartup = TRUE; #endif // FEATURE_EVENT_TRACE return EnsureEEStarted(flags); @@ -1139,7 +1139,7 @@ HRESULT EEStartup(COINITIEE fFlags) _ASSERTE(!g_fEEStarted && !g_fEEInit && SUCCEEDED (g_EEStartupStatus)); - PAL_TRY(COINITIEE *, pfFlags, &fFlags) + PAL_TRY(COINITIEE*, pfFlags, &fFlags) { #ifndef CROSSGEN_COMPILE InitializeClrNotifications(); diff --git a/src/vm/coreassemblyspec.cpp b/src/vm/coreassemblyspec.cpp index 4dc61ecc80c9..4095841e0e73 100644 --- a/src/vm/coreassemblyspec.cpp +++ b/src/vm/coreassemblyspec.cpp @@ -19,7 +19,7 @@ #include "domainfile.h" #include "holder.h" #include "../binder/inc/assemblybinder.hpp" - +#include "bundle.h" #ifdef FEATURE_PREJIT #include "compile.h" #endif @@ -197,10 +197,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; @@ -211,11 +212,13 @@ STDAPI BinderAcquirePEImage(LPCWSTR wszAssemblyPath, PEImageHolder pImage = NULL; PEImageHolder pNativeImage = NULL; + AppDomain* pDomain = ::GetAppDomain(); + #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(); @@ -227,7 +230,7 @@ STDAPI BinderAcquirePEImage(LPCWSTR wszAssemblyPath, else #endif { - pImage = PEImage::OpenImage(wszAssemblyPath, MDInternalImport_Default); + pImage = PEImage::OpenImage(wszAssemblyPath, MDInternalImport_Default, bundleFileLocation); // Make sure that the IL image can be opened if the native image is not available. hr=pImage->TryOpenFile(); diff --git a/src/vm/crossgen/CMakeLists.txt b/src/vm/crossgen/CMakeLists.txt index 9b52d77a2d8c..a98d04612381 100644 --- a/src/vm/crossgen/CMakeLists.txt +++ b/src/vm/crossgen/CMakeLists.txt @@ -7,6 +7,7 @@ set(VM_CROSSGEN_SOURCES ../assemblyspec.cpp ../baseassemblyspec.cpp ../binder.cpp + ../bundle.cpp ../ceeload.cpp ../ceemain.cpp ../class.cpp diff --git a/src/vm/peimage.cpp b/src/vm/peimage.cpp index 528df824d770..8dd3a498fb34 100644 --- a/src/vm/peimage.cpp +++ b/src/vm/peimage.cpp @@ -1325,7 +1325,6 @@ void PEImage::LoadNoMetaData() } } - #endif //DACCESS_COMPILE //------------------------------------------------------------------------------- @@ -1364,19 +1363,20 @@ 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) @@ -1410,15 +1410,17 @@ 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; if (GetLastError()) @@ -1451,7 +1453,6 @@ BOOL PEImage::IsPtrInImage(PTR_CVOID data) return FALSE; } - #if !defined(DACCESS_COMPILE) PEImage * PEImage::OpenImage( ICLRPrivResource * pIResource, diff --git a/src/vm/peimage.h b/src/vm/peimage.h index 28e6d428ce41..7b16d944fc23 100644 --- a/src/vm/peimage.h +++ b/src/vm/peimage.h @@ -19,6 +19,7 @@ #include "peimagelayout.h" #include "sstring.h" #include "holder.h" +#include class SimpleRWLock; // -------------------------------------------------------------------------------- @@ -86,8 +87,6 @@ class PEImage // DO NOT USE these unless you want a private copy-on-write mapping of // the file. - - public: ~PEImage(); PEImage(); @@ -102,8 +101,8 @@ class PEImage #endif // !FEATURE_PAL 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) void Clone(MDInternalImportFlags flags, PTR_PEImage* ppImage) @@ -148,8 +147,13 @@ class PEImage // Accessors const SString &GetPath(); + const SString &GetPathToLoad(); + BOOL IsFile(); HANDLE GetFileHandle(); + INT64 GetOffset() const; + INT64 GetSize() const; + void SetFileHandle(HANDLE hFile); HRESULT TryOpenFile(); @@ -238,7 +242,7 @@ class PEImage // Private routines // ------------------------------------------------------------ - void Init(LPCWSTR pPath); + void Init(LPCWSTR pPath, BundleFileLocation bundleFileLocation); void Init(IStream* pStream, UINT64 uStreamAsmId, DWORD dwModuleId, BOOL resourceFile); @@ -274,6 +278,10 @@ class PEImage SString m_path; LONG m_refCount; + BundleFileLocation m_bundleFileLocation; // If this image is located within a single-file bundle, + // the location within the bundle. If m_bundleFileLocation is vaild, + // it takes precedence over m_path for loading. + // This variable will have the data of module name. // It is only used by DAC to remap fusion loaded modules back to // disk IL. This really is a workaround. The real fix is for fusion loader diff --git a/src/vm/peimage.inl b/src/vm/peimage.inl index add3900ce870..6eae52f22c73 100644 --- a/src/vm/peimage.inl +++ b/src/vm/peimage.inl @@ -33,6 +33,26 @@ inline const SString &PEImage::GetPath() return m_path; } +inline const SString &PEImage::GetPathToLoad() +{ + LIMITED_METHOD_DAC_CONTRACT; + + return m_bundleFileLocation.IsValid() ? m_bundleFileLocation.Path() : m_path; +} + +inline INT64 PEImage::GetOffset() const +{ + LIMITED_METHOD_CONTRACT; + + return m_bundleFileLocation.Offset; +} + +inline INT64 PEImage::GetSize() const +{ + LIMITED_METHOD_CONTRACT; + return m_bundleFileLocation.Size; +} + inline void PEImage::SetModuleFileNameHintForDAC() { LIMITED_METHOD_DAC_CONTRACT; @@ -66,12 +86,11 @@ inline const SString &PEImage::GetModuleFileNameHintForDAC() #endif - inline BOOL PEImage::IsFile() { WRAPPER_NO_CONTRACT; - return !m_path.IsEmpty(); + return !GetPathToLoad().IsEmpty(); } #ifndef DACCESS_COMPILE @@ -407,7 +426,6 @@ inline BOOL PEImage::HasContents() } } - inline CHECK PEImage::CheckFormat() { WRAPPER_NO_CONTRACT; @@ -421,7 +439,7 @@ inline CHECK PEImage::CheckFormat() CHECK_OK; } -inline void PEImage::Init(LPCWSTR pPath) +inline void PEImage::Init(LPCWSTR pPath, BundleFileLocation bundleFileLocation) { CONTRACTL { @@ -430,8 +448,10 @@ inline void PEImage::Init(LPCWSTR pPath) MODE_ANY; } CONTRACTL_END; + m_path = pPath; m_path.Normalize(); + m_bundleFileLocation = bundleFileLocation; SetModuleFileNameHintForDAC(); } #ifndef DACCESS_COMPILE @@ -463,14 +483,14 @@ inline PTR_PEImage PEImage::FindByPath(LPCWSTR pPath) } /* static */ -inline PTR_PEImage PEImage::OpenImage(LPCWSTR pPath, MDInternalImportFlags flags /* = MDInternalImport_Default */) +inline PTR_PEImage PEImage::OpenImage(LPCWSTR pPath, MDInternalImportFlags flags /* = MDInternalImport_Default */, BundleFileLocation bundleFileLocation) { BOOL fUseCache = !((flags & MDInternalImport_NoCache) == MDInternalImport_NoCache); if (!fUseCache) { PEImageHolder pImage(new PEImage); - pImage->Init(pPath); + pImage->Init(pPath, bundleFileLocation); return dac_cast(pImage.Extract()); } @@ -478,7 +498,6 @@ inline PTR_PEImage PEImage::OpenImage(LPCWSTR pPath, MDInternalImportFlags flags PEImage* found = FindByPath(pPath); - if (found == (PEImage*) INVALIDENTRY) { // We did not find the entry in the Cache, and we've been asked to only use the cache. @@ -492,7 +511,7 @@ inline PTR_PEImage PEImage::OpenImage(LPCWSTR pPath, MDInternalImportFlags flags if (flags & MDInternalImport_TrustedNativeImage) pImage->SetIsTrustedNativeImage(); #endif - pImage->Init(pPath); + pImage->Init(pPath, bundleFileLocation); pImage->AddToHashMap(); return dac_cast(pImage.Extract()); @@ -530,9 +549,6 @@ inline void PEImage::AddToHashMap() #endif - - - inline BOOL PEImage::Has32BitNTHeaders() { WRAPPER_NO_CONTRACT; diff --git a/src/vm/peimagelayout.cpp b/src/vm/peimagelayout.cpp index 78fc62c42f7f..0c8434fcb306 100644 --- a/src/vm/peimagelayout.cpp +++ b/src/vm/peimagelayout.cpp @@ -422,6 +422,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)); @@ -458,16 +460,19 @@ MappedImageLayout::MappedImageLayout(PEImage* pOwner) 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)); @@ -523,7 +528,7 @@ MappedImageLayout::MappedImageLayout(PEImage* pOwner) #else //!FEATURE_PAL #ifndef CROSSGEN_COMPILE - m_LoadedFile = PAL_LOADLoadPEFile(hFile); + m_LoadedFile = PAL_LOADLoadPEFile(hFile, offset); if (m_LoadedFile == NULL) { @@ -606,13 +611,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 @@ -622,11 +633,14 @@ 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); + m_FileView.Assign(CLRMapViewOfFile(m_FileMap, FILE_MAP_READ, highPart, lowPart, (SIZE_T)size)); if (m_FileView == NULL) ThrowLastError(); } - Init(m_FileView, size); + + Init(m_FileView, (COUNT_T) size); } diff --git a/src/vm/peimagelayout.h b/src/vm/peimagelayout.h index 72c0cb7a2d0d..08e12557c6fd 100644 --- a/src/vm/peimagelayout.h +++ b/src/vm/peimagelayout.h @@ -161,6 +161,7 @@ class FlatImageLayout: public PEImageLayout protected: HandleHolder m_FileMap; CLRMapViewHolder m_FileView; + public: #ifndef DACCESS_COMPILE FlatImageLayout(PEImage* pOwner);