[cosmetics] update date in GPL header
[vuplus_xbmc] / xbmc / cores / DllLoader / DllLoaderContainer.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://www.xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include "DllLoaderContainer.h"
22 #ifdef _LINUX
23 #include "SoLoader.h"
24 #endif
25 #ifdef _WIN32
26 #include "Win32DllLoader.h"
27 #endif
28 #include "DllLoader.h"
29 #include "dll_tracker.h" // for python unload hack
30 #include "filesystem/File.h"
31 #include "utils/URIUtils.h"
32 #include "utils/StringUtils.h"
33 #include "utils/log.h"
34 #include "URL.h"
35
36 #define ENV_PARTIAL_PATH "special://xbmcbin/system/;" \
37                  "special://xbmcbin/system/players/mplayer/;" \
38                  "special://xbmcbin/system/players/dvdplayer/;" \
39                  "special://xbmcbin/system/players/paplayer/;" \
40                  "special://xbmcbin/system/python/;" \
41                  "special://xbmc/system/;" \
42                  "special://xbmc/system/players/mplayer/;" \
43                  "special://xbmc/system/players/dvdplayer/;" \
44                  "special://xbmc/system/players/paplayer/;" \
45                  "special://xbmc/system/python/"
46
47 #if defined(TARGET_DARWIN)
48 #define ENV_PATH ENV_PARTIAL_PATH \
49                  ";special://frameworks/"
50 #else
51 #define ENV_PATH ENV_PARTIAL_PATH
52 #endif
53
54 //Define this to get loggin on all calls to load/unload of dlls
55 //#define LOGALL
56
57
58 using namespace XFILE;
59
60 LibraryLoader* DllLoaderContainer::m_dlls[64] = {};
61 int        DllLoaderContainer::m_iNrOfDlls = 0;
62 bool       DllLoaderContainer::m_bTrack = true;
63
64 void DllLoaderContainer::Clear()
65 {
66 }
67
68 HMODULE DllLoaderContainer::GetModuleAddress(const char* sName)
69 {
70   return (HMODULE)GetModule(sName);
71 }
72
73 LibraryLoader* DllLoaderContainer::GetModule(const char* sName)
74 {
75   for (int i = 0; m_dlls[i] != NULL && i < m_iNrOfDlls; i++)
76   {
77     if (stricmp(m_dlls[i]->GetName(), sName) == 0) return m_dlls[i];
78     if (!m_dlls[i]->IsSystemDll() && stricmp(m_dlls[i]->GetFileName(), sName) == 0) return m_dlls[i];
79   }
80
81   return NULL;
82 }
83
84 LibraryLoader* DllLoaderContainer::GetModule(HMODULE hModule)
85 {
86   for (int i = 0; m_dlls[i] != NULL && i < m_iNrOfDlls; i++)
87   {
88     if (m_dlls[i]->GetHModule() == hModule) return m_dlls[i];
89   }
90   return NULL;
91 }
92
93 LibraryLoader* DllLoaderContainer::LoadModule(const char* sName, const char* sCurrentDir/*=NULL*/, bool bLoadSymbols/*=false*/)
94 {
95   LibraryLoader* pDll=NULL;
96
97   if (IsSystemDll(sName))
98   {
99     pDll = GetModule(sName);
100   }
101   else if (sCurrentDir)
102   {
103     CStdString strPath=sCurrentDir;
104     strPath+=sName;
105     pDll = GetModule(strPath.c_str());
106   }
107
108   if (!pDll)
109   {
110     pDll = GetModule(sName);
111   }
112
113   if (!pDll)
114   {
115     pDll = FindModule(sName, sCurrentDir, bLoadSymbols);
116   }
117   else if (!pDll->IsSystemDll())
118   {
119     pDll->IncrRef();
120
121 #ifdef LOGALL
122     CLog::Log(LOGDEBUG, "Already loaded Dll %s at 0x%x", pDll->GetFileName(), pDll);
123 #endif
124
125   }
126
127   return pDll;
128 }
129
130 LibraryLoader* DllLoaderContainer::FindModule(const char* sName, const char* sCurrentDir, bool bLoadSymbols)
131 {
132   if (URIUtils::IsInArchive(sName))
133   {
134     CURL url(sName);
135     CStdString newName = "special://temp/";
136     newName += url.GetFileName();
137     CFile::Cache(sName, newName);
138     return FindModule(newName, sCurrentDir, bLoadSymbols);
139   }
140
141   if (CURL::IsFullPath(sName))
142   { //  Has a path, just try to load
143     return LoadDll(sName, bLoadSymbols);
144   }
145 #ifdef _LINUX
146   else if (strcmp(sName, "xbmc.so") == 0)
147     return LoadDll(sName, bLoadSymbols);
148 #endif
149   else if (sCurrentDir)
150   { // in the path of the parent dll?
151     CStdString strPath=sCurrentDir;
152     strPath+=sName;
153
154     if (CFile::Exists(strPath))
155       return LoadDll(strPath.c_str(), bLoadSymbols);
156   }
157
158   //  in environment variable?
159   CStdStringArray vecEnv;
160
161 #if defined(TARGET_ANDROID)
162   CStdString systemLibs = getenv("XBMC_ANDROID_SYSTEM_LIBS");
163   StringUtils::SplitString(systemLibs, ":", vecEnv);
164   CStdString localLibs = getenv("XBMC_ANDROID_LIBS");
165   vecEnv.insert(vecEnv.begin(),localLibs);
166 #else
167   StringUtils::SplitString(ENV_PATH, ";", vecEnv);
168 #endif
169   LibraryLoader* pDll = NULL;
170
171   for (int i=0; i<(int)vecEnv.size(); ++i)
172   {
173     CStdString strPath=vecEnv[i];
174     URIUtils::AddSlashAtEnd(strPath);
175
176 #ifdef LOGALL
177     CLog::Log(LOGDEBUG, "Searching for the dll %s in directory %s", sName, strPath.c_str());
178 #endif
179
180     strPath+=sName;
181
182     // Have we already loaded this dll
183     if ((pDll = GetModule(strPath.c_str())) != NULL)
184       return pDll;
185
186     if (CFile::Exists(strPath))
187       return LoadDll(strPath.c_str(), bLoadSymbols);
188   }
189
190   // can't find it in any of our paths - could be a system dll
191   if ((pDll = LoadDll(sName, bLoadSymbols)) != NULL)
192     return pDll;
193
194   CLog::Log(LOGDEBUG, "Dll %s was not found in path", sName);
195   return NULL;
196 }
197
198 void DllLoaderContainer::ReleaseModule(LibraryLoader*& pDll)
199 {
200   if (!pDll)
201     return;
202   if (pDll->IsSystemDll())
203   {
204     CLog::Log(LOGFATAL, "%s is a system dll and should never be released", pDll->GetName());
205     return;
206   }
207
208   int iRefCount=pDll->DecrRef();
209   if (iRefCount==0)
210   {
211
212 #ifdef LOGALL
213     CLog::Log(LOGDEBUG, "Releasing Dll %s", pDll->GetFileName());
214 #endif
215
216     if (!pDll->HasSymbols())
217     {
218       pDll->Unload();
219       delete pDll;
220       pDll=NULL;
221     }
222     else
223       CLog::Log(LOGINFO, "%s has symbols loaded and can never be unloaded", pDll->GetName());
224   }
225 #ifdef LOGALL
226   else
227   {
228     CLog::Log(LOGDEBUG, "Dll %s is still referenced with a count of %d", pDll->GetFileName(), iRefCount);
229   }
230 #endif
231 }
232
233 LibraryLoader* DllLoaderContainer::LoadDll(const char* sName, bool bLoadSymbols)
234 {
235
236 #ifdef LOGALL
237   CLog::Log(LOGDEBUG, "Loading dll %s", sName);
238 #endif
239
240   LibraryLoader* pLoader;
241 #ifdef _LINUX
242   if (strstr(sName, ".so") != NULL || strstr(sName, ".vis") != NULL || strstr(sName, ".xbs") != NULL
243       || strstr(sName, ".mvis") != NULL || strstr(sName, ".dylib") != NULL || strstr(sName, ".framework") != NULL || strstr(sName, ".pvr") != NULL)
244     pLoader = new SoLoader(sName, bLoadSymbols);
245   else
246 #elif defined(_WIN32)
247   if (1)
248     pLoader = new Win32DllLoader(sName);
249   else
250 #endif
251     pLoader = new DllLoader(sName, m_bTrack, false, bLoadSymbols);
252
253   if (!pLoader)
254   {
255     CLog::Log(LOGERROR, "Unable to create dll %s", sName);
256     return NULL;
257   }
258
259   if (!pLoader->Load())
260   {
261     delete pLoader;
262     return NULL;
263   }
264
265   return pLoader;
266 }
267
268 bool DllLoaderContainer::IsSystemDll(const char* sName)
269 {
270   for (int i = 0; m_dlls[i] != NULL && i < m_iNrOfDlls; i++)
271   {
272     if (m_dlls[i]->IsSystemDll() && stricmp(m_dlls[i]->GetName(), sName) == 0) return true;
273   }
274
275   return false;
276 }
277
278 int DllLoaderContainer::GetNrOfModules()
279 {
280   return m_iNrOfDlls;
281 }
282
283 LibraryLoader* DllLoaderContainer::GetModule(int iPos)
284 {
285   if (iPos < m_iNrOfDlls) return m_dlls[iPos];
286   return NULL;
287 }
288
289 void DllLoaderContainer::RegisterDll(LibraryLoader* pDll)
290 {
291   for (int i = 0; i < 64; i++)
292   {
293     if (m_dlls[i] == NULL)
294     {
295       m_dlls[i] = pDll;
296       m_iNrOfDlls++;
297       break;
298     }
299   }
300 }
301
302 void DllLoaderContainer::UnRegisterDll(LibraryLoader* pDll)
303 {
304   if (pDll)
305   {
306     if (pDll->IsSystemDll())
307     {
308       CLog::Log(LOGFATAL, "%s is a system dll and should never be removed", pDll->GetName());
309     }
310     else
311     {
312       // remove from the list
313       bool bRemoved = false;
314       for (int i = 0; i < m_iNrOfDlls && m_dlls[i]; i++)
315       {
316         if (m_dlls[i] == pDll) bRemoved = true;
317         if (bRemoved && i + 1 < m_iNrOfDlls)
318         {
319           m_dlls[i] = m_dlls[i + 1];
320         }
321       }
322       if (bRemoved)
323       {
324         m_iNrOfDlls--;
325         m_dlls[m_iNrOfDlls] = NULL;
326       }
327     }
328   }
329 }
330
331 void DllLoaderContainer::UnloadPythonDlls()
332 {
333   // unload all dlls that python24.dll could have loaded
334   for (int i = 0; m_dlls[i] != NULL && i < m_iNrOfDlls; i++)
335   {
336     char* name = m_dlls[i]->GetName();
337     if (strstr(name, ".pyd") != NULL)
338     {
339       LibraryLoader* pDll = m_dlls[i];
340       ReleaseModule(pDll);
341       i = 0;
342     }
343   }
344
345   // last dll to unload, python24.dll
346   for (int i = 0; m_dlls[i] != NULL && i < m_iNrOfDlls; i++)
347   {
348     char* name = m_dlls[i]->GetName();
349
350 #ifdef HAVE_LIBPYTHON2_6
351     if (strstr(name, "python26.dll") != NULL)
352 #else
353     if (strstr(name, "python24.dll") != NULL)
354 #endif
355     {
356       LibraryLoader* pDll = m_dlls[i];
357       pDll->IncrRef();
358       while (pDll->DecrRef() > 1) pDll->DecrRef();
359
360       // since we freed all python extension dlls first, we have to remove any associations with them first
361       DllTrackInfo* info = tracker_get_dlltrackinfo_byobject((DllLoader*) pDll);
362       if (info != NULL)
363       {
364         info->dllList.clear();
365       }
366
367       ReleaseModule(pDll);
368       break;
369     }
370   }
371
372
373 }