diff --git a/package-rhel-riscv.sh b/package-rhel-riscv.sh
index fc8ed6a7..c08bace0 100644
--- a/package-rhel-riscv.sh
+++ b/package-rhel-riscv.sh
@@ -40,6 +40,23 @@ DOTNET_SDK_URL="${DOTNET_RISCV_BASE}/${DOTNET_RISCV_VERSION}/${DOTNET_RISCV_FILE
SKIA_VER="${SKIA_VER:-3.119.2}"
HARFBUZZ_VER="${HARFBUZZ_VER:-8.3.1.3}"
+# SQLite amalgamation version for the linux-riscv64 native build.
+#
+# Keep in sync with the SourceGear.sqlite3 package referenced from
+# v2rayN/Directory.Packages.props (it pins the SQLite binaries shipped on the
+# x64/arm64/macOS RIDs).
+#
+# Mapping rule: SourceGear.sqlite3 X.Y.Z.W -> SQLite vX.Y.Z
+# -> SQLITE_AMALGAMATION_VER = sprintf("%d%02d%02d00", X, Y, Z)
+# -> SQLITE_AMALGAMATION_YEAR = year of the upstream SQLite release
+#
+# Currently: SourceGear.sqlite3 3.50.4.5 -> SQLite 3.50.4 (released 2025).
+#
+# Override via the environment if you need a different version on RISC-V:
+# SQLITE_AMALGAMATION_YEAR=2026 SQLITE_AMALGAMATION_VER=3530000 ./package-rhel-riscv.sh
+SQLITE_AMALGAMATION_YEAR="${SQLITE_AMALGAMATION_YEAR:-2025}"
+SQLITE_AMALGAMATION_VER="${SQLITE_AMALGAMATION_VER:-3500400}"
+
# If the first argument starts with --, do not treat it as a version number
if [[ "${VERSION_ARG:-}" == --* ]]; then
VERSION_ARG=""
@@ -111,9 +128,11 @@ build_sqlite_native_riscv64() {
mkdir -p "$outdir"
workdir="$(mktemp -d)"
- # SQLite 3.53.0 amalgamation
- sqlite_year="2026"
- sqlite_ver="3530000"
+ # SQLite amalgamation. Version is configured at the top of this script
+ # (SQLITE_AMALGAMATION_YEAR / SQLITE_AMALGAMATION_VER) and must be kept in
+ # sync with the SourceGear.sqlite3 package version used on other RIDs.
+ sqlite_year="$SQLITE_AMALGAMATION_YEAR"
+ sqlite_ver="$SQLITE_AMALGAMATION_VER"
sqlite_zip="sqlite-amalgamation-${sqlite_ver}.zip"
echo "[+] Download SQLite amalgamation: ${sqlite_zip}"
diff --git a/v2rayN/ServiceLib/Common/Utils.cs b/v2rayN/ServiceLib/Common/Utils.cs
index 2824bd83..183a5a7c 100644
--- a/v2rayN/ServiceLib/Common/Utils.cs
+++ b/v2rayN/ServiceLib/Common/Utils.cs
@@ -862,17 +862,34 @@ public class Utils
return systemHosts;
}
+ ///
+ /// Reads and merges the system hosts file(s) for the current platform.
+ /// On Windows: C:\Windows\System32\drivers\etc\hosts plus
+ /// hosts.ics (created by Internet Connection Sharing).
+ /// On Linux/macOS: /etc/hosts.
+ /// On any other platform: returns an empty dictionary.
+ ///
public static Dictionary GetSystemHosts()
{
- var hosts = GetSystemHosts(@"C:\Windows\System32\drivers\etc\hosts");
- var hostsIcs = GetSystemHosts(@"C:\Windows\System32\drivers\etc\hosts.ics");
-
- foreach (var (key, value) in hostsIcs)
+ if (IsWindows())
{
- hosts[key] = value;
+ var hosts = GetSystemHosts(@"C:\Windows\System32\drivers\etc\hosts");
+ var hostsIcs = GetSystemHosts(@"C:\Windows\System32\drivers\etc\hosts.ics");
+
+ foreach (var (key, value) in hostsIcs)
+ {
+ hosts[key] = value;
+ }
+
+ return hosts;
}
- return hosts;
+ if (IsLinux() || IsMacOS())
+ {
+ return GetSystemHosts("/etc/hosts");
+ }
+
+ return new Dictionary();
}
public static async Task GetCliWrapOutput(string filePath, string? arg)
@@ -1117,8 +1134,10 @@ public class Utils
[SupportedOSPlatformGuard("windows")]
public static bool IsWindows() => OperatingSystem.IsWindows();
+ [SupportedOSPlatformGuard("linux")]
public static bool IsLinux() => OperatingSystem.IsLinux();
+ [SupportedOSPlatformGuard("macos")]
public static bool IsMacOS() => OperatingSystem.IsMacOS();
[UnsupportedOSPlatformGuard("windows")]
diff --git a/v2rayN/ServiceLib/Handler/AutoStartupHandler.cs b/v2rayN/ServiceLib/Handler/AutoStartupHandler.cs
index e40af63f..fd7d7fc9 100644
--- a/v2rayN/ServiceLib/Handler/AutoStartupHandler.cs
+++ b/v2rayN/ServiceLib/Handler/AutoStartupHandler.cs
@@ -127,6 +127,7 @@ public static class AutoStartupHandler
#region Linux
+ [SupportedOSPlatform("linux")]
private static async Task ClearTaskLinux()
{
try
@@ -140,6 +141,7 @@ public static class AutoStartupHandler
await Task.CompletedTask;
}
+ [SupportedOSPlatform("linux")]
private static async Task SetTaskLinux()
{
try
@@ -160,6 +162,7 @@ public static class AutoStartupHandler
}
}
+ [SupportedOSPlatform("linux")]
private static string GetHomePathLinux()
{
var homePath = Path.Combine(Utils.GetHomePath(), ".config", "autostart", $"{Global.AppName}.desktop");
@@ -171,6 +174,7 @@ public static class AutoStartupHandler
#region macOS
+ [SupportedOSPlatform("macos")]
private static async Task ClearTaskOSX()
{
try
@@ -190,6 +194,7 @@ public static class AutoStartupHandler
}
}
+ [SupportedOSPlatform("macos")]
private static async Task SetTaskOSX()
{
try
@@ -207,6 +212,7 @@ public static class AutoStartupHandler
}
}
+ [SupportedOSPlatform("macos")]
private static string GetLaunchAgentPathMacOS()
{
var homePath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
@@ -215,6 +221,7 @@ public static class AutoStartupHandler
return launchAgentPath;
}
+ [SupportedOSPlatform("macos")]
private static string GenerateLaunchAgentPlist()
{
var exePath = Utils.GetExePath();
diff --git a/v2rayN/ServiceLib/Handler/SysProxy/ProxySettingLinux.cs b/v2rayN/ServiceLib/Handler/SysProxy/ProxySettingLinux.cs
index 4929c72e..14d61d9c 100644
--- a/v2rayN/ServiceLib/Handler/SysProxy/ProxySettingLinux.cs
+++ b/v2rayN/ServiceLib/Handler/SysProxy/ProxySettingLinux.cs
@@ -1,5 +1,6 @@
namespace ServiceLib.Handler.SysProxy;
+[SupportedOSPlatform("linux")]
public static class ProxySettingLinux
{
private static readonly string _proxySetFileName = $"{Global.ProxySetLinuxShellFileName.Replace(Global.NamespaceSample, "")}.sh";
diff --git a/v2rayN/ServiceLib/Handler/SysProxy/ProxySettingOSX.cs b/v2rayN/ServiceLib/Handler/SysProxy/ProxySettingOSX.cs
index 56fbe24d..b598a06c 100644
--- a/v2rayN/ServiceLib/Handler/SysProxy/ProxySettingOSX.cs
+++ b/v2rayN/ServiceLib/Handler/SysProxy/ProxySettingOSX.cs
@@ -1,5 +1,6 @@
namespace ServiceLib.Handler.SysProxy;
+[SupportedOSPlatform("macos")]
public static class ProxySettingOSX
{
private static readonly string _proxySetFileName = $"{Global.ProxySetOSXShellFileName.Replace(Global.NamespaceSample, "")}.sh";
diff --git a/v2rayN/ServiceLib/Manager/AppManager.cs b/v2rayN/ServiceLib/Manager/AppManager.cs
index 34f6b3f1..95bef61b 100644
--- a/v2rayN/ServiceLib/Manager/AppManager.cs
+++ b/v2rayN/ServiceLib/Manager/AppManager.cs
@@ -54,6 +54,13 @@ public sealed class AppManager
public bool InitApp()
{
+ // Initialize the native SQLite provider explicitly before any database
+ // access. The call is idempotent, so it cooperates with the static
+ // constructor in SQLiteHelper. Having it here as well ensures that any
+ // future code path which reaches the database without going through
+ // SQLiteHelper still works (e.g. background services or tests).
+ SQLitePCL.Batteries_V2.Init();
+
if (Utils.HasWritePermission() == false)
{
Environment.SetEnvironmentVariable(Global.LocalAppData, "1", EnvironmentVariableTarget.Process);
diff --git a/v2rayN/ServiceLib/Manager/CoreManager.cs b/v2rayN/ServiceLib/Manager/CoreManager.cs
index 41d2343b..ce1f4a4f 100644
--- a/v2rayN/ServiceLib/Manager/CoreManager.cs
+++ b/v2rayN/ServiceLib/Manager/CoreManager.cs
@@ -8,6 +8,11 @@ public class CoreManager
private static readonly Lazy _instance = new(() => new());
public static CoreManager Instance => _instance.Value;
private Config _config;
+
+ // WindowsJobService is a Windows-only type. The field-level attribute
+ // narrows the platform context for the analyzer in every read/assign
+ // location. Actual access goes through AddProcessJob, which guards the
+ // usage with Utils.IsWindows() so the field stays null on non-Windows.
[SupportedOSPlatform("windows")]
private WindowsJobService? _processJob;
private ProcessService? _processService;