From 07ffa7d08bf540bfb8df103b9c7b195e08a1d9e0 Mon Sep 17 00:00:00 2001 From: Rafael Espindola Date: Wed, 26 Jun 2013 05:01:35 +0000 Subject: [PATCH] Port GetMainExecutable over to PathV2. I will remove the V1 version as soon as I change clang in the next commit. llvm-svn: 184914 --- include/llvm/Support/FileSystem.h | 5 +- lib/Support/Unix/PathV2.inc | 106 ++++++++++++++++++++++++++++++ lib/Support/Windows/PathV2.inc | 7 ++ tools/bugpoint/ToolRunner.cpp | 4 +- tools/llvm-config/llvm-config.cpp | 6 +- unittests/Support/ProgramTest.cpp | 6 +- 6 files changed, 126 insertions(+), 8 deletions(-) diff --git a/include/llvm/Support/FileSystem.h b/include/llvm/Support/FileSystem.h index 5c514dc95c8..699bf4b391d 100644 --- a/include/llvm/Support/FileSystem.h +++ b/include/llvm/Support/FileSystem.h @@ -696,7 +696,10 @@ error_code map_file_pages(const Twine &path, off_t file_offset, size_t size, /// platform specific error_code. error_code unmap_file_pages(void *base, size_t size); - +/// Return the path to the main executable, given the value of argv[0] from +/// program startup and the address of main itself. In extremis, this function +/// may fail and return an empty path. +std::string getMainExecutable(const char *argv0, void *MainExecAddr); /// @} /// @name Iterators diff --git a/lib/Support/Unix/PathV2.inc b/lib/Support/Unix/PathV2.inc index f5903f15306..c7a1c92d46d 100644 --- a/lib/Support/Unix/PathV2.inc +++ b/lib/Support/Unix/PathV2.inc @@ -109,6 +109,112 @@ namespace { namespace llvm { namespace sys { namespace fs { +#if defined(__FreeBSD__) || defined (__NetBSD__) || defined(__Bitrig__) || \ + defined(__OpenBSD__) || defined(__minix) || defined(__FreeBSD_kernel__) || \ + defined(__linux__) || defined(__CYGWIN__) +static int +test_dir(char buf[PATH_MAX], char ret[PATH_MAX], + const char *dir, const char *bin) +{ + struct stat sb; + + snprintf(buf, PATH_MAX, "%s/%s", dir, bin); + if (realpath(buf, ret) == NULL) + return (1); + if (stat(buf, &sb) != 0) + return (1); + + return (0); +} + +static char * +getprogpath(char ret[PATH_MAX], const char *bin) +{ + char *pv, *s, *t, buf[PATH_MAX]; + + /* First approach: absolute path. */ + if (bin[0] == '/') { + if (test_dir(buf, ret, "/", bin) == 0) + return (ret); + return (NULL); + } + + /* Second approach: relative path. */ + if (strchr(bin, '/') != NULL) { + if (getcwd(buf, PATH_MAX) == NULL) + return (NULL); + if (test_dir(buf, ret, buf, bin) == 0) + return (ret); + return (NULL); + } + + /* Third approach: $PATH */ + if ((pv = getenv("PATH")) == NULL) + return (NULL); + s = pv = strdup(pv); + if (pv == NULL) + return (NULL); + while ((t = strsep(&s, ":")) != NULL) { + if (test_dir(buf, ret, t, bin) == 0) { + free(pv); + return (ret); + } + } + free(pv); + return (NULL); +} +#endif // __FreeBSD__ || __NetBSD__ || __FreeBSD_kernel__ + +/// GetMainExecutable - Return the path to the main executable, given the +/// value of argv[0] from program startup. +std::string getMainExecutable(const char *argv0, void *MainAddr) { +#if defined(__APPLE__) + // On OS X the executable path is saved to the stack by dyld. Reading it + // from there is much faster than calling dladdr, especially for large + // binaries with symbols. + char exe_path[MAXPATHLEN]; + uint32_t size = sizeof(exe_path); + if (_NSGetExecutablePath(exe_path, &size) == 0) { + char link_path[MAXPATHLEN]; + if (realpath(exe_path, link_path)) + return Path(link_path); + } +#elif defined(__FreeBSD__) || defined (__NetBSD__) || defined(__Bitrig__) || \ + defined(__OpenBSD__) || defined(__minix) || defined(__FreeBSD_kernel__) + char exe_path[PATH_MAX]; + + if (getprogpath(exe_path, argv0) != NULL) + return Path(exe_path); +#elif defined(__linux__) || defined(__CYGWIN__) + char exe_path[MAXPATHLEN]; + StringRef aPath("/proc/self/exe"); + if (sys::fs::exists(aPath)) { + // /proc is not always mounted under Linux (chroot for example). + ssize_t len = readlink(aPath.str().c_str(), exe_path, sizeof(exe_path)); + if (len >= 0) + return StringRef(exe_path, len); + } else { + // Fall back to the classical detection. + if (getprogpath(exe_path, argv0) != NULL) + return exe_path; + } +#elif defined(HAVE_DLFCN_H) + // Use dladdr to get executable path if available. + Dl_info DLInfo; + int err = dladdr(MainAddr, &DLInfo); + if (err == 0) + return Path(); + + // If the filename is a symlink, we need to resolve and return the location of + // the actual executable. + char link_path[MAXPATHLEN]; + if (realpath(DLInfo.dli_fname, link_path)) + return Path(link_path); +#else +#error GetMainExecutable is not implemented on this host yet. +#endif + return ""; +} TimeValue file_status::getLastModificationTime() const { TimeValue Ret; diff --git a/lib/Support/Windows/PathV2.inc b/lib/Support/Windows/PathV2.inc index 0c12bb440e8..6987e5fc9da 100644 --- a/lib/Support/Windows/PathV2.inc +++ b/lib/Support/Windows/PathV2.inc @@ -128,6 +128,13 @@ namespace llvm { namespace sys { namespace fs { +std::string getMainExecutable(const char *argv0, void *MainExecAddr) { + char pathname[MAX_PATH]; + DWORD ret = ::GetModuleFileNameA(NULL, pathname, MAX_PATH); + return ret != MAX_PATH ? pathname : ""; +} + + TimeValue file_status::getLastModificationTime() const { ULARGE_INTEGER UI; UI.LowPart = LastWriteTimeLow; diff --git a/tools/bugpoint/ToolRunner.cpp b/tools/bugpoint/ToolRunner.cpp index 4e6f6e27cca..9cc5f9a7e6b 100644 --- a/tools/bugpoint/ToolRunner.cpp +++ b/tools/bugpoint/ToolRunner.cpp @@ -252,8 +252,8 @@ static std::string PrependMainExecutablePath(const std::string &ExeName, // Check the directory that the calling program is in. We can do // this if ProgramPath contains at least one / character, indicating that it // is a relative path to the executable itself. - sys::Path Main = sys::Path::GetMainExecutable(Argv0, MainAddr); - StringRef Result = sys::path::parent_path(Main.str()); + std::string Main = sys::fs::getMainExecutable(Argv0, MainAddr); + StringRef Result = sys::path::parent_path(Main); if (!Result.empty()) { SmallString<128> Storage = Result; diff --git a/tools/llvm-config/llvm-config.cpp b/tools/llvm-config/llvm-config.cpp index 5151d1a0911..fc3ab68f7d8 100644 --- a/tools/llvm-config/llvm-config.cpp +++ b/tools/llvm-config/llvm-config.cpp @@ -162,11 +162,11 @@ Typical components:\n\ } /// \brief Compute the path to the main executable. -llvm::sys::Path GetExecutablePath(const char *Argv0) { +std::string GetExecutablePath(const char *Argv0) { // This just needs to be some symbol in the binary; C++ doesn't // allow taking the address of ::main however. void *P = (void*) (intptr_t) GetExecutablePath; - return llvm::sys::Path::GetMainExecutable(Argv0, P); + return llvm::sys::fs::getMainExecutable(Argv0, P); } int main(int argc, char **argv) { @@ -180,7 +180,7 @@ int main(int argc, char **argv) { // tree. bool IsInDevelopmentTree; enum { MakefileStyle, CMakeStyle, CMakeBuildModeStyle } DevelopmentTreeLayout; - llvm::SmallString<256> CurrentPath(GetExecutablePath(argv[0]).str()); + llvm::SmallString<256> CurrentPath(GetExecutablePath(argv[0])); std::string CurrentExecPrefix; std::string ActiveObjRoot; diff --git a/unittests/Support/ProgramTest.cpp b/unittests/Support/ProgramTest.cpp index 1cf53d50b02..7886761c9aa 100644 --- a/unittests/Support/ProgramTest.cpp +++ b/unittests/Support/ProgramTest.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Support/PathV1.h" #include "llvm/Support/Program.h" @@ -56,7 +57,8 @@ TEST(ProgramTest, CreateProcessTrailingSlash) { exit(1); } - Path my_exe = Path::GetMainExecutable(TestMainArgv0, &ProgramTestStringArg1); + std::string my_exe = + sys::fs::getMainExecutable(TestMainArgv0, &ProgramTestStringArg1); const char *argv[] = { my_exe.c_str(), "--gtest_filter=ProgramTest.CreateProcessTrailingSlashChild", @@ -80,7 +82,7 @@ TEST(ProgramTest, CreateProcessTrailingSlash) { StringRef nul("/dev/null"); #endif const StringRef *redirects[] = { &nul, &nul, 0 }; - int rc = ExecuteAndWait(my_exe.str(), argv, &envp[0], redirects, + int rc = ExecuteAndWait(my_exe, argv, &envp[0], redirects, /*secondsToWait=*/ 10, /*memoryLimit=*/ 0, &error, &ExecutionFailed); EXPECT_FALSE(ExecutionFailed) << error;