2016-09-14 22:49:22 +03:00
//--------------------------------------------------------------------*- C++ -*-
// CLING - the C++ LLVM-based InterpreterG :)
// author: Roman Zulak
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------
# include "cling/Utils/Platform.h"
# if defined(LLVM_ON_UNIX)
2016-09-15 20:30:18 +03:00
# include "cling/Utils/Paths.h"
2016-09-30 00:16:27 +03:00
# include "llvm/ADT/SmallString.h"
2016-09-15 22:06:01 +03:00
2016-10-20 10:43:21 +03:00
# include <array>
# include <atomic>
2016-09-14 22:49:22 +03:00
# include <string>
2016-12-16 21:33:42 +03:00
# include <cxxabi.h>
2016-09-15 21:17:17 +03:00
# include <dlfcn.h>
2016-09-15 22:06:01 +03:00
# include <errno.h>
# include <fcntl.h>
2016-09-14 22:49:22 +03:00
# include <unistd.h>
2019-07-30 18:57:39 +03:00
# include <sys/mman.h>
2016-09-14 22:49:22 +03:00
2016-10-20 10:43:21 +03:00
// PATH_MAX
2016-09-14 22:49:22 +03:00
# ifdef __APPLE__
2016-10-20 10:43:21 +03:00
# include <sys/syslimits.h>
2016-09-14 22:49:22 +03:00
# else
# include <limits.h>
# endif
2016-09-15 21:04:27 +03:00
# define PATH_MAXC (PATH_MAX+1)
2016-09-14 22:49:22 +03:00
namespace cling {
namespace utils {
namespace platform {
2016-10-20 10:43:21 +03:00
namespace {
struct PointerCheck {
private :
// A simple round-robin cache: what enters first, leaves first.
// MRU cache wasn't worth the extra CPU cycles.
2018-09-24 18:59:31 +03:00
static thread_local std : : array < const void * , 8 > lines ;
static thread_local unsigned mostRecent ;
2019-07-30 18:57:39 +03:00
size_t page_size ;
2016-10-20 10:43:21 +03:00
// Concurrent writes to the same cache element can result in invalid cache
// elements, causing pointer address not being available in the cache even
// though they should be, i.e. false cache misses. While can cause a
// slow-down, the cost for keeping the cache thread-local or atomic is
// much higher (yes, this was measured).
void push ( const void * P ) {
2018-09-24 18:59:31 +03:00
mostRecent = ( mostRecent + 1 ) % lines . size ( ) ;
lines [ mostRecent ] = P ;
2016-10-20 10:43:21 +03:00
}
public :
2019-07-30 18:57:39 +03:00
PointerCheck ( ) {
page_size = : : sysconf ( _SC_PAGESIZE ) ;
2016-10-20 10:43:21 +03:00
}
bool operator ( ) ( const void * P ) {
2018-05-24 10:25:20 +03:00
// std::find is considerably slower, do manual search instead.
if ( P = = lines [ 0 ] | | P = = lines [ 1 ] | | P = = lines [ 2 ] | | P = = lines [ 3 ]
| | P = = lines [ 4 ] | | P = = lines [ 5 ] | | P = = lines [ 6 ] | | P = = lines [ 7 ] )
2016-10-20 10:43:21 +03:00
return true ;
// There is a POSIX way of finding whether an address
// can be accessed for reading.
2019-07-30 18:57:39 +03:00
void * base = ( void * ) ( ( ( ( const size_t ) P ) / page_size ) * page_size ) ;
if ( : : msync ( base , page_size , 0 ) ! = 0 ) {
2016-10-20 10:43:21 +03:00
assert ( errno = = EFAULT & & " unexpected write error at address " ) ;
return false ;
}
push ( P ) ;
return true ;
}
} ;
2018-09-24 18:59:31 +03:00
thread_local std : : array < const void * , 8 > PointerCheck : : lines = { } ;
thread_local unsigned PointerCheck : : mostRecent = 0 ;
2016-10-20 10:43:21 +03:00
}
bool IsMemoryValid ( const void * P ) {
static PointerCheck sPointerCheck ;
return sPointerCheck ( P ) ;
}
2016-09-14 22:49:22 +03:00
std : : string GetCwd ( ) {
2016-09-15 21:04:27 +03:00
char Buffer [ PATH_MAXC ] ;
2016-09-14 22:49:22 +03:00
if ( : : getcwd ( Buffer , sizeof ( Buffer ) ) )
return Buffer ;
: : perror ( " Could not get current working directory " ) ;
return std : : string ( ) ;
}
2016-09-18 01:28:29 +03:00
static void DLErr ( std : : string * Err ) {
if ( ! Err )
return ;
if ( const char * DyLibError = : : dlerror ( ) )
* Err = DyLibError ;
}
2016-09-15 21:17:17 +03:00
const void * DLOpen ( const std : : string & Path , std : : string * Err ) {
void * Lib = dlopen ( Path . c_str ( ) , RTLD_LAZY | RTLD_GLOBAL ) ;
2016-09-18 01:28:29 +03:00
DLErr ( Err ) ;
2016-09-15 21:17:17 +03:00
return Lib ;
}
2016-09-18 01:28:29 +03:00
const void * DLSym ( const std : : string & Name , std : : string * Err ) {
if ( const void * Self = : : dlopen ( nullptr , RTLD_GLOBAL ) ) {
// get dlopen error if there is one
DLErr ( Err ) ;
const void * Sym = : : dlsym ( const_cast < void * > ( Self ) , Name . c_str ( ) ) ;
// overwrite error if dlsym caused one
DLErr ( Err ) ;
// only get dlclose error if dlopen & dlsym haven't emited one
DLClose ( Self , Err & & Err - > empty ( ) ? Err : nullptr ) ;
return Sym ;
}
DLErr ( Err ) ;
return nullptr ;
}
2016-09-15 21:17:17 +03:00
void DLClose ( const void * Lib , std : : string * Err ) {
: : dlclose ( const_cast < void * > ( Lib ) ) ;
2016-09-18 01:28:29 +03:00
DLErr ( Err ) ;
2016-09-15 21:17:17 +03:00
}
2016-09-15 21:04:27 +03:00
std : : string NormalizePath ( const std : : string & Path ) {
char Buf [ PATH_MAXC ] ;
if ( const char * Result = : : realpath ( Path . c_str ( ) , Buf ) )
return std : : string ( Result ) ;
: : perror ( " realpath " ) ;
return std : : string ( ) ;
}
2016-09-30 00:16:27 +03:00
bool Popen ( const std : : string & Cmd , llvm : : SmallVectorImpl < char > & Buf , bool RdE ) {
if ( FILE * PF = : : popen ( RdE ? ( Cmd + " 2>&1 " ) . c_str ( ) : Cmd . c_str ( ) , " r " ) ) {
Buf . resize ( 0 ) ;
const size_t Chunk = Buf . capacity_in_bytes ( ) ;
while ( true ) {
const size_t Len = Buf . size ( ) ;
Buf . resize ( Len + Chunk ) ;
const size_t R = : : fread ( & Buf [ Len ] , sizeof ( char ) , Chunk , PF ) ;
if ( R < Chunk ) {
Buf . resize ( Len + R ) ;
break ;
}
}
: : pclose ( PF ) ;
return ! Buf . empty ( ) ;
}
return false ;
}
2016-09-15 20:30:18 +03:00
bool GetSystemLibraryPaths ( llvm : : SmallVectorImpl < std : : string > & Paths ) {
# if defined(__APPLE__) || defined(__CYGWIN__)
Paths . push_back ( " /usr/local/lib/ " ) ;
Paths . push_back ( " /usr/X11R6/lib/ " ) ;
Paths . push_back ( " /usr/lib/ " ) ;
Paths . push_back ( " /lib/ " ) ;
# ifndef __APPLE__
Paths . push_back ( " /lib/x86_64-linux-gnu/ " ) ;
Paths . push_back ( " /usr/local/lib64/ " ) ;
Paths . push_back ( " /usr/lib64/ " ) ;
Paths . push_back ( " /lib64/ " ) ;
# endif
# else
2016-09-30 00:16:27 +03:00
llvm : : SmallString < 1024 > Buf ;
platform : : Popen ( " LD_DEBUG=libs LD_PRELOAD=DOESNOTEXIST ls " , Buf , true ) ;
const llvm : : StringRef Result = Buf . str ( ) ;
2016-09-15 20:30:18 +03:00
const std : : size_t NPos = std : : string : : npos ;
const std : : size_t LD = Result . find ( " (LD_LIBRARY_PATH) " ) ;
std : : size_t From = Result . find ( " search path= " , LD = = NPos ? 0 : LD ) ;
if ( From ! = NPos ) {
const std : : size_t To = Result . find ( " (system search path) " , From ) ;
if ( To ! = NPos ) {
From + = 12 ;
std : : string SysPath = Result . substr ( From , To - From ) ;
SysPath . erase ( std : : remove_if ( SysPath . begin ( ) , SysPath . end ( ) , isspace ) ,
SysPath . end ( ) ) ;
llvm : : SmallVector < llvm : : StringRef , 10 > CurPaths ;
SplitPaths ( SysPath , CurPaths ) ;
for ( const auto & Path : CurPaths )
Paths . push_back ( Path . str ( ) ) ;
}
}
# endif
return true ;
}
2016-12-16 21:33:42 +03:00
std : : string Demangle ( const std : : string & Symbol ) {
struct AutoFree {
char * Str ;
AutoFree ( char * Ptr ) : Str ( Ptr ) { }
~ AutoFree ( ) { : : free ( Str ) ; } ;
} ;
int status = 0 ;
2017-02-02 23:54:54 +03:00
// Some implementations of __cxa_demangle are giving back length of allocation
// Passing NULL for length seems to guarantee null termination.
AutoFree af ( abi : : __cxa_demangle ( Symbol . c_str ( ) , NULL , NULL , & status ) ) ;
return status = = 0 ? std : : string ( af . Str ) : std : : string ( ) ;
2016-12-16 21:33:42 +03:00
}
2016-09-14 22:49:22 +03:00
} // namespace platform
} // namespace utils
} // namespace cling
# endif // LLVM_ON_UNIX