diff options
Diffstat (limited to 'src/corefx/System.Globalization.Native/icushim.cpp')
-rw-r--r-- | src/corefx/System.Globalization.Native/icushim.cpp | 226 |
1 files changed, 226 insertions, 0 deletions
diff --git a/src/corefx/System.Globalization.Native/icushim.cpp b/src/corefx/System.Globalization.Native/icushim.cpp new file mode 100644 index 0000000000..63f111b8b1 --- /dev/null +++ b/src/corefx/System.Globalization.Native/icushim.cpp @@ -0,0 +1,226 @@ +// 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. +// + +#include <dlfcn.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> + +#include "icushim.h" + +// Define pointers to all the used ICU functions +#define PER_FUNCTION_BLOCK(fn, lib) decltype(fn)* fn##_ptr; +FOR_ALL_ICU_FUNCTIONS +#undef PER_FUNCTION_BLOCK + +static void* libicuuc = nullptr; +static void* libicui18n = nullptr; + +// Version ranges to search for each of the three version components +// The rationale for major version range is that we support versions higher or +// equal to the version we are built against and less or equal to that version +// plus 20 to give us enough headspace. The ICU seems to version about twice +// a year. +static const int MinICUVersion = U_ICU_VERSION_MAJOR_NUM; +static const int MaxICUVersion = MinICUVersion + 20; +static const int MinMinorICUVersion = 1; +static const int MaxMinorICUVersion = 5; +static const int MinSubICUVersion = 1; +static const int MaxSubICUVersion = 5; + +// .x.x.x, considering the max number of decimal digits for each component +static const int MaxICUVersionStringLength = 33; + +// Get filename of an ICU library with the requested version in the name +// There are three possible cases of the version components values: +// 1. Only majorVer is not equal to -1 => result is baseFileName.majorver +// 2. Only majorVer and minorVer are not equal to -1 => result is baseFileName.majorver.minorVer +// 3. All components are not equal to -1 => result is baseFileName.majorver.minorVer.subver +void GetVersionedLibFileName(const char* baseFileName, int majorVer, int minorVer, int subVer, char* result) +{ + assert(majorVer != -1); + + int nameLen = sprintf(result, "%s.%d", baseFileName, majorVer); + + if (minorVer != -1) + { + nameLen += sprintf(result + nameLen, ".%d", minorVer); + if (subVer != -1) + { + sprintf(result + nameLen, ".%d", subVer); + } + } +} + +// Try to open the necessary ICU libraries +bool OpenICULibraries(int majorVer, int minorVer, int subVer) +{ + char libicuucName[64]; + char libicui18nName[64]; + + static_assert(sizeof("libicuuc.so") + MaxICUVersionStringLength <= sizeof(libicuucName), "The libicuucName is too small"); + GetVersionedLibFileName("libicuuc.so", majorVer, minorVer, subVer, libicuucName); + + static_assert(sizeof("libicui18n.so") + MaxICUVersionStringLength <= sizeof(libicui18nName), "The libicui18nName is too small"); + GetVersionedLibFileName("libicui18n.so", majorVer, minorVer, subVer, libicui18nName); + + libicuuc = dlopen(libicuucName, RTLD_LAZY); + if (libicuuc != nullptr) + { + libicui18n = dlopen(libicui18nName, RTLD_LAZY); + if (libicui18n == nullptr) + { + dlclose(libicuuc); + libicuuc = nullptr; + } + } + + return libicuuc != nullptr; +} + +// Select libraries using the version override specified by the CLR_ICU_VERSION_OVERRIDE +// environment variable. +// The format of the string in this variable is majorVer[.minorVer[.subVer]] (the brackets +// indicate optional parts). +bool FindLibUsingOverride(int* majorVer, int* minorVer, int* subVer) +{ + char* versionOverride = getenv("CLR_ICU_VERSION_OVERRIDE"); + if (versionOverride != nullptr) + { + int first = -1; + int second = -1; + int third = -1; + + int matches = sscanf(versionOverride, "%d.%d.%d", &first, &second, &third); + if (matches > 0) + { + if (OpenICULibraries(first, second, third)) + { + *majorVer = first; + *minorVer = second; + *subVer = third; + return true; + } + } + } + + return false; +} + +// Select the highest supported version of ICU present on the local machine +// Search for library files with names including the major and minor version. +bool FindLibWithMajorMinorVersion(int* majorVer, int* minorVer) +{ + for (int i = MaxICUVersion; i >= MinICUVersion; i--) + { + for (int j = MaxMinorICUVersion; j >= MinMinorICUVersion; j--) + { + if (OpenICULibraries(i, j, -1)) + { + *majorVer = i; + *minorVer = j; + return true; + } + } + } + + return false; +} + +// Select the highest supported version of ICU present on the local machine +// Search for library files with names including the major, minor and sub version. +bool FindLibWithMajorMinorSubVersion(int* majorVer, int* minorVer, int* subVer) +{ + for (int i = MaxICUVersion; i >= MinICUVersion; i--) + { + for (int j = MaxMinorICUVersion; j >= MinMinorICUVersion; j--) + { + for (int k = MaxSubICUVersion; k >= MinSubICUVersion; k--) + { + if (OpenICULibraries(i, j, k)) + { + *majorVer = i; + *minorVer = j; + *subVer = k; + return true; + } + } + } + } + + return false; +} + +// This function is ran at the end of dlopen for the current shared library +__attribute__((constructor)) +void InitializeICUShim() +{ + int majorVer = -1; + int minorVer = -1; + int subVer = -1; + + if (!FindLibUsingOverride(&majorVer, &minorVer, &subVer) && + !FindLibWithMajorMinorVersion(&majorVer, &minorVer) && + !FindLibWithMajorMinorSubVersion(&majorVer, &minorVer, &subVer)) + { + // No usable ICU version found + fprintf(stderr, "No usable version of the ICU libraries was found\n"); + abort(); + } + + char symbolName[128]; + char symbolVersion[MaxICUVersionStringLength + 1] = ""; + + // Find out the format of the version string added to each symbol + // First try just the unversioned symbol + if (dlsym(libicuuc, "u_strlen") == nullptr) + { + // Now try just the _majorVer added + sprintf(symbolVersion, "_%d", majorVer); + sprintf(symbolName, "u_strlen%s", symbolVersion); + if (dlsym(libicuuc, symbolName) == nullptr) + { + // Now try the _majorVer_minorVer added + sprintf(symbolVersion, "_%d_%d", majorVer, minorVer); + sprintf(symbolName, "u_strlen%s", symbolVersion); + if (dlsym(libicuuc, symbolName) == nullptr) + { + // Finally, try the _majorVer_minorVer_subVer added + sprintf(symbolVersion, "_%d_%d_%d", majorVer, minorVer, subVer); + sprintf(symbolName, "u_strlen%s", symbolVersion); + if (dlsym(libicuuc, symbolName) == nullptr) + { + fprintf(stderr, "ICU libraries use unknown symbol versioning\n"); + abort(); + } + } + } + } + + // Get pointers to all the ICU functions that are needed +#define PER_FUNCTION_BLOCK(fn, lib) \ + static_assert((sizeof(#fn) + MaxICUVersionStringLength + 1) <= sizeof(symbolName), "The symbolName is too small for symbol " #fn); \ + sprintf(symbolName, #fn "%s", symbolVersion); \ + fn##_ptr = (decltype(fn)*)dlsym(lib, symbolName); \ + if (fn##_ptr == NULL) { fprintf(stderr, "Cannot get symbol %s from " #lib "\n", symbolName); abort(); } + + FOR_ALL_ICU_FUNCTIONS +#undef PER_FUNCTION_BLOCK +} + +__attribute__((destructor)) +void ShutdownICUShim() +{ + if (libicuuc != nullptr) + { + dlclose(libicuuc); + } + + if (libicui18n != nullptr) + { + dlclose(libicui18n); + } +} |