From 2325b65e911f2661e6b14993e8d02f5277ec1d09 Mon Sep 17 00:00:00 2001 From: Swaroop Sridhar Date: Fri, 10 Apr 2020 19:27:32 -0700 Subject: [PATCH 1/3] Single-File: Pass BUNDLE_PROBE property to the runtime As described in the [design doc](https://github.com/dotnet/designs/blob/master/accepted/2020/single-file/design.md#startup), pass the bundle_probe function pointer encoded as a string to the runtime. --- src/installer/corehost/cli/bundle/runner.cpp | 50 ++++++++++- src/installer/corehost/cli/bundle/runner.h | 16 +++- src/installer/corehost/cli/hostmisc/pal.h | 8 +- .../corehost/cli/hostmisc/pal.unix.cpp | 22 ++++- .../corehost/cli/hostmisc/pal.windows.cpp | 12 +-- .../corehost/cli/hostpolicy/coreclr.cpp | 3 +- .../corehost/cli/hostpolicy/coreclr.h | 2 +- .../cli/hostpolicy/hostpolicy_context.cpp | 7 ++ .../BundleProbeTester.csproj | 14 +++ .../TestProjects/BundleProbeTester/Program.cs | 89 +++++++++++++++++++ .../AppHost.Bundle.Tests/BundleProbe.cs | 77 ++++++++++++++++ 11 files changed, 284 insertions(+), 16 deletions(-) create mode 100644 src/installer/test/Assets/TestProjects/BundleProbeTester/BundleProbeTester.csproj create mode 100644 src/installer/test/Assets/TestProjects/BundleProbeTester/Program.cs create mode 100644 src/installer/test/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleProbe.cs diff --git a/src/installer/corehost/cli/bundle/runner.cpp b/src/installer/corehost/cli/bundle/runner.cpp index 58d10a817855f9..95af0470f49e80 100644 --- a/src/installer/corehost/cli/bundle/runner.cpp +++ b/src/installer/corehost/cli/bundle/runner.cpp @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// 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. @@ -48,11 +48,11 @@ StatusCode runner_t::extract() } } -const file_entry_t* runner_t::probe(const pal::string_t& path) const +const file_entry_t* runner_t::probe(const pal::string_t &relative_path) const { for (const file_entry_t& entry : m_manifest.files) { - if (entry.relative_path() == path) + if (pal::pathcmp(entry.relative_path(), relative_path) == 0) { return &entry; } @@ -81,3 +81,47 @@ bool runner_t::locate(const pal::string_t& relative_path, pal::string_t& full_pa return true; } + +bool STDMETHODCALLTYPE runner_t::bundle_probe(const wchar_t *path, int64_t *offset, int64_t *size) +{ + if (path == nullptr) + { + return false; + } + + pal::string_t file_path; + + if (!pal::unicode_palstring(path, &file_path)) + { + trace::warning(_X("Failure probing contents of the application bundle.")); + trace::warning(_X("Failed to convert path [%ls] to UTF8"), path); + + return false; + } + + const file_entry_t* entry = app()->probe(file_path); + if (entry != nullptr) + { + *offset = entry->offset(); + *size = entry->size(); + + assert(*offset != 0); + + return true; + } + + return false; +} + +pal::string_t runner_t::get_bundle_probe() +{ + if (!is_single_file_bundle()) + { + return _X(""); + } + + pal::stringstream_t ptr_stream; + ptr_stream << "0x" << std::hex << (size_t)(&bundle_probe); + + return ptr_stream.str(); +} diff --git a/src/installer/corehost/cli/bundle/runner.h b/src/installer/corehost/cli/bundle/runner.h index fb4d74bf877a2f..9287c837296c25 100644 --- a/src/installer/corehost/cli/bundle/runner.h +++ b/src/installer/corehost/cli/bundle/runner.h @@ -27,7 +27,7 @@ namespace bundle const pal::string_t& extraction_path() const { return m_extraction_path; } - const file_entry_t *probe(const pal::string_t& 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) const; static StatusCode process_manifest_and_extract() @@ -37,8 +37,22 @@ namespace bundle static const runner_t* app() { return (const runner_t*)the_app; } + // Obtain the bundle_probe function (encoded as a string) to be passed to the runtime. + static pal::string_t get_bundle_probe(); + private: + // Probe the app-bundle for the file 'path' and return its location ('offset', 'size') if found. + // This method is intnded to be used by the runtime to probe for bundled assemblies + // This method assumes that the currently executing app is a single-file bundle. + // + // bundle_probe recieves its path argument as wchar_t* instead of pal::char_t*, because: + // * The host uses Unicode strings on Windows and UTF8 strings on Unix + // * The runtime uses Unicode strings on all platforms + // Using a unicode encoded path presents a uniform interface to the runtime + // and minimizes the number if Unicode <-> UTF8 conversions necessary. + static bool __stdcall bundle_probe(const wchar_t* path, int64_t* offset, int64_t* size); + StatusCode extract(); manifest_t m_manifest; diff --git a/src/installer/corehost/cli/hostmisc/pal.h b/src/installer/corehost/cli/hostmisc/pal.h index 10cb0e2c302ae7..12b9ec480e3dda 100644 --- a/src/installer/corehost/cli/hostmisc/pal.h +++ b/src/installer/corehost/cli/hostmisc/pal.h @@ -146,7 +146,9 @@ namespace pal inline int strcasecmp(const char_t* str1, const char_t* str2) { return ::_wcsicmp(str1, str2); } inline int strncmp(const char_t* str1, const char_t* str2, int len) { return ::wcsncmp(str1, str2, len); } inline int strncasecmp(const char_t* str1, const char_t* str2, int len) { return ::_wcsnicmp(str1, str2, len); } - + inline int pathcmp(const pal::string_t &path1, const pal::string_t &path2) { return strcasecmp(path1.c_str(), path2.c_str()); } + inline string_t to_string(int value) { return std::to_wstring(value); } + inline size_t strlen(const char_t* str) { return ::wcslen(str); } inline FILE * file_open(const string_t& path, const char_t* mode) { return ::_wfopen(path.c_str(), mode); } @@ -204,6 +206,8 @@ namespace pal inline int strcasecmp(const char_t* str1, const char_t* str2) { return ::strcasecmp(str1, str2); } inline int strncmp(const char_t* str1, const char_t* str2, int len) { return ::strncmp(str1, str2, len); } inline int strncasecmp(const char_t* str1, const char_t* str2, int len) { return ::strncasecmp(str1, str2, len); } + inline int pathcmp(const pal::string_t& path1, const pal::string_t& path2) { return strcmp(path1.c_str(), path2.c_str()); } + inline string_t to_string(int value) { return std::to_string(value); } inline size_t strlen(const char_t* str) { return ::strlen(str); } inline FILE * file_open(const string_t& path, const char_t* mode) { return fopen(path.c_str(), mode); } @@ -237,7 +241,6 @@ namespace pal return ret; } - string_t to_string(int value); string_t get_timestamp(); bool getcwd(string_t* recv); @@ -297,6 +300,7 @@ namespace pal bool get_default_bundle_extraction_base_dir(string_t& extraction_dir); int xtoi(const char_t* input); + bool unicode_palstring(const wchar_t* str, pal::string_t* out); bool get_loaded_library(const char_t *library_name, const char *symbol_name, /*out*/ dll_t *dll, /*out*/ string_t *path); bool load_library(const string_t* path, dll_t* dll); diff --git a/src/installer/corehost/cli/hostmisc/pal.unix.cpp b/src/installer/corehost/cli/hostmisc/pal.unix.cpp index 3626761b0ecbb4..fc3ff040877faa 100644 --- a/src/installer/corehost/cli/hostmisc/pal.unix.cpp +++ b/src/installer/corehost/cli/hostmisc/pal.unix.cpp @@ -17,9 +17,12 @@ #include #include #include +#include +#include #include #include "config.h" + #if defined(TARGET_OSX) #include #include @@ -39,8 +42,6 @@ #define DT_LNK 10 #endif -pal::string_t pal::to_string(int value) { return std::to_string(value); } - pal::string_t pal::to_lower(const pal::string_t& in) { pal::string_t ret = in; @@ -254,6 +255,23 @@ int pal::xtoi(const char_t* input) return atoi(input); } +bool pal::unicode_palstring(const wchar_t* str, pal::string_t* out) +{ + out->clear(); + + std::mbstate_t mbstate; + memset(&mbstate, 0, sizeof(std::mbstate_t)); + + // Passing a nullptr destination computes the length of the destination string + // without the null-termination character. + size_t mbsize= wcsrtombs(nullptr, &str, 0, &mbstate) + 1; + + out->resize(mbsize, '\0'); + size_t written_size = std::wcsrtombs(&(*out)[0], &str, mbsize, &mbstate); + + return written_size != -1; +} + bool pal::is_path_rooted(const pal::string_t& path) { return path.front() == '/'; diff --git a/src/installer/corehost/cli/hostmisc/pal.windows.cpp b/src/installer/corehost/cli/hostmisc/pal.windows.cpp index f8348a05d167b0..86204df36c9675 100644 --- a/src/installer/corehost/cli/hostmisc/pal.windows.cpp +++ b/src/installer/corehost/cli/hostmisc/pal.windows.cpp @@ -49,11 +49,6 @@ pal::string_t pal::to_lower(const pal::string_t& in) return ret; } -pal::string_t pal::to_string(int value) -{ - return std::to_wstring(value); -} - pal::string_t pal::get_timestamp() { std::time_t t = std::time(0); @@ -605,7 +600,6 @@ bool pal::get_default_bundle_extraction_base_dir(pal::string_t& extraction_dir) return realpath(&extraction_dir); } - static bool wchar_convert_helper(DWORD code_page, const char* cstr, int len, pal::string_t* out) { out->clear(); @@ -649,6 +643,12 @@ bool pal::clr_palstring(const char* cstr, pal::string_t* out) return wchar_convert_helper(CP_UTF8, cstr, ::strlen(cstr), out); } +bool pal::unicode_palstring(const wchar_t* str, pal::string_t* out) +{ + out->assign(str); + return true; +} + // Return if path is valid and file exists, return true and adjust path as appropriate. bool pal::realpath(string_t* path, bool skip_error_logging) { diff --git a/src/installer/corehost/cli/hostpolicy/coreclr.cpp b/src/installer/corehost/cli/hostpolicy/coreclr.cpp index b329a72d85886a..8c513d0b857ecc 100644 --- a/src/installer/corehost/cli/hostpolicy/coreclr.cpp +++ b/src/installer/corehost/cli/hostpolicy/coreclr.cpp @@ -204,7 +204,8 @@ namespace _X("STARTUP_HOOKS"), _X("APP_PATHS"), _X("APP_NI_PATHS"), - _X("RUNTIME_IDENTIFIER") + _X("RUNTIME_IDENTIFIER"), + _X("BUNDLE_PROBE") }; static_assert((sizeof(PropertyNameMapping) / sizeof(*PropertyNameMapping)) == static_cast(common_property::Last), "Invalid property count"); diff --git a/src/installer/corehost/cli/hostpolicy/coreclr.h b/src/installer/corehost/cli/hostpolicy/coreclr.h index a06e81fe2e346f..422b57c52d59d0 100644 --- a/src/installer/corehost/cli/hostpolicy/coreclr.h +++ b/src/installer/corehost/cli/hostpolicy/coreclr.h @@ -68,7 +68,7 @@ enum class common_property AppPaths, AppNIPaths, RuntimeIdentifier, - + BundleProbe, // Sentinel value - new values should be defined above Last }; diff --git a/src/installer/corehost/cli/hostpolicy/hostpolicy_context.cpp b/src/installer/corehost/cli/hostpolicy/hostpolicy_context.cpp index bb4a965126d962..fdf59da025e200 100644 --- a/src/installer/corehost/cli/hostpolicy/hostpolicy_context.cpp +++ b/src/installer/corehost/cli/hostpolicy/hostpolicy_context.cpp @@ -7,6 +7,7 @@ #include "deps_resolver.h" #include #include +#include "bundle/runner.h" namespace { @@ -198,5 +199,11 @@ int hostpolicy_context_t::initialize(hostpolicy_init_t &hostpolicy_init, const a } } + // Single-File Bundle Probe + if (bundle::info_t::is_single_file_bundle()) + { + coreclr_properties.add(common_property::BundleProbe, bundle::runner_t::get_bundle_probe().c_str()); + } + return StatusCode::Success; } diff --git a/src/installer/test/Assets/TestProjects/BundleProbeTester/BundleProbeTester.csproj b/src/installer/test/Assets/TestProjects/BundleProbeTester/BundleProbeTester.csproj new file mode 100644 index 00000000000000..c1141db5ab1ad4 --- /dev/null +++ b/src/installer/test/Assets/TestProjects/BundleProbeTester/BundleProbeTester.csproj @@ -0,0 +1,14 @@ + + + + $(NETCoreAppFramework) + Exe + $(TestTargetRid) + $(MNAVersion) + + + + true + + + diff --git a/src/installer/test/Assets/TestProjects/BundleProbeTester/Program.cs b/src/installer/test/Assets/TestProjects/BundleProbeTester/Program.cs new file mode 100644 index 00000000000000..6a75482b95071f --- /dev/null +++ b/src/installer/test/Assets/TestProjects/BundleProbeTester/Program.cs @@ -0,0 +1,89 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.InteropServices; + +namespace BundleProbeTester +{ + public static class Program + { + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate bool BundleProbeDelegate([MarshalAs(UnmanagedType.LPWStr)] string path, IntPtr size, IntPtr offset); + + unsafe static bool Probe(BundleProbeDelegate bundleProbe, string path, bool isExpected) + { + Int64 size, offset; + bool exists = bundleProbe(path, (IntPtr)(&offset), (IntPtr)(&size)); + + switch (exists, isExpected) + { + case (true, true): + if (size > 0 && offset > 0) + { + return true; + } + + Console.WriteLine($"Invalid location obtained for {path} within bundle."); + return false; + + case (true, false): + Console.WriteLine($"Unexpected file {path} found in bundle."); + return false; + + case (false, true): + Console.WriteLine($"Expected file {path} not found in bundle."); + return false; + + case (false, false): + return true; + } + + return false; // dummy + } + + public static int Main(string[] args) + { + bool isSingleFile = args.Length > 0 && args[0].Equals("SingleFile"); + object probeObject = System.AppDomain.CurrentDomain.GetData("BUNDLE_PROBE"); + + if (!isSingleFile) + { + if (probeObject != null) + { + Console.WriteLine("BUNDLE_PROBE property passed in for a non-single-file app"); + return -1; + } + + Console.WriteLine("No BUNDLE_PROBE"); + return 0; + } + + if (probeObject == null) + { + Console.WriteLine("BUNDLE_PROBE property not passed in for a single-file app"); + return -2; + } + + string probeString = probeObject as string; + IntPtr probePtr = (IntPtr)Convert.ToUInt64(probeString, 16); + BundleProbeDelegate bundleProbeDelegate = Marshal.GetDelegateForFunctionPointer(probePtr); + bool success = + Probe(bundleProbeDelegate, "BundleProbeTester.dll", isExpected: true) && + Probe(bundleProbeDelegate, "BundleProbeTester.runtimeconfig.json", isExpected: true) && + Probe(bundleProbeDelegate, "System.Private.CoreLib.dll", isExpected: true) && + Probe(bundleProbeDelegate, "hostpolicy.dll", isExpected: false) && + Probe(bundleProbeDelegate, "--", isExpected: false) && + Probe(bundleProbeDelegate, "", isExpected: false); + + if (!success) + { + return -3; + } + + Console.WriteLine("BUNDLE_PROBE OK"); + return 0; + } + } +} diff --git a/src/installer/test/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleProbe.cs b/src/installer/test/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleProbe.cs new file mode 100644 index 00000000000000..b178e0ee36e6d0 --- /dev/null +++ b/src/installer/test/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleProbe.cs @@ -0,0 +1,77 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.IO; +using Xunit; +using Microsoft.DotNet.Cli.Build.Framework; +using Microsoft.DotNet.CoreSetup.Test; +using BundleTests.Helpers; +using System.Threading; + +namespace AppHost.Bundle.Tests +{ + public class BundleProbe : IClassFixture + { + private SharedTestState sharedTestState; + + public BundleProbe(SharedTestState fixture) + { + sharedTestState = fixture; + } + + [Fact] + private void Bundle_Probe_Not_Passed_For_Non_Single_File_App() + { + var fixture = sharedTestState.TestFixture.Copy(); + string appExe = BundleHelper.GetHostPath(fixture); + + Command.Create(appExe) + .CaptureStdErr() + .CaptureStdOut() + .Execute() + .Should() + .Pass() + .And + .HaveStdOutContaining("No BUNDLE_PROBE"); + } + + [Fact] + private void Bundle_Probe_Passed_For_Single_File_App() + { + var fixture = sharedTestState.TestFixture.Copy(); + string singleFile = BundleHelper.BundleApp(fixture); + + Command.Create(singleFile, "SingleFile") + .CaptureStdErr() + .CaptureStdOut() + .Execute() + .Should() + .Pass() + .And + .HaveStdOutContaining("BUNDLE_PROBE OK"); + } + + public class SharedTestState : IDisposable + { + public TestProjectFixture TestFixture { get; set; } + public RepoDirectoriesProvider RepoDirectories { get; set; } + + public SharedTestState() + { + RepoDirectories = new RepoDirectoriesProvider(); + TestFixture = new TestProjectFixture("BundleProbeTester", RepoDirectories); + TestFixture + .EnsureRestoredForRid(TestFixture.CurrentRid, RepoDirectories.CorehostPackages) + .PublishProject(runtime: TestFixture.CurrentRid, + outputDirectory: BundleHelper.GetPublishPath(TestFixture)); + } + + public void Dispose() + { + TestFixture.Dispose(); + } + } + } +} From c06e06c7209d842a1c0446ab4b5bf6a281ffd26c Mon Sep 17 00:00:00 2001 From: Swaroop Sridhar Date: Wed, 15 Apr 2020 02:48:17 -0700 Subject: [PATCH 2/3] Address feedback by @vitek-katas --- src/installer/corehost/cli/bundle/runner.cpp | 59 ++++++------------- src/installer/corehost/cli/bundle/runner.h | 17 +----- .../cli/hostpolicy/hostpolicy_context.cpp | 40 ++++++++++++- 3 files changed, 58 insertions(+), 58 deletions(-) diff --git a/src/installer/corehost/cli/bundle/runner.cpp b/src/installer/corehost/cli/bundle/runner.cpp index 95af0470f49e80..19fc8bcd475761 100644 --- a/src/installer/corehost/cli/bundle/runner.cpp +++ b/src/installer/corehost/cli/bundle/runner.cpp @@ -61,67 +61,42 @@ const file_entry_t* runner_t::probe(const pal::string_t &relative_path) const return nullptr; } -bool runner_t::locate(const pal::string_t& relative_path, pal::string_t& full_path) const +bool runner_t::probe(const pal::string_t& relative_path, int64_t* offset, int64_t* size) const { - const bundle::runner_t* app = bundle::runner_t::app(); - const bundle::file_entry_t* entry = app->probe(relative_path); + const bundle::file_entry_t* entry = probe(relative_path); if (entry == nullptr) { - full_path.clear(); 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()); + assert(entry->offset() != 0); + + *offset = entry->offset(); + *size = entry->size(); - full_path.assign(app->extraction_path()); - append_path(&full_path, relative_path.c_str()); return true; } -bool STDMETHODCALLTYPE runner_t::bundle_probe(const wchar_t *path, int64_t *offset, int64_t *size) -{ - if (path == nullptr) - { - return false; - } - pal::string_t file_path; +bool runner_t::locate(const pal::string_t& relative_path, pal::string_t& full_path) const +{ + const bundle::file_entry_t* entry = probe(relative_path); - if (!pal::unicode_palstring(path, &file_path)) + if (entry == nullptr) { - trace::warning(_X("Failure probing contents of the application bundle.")); - trace::warning(_X("Failed to convert path [%ls] to UTF8"), path); - + full_path.clear(); return false; } - const file_entry_t* entry = app()->probe(file_path); - if (entry != nullptr) - { - *offset = entry->offset(); - *size = entry->size(); - - assert(*offset != 0); + // 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()); - return true; - } + full_path.assign(extraction_path()); + append_path(&full_path, relative_path.c_str()); - return false; + return true; } -pal::string_t runner_t::get_bundle_probe() -{ - if (!is_single_file_bundle()) - { - return _X(""); - } - - pal::stringstream_t ptr_stream; - ptr_stream << "0x" << std::hex << (size_t)(&bundle_probe); - - return ptr_stream.str(); -} diff --git a/src/installer/corehost/cli/bundle/runner.h b/src/installer/corehost/cli/bundle/runner.h index 9287c837296c25..39c6b6b1190b01 100644 --- a/src/installer/corehost/cli/bundle/runner.h +++ b/src/installer/corehost/cli/bundle/runner.h @@ -27,7 +27,7 @@ namespace bundle const pal::string_t& extraction_path() const { return m_extraction_path; } - const file_entry_t *probe(const pal::string_t& relative_path) const; + 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; static StatusCode process_manifest_and_extract() @@ -37,23 +37,10 @@ namespace bundle static const runner_t* app() { return (const runner_t*)the_app; } - // Obtain the bundle_probe function (encoded as a string) to be passed to the runtime. - static pal::string_t get_bundle_probe(); - private: - // Probe the app-bundle for the file 'path' and return its location ('offset', 'size') if found. - // This method is intnded to be used by the runtime to probe for bundled assemblies - // This method assumes that the currently executing app is a single-file bundle. - // - // bundle_probe recieves its path argument as wchar_t* instead of pal::char_t*, because: - // * The host uses Unicode strings on Windows and UTF8 strings on Unix - // * The runtime uses Unicode strings on all platforms - // Using a unicode encoded path presents a uniform interface to the runtime - // and minimizes the number if Unicode <-> UTF8 conversions necessary. - static bool __stdcall bundle_probe(const wchar_t* path, int64_t* offset, int64_t* size); - StatusCode extract(); + const file_entry_t* probe(const pal::string_t& relative_path) const; manifest_t m_manifest; pal::string_t m_extraction_path; diff --git a/src/installer/corehost/cli/hostpolicy/hostpolicy_context.cpp b/src/installer/corehost/cli/hostpolicy/hostpolicy_context.cpp index fdf59da025e200..7eb71450a803f4 100644 --- a/src/installer/corehost/cli/hostpolicy/hostpolicy_context.cpp +++ b/src/installer/corehost/cli/hostpolicy/hostpolicy_context.cpp @@ -8,6 +8,7 @@ #include #include #include "bundle/runner.h" +#include "bundle/file_entry.h" namespace { @@ -16,6 +17,39 @@ namespace trace::error(_X("Duplicate runtime property found: %s"), property_key); trace::error(_X("It is invalid to specify values for properties populated by the hosting layer in the the application's .runtimeconfig.json")); } + + // bundle_probe: + // Probe the app-bundle for the file 'path' and return its location ('offset', 'size') if found. + // + // This function is an API exported to the runtime via the BUNDLE_PROBE property. + // This function used by the runtime to probe for bundled assemblies + // This function assumes that the currently executing app is a single-file bundle. + // + // bundle_probe recieves its path argument as wchar_t* instead of pal::char_t*, because: + // * The host uses Unicode strings on Windows and UTF8 strings on Unix + // * The runtime uses Unicode strings on all platforms + // Using a unicode encoded path presents a uniform interface to the runtime + // and minimizes the number if Unicode <-> UTF8 conversions necessary. + + bool STDMETHODCALLTYPE bundle_probe(const wchar_t* path, int64_t* offset, int64_t* size) + { + if (path == nullptr) + { + return false; + } + + pal::string_t file_path; + + if (!pal::unicode_palstring(path, &file_path)) + { + trace::warning(_X("Failure probing contents of the application bundle.")); + trace::warning(_X("Failed to convert path [%ls] to UTF8"), path); + + return false; + } + + return bundle::runner_t::app()->probe(file_path, offset, size); + } } int hostpolicy_context_t::initialize(hostpolicy_init_t &hostpolicy_init, const arguments_t &args, bool enable_breadcrumbs) @@ -202,7 +236,11 @@ int hostpolicy_context_t::initialize(hostpolicy_init_t &hostpolicy_init, const a // Single-File Bundle Probe if (bundle::info_t::is_single_file_bundle()) { - coreclr_properties.add(common_property::BundleProbe, bundle::runner_t::get_bundle_probe().c_str()); + // Encode the bundle_probe function pointer as a string, and pass it to the runtime. + pal::stringstream_t ptr_stream; + ptr_stream << "0x" << std::hex << (size_t)(&bundle_probe); + + coreclr_properties.add(common_property::BundleProbe, ptr_stream.str().c_str()); } return StatusCode::Success; From adb4e6345c09f200e49c55b728c0ccc1daaa8e0b Mon Sep 17 00:00:00 2001 From: Swaroop Sridhar Date: Fri, 17 Apr 2020 02:01:44 -0700 Subject: [PATCH 3/3] bundle_probe() use char16_t* string type Type the path-string as a char16_t* string instead of wchar_t* wchar_t* size varies by architecture. bool STDMETHODCALLTYPE bundle_probe(const char16_t* path, int64_t* offset, int64_t* size); --- src/installer/corehost/cli/hostmisc/pal.h | 2 +- .../corehost/cli/hostmisc/pal.unix.cpp | 20 ++++++------------- .../corehost/cli/hostmisc/pal.windows.cpp | 4 ++-- .../cli/hostpolicy/hostpolicy_context.cpp | 12 +++++++---- 4 files changed, 17 insertions(+), 21 deletions(-) diff --git a/src/installer/corehost/cli/hostmisc/pal.h b/src/installer/corehost/cli/hostmisc/pal.h index 12b9ec480e3dda..fefe5c21bd4463 100644 --- a/src/installer/corehost/cli/hostmisc/pal.h +++ b/src/installer/corehost/cli/hostmisc/pal.h @@ -300,7 +300,7 @@ namespace pal bool get_default_bundle_extraction_base_dir(string_t& extraction_dir); int xtoi(const char_t* input); - bool unicode_palstring(const wchar_t* str, pal::string_t* out); + bool unicode_palstring(const char16_t* str, pal::string_t* out); bool get_loaded_library(const char_t *library_name, const char *symbol_name, /*out*/ dll_t *dll, /*out*/ string_t *path); bool load_library(const string_t* path, dll_t* dll); diff --git a/src/installer/corehost/cli/hostmisc/pal.unix.cpp b/src/installer/corehost/cli/hostmisc/pal.unix.cpp index fc3ff040877faa..20612a16f6fcfe 100644 --- a/src/installer/corehost/cli/hostmisc/pal.unix.cpp +++ b/src/installer/corehost/cli/hostmisc/pal.unix.cpp @@ -17,12 +17,11 @@ #include #include #include -#include -#include +#include +#include #include #include "config.h" - #if defined(TARGET_OSX) #include #include @@ -255,21 +254,14 @@ int pal::xtoi(const char_t* input) return atoi(input); } -bool pal::unicode_palstring(const wchar_t* str, pal::string_t* out) +bool pal::unicode_palstring(const char16_t* str, pal::string_t* out) { out->clear(); - std::mbstate_t mbstate; - memset(&mbstate, 0, sizeof(std::mbstate_t)); - - // Passing a nullptr destination computes the length of the destination string - // without the null-termination character. - size_t mbsize= wcsrtombs(nullptr, &str, 0, &mbstate) + 1; + std::wstring_convert, char16_t> conversion; + out->assign(conversion.to_bytes(str)); - out->resize(mbsize, '\0'); - size_t written_size = std::wcsrtombs(&(*out)[0], &str, mbsize, &mbstate); - - return written_size != -1; + return true; } bool pal::is_path_rooted(const pal::string_t& path) diff --git a/src/installer/corehost/cli/hostmisc/pal.windows.cpp b/src/installer/corehost/cli/hostmisc/pal.windows.cpp index 86204df36c9675..1211c3a6692b48 100644 --- a/src/installer/corehost/cli/hostmisc/pal.windows.cpp +++ b/src/installer/corehost/cli/hostmisc/pal.windows.cpp @@ -643,9 +643,9 @@ bool pal::clr_palstring(const char* cstr, pal::string_t* out) return wchar_convert_helper(CP_UTF8, cstr, ::strlen(cstr), out); } -bool pal::unicode_palstring(const wchar_t* str, pal::string_t* out) +bool pal::unicode_palstring(const char16_t* str, pal::string_t* out) { - out->assign(str); + out->assign((const wchar_t *)str); return true; } diff --git a/src/installer/corehost/cli/hostpolicy/hostpolicy_context.cpp b/src/installer/corehost/cli/hostpolicy/hostpolicy_context.cpp index 7eb71450a803f4..c78c51493a1ef4 100644 --- a/src/installer/corehost/cli/hostpolicy/hostpolicy_context.cpp +++ b/src/installer/corehost/cli/hostpolicy/hostpolicy_context.cpp @@ -25,13 +25,17 @@ namespace // This function used by the runtime to probe for bundled assemblies // This function assumes that the currently executing app is a single-file bundle. // - // bundle_probe recieves its path argument as wchar_t* instead of pal::char_t*, because: + // bundle_probe recieves its path argument as cha16_t* instead of pal::char_t*, because: // * The host uses Unicode strings on Windows and UTF8 strings on Unix // * The runtime uses Unicode strings on all platforms - // Using a unicode encoded path presents a uniform interface to the runtime - // and minimizes the number if Unicode <-> UTF8 conversions necessary. + // * Using a unicode encoded path presents a uniform interface to the runtime + // and minimizes the number if Unicode <-> UTF8 conversions necessary. + // + // The unicode char type is char16_t* instead of whcar_t*, because: + // * wchar_t is 16-bit encoding on Windows while it is 32-bit encoding on most Unix systems + // * The runtime uses 16-bit encoded unicode characters. - bool STDMETHODCALLTYPE bundle_probe(const wchar_t* path, int64_t* offset, int64_t* size) + bool STDMETHODCALLTYPE bundle_probe(const char16_t* path, int64_t* offset, int64_t* size) { if (path == nullptr) {