diff --git a/src/mono/wasi/build/WasiApp.CoreCLR.targets b/src/mono/wasi/build/WasiApp.CoreCLR.targets index 0c61a7cea23567..7fe3ef641aac2d 100644 --- a/src/mono/wasi/build/WasiApp.CoreCLR.targets +++ b/src/mono/wasi/build/WasiApp.CoreCLR.targets @@ -209,6 +209,12 @@ SourceFiles="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)icudt.dat" DestinationFolder="$(WasmAppDir)managed" SkipUnchangedFiles="true" /> + + diff --git a/src/native/corehost/wasihost/wasihost.cpp b/src/native/corehost/wasihost/wasihost.cpp index b1b4068b26a43d..baad2e070c90e8 100644 --- a/src/native/corehost/wasihost/wasihost.cpp +++ b/src/native/corehost/wasihost/wasihost.cpp @@ -6,6 +6,7 @@ // real wasi:cli/run main() instead of a JS driver. // See https://github.com/dotnet/runtime/issues/130129. +#include #include #include #include @@ -158,6 +159,110 @@ static string_t build_tpa(const string_t& core_root, const string_t& core_librar return tpa_list.str(); } +// ECMA-335 compressed unsigned integer (II.23.2), as written by BlobBuilder.WriteCompressedInteger. +static bool read_compressed_uint(const uint8_t*& cur, const uint8_t* end, uint32_t& value) +{ + if (cur >= end) + return false; + + uint8_t b = cur[0]; + if ((b & 0x80) == 0) + { + value = b; + cur += 1; + return true; + } + + if ((b & 0xC0) == 0x80) + { + if (end - cur < 2) + return false; + + value = (static_cast(b & 0x3F) << 8) | cur[1]; + cur += 2; + return true; + } + + if ((b & 0xE0) == 0xC0) + { + if (end - cur < 4) + return false; + + value = (static_cast(b & 0x1F) << 24) | (static_cast(cur[1]) << 16) | (static_cast(cur[2]) << 8) | cur[3]; + cur += 4; + return true; + } + + return false; +} + +// Length-prefixed UTF-8 string, as written by BlobBuilder.WriteSerializedString. +static bool read_serialized_string(const uint8_t*& cur, const uint8_t* end, std::string& value) +{ + uint32_t len; + if (!read_compressed_uint(cur, end, len) || static_cast(end - cur) < len) + return false; + + value.assign(reinterpret_cast(cur), len); + cur += len; + return true; +} + +// Appends the app's runtimeconfig configProperties (feature switches, RuntimeHostConfigurationOption +// items, ...) to the init properties. Reads the runtimeconfig.bin that RuntimeConfigParserTask writes +// (the same file Mono's WASI driver consumes): a compressed count followed by that many key/value +// serialized strings. Properties already set by the host take precedence. A missing file is not an +// error. +static bool add_runtimeconfig_properties(const string_t& path) +{ + FILE* file = std::fopen(path.c_str(), "rb"); + if (file == nullptr) + return true; + + std::vector data; + uint8_t buffer[4096]; + size_t read; + while ((read = std::fread(buffer, 1, sizeof(buffer), file)) > 0) + data.insert(data.end(), buffer, buffer + read); + + bool read_error = std::ferror(file) != 0; + std::fclose(file); + + const uint8_t* cur = data.data(); + const uint8_t* end = cur + data.size(); + uint32_t count = 0; + bool valid = !read_error && read_compressed_uint(cur, end, count); + std::vector keys; + std::vector values; + for (uint32_t i = 0; valid && i < count; ++i) + { + std::string key; + std::string value; + valid = read_serialized_string(cur, end, key) && read_serialized_string(cur, end, value); + keys.push_back(std::move(key)); + values.push_back(std::move(value)); + } + + if (!valid) + { + std::fprintf(stderr, "Failed to read runtime configuration from '%s'\n", path.c_str()); + return false; + } + + size_t host_property_count = s_property_keys.size(); + for (size_t i = 0; i < keys.size(); ++i) + { + auto host_keys_end = s_property_keys.begin() + host_property_count; + if (std::find(s_property_keys.begin(), host_keys_end, keys[i]) != host_keys_end) + continue; + + s_property_keys.push_back(std::move(keys[i])); + s_property_values.push_back(std::move(values[i])); + } + + return true; +} + static size_t HOST_CONTRACT_CALLTYPE get_runtime_property( const char* key, /*out*/ char* value_buffer, @@ -248,6 +353,12 @@ int main(int argc, char* argv[]) s_property_values.push_back(ss.str()); } + // The app bundle places runtimeconfig.bin next to the entry assembly. + string_t runtimeconfig_path = app_path; + runtimeconfig_path.append(W("runtimeconfig.bin")); + if (!add_runtimeconfig_properties(runtimeconfig_path)) + return -1; + std::vector property_keys; std::vector property_values; for (const std::string& key : s_property_keys)