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)