8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-22 20:03:02 +01:00

Fix ICU loading in Android.

This commit is contained in:
Adriano dos Santos Fernandes 2023-02-04 13:55:06 -03:00
parent 85b84da6df
commit c969aa0575
6 changed files with 245 additions and 236 deletions

View File

@ -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;
}

View File

@ -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 <typename T> 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.

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 <typename T> void getEntryPoint(const char* name, ModuleLoader::Module* module, T& ptr,
template <typename T> 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<UnicodeUtil::ICUModules> 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<string>& 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);

View File

@ -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,