//| Copyright: (C) 2020-2024 Kevin Larke //| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file. #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" #include "cwTest.h" #include "cwObject.h" #include "cwMem.h" #include "cwFileSys.h" #include "cwLib.h" #ifdef OS_LINUX #include typedef void* dynLibH_t; bool _cmLibIsNull( dynLibH_t lh ) { return lh == nullptr; }; const char* _cmLibSysError() { const char* msg = dlerror(); if( msg == nullptr ) msg = ""; return msg; } dynLibH_t _cmLibOpen( const char* libFn ) { return dlopen(libFn,RTLD_LAZY); } const char* _cmLibClose( dynLibH_t* lH ) { if( *lH != nullptr ) { if( dlclose(*lH) == 0 ) *lH = nullptr; else return dlerror(); } return nullptr; } void* _cmLibSym( dynLibH_t h, const char* symLabel ) { return dlsym(h,symLabel); } #endif namespace cw { namespace lib { typedef void* dynLibH_t; typedef struct node_str { char* fn; // NULL for available nodes unsigned id; // kInvalidId for available nodes dynLibH_t dynLibH; // The platform dependent library handle struct node_str* link; } node_t; typedef struct lib_str { node_t* list; // list of open libraries unsigned id; // next library id } lib_t; lib_t* _handleToPtr( handle_t h ) { return handleToPtr(h); } // Given a library id return the associated node node_t* _libIdToNode( lib_t* p, unsigned libId ) { node_t* np = p->list; while( np != nullptr ) { if( np->id == libId ) return np; np = np->link; } return nullptr; } // Close a library and make it's node available for reuse rc_t _closeLib( node_t* np ) { rc_t rc = kOkRC; const char* errMsg = nullptr; // tell the system to close the library if((errMsg = _cmLibClose( &np->dynLibH )) != nullptr ) rc = cwLogError(kInvalidOpRC,"Library %s close failed. Error:%s.", np->fn, errMsg ); else { // mark the node as available mem::release(np->fn); np->id = kInvalidId; } return rc; } // Finalize the library manager rc_t _finalize( lib_t* p ) { rc_t rc = kOkRC; node_t* np = p->list; while( np!=nullptr ) { node_t* n0p = np->link; // close the node's library rc_t rc0 = _closeLib( np ); // store the error code if( rc0 != kOkRC ) rc = rc0; // release the node mem::release(np); np = n0p; } mem::release(p); return rc; } } } cw::rc_t cw::lib::initialize( handle_t& h, const char* dirStr ) { rc_t rc; if((rc = finalize(h)) != kOkRC ) return rc; lib_t* p = mem::allocZ(); h.set(p); return rc; } cw::rc_t cw::lib::finalize( handle_t& h ) { rc_t rc = kOkRC; if( !h.isValid() ) return rc; lib_t* p = _handleToPtr(h); if((rc = _finalize(p)) != kOkRC ) return rc; h.clear(); return rc; } cw::rc_t cw::lib::open( handle_t h, const char* fn, unsigned& libIdRef ) { rc_t rc = kOkRC; lib_t* p = _handleToPtr(h); dynLibH_t lH = _cmLibOpen(fn); node_t* np = p->list; unsigned idx = 0; libIdRef = kInvalidId; if( _cmLibIsNull(lH) ) { // There is apparently no way to get an error code which indicates that the // file load attempt failed because the file was not a shared library - // which should not generate an error message - therefore // we must match the end of the the error string returned by dlerror() with // 'invalid ELF header'. const char* errMsg = _cmLibSysError(); const char* s = "invalid ELF header"; unsigned sn = strlen(s); unsigned mn = strlen(errMsg); // Did this load fail because the file was not a shared library? if( errMsg!=nullptr && mn>sn && strcmp(errMsg+mn-sn,s)==0 ) rc = cwLogError(kOpenFailRC,"Library load failed. No error message." ); // signal error but no error message else rc = cwLogError(kOpenFailRC,"Library load failed. System Message: %s", errMsg ); return rc; } // find an available node while( np != nullptr ) { if( np->fn == nullptr ) break; np = np->link; ++idx; } // no available node was found - allocate a new one if( np == nullptr ) { np = mem::allocZ(1); np->link = p->list; p->list = np; } // initialize the node np->fn = mem::duplStr(fn); np->dynLibH = lH; np->id = p->id++; libIdRef = np->id; return idx; } cw::rc_t cw::lib::close( handle_t h, unsigned libId ) { lib_t* p = _handleToPtr(h); node_t* np; // locate the library to close if((np = _libIdToNode(p,libId)) == nullptr ) return cwLogError(kInvalidIdRC,"Library close failed. The library with id:%i not found.",libId); return _closeLib(np); } void* cw::lib::symbol( handle_t h, unsigned libId, const char* symName ) { void* f; lib_t* p = _handleToPtr(h); node_t* np = _libIdToNode(p,libId); if( (np == NULL) || _cmLibIsNull(np->dynLibH) ) { cwLogError(kInvalidArgRC,"The library id %i is not valid or the library is closed.",libId); return NULL; } if((f = _cmLibSym(np->dynLibH,symName)) == NULL) { cwLogError(kInvalidArgRC,"The dynamic symbol '%s' was not found. System Message: %s", cwStringNullGuard(symName), _cmLibSysError()); return NULL; } return f; } cw::rc_t cw::lib::scan( handle_t h, const char* dirStr ) { rc_t rc = kOkRC; unsigned dirEntryN = 0; filesys::dirEntry_t* de = dirEntries( dirStr, filesys::kFileFsFl, &dirEntryN ); unsigned libId; for(unsigned i=0; ilist; for(; np!=nullptr; np=np->link) ++n; return n; } unsigned cw::lib::indexToId( handle_t h, unsigned idx ) { lib_t* p = _handleToPtr(h); unsigned n = 0; node_t* np = p->list; for(; np!=nullptr; np=np->link) if( n == idx ) return np->id; cwLogError(kInvalidArgRC, "The library index %i is not valid.",idx); return kInvalidId; } const char* cw::lib::name( handle_t h, unsigned id ) { lib_t* p = _handleToPtr(h); node_t* np; if((np = _libIdToNode(p,id)) == nullptr ) { cwLogError(kInvalidArgRC,"The library associated with the the id:%i could not be found.",id); return nullptr; } return np->fn; }