From 71be9f17732e8061d631350fd6b14b5d12e8222c Mon Sep 17 00:00:00 2001 From: Adeel Mujahid <3840695+am11@users.noreply.github.com> Date: Mon, 8 Jun 2026 15:09:42 +0300 Subject: [PATCH 1/3] Add OpenBSD support for ICU library loading OpenBSD uses ABI versioning in SONAME rather than ICU version numbering. Use unversioned dlopen calls and probe symbol versioning instead of attempting to load versioned library names. --- .../System.Globalization.Native/pal_icushim.c | 63 ++++++++++++++++++- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/src/native/libs/System.Globalization.Native/pal_icushim.c b/src/native/libs/System.Globalization.Native/pal_icushim.c index 80107560acf2d3..f40763f3a66ec0 100644 --- a/src/native/libs/System.Globalization.Native/pal_icushim.c +++ b/src/native/libs/System.Globalization.Native/pal_icushim.c @@ -230,7 +230,35 @@ static int FindICULibs(char* symbolName, char* symbolVersion) return false; } -#else // !TARGET_WINDOWS && !TARGET_OSX && !TARGET_ANDROID +#elif defined(TARGET_OPENBSD) + +// OpenBSD uses ABI versioning in SONAME, not ICU version. Use unversioned dlopen. +static int FindICULibs(char* symbolName, char* symbolVersion) +{ + libicuuc = dlopen("libicuuc.so", RTLD_LAZY); + if (libicuuc == NULL) + return false; + + libicui18n = dlopen("libicui18n.so", RTLD_LAZY); + if (libicui18n == NULL) + { + dlclose(libicuuc); + libicuuc = NULL; + return false; + } + + char symbolSuffix[SYMBOL_CUSTOM_SUFFIX_SIZE]=""; + if (FindSymbolVersion(-1, -1, -1, symbolName, symbolVersion, MaxICUVersionStringLength, symbolSuffix)) + return true; + + dlclose(libicuuc); + dlclose(libicui18n); + libicuuc = NULL; + libicui18n = NULL; + return false; +} + +#else // !TARGET_WINDOWS && !TARGET_OSX && !TARGET_ANDROID && !TARGET_OPENBSD // Version ranges to search for each of the three version components // The rationale for major version range is that we support versions higher or @@ -442,6 +470,35 @@ static void InitializeUColClonePointers(char* symbolVersion) } } +static void InitializeVariableMaxAndTopPointers(char* symbolVersion) +{ + if (ucol_setMaxVariable_ptr != NULL) + { + return; + } + +#if defined(TARGET_OSX) || defined(TARGET_ANDROID) + // OSX and Android always run against ICU version which has ucol_setMaxVariable. + // We shouldn't come here. + (void)symbolVersion; + assert(false); +#elif defined(TARGET_WINDOWS) + char symbolName[SYMBOL_NAME_SIZE]; + sprintf_s(symbolName, SYMBOL_NAME_SIZE, "ucol_setVariableTop%s", symbolVersion); + ucol_setVariableTop_ptr = (ucol_setVariableTop_func)GetProcAddress((HMODULE)libicui18n, symbolName); +#else + char symbolName[SYMBOL_NAME_SIZE]; + snprintf(symbolName, SYMBOL_NAME_SIZE, "ucol_setVariableTop%s", symbolVersion); + ucol_setVariableTop_ptr = (ucol_setVariableTop_func)dlsym(libicui18n, symbolName); +#endif // defined(TARGET_OSX) || defined(TARGET_ANDROID) + + if (ucol_setVariableTop_ptr == NULL) + { + fprintf(stderr, "Cannot get the symbols of ICU APIs ucol_setMaxVariable or ucol_setVariableTop.\n"); + abort(); + } +} + // GlobalizationNative_LoadICU // This method get called from the managed side during the globalization initialization. // This method shouldn't get called at all if we are running in globalization invariant mode @@ -458,7 +515,7 @@ int32_t GlobalizationNative_LoadICU(void) return false; } -#elif defined(TARGET_ANDROID) +#elif defined(TARGET_ANDROID) || defined(TARGET_OPENBSD) if (!FindICULibs(symbolName, symbolVersion)) { return false; @@ -479,6 +536,7 @@ int32_t GlobalizationNative_LoadICU(void) FOR_ALL_ICU_FUNCTIONS ValidateICUDataCanLoad(); + InitializeVariableMaxAndTopPointers(symbolVersion); InitializeUColClonePointers(symbolVersion); return true; @@ -535,6 +593,7 @@ void GlobalizationNative_InitICUFunctions(void* icuuc, void* icuin, const char* FOR_ALL_ICU_FUNCTIONS ValidateICUDataCanLoad(); + InitializeVariableMaxAndTopPointers(symbolVersion); InitializeUColClonePointers(symbolVersion); } From bdb146b72c2a090edb6a191e3cf9bd8c1e4597f8 Mon Sep 17 00:00:00 2001 From: Adeel Mujahid <3840695+am11@users.noreply.github.com> Date: Mon, 8 Jun 2026 15:16:55 +0300 Subject: [PATCH 2/3] . Co-authored-by: Adeel Mujahid <3840695+am11@users.noreply.github.com> --- .../System.Globalization.Native/pal_icushim.c | 31 ------------------- 1 file changed, 31 deletions(-) diff --git a/src/native/libs/System.Globalization.Native/pal_icushim.c b/src/native/libs/System.Globalization.Native/pal_icushim.c index f40763f3a66ec0..d14f61dca7167f 100644 --- a/src/native/libs/System.Globalization.Native/pal_icushim.c +++ b/src/native/libs/System.Globalization.Native/pal_icushim.c @@ -470,35 +470,6 @@ static void InitializeUColClonePointers(char* symbolVersion) } } -static void InitializeVariableMaxAndTopPointers(char* symbolVersion) -{ - if (ucol_setMaxVariable_ptr != NULL) - { - return; - } - -#if defined(TARGET_OSX) || defined(TARGET_ANDROID) - // OSX and Android always run against ICU version which has ucol_setMaxVariable. - // We shouldn't come here. - (void)symbolVersion; - assert(false); -#elif defined(TARGET_WINDOWS) - char symbolName[SYMBOL_NAME_SIZE]; - sprintf_s(symbolName, SYMBOL_NAME_SIZE, "ucol_setVariableTop%s", symbolVersion); - ucol_setVariableTop_ptr = (ucol_setVariableTop_func)GetProcAddress((HMODULE)libicui18n, symbolName); -#else - char symbolName[SYMBOL_NAME_SIZE]; - snprintf(symbolName, SYMBOL_NAME_SIZE, "ucol_setVariableTop%s", symbolVersion); - ucol_setVariableTop_ptr = (ucol_setVariableTop_func)dlsym(libicui18n, symbolName); -#endif // defined(TARGET_OSX) || defined(TARGET_ANDROID) - - if (ucol_setVariableTop_ptr == NULL) - { - fprintf(stderr, "Cannot get the symbols of ICU APIs ucol_setMaxVariable or ucol_setVariableTop.\n"); - abort(); - } -} - // GlobalizationNative_LoadICU // This method get called from the managed side during the globalization initialization. // This method shouldn't get called at all if we are running in globalization invariant mode @@ -536,7 +507,6 @@ int32_t GlobalizationNative_LoadICU(void) FOR_ALL_ICU_FUNCTIONS ValidateICUDataCanLoad(); - InitializeVariableMaxAndTopPointers(symbolVersion); InitializeUColClonePointers(symbolVersion); return true; @@ -593,7 +563,6 @@ void GlobalizationNative_InitICUFunctions(void* icuuc, void* icuin, const char* FOR_ALL_ICU_FUNCTIONS ValidateICUDataCanLoad(); - InitializeVariableMaxAndTopPointers(symbolVersion); InitializeUColClonePointers(symbolVersion); } From fd4f9edb9ed132f5e48b2da65197031ac51b2431 Mon Sep 17 00:00:00 2001 From: Adeel Mujahid <3840695+am11@users.noreply.github.com> Date: Mon, 8 Jun 2026 15:32:31 +0300 Subject: [PATCH 3/3] . --- src/native/libs/System.Globalization.Native/pal_icushim.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/native/libs/System.Globalization.Native/pal_icushim.c b/src/native/libs/System.Globalization.Native/pal_icushim.c index d14f61dca7167f..1ed7de84c12863 100644 --- a/src/native/libs/System.Globalization.Native/pal_icushim.c +++ b/src/native/libs/System.Globalization.Native/pal_icushim.c @@ -29,7 +29,7 @@ FOR_ALL_ICU_FUNCTIONS #define SYMBOL_NAME_SIZE (128 + SYMBOL_CUSTOM_SUFFIX_SIZE) #define MaxICUVersionStringWithSuffixLength (MaxICUVersionStringLength + SYMBOL_CUSTOM_SUFFIX_SIZE) -#if defined(TARGET_WINDOWS) || defined(TARGET_OSX) || defined(TARGET_ANDROID) +#if defined(TARGET_WINDOWS) || defined(TARGET_OSX) || defined(TARGET_ANDROID) || defined(TARGET_OPENBSD) #define MaxICUVersionStringLength 33