diff --git a/src/common/os/darwin/mod_loader.cpp b/src/common/os/darwin/mod_loader.cpp index 8c0eae6d18..463209174b 100644 --- a/src/common/os/darwin/mod_loader.cpp +++ b/src/common/os/darwin/mod_loader.cpp @@ -49,7 +49,8 @@ public: {} ~DlfcnModule(); - void* findSymbol (ISC_STATUS*, const Firebird::string&); + void* findSymbol(ISC_STATUS*, const Firebird::string&) override; + bool getRealPath(const Firebird::string& anySymbol, Firebird::PathName& path) override; private: void* module; @@ -155,4 +156,30 @@ void* DlfcnModule::findSymbol(ISC_STATUS* status, const Firebird::string& symNam return result; } +bool DlfcnModule::getRealPath(const Firebird::string& anySymbol, Firebird::PathName& path) +{ +#ifdef HAVE_DLADDR + void* symbolPtr = dlsym(module, anySymbol.c_str()); + if (!symbolPtr) + symbolPtr = dlsym(module, ('_' + anySymbol).c_str()); + + if (symbolPtr) + { + Dl_info info; + if (dladdr(symbolPtr, &info)) + { + char buffer[PATH_MAX]; + + if (realpath(info.dli_fname, buffer)) + { + path = buffer; + return true; + } + } + } +#endif // HAVE_DLADDR + + path.clear(); + return false; +} diff --git a/src/common/os/mod_loader.h b/src/common/os/mod_loader.h index 1c27966d91..09ee853e76 100644 --- a/src/common/os/mod_loader.h +++ b/src/common/os/mod_loader.h @@ -62,6 +62,8 @@ public: **/ virtual void* findSymbol(ISC_STATUS*, const Firebird::string&) = 0; + virtual bool getRealPath(const Firebird::string& anySymbol, Firebird::PathName& path) = 0; + template T& findSymbol(ISC_STATUS* status, const Firebird::string& symbol, T& ptr) { return (ptr = (T)(findSymbol(status, symbol))); @@ -72,10 +74,6 @@ public: const Firebird::PathName fileName; -#ifdef LINUX - virtual bool getRealPath(Firebird::PathName& path) = 0; -#endif - protected: /// The constructor is protected so normal code can't allocate instances /// of the class, but the class itself is still able to be subclassed. diff --git a/src/common/os/posix/mod_loader.cpp b/src/common/os/posix/mod_loader.cpp index ac0d61445c..5bf928e7ad 100644 --- a/src/common/os/posix/mod_loader.cpp +++ b/src/common/os/posix/mod_loader.cpp @@ -51,7 +51,7 @@ public: void* findSymbol(ISC_STATUS*, const Firebird::string&) override; - bool getRealPath(Firebird::PathName& path) override; + bool getRealPath(const Firebird::string& anySymbol, Firebird::PathName& path) override; private: void* module; @@ -157,40 +157,7 @@ DlfcnModule::DlfcnModule(MemoryPool& pool, const Firebird::PathName& aFileName, module(m), realPath(pool) { -#ifdef HAVE_DLINFO - char b[PATH_MAX]; - -#ifdef HAVE_RTLD_DI_ORIGIN - if (dlinfo(module, RTLD_DI_ORIGIN, b) == 0) - { - realPath = b; - realPath += '/'; - realPath += fileName; - - if (realpath(realPath.c_str(), b)) - { - realPath = b; - return; - } - } -#endif - -#ifdef HAVE_RTLD_DI_LINKMAP - struct link_map* lm; - if (dlinfo(module, RTLD_DI_LINKMAP, &lm) == 0) - { - if (realpath(lm->l_name, b)) - { - realPath = b; - return; - } - } -#endif - -#endif - - // Error getting real path. - realPath.clear(); + getRealPath("", realPath); } DlfcnModule::~DlfcnModule() @@ -202,6 +169,7 @@ DlfcnModule::~DlfcnModule() void* DlfcnModule::findSymbol(ISC_STATUS* status, const Firebird::string& symName) { void* result = dlsym(module, symName.c_str()); + if (!result) { Firebird::string newSym = '_' + symName; @@ -253,11 +221,69 @@ void* DlfcnModule::findSymbol(ISC_STATUS* status, const Firebird::string& symNam return result; } -bool DlfcnModule::getRealPath(Firebird::PathName& path) +bool DlfcnModule::getRealPath(const Firebird::string& anySymbol, Firebird::PathName& path) { - if (realPath.isEmpty()) - return false; + if (realPath.hasData()) + { + path = realPath; + return true; + } - path = realPath; - return true; + char buffer[PATH_MAX]; + +#ifdef HAVE_DLINFO +#ifdef HAVE_RTLD_DI_ORIGIN + if (dlinfo(module, RTLD_DI_ORIGIN, buffer) == 0) + { + path = buffer; + path += '/'; + path += fileName; + + if (realpath(path.c_str(), buffer)) + { + path = buffer; + return true; + } + } +#endif + +#ifdef HAVE_RTLD_DI_LINKMAP + struct link_map* lm; + if (dlinfo(module, RTLD_DI_LINKMAP, &lm) == 0) + { + if (realpath(lm->l_name, buffer)) + { + path = buffer; + return true; + } + } +#endif + +#endif + +#ifdef HAVE_DLADDR + if (anySymbol.hasData()) + { + void* symbolPtr = dlsym(module, anySymbol.c_str()); + + if (!symbolPtr) + symbolPtr = dlsym(module, ('_' + anySymbol).c_str()); + + if (symbolPtr) + { + Dl_info info; + if (dladdr(symbolPtr, &info)) + { + if (realpath(info.dli_fname, buffer)) + { + path = buffer; + return true; + } + } + } + } +#endif // HAVE_DLADDR + + path.clear(); + return false; } diff --git a/src/common/os/win32/mod_loader.cpp b/src/common/os/win32/mod_loader.cpp index 5173b00daa..c4418feceb 100644 --- a/src/common/os/win32/mod_loader.cpp +++ b/src/common/os/win32/mod_loader.cpp @@ -172,7 +172,9 @@ public: ~Win32Module(); - void *findSymbol(ISC_STATUS* status, const string&); + void* findSymbol(ISC_STATUS* status, const string&) override; + + bool getRealPath(const string& anySymbol, PathName& path) override; private: const HMODULE module; @@ -272,3 +274,16 @@ void* Win32Module::findSymbol(ISC_STATUS* status, const string& symName) return (void*) result; } + +bool Win32Module::getRealPath(const string& /*anySymbol*/, PathName& path) +{ + char moduleFileName[MAX_PATH]; + + if (GetModuleFileName(module, moduleFileName, sizeof(moduleFileName)) != 0) + { + path = moduleFileName; + return true; + } + + return false; +} diff --git a/src/common/unicode_util.cpp b/src/common/unicode_util.cpp index 97e020590e..585d39cae4 100644 --- a/src/common/unicode_util.cpp +++ b/src/common/unicode_util.cpp @@ -70,6 +70,10 @@ const char* const inTemplate = "libicui18n.so.%s"; const char* const ucTemplate = "libicuuc.so.%s"; #endif +#ifdef ANDROID +const char* const dataTemplate = "libicudata.so.%s"; +#endif + // encapsulate ICU library struct BaseICU { @@ -80,22 +84,22 @@ private: public: BaseICU(int aMajorVersion, int aMinorVersion) : majorVersion(aMajorVersion), - minorVersion(aMinorVersion) + minorVersion(aMinorVersion), + isSystem(aMajorVersion == 0) { } + ModuleLoader::Module* formatAndLoad(const char* templateName); void initialize(ModuleLoader::Module* module); - template void getEntryPoint(const char* name, ModuleLoader::Module* module, T& ptr, + template string getEntryPoint(const char* name, ModuleLoader::Module* module, T& ptr, bool optional = false) { // System-wide ICU have no version number at entries names if (!majorVersion) { - fb_assert(false); // ASF: I don't think this code path is correct. - if (module->findSymbol(NULL, name, ptr)) - return; + return name; } else { @@ -111,63 +115,132 @@ public: { symbol.printf(pattern, name, majorVersion, minorVersion); if (module->findSymbol(NULL, symbol, ptr)) - return; + return symbol; } } if (!optional) (Arg::Gds(isc_icu_entrypoint) << name).raise(); + + return ""; } int majorVersion; int minorVersion; + bool isSystem; + void (U_EXPORT2* u_getVersion) (UVersionInfo versionArray) = nullptr; }; +ModuleLoader::Module* BaseICU::formatAndLoad(const char* templateName) +{ + ModuleLoader::Module* module = nullptr; + + // System-wide ICU have no version number at file names + if (isSystem) + { + PathName filename; + filename.printf(templateName, ""); + filename.rtrim("."); + + //gds__log("ICU: link %s", filename.c_str()); + + module = ModuleLoader::fixAndLoadModule(NULL, filename); + } + else + { + fb_assert(majorVersion); + + // ICU has several schemas for placing version into file name + const char* const patterns[] = + { +#ifdef WIN_NT + "%d", +#endif + "%d_%d", + "%d.%d", + "%d%d" + }; + + PathName s, filename; + for (auto pattern : patterns) + { + s.printf(pattern, majorVersion, minorVersion); + filename.printf(templateName, s.c_str()); + + module = ModuleLoader::fixAndLoadModule(NULL, filename); + if (module) + break; + } + +#ifndef WIN_NT + // There is no sence to try pattern "%d" for different minor versions + // ASF: In Windows ICU 63.1 libraries use 63.dll suffix. This is handled in 'patterns' above. + if (!module && minorVersion == 0) + { + s.printf("%d", majorVersion); + filename.printf(templateName, s.c_str()); + + module = ModuleLoader::fixAndLoadModule(NULL, filename); + } +#endif + } + + return module; +} + void BaseICU::initialize(ModuleLoader::Module* module) { + getEntryPoint("u_getVersion", module, u_getVersion); + + UVersionInfo versionInfo; + u_getVersion(versionInfo); + + if (!isSystem && (versionInfo[0] != majorVersion || versionInfo[1] != minorVersion)) + { + string err; + err.printf( + "Wrong version of icu module: loaded %d.%d, expected %d.%d", + (int) versionInfo[0], (int) versionInfo[1], + this->majorVersion, this->minorVersion); + + (Arg::Gds(isc_random) << Arg::Str(err)).raise(); + } + + majorVersion = versionInfo[0]; + minorVersion = versionInfo[1]; + void (U_EXPORT2 *uInit)(UErrorCode* status); void (U_EXPORT2 *uSetTimeZoneFilesDirectory)(const char* path, UErrorCode* status); void (U_EXPORT2 *uSetDataDirectory)(const char* directory); getEntryPoint("u_init", module, uInit, true); getEntryPoint("u_setTimeZoneFilesDirectory", module, uSetTimeZoneFilesDirectory, true); - getEntryPoint("u_setDataDirectory", module, uSetDataDirectory, true); + const auto uSetDataDirectorySymbolName = getEntryPoint("u_setDataDirectory", module, uSetDataDirectory, true); -#if defined(WIN_NT) || defined(DARWIN) || defined(ANDROID) if (uSetDataDirectory) { - // call uSetDataDirectory only if .dat file is exists at same folder - // as the loaded module + // call uSetDataDirectory only if .dat file exists at same folder as the loaded module + + PathName modulePathName; + if (!module->getRealPath(uSetDataDirectorySymbolName.c_str(), modulePathName)) + modulePathName = module->fileName; PathName path, file, fullName; - PathUtils::splitLastComponent(path, file, module->fileName); + PathUtils::splitLastComponent(path, file, modulePathName); -#ifdef WIN_NT - // icuucXX.dll -> icudtXX.dll - file.replace(3, 2, "dt"); - - // icudtXX.dll -> icudtXXl.dat - const FB_SIZE_T pos = file.find_last_of('.'); - file.erase(pos); - file.append("l.dat"); + file.printf("icudt%u%c.dat", majorVersion, +#ifdef WORDS_BIGENDIAN + 'b' #else - // libicuuc.so.XX -> icudtXX - const FB_SIZE_T pos = file.find_last_of('.'); - if (pos > 0 && pos != file.npos) - { - file.replace(0, pos + 1, "icudt"); - } - - // icudtXX -> icudtXXl.dat - file += "l.dat"; + 'l' #endif + ); PathUtils::concatPath(fullName, path, file); if (PathUtils::canAccess(fullName, 0)) uSetDataDirectory(path.c_str()); } -#endif if (uInit) { @@ -195,10 +268,6 @@ void BaseICU::initialize(ModuleLoader::Module* module) namespace Jrd { -static ModuleLoader::Module* formatAndLoad(const char* templateName, - int& majorVersion, int& minorVersion); - - // encapsulate ICU collations libraries struct UnicodeUtil::ICU : public BaseICU { @@ -316,7 +385,16 @@ private: ImplementConversionICU(int aMajorVersion, int aMinorVersion) : BaseICU(aMajorVersion, aMinorVersion) { - module = formatAndLoad(ucTemplate, this->majorVersion, this->minorVersion); +#ifdef ANDROID + auto dataModule = formatAndLoad(dataTemplate); +#endif + + module = formatAndLoad(ucTemplate); + +#ifdef ANDROID + delete dataModule; +#endif + if (!module) return; @@ -325,7 +403,6 @@ private: getEntryPoint("ucnv_open", module, ucnv_open); getEntryPoint("ucnv_close", module, ucnv_close); getEntryPoint("ucnv_fromUChars", module, ucnv_fromUChars); - getEntryPoint("u_getVersion", module, u_getVersion); getEntryPoint("u_tolower", module, u_tolower); getEntryPoint("u_toupper", module, u_toupper); getEntryPoint("u_strCompare", module, u_strCompare); @@ -344,19 +421,10 @@ private: getEntryPoint("u_strcmp", module, ustrcmp); - inModule = formatAndLoad(inTemplate, aMajorVersion, aMinorVersion); + inModule = formatAndLoad(inTemplate); if (!inModule) return; - if (aMajorVersion != this->majorVersion || aMinorVersion != this->minorVersion) - { - string err; - err.printf("Wrong version of IN icu module: loaded %d.%d, expected %d.%d", - aMajorVersion, aMinorVersion, this->majorVersion, this->minorVersion); - - (Arg::Gds(isc_random) << Arg::Str(err)).raise(); - } - getEntryPoint("ucal_getTZDataVersion", inModule, ucalGetTZDataVersion); getEntryPoint("ucal_getDefaultTimeZone", inModule, ucalGetDefaultTimeZone); getEntryPoint("ucal_open", inModule, ucalOpen); @@ -384,11 +452,8 @@ public: if (o) { - UVersionInfo versionInfo; - o->u_getVersion(versionInfo); - - o->vMajor = versionInfo[0]; - o->vMinor = versionInfo[1]; + o->vMajor = o->majorVersion; + o->vMinor = o->minorVersion; } return o; @@ -430,128 +495,6 @@ static const char* const COLL_30_VERSION = "41.128.4.4"; // ICU 3.0 collator ver static GlobalPtr icuModules; -#ifdef LINUX -static bool extractVersionFromPath(const PathName& realPath, int& major, int& minor) -{ - major = 0; - minor = 0; - int mult = 1; - - const FB_SIZE_T len = realPath.length(); - const char* buf = realPath.begin(); - - bool dot = false; - for (const char* p = buf + len - 1; p >= buf; p--) - { - if (*p >= '0' && *p < '9') - { - major += (*p - '0') * mult; - mult *= 10; - } - else if (*p == '.' && !dot) - { - dot = true; - minor = major; - major = 0; - mult = 1; - } - else - { - break; - } - } - - if (minor && !major) - { - major = minor; - minor = 0; - } - - return major != 0; -} -#endif - -static ModuleLoader::Module* formatAndLoad(const char* templateName, - int& majorVersion, int& minorVersion) -{ -#ifdef ANDROID - static ModuleLoader::Module* dat = ModuleLoader::loadModule(NULL, - fb_utils::getPrefix(Firebird::IConfigManager::DIR_LIB, "libicudata.so")); - - Firebird::PathName newName = fb_utils::getPrefix(Firebird::IConfigManager::DIR_LIB, templateName); - templateName = newName.c_str(); -#endif - - ModuleLoader::Module* module = nullptr; - - // System-wide ICU have no version number at file names - if (!majorVersion) - { - PathName filename; - filename.printf(templateName, ""); - filename.rtrim("."); - - //gds__log("ICU: link %s", filename.c_str()); - - module = ModuleLoader::fixAndLoadModule(NULL, filename); - -#ifdef LINUX - // try to resolve symlinks and extract version numbers from suffix - PathName realPath; - if (module && module->getRealPath(realPath)) - { - //gds__log("ICU: module name %s, real path %s", module->fileName.c_str(), realPath.c_str()); - - int major, minor; - if (extractVersionFromPath(realPath, major, minor)) - { - //gds__log("ICU: extracted version %d.%d", major, minor); - majorVersion = major; - minorVersion = minor; - } - } -#endif - } - else - { - // ICU has several schemas for placing version into file name - const char* const patterns[] = - { -#ifdef WIN_NT - "%d", -#endif - "%d_%d", - "%d.%d", - "%d%d" - }; - - PathName s, filename; - for (auto pattern : patterns) - { - s.printf(pattern, majorVersion, minorVersion); - filename.printf(templateName, s.c_str()); - - module = ModuleLoader::fixAndLoadModule(NULL, filename); - if (module) - break; - } - -#ifndef WIN_NT - // There is no sence to try pattern "%d" for different minor versions - // ASF: In Windows ICU 63.1 libraries use 63.dll suffix. This is handled in 'patterns' above. - if (!module && minorVersion == 0) - { - s.printf("%d", majorVersion); - filename.printf(templateName, s.c_str()); - - module = ModuleLoader::fixAndLoadModule(NULL, filename); - } -#endif - } - - return module; -} - static void getVersions(const string& configInfo, ObjectsArray& versions) { @@ -1220,7 +1163,16 @@ UnicodeUtil::ICU* UnicodeUtil::loadICU(const string& icuVersion, const string& c icu = FB_NEW_POOL(*getDefaultMemoryPool()) ICU(majorVersion, minorVersion); - icu->ucModule = formatAndLoad(ucTemplate, icu->majorVersion, icu->minorVersion); +#ifdef ANDROID + auto dataModule = icu->formatAndLoad(dataTemplate); +#endif + + icu->ucModule = icu->formatAndLoad(ucTemplate); + +#ifdef ANDROID + delete dataModule; +#endif + if (!icu->ucModule) { gds__log("failed to load UC icu module version %s", configVersion.c_str()); @@ -1228,26 +1180,18 @@ UnicodeUtil::ICU* UnicodeUtil::loadICU(const string& icuVersion, const string& c continue; } - icu->inModule = formatAndLoad(inTemplate, majorVersion, minorVersion); - if (!icu->inModule) - { - gds__log("failed to load IN icu module version %s", configVersion.c_str()); - delete icu; - continue; - } - - if (icu->majorVersion != majorVersion || icu->minorVersion != minorVersion) - { - gds__log("Wrong version of IN icu module: loaded %d.%d, expected %d.%d", - majorVersion, minorVersion, icu->majorVersion, icu->minorVersion); - delete icu; - continue; - } - try { icu->initialize(icu->ucModule); + icu->inModule = icu->formatAndLoad(inTemplate); + if (!icu->inModule) + { + gds__log("failed to load IN icu module version %s", configVersion.c_str()); + delete icu; + continue; + } + icu->getEntryPoint("u_versionToString", icu->ucModule, icu->uVersionToString); icu->getEntryPoint("uloc_countAvailable", icu->ucModule, icu->ulocCountAvailable); icu->getEntryPoint("uloc_getAvailable", icu->ucModule, icu->ulocGetAvailable); diff --git a/src/common/unicode_util.h b/src/common/unicode_util.h index 22c00ba2c1..09de723045 100644 --- a/src/common/unicode_util.h +++ b/src/common/unicode_util.h @@ -58,7 +58,6 @@ public: const UChar *src, int32_t srcLength, UErrorCode *pErrorCode); - void (U_EXPORT2* u_getVersion) (UVersionInfo versionArray); UChar32 (U_EXPORT2* u_tolower) (UChar32 c); UChar32 (U_EXPORT2* u_toupper) (UChar32 c); int32_t (U_EXPORT2* u_strCompare) (const UChar* s1, int32_t length1,