Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

567 lines
16 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //===========================================================================//
  6. #if defined( _WIN32 ) && !defined( _X360 )
  7. #include <windows.h>
  8. #endif
  9. #if !defined( DONT_PROTECT_FILEIO_FUNCTIONS )
  10. #define DONT_PROTECT_FILEIO_FUNCTIONS // for protected_things.h
  11. #endif
  12. #if defined( PROTECTED_THINGS_ENABLE )
  13. #undef PROTECTED_THINGS_ENABLE // from protected_things.h
  14. #endif
  15. #include <stdio.h>
  16. #include "interface.h"
  17. #include "basetypes.h"
  18. #include "tier0/dbg.h"
  19. #include <string.h>
  20. #include <stdlib.h>
  21. #include "tier1/strtools.h"
  22. #include "tier0/icommandline.h"
  23. #include "tier0/dbg.h"
  24. #include "tier0/threadtools.h"
  25. #ifdef _WIN32
  26. #include <direct.h> // getcwd
  27. #elif POSIX
  28. #include <dlfcn.h>
  29. #include <unistd.h>
  30. #define _getcwd getcwd
  31. #endif
  32. #if defined( _X360 )
  33. #include "xbox/xbox_win32stubs.h"
  34. #endif
  35. // memdbgon must be the last include file in a .cpp file!!!
  36. #include "tier0/memdbgon.h"
  37. // ------------------------------------------------------------------------------------ //
  38. // InterfaceReg.
  39. // ------------------------------------------------------------------------------------ //
  40. InterfaceReg *InterfaceReg::s_pInterfaceRegs = NULL;
  41. InterfaceReg::InterfaceReg( InstantiateInterfaceFn fn, const char *pName ) :
  42. m_pName(pName)
  43. {
  44. m_CreateFn = fn;
  45. m_pNext = s_pInterfaceRegs;
  46. s_pInterfaceRegs = this;
  47. }
  48. // ------------------------------------------------------------------------------------ //
  49. // CreateInterface.
  50. // This is the primary exported function by a dll, referenced by name via dynamic binding
  51. // that exposes an opqaue function pointer to the interface.
  52. //
  53. // We have the Internal variant so Sys_GetFactoryThis() returns the correct internal
  54. // symbol under GCC/Linux/Mac as CreateInterface is DLL_EXPORT so its global so the loaders
  55. // on those OS's pick exactly 1 of the CreateInterface symbols to be the one that is process wide and
  56. // all Sys_GetFactoryThis() calls find that one, which doesn't work. Using the internal walkthrough here
  57. // makes sure Sys_GetFactoryThis() has the dll specific symbol and GetProcAddress() returns the module specific
  58. // function for CreateInterface again getting the dll specific symbol we need.
  59. // ------------------------------------------------------------------------------------ //
  60. void* CreateInterfaceInternal( const char *pName, int *pReturnCode )
  61. {
  62. InterfaceReg *pCur;
  63. for (pCur=InterfaceReg::s_pInterfaceRegs; pCur; pCur=pCur->m_pNext)
  64. {
  65. if (strcmp(pCur->m_pName, pName) == 0)
  66. {
  67. if (pReturnCode)
  68. {
  69. *pReturnCode = IFACE_OK;
  70. }
  71. return pCur->m_CreateFn();
  72. }
  73. }
  74. if (pReturnCode)
  75. {
  76. *pReturnCode = IFACE_FAILED;
  77. }
  78. return NULL;
  79. }
  80. void* CreateInterface( const char *pName, int *pReturnCode )
  81. {
  82. return CreateInterfaceInternal( pName, pReturnCode );
  83. }
  84. #ifdef POSIX
  85. // Linux doesn't have this function so this emulates its functionality
  86. void *GetModuleHandle(const char *name)
  87. {
  88. void *handle;
  89. if( name == NULL )
  90. {
  91. // hmm, how can this be handled under linux....
  92. // is it even needed?
  93. return NULL;
  94. }
  95. if( (handle=dlopen(name, RTLD_NOW))==NULL)
  96. {
  97. printf("DLOPEN Error:%s\n",dlerror());
  98. // couldn't open this file
  99. return NULL;
  100. }
  101. // read "man dlopen" for details
  102. // in short dlopen() inc a ref count
  103. // so dec the ref count by performing the close
  104. dlclose(handle);
  105. return handle;
  106. }
  107. #endif
  108. #if defined( _WIN32 ) && !defined( _X360 )
  109. #define WIN32_LEAN_AND_MEAN
  110. #include "windows.h"
  111. #endif
  112. //-----------------------------------------------------------------------------
  113. // Purpose: returns a pointer to a function, given a module
  114. // Input : pModuleName - module name
  115. // *pName - proc name
  116. //-----------------------------------------------------------------------------
  117. static void *Sys_GetProcAddress( const char *pModuleName, const char *pName )
  118. {
  119. HMODULE hModule = (HMODULE)GetModuleHandle( pModuleName );
  120. #ifdef WIN32
  121. return (void *)GetProcAddress( hModule, pName );
  122. #else
  123. return (void *)dlsym( (void *)hModule, pName );
  124. #endif
  125. }
  126. #if !defined(LINUX)
  127. static void *Sys_GetProcAddress( HMODULE hModule, const char *pName )
  128. {
  129. #ifdef WIN32
  130. return (void *)GetProcAddress( hModule, pName );
  131. #else
  132. return (void *)dlsym( (void *)hModule, pName );
  133. #endif
  134. }
  135. #endif
  136. bool Sys_IsDebuggerPresent()
  137. {
  138. return Plat_IsInDebugSession();
  139. }
  140. struct ThreadedLoadLibaryContext_t
  141. {
  142. const char *m_pLibraryName;
  143. HMODULE m_hLibrary;
  144. };
  145. #ifdef _WIN32
  146. // wraps LoadLibraryEx() since 360 doesn't support that
  147. static HMODULE InternalLoadLibrary( const char *pName, Sys_Flags flags )
  148. {
  149. #if defined(_X360)
  150. return LoadLibrary( pName );
  151. #else
  152. if ( flags & SYS_NOLOAD )
  153. return GetModuleHandle( pName );
  154. else
  155. return LoadLibraryEx( pName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH );
  156. #endif
  157. }
  158. unsigned ThreadedLoadLibraryFunc( void *pParam )
  159. {
  160. ThreadedLoadLibaryContext_t *pContext = (ThreadedLoadLibaryContext_t*)pParam;
  161. pContext->m_hLibrary = InternalLoadLibrary( pContext->m_pLibraryName, SYS_NOFLAGS );
  162. return 0;
  163. }
  164. #endif // _WIN32
  165. HMODULE Sys_LoadLibrary( const char *pLibraryName, Sys_Flags flags )
  166. {
  167. char str[ 1024 ];
  168. // Note: DLL_EXT_STRING can be "_srv.so" or "_360.dll". So be careful
  169. // when using the V_*Extension* routines...
  170. const char *pDllStringExtension = V_GetFileExtension( DLL_EXT_STRING );
  171. const char *pModuleExtension = pDllStringExtension ? ( pDllStringExtension - 1 ) : DLL_EXT_STRING;
  172. Q_strncpy( str, pLibraryName, sizeof(str) );
  173. if ( IsX360() )
  174. {
  175. // old, probably busted, behavior for xbox
  176. if ( !Q_stristr( str, pModuleExtension ) )
  177. {
  178. V_SetExtension( str, pModuleExtension, sizeof(str) );
  179. }
  180. }
  181. else
  182. {
  183. // always force the final extension to be .dll
  184. V_SetExtension( str, pModuleExtension, sizeof(str) );
  185. }
  186. Q_FixSlashes( str );
  187. #ifdef _WIN32
  188. ThreadedLoadLibraryFunc_t threadFunc = GetThreadedLoadLibraryFunc();
  189. if ( !threadFunc )
  190. return InternalLoadLibrary( str, flags );
  191. // We shouldn't be passing noload while threaded.
  192. Assert( !( flags & SYS_NOLOAD ) );
  193. ThreadedLoadLibaryContext_t context;
  194. context.m_pLibraryName = str;
  195. context.m_hLibrary = 0;
  196. ThreadHandle_t h = CreateSimpleThread( ThreadedLoadLibraryFunc, &context );
  197. #ifdef _X360
  198. ThreadSetAffinity( h, XBOX_PROCESSOR_3 );
  199. #endif
  200. unsigned int nTimeout = 0;
  201. while( ThreadWaitForObject( h, true, nTimeout ) == TW_TIMEOUT )
  202. {
  203. nTimeout = threadFunc();
  204. }
  205. ReleaseThreadHandle( h );
  206. return context.m_hLibrary;
  207. #elif POSIX
  208. int dlopen_mode = RTLD_NOW;
  209. if ( flags & SYS_NOLOAD )
  210. dlopen_mode |= RTLD_NOLOAD;
  211. HMODULE ret = ( HMODULE )dlopen( str, dlopen_mode );
  212. if ( !ret && !( flags & SYS_NOLOAD ) )
  213. {
  214. const char *pError = dlerror();
  215. if ( pError && ( strstr( pError, "No such file" ) == 0 ) && ( strstr( pError, "image not found" ) == 0 ) )
  216. {
  217. Msg( " failed to dlopen %s error=%s\n", str, pError );
  218. }
  219. }
  220. return ret;
  221. #endif
  222. }
  223. static bool s_bRunningWithDebugModules = false;
  224. //-----------------------------------------------------------------------------
  225. // Purpose: Loads a DLL/component from disk and returns a handle to it
  226. // Input : *pModuleName - filename of the component
  227. // Output : opaque handle to the module (hides system dependency)
  228. //-----------------------------------------------------------------------------
  229. CSysModule *Sys_LoadModule( const char *pModuleName, Sys_Flags flags /* = SYS_NOFLAGS (0) */ )
  230. {
  231. // If using the Steam filesystem, either the DLL must be a minimum footprint
  232. // file in the depot (MFP) or a filesystem GetLocalCopy() call must be made
  233. // prior to the call to this routine.
  234. char szCwd[1024];
  235. HMODULE hDLL = NULL;
  236. if ( !Q_IsAbsolutePath( pModuleName ) )
  237. {
  238. // full path wasn't passed in, using the current working dir
  239. _getcwd( szCwd, sizeof( szCwd ) );
  240. if ( IsX360() )
  241. {
  242. int i = CommandLine()->FindParm( "-basedir" );
  243. if ( i )
  244. {
  245. V_strcpy_safe( szCwd, CommandLine()->GetParm( i + 1 ) );
  246. }
  247. }
  248. if (szCwd[strlen(szCwd) - 1] == '/' || szCwd[strlen(szCwd) - 1] == '\\' )
  249. {
  250. szCwd[strlen(szCwd) - 1] = 0;
  251. }
  252. char szAbsoluteModuleName[1024];
  253. size_t cCwd = strlen( szCwd );
  254. if ( strstr( pModuleName, "bin/") == pModuleName || ( szCwd[ cCwd - 1 ] == 'n' && szCwd[ cCwd - 2 ] == 'i' && szCwd[ cCwd - 3 ] == 'b' ) )
  255. {
  256. // don't make bin/bin path
  257. Q_snprintf( szAbsoluteModuleName, sizeof(szAbsoluteModuleName), "%s/%s", szCwd, pModuleName );
  258. }
  259. else
  260. {
  261. Q_snprintf( szAbsoluteModuleName, sizeof(szAbsoluteModuleName), "%s/bin/%s", szCwd, pModuleName );
  262. }
  263. hDLL = Sys_LoadLibrary( szAbsoluteModuleName, flags );
  264. }
  265. if ( !hDLL )
  266. {
  267. // full path failed, let LoadLibrary() try to search the PATH now
  268. hDLL = Sys_LoadLibrary( pModuleName, flags );
  269. #if defined( _DEBUG )
  270. if ( !hDLL )
  271. {
  272. // So you can see what the error is in the debugger...
  273. #if defined( _WIN32 ) && !defined( _X360 )
  274. char *lpMsgBuf;
  275. FormatMessage(
  276. FORMAT_MESSAGE_ALLOCATE_BUFFER |
  277. FORMAT_MESSAGE_FROM_SYSTEM |
  278. FORMAT_MESSAGE_IGNORE_INSERTS,
  279. NULL,
  280. GetLastError(),
  281. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
  282. (LPTSTR) &lpMsgBuf,
  283. 0,
  284. NULL
  285. );
  286. LocalFree( (HLOCAL)lpMsgBuf );
  287. #elif defined( _X360 )
  288. DWORD error = GetLastError();
  289. Msg( "Error(%d) - Failed to load %s:\n", error, pModuleName );
  290. #else
  291. Msg( "Failed to load %s: %s\n", pModuleName, dlerror() );
  292. #endif // _WIN32
  293. }
  294. #endif // DEBUG
  295. }
  296. #if !defined(LINUX)
  297. // If running in the debugger, assume debug binaries are okay, otherwise they must run with -allowdebug
  298. if ( Sys_GetProcAddress( hDLL, "BuiltDebug" ) )
  299. {
  300. if ( !IsX360() && hDLL &&
  301. !CommandLine()->FindParm( "-allowdebug" ) &&
  302. !Sys_IsDebuggerPresent() )
  303. {
  304. Error( "Module %s is a debug build\n", pModuleName );
  305. }
  306. DevWarning( "Module %s is a debug build\n", pModuleName );
  307. if ( !s_bRunningWithDebugModules )
  308. {
  309. s_bRunningWithDebugModules = true;
  310. #if 0 //def IS_WINDOWS_PC
  311. char chMemoryName[ MAX_PATH ];
  312. DebugKernelMemoryObjectName( chMemoryName );
  313. (void) CreateFileMapping( INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 1024, chMemoryName );
  314. // Created a shared memory kernel object specific to process id
  315. // Existence of this object indicates that we have debug modules loaded
  316. #endif
  317. }
  318. }
  319. #endif
  320. return reinterpret_cast<CSysModule *>(hDLL);
  321. }
  322. //-----------------------------------------------------------------------------
  323. // Purpose: Determine if any debug modules were loaded
  324. //-----------------------------------------------------------------------------
  325. bool Sys_RunningWithDebugModules()
  326. {
  327. if ( !s_bRunningWithDebugModules )
  328. {
  329. #if 0 //def IS_WINDOWS_PC
  330. char chMemoryName[ MAX_PATH ];
  331. DebugKernelMemoryObjectName( chMemoryName );
  332. HANDLE hObject = OpenFileMapping( FILE_MAP_READ, FALSE, chMemoryName );
  333. if ( hObject && hObject != INVALID_HANDLE_VALUE )
  334. {
  335. CloseHandle( hObject );
  336. s_bRunningWithDebugModules = true;
  337. }
  338. #endif
  339. }
  340. return s_bRunningWithDebugModules;
  341. }
  342. //-----------------------------------------------------------------------------
  343. // Purpose: Unloads a DLL/component from
  344. // Input : *pModuleName - filename of the component
  345. // Output : opaque handle to the module (hides system dependency)
  346. //-----------------------------------------------------------------------------
  347. void Sys_UnloadModule( CSysModule *pModule )
  348. {
  349. if ( !pModule )
  350. return;
  351. HMODULE hDLL = reinterpret_cast<HMODULE>(pModule);
  352. #ifdef _WIN32
  353. FreeLibrary( hDLL );
  354. #elif defined(POSIX)
  355. dlclose((void *)hDLL);
  356. #endif
  357. }
  358. //-----------------------------------------------------------------------------
  359. // Purpose: returns a pointer to a function, given a module
  360. // Input : module - windows HMODULE from Sys_LoadModule()
  361. // *pName - proc name
  362. // Output : factory for this module
  363. //-----------------------------------------------------------------------------
  364. CreateInterfaceFn Sys_GetFactory( CSysModule *pModule )
  365. {
  366. if ( !pModule )
  367. return NULL;
  368. HMODULE hDLL = reinterpret_cast<HMODULE>(pModule);
  369. #ifdef _WIN32
  370. return reinterpret_cast<CreateInterfaceFn>(GetProcAddress( hDLL, CREATEINTERFACE_PROCNAME ));
  371. #elif defined(POSIX)
  372. // Linux gives this error:
  373. //../public/interface.cpp: In function `IBaseInterface *(*Sys_GetFactory
  374. //(CSysModule *)) (const char *, int *)':
  375. //../public/interface.cpp:154: ISO C++ forbids casting between
  376. //pointer-to-function and pointer-to-object
  377. //
  378. // so lets get around it :)
  379. return (CreateInterfaceFn)(GetProcAddress( (void *)hDLL, CREATEINTERFACE_PROCNAME ));
  380. #endif
  381. }
  382. //-----------------------------------------------------------------------------
  383. // Purpose: returns the instance of this module
  384. // Output : interface_instance_t
  385. //-----------------------------------------------------------------------------
  386. CreateInterfaceFn Sys_GetFactoryThis( void )
  387. {
  388. return &CreateInterfaceInternal;
  389. }
  390. //-----------------------------------------------------------------------------
  391. // Purpose: returns the instance of the named module
  392. // Input : *pModuleName - name of the module
  393. // Output : interface_instance_t - instance of that module
  394. //-----------------------------------------------------------------------------
  395. CreateInterfaceFn Sys_GetFactory( const char *pModuleName )
  396. {
  397. #ifdef _WIN32
  398. return static_cast<CreateInterfaceFn>( Sys_GetProcAddress( pModuleName, CREATEINTERFACE_PROCNAME ) );
  399. #elif defined(POSIX)
  400. // see Sys_GetFactory( CSysModule *pModule ) for an explanation
  401. return (CreateInterfaceFn)( Sys_GetProcAddress( pModuleName, CREATEINTERFACE_PROCNAME ) );
  402. #endif
  403. }
  404. //-----------------------------------------------------------------------------
  405. // Purpose: get the interface for the specified module and version
  406. // Input :
  407. // Output :
  408. //-----------------------------------------------------------------------------
  409. bool Sys_LoadInterface(
  410. const char *pModuleName,
  411. const char *pInterfaceVersionName,
  412. CSysModule **pOutModule,
  413. void **pOutInterface )
  414. {
  415. CSysModule *pMod = Sys_LoadModule( pModuleName );
  416. if ( !pMod )
  417. return false;
  418. CreateInterfaceFn fn = Sys_GetFactory( pMod );
  419. if ( !fn )
  420. {
  421. Sys_UnloadModule( pMod );
  422. return false;
  423. }
  424. *pOutInterface = fn( pInterfaceVersionName, NULL );
  425. if ( !( *pOutInterface ) )
  426. {
  427. Sys_UnloadModule( pMod );
  428. return false;
  429. }
  430. if ( pOutModule )
  431. *pOutModule = pMod;
  432. return true;
  433. }
  434. //-----------------------------------------------------------------------------
  435. // Purpose: Place this as a singleton at module scope (e.g.) and use it to get the factory from the specified module name.
  436. //
  437. // When the singleton goes out of scope (.dll unload if at module scope),
  438. // then it'll call Sys_UnloadModule on the module so that the refcount is decremented
  439. // and the .dll actually can unload from memory.
  440. //-----------------------------------------------------------------------------
  441. CDllDemandLoader::CDllDemandLoader( char const *pchModuleName ) :
  442. m_pchModuleName( pchModuleName ),
  443. m_hModule( 0 ),
  444. m_bLoadAttempted( false )
  445. {
  446. }
  447. CDllDemandLoader::~CDllDemandLoader()
  448. {
  449. Unload();
  450. }
  451. CreateInterfaceFn CDllDemandLoader::GetFactory()
  452. {
  453. if ( !m_hModule && !m_bLoadAttempted )
  454. {
  455. m_bLoadAttempted = true;
  456. m_hModule = Sys_LoadModule( m_pchModuleName );
  457. }
  458. if ( !m_hModule )
  459. {
  460. return NULL;
  461. }
  462. return Sys_GetFactory( m_hModule );
  463. }
  464. void CDllDemandLoader::Unload()
  465. {
  466. if ( m_hModule )
  467. {
  468. Sys_UnloadModule( m_hModule );
  469. m_hModule = 0;
  470. }
  471. }
  472. #if defined( STAGING_ONLY ) && defined( _WIN32 )
  473. typedef USHORT( WINAPI RtlCaptureStackBackTrace_FUNC )(
  474. ULONG frames_to_skip,
  475. ULONG frames_to_capture,
  476. PVOID *backtrace,
  477. PULONG backtrace_hash );
  478. extern "C" int backtrace( void **buffer, int size )
  479. {
  480. HMODULE hNTDll = GetModuleHandleA( "ntdll.dll" );
  481. static RtlCaptureStackBackTrace_FUNC * const pfnRtlCaptureStackBackTrace =
  482. ( RtlCaptureStackBackTrace_FUNC * )GetProcAddress( hNTDll, "RtlCaptureStackBackTrace" );
  483. if ( !pfnRtlCaptureStackBackTrace )
  484. return 0;
  485. return (int)pfnRtlCaptureStackBackTrace( 2, size, buffer, 0 );
  486. }
  487. #endif // STAGING_ONLY && _WIN32