Hello,
I am encountering an unsual problem when linking a library to macOS Frameworks: as soon as I'm linking to some specific frameworks (detailed below), dlclose will no longer work, meaning that the said library will not be unloaded from process memory.
This is causing problems, because the update functionality of my application will not work - calling dlopen again will load the "old" library.
The build details are:
- macOS High Sierra (10.13.6)
- Xcode 9.4.1
- C++ Language Dialect: C++14 [-std=c++14]
- C++ Standard Library: libc++ (LLVM C++ standard library with C++11 support)
Steps to reproduce the problem:
- Library (dylib) is linked ("Link Binary with Libraries") to: CoreFoundation and IOKit, plus the following additional frameworks - OpenDirectory, SystemConfiguration, Security, CoreServices, ApplicationServices
- Compiling works fine, of course, and then the library is loaded, using dlopen by another application (Unix / terminal app)
- After calling dlclose, the library is still reported as being loaded in the process memory
The problem will only happen when:
- the terminal app is ran from a terminal window (it cannot be reproduced when using Xcode directly to run / debug it)
- the library is linked to CoreFoundation, IOKit plus any one of the other frameworks (OpenDirectory, SystemConfiguration, Security, CoreServices, ApplicationServices)
Does anyone know what may be causing this problem?
On Linux based systems, the "-fno-gnu-unique" flag will not fix the problem, but at least offer the option to use the new / updated library. Is there, maybe, a similar flag for macOS?
Any help would be greatly appreciated.
Code snippet below:
// Returns the full path of the application executable file
const char* getApplicationPath()
{
static char s_szDir[1024] = {0};
if (!*s_szDir) {
char buffer[1024];
char *answer = getcwd(buffer, sizeof(buffer));
strncpy(s_szDir, answer, 1024);
s_szDir[1024-1] = '\0';
}
return s_szDir;
}
// prints all loaded modules in the process memory (address space)
void printLoadedModules( )
{
std::cout<<"\ndynamic libraries loaded in the process memory:";
int count = _dyld_image_count();
for(int i = 0; i < count; i++ )
{
const char * imageName = _dyld_get_image_name(i);
if( !imageName )
continue;
std::string strImageName = imageName;
if( strImageName.find( "libwa" ) != std::string::npos )
std::cout<<"\n " << strImageName;
}
}
int main(int argc, const char * argv[])
{
std::string lib_path = getApplicationPath();
lib_path += "/libtest.dylib";
// Step 1: Print loaded libraries in the process memory before dlopen()
std::cout<<"\n### Before dlopen \n";
printLoadedModules();
// Step 2: Load libtest.dylib
void * lib_handle = (void*) dlopen( lib_path.c_str() , RTLD_LOCAL );
if( !lib_handle )
{
std::cout<<"\nFailed to load libtest.dylib with error \n"<<dlerror();
exit(1);
}
// Step 3: Print loaded libraries in the process memory after dlopen()
std::cout<<"\n\n### After dlopen \n";
printLoadedModules();
// Step 4: Unload libtest.dylib
int close_code = dlclose( lib_handle );
lib_handle = nullptr;
// Step 5: Print loaded libraries in the process memory after dlclose()
std::cout<<"\n\n### After dlclose (closed code: " << close_code << ")\n";
printLoadedModules();
std::cout<<"\n\n";
return 0;
}
Thank you.