Skip to content

Commit

Permalink
Fix ilasm, ildasm, crossgen and others launch from PATH (dotnet#40791)
Browse files Browse the repository at this point in the history
* Fix ilasm, ildasm and crossgen launch from PATH

When trying to launch ilasm, ildasm, crossgen or any tool using
PAL_Initialize that are on the current PATH just by their file names,
they were crashing with SIGSEGV.
The problem was that the current exe path was extracted incorrectly
in that case.

This change fixes the problem using the same methods that corerun
uses to figure out the current exe path.

* Add missing FreeBSD headers

* Reflect PR feedback

* Update src/coreclr/src/pal/src/init/pal.cpp

* Fix typo in Solaris version of the code

Co-authored-by: Jan Kotas <[email protected]>
  • Loading branch information
janvorli and jkotas authored Aug 15, 2020
1 parent 732ea74 commit 75495b7
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 25 deletions.
1 change: 1 addition & 0 deletions src/coreclr/src/pal/src/config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#cmakedefine01 HAVE_PTHREAD_NP_H
#cmakedefine01 HAVE_AUXV_HWCAP_H
#cmakedefine01 HAVE_SYS_PTRACE_H
#cmakedefine01 HAVE_GETAUXVAL

#cmakedefine01 HAVE_KQUEUE
#cmakedefine01 HAVE_PTHREAD_SUSPEND
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/src/pal/src/configure.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ check_include_files(numa.h HAVE_NUMA_H)
check_include_files(pthread_np.h HAVE_PTHREAD_NP_H)
check_include_files("sys/auxv.h;asm/hwcap.h" HAVE_AUXV_HWCAP_H)
check_include_files("sys/ptrace.h" HAVE_SYS_PTRACE_H)
check_symbol_exists(getauxval sys/auxv.h HAVE_GETAUXVAL)

set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_DL_LIBS})

Expand Down
176 changes: 151 additions & 25 deletions src/coreclr/src/pal/src/init/pal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,17 @@ int CacheLineSize;
#endif
#endif

#if defined(__FreeBSD__)
#include <sys/types.h>
#include <sys/param.h>
#endif
#if HAVE_GETAUXVAL
#include <sys/auxv.h>
#endif
#if HAVE_SYS_SYSCTL_H || defined(__FreeBSD__)
#include <sys/sysctl.h>
#endif

#include <algorithm>

using namespace CorUnix;
Expand Down Expand Up @@ -125,7 +136,7 @@ static DWORD g_initializeDLLFlags = PAL_INITIALIZE_DLL;
static int Initialize(int argc, const char *const argv[], DWORD flags);
static BOOL INIT_IncreaseDescriptorLimit(void);
static LPWSTR INIT_FormatCommandLine (int argc, const char * const *argv);
static LPWSTR INIT_ConvertEXEPath(LPCSTR exe_name);
static LPWSTR INIT_GetCurrentEXEPath();
static BOOL INIT_SharedFilesPath(void);

#ifdef _DEBUG
Expand Down Expand Up @@ -560,7 +571,7 @@ Initialize(
}

/* find out the application's full path */
exe_path = INIT_ConvertEXEPath(argv[0]);
exe_path = INIT_GetCurrentEXEPath();
if (NULL == exe_path)
{
ERROR("Unable to find exe path\n");
Expand Down Expand Up @@ -1265,45 +1276,160 @@ static LPWSTR INIT_FormatCommandLine (int argc, const char * const *argv)
return retval;
}

#if defined(__linux__)
#define symlinkEntrypointExecutable "/proc/self/exe"
#elif !defined(__APPLE__)
#define symlinkEntrypointExecutable "/proc/curproc/exe"
#endif

bool GetAbsolutePath(const char* path, PathCharString& absolutePath)
{
bool result = false;

char realPath[PATH_MAX];
if (realpath(path, realPath) != nullptr && realPath[0] != '\0')
{
absolutePath.Set(realPath, strlen(realPath));
// realpath should return canonicalized path without the trailing slash
_ASSERTE(absolutePath[absolutePath.GetCount() - 1] != '/');

result = true;
}

return result;
}

bool GetEntrypointExecutableAbsolutePath(PathCharString& entrypointExecutable)
{
bool result = false;

entrypointExecutable.Clear();

// Get path to the executable for the current process using
// platform specific means.
#if defined(__APPLE__)

// On Mac, we ask the OS for the absolute path to the entrypoint executable
uint32_t lenActualPath = 0;
if (_NSGetExecutablePath(nullptr, &lenActualPath) == -1)
{
// OSX has placed the actual path length in lenActualPath,
// so re-attempt the operation
PathCharString resizedPath;
char *pResizedPath = resizedPath.OpenStringBuffer(lenActualPath);
if (_NSGetExecutablePath(pResizedPath, &lenActualPath) == 0)
{
resizedPath.CloseBuffer(lenActualPath - 1);
entrypointExecutable.Set(resizedPath);
result = true;
}
}
#elif defined (__FreeBSD__)
static const int name[] =
{
CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1
};
char path[PATH_MAX];
size_t len;

len = sizeof(path);
if (sysctl(name, 4, path, &len, nullptr, 0) == 0)
{
entrypointExecutable.Set(path, len);
result = true;
}
else
{
// ENOMEM
result = false;
}
#elif defined(__NetBSD__) && defined(KERN_PROC_PATHNAME)
static const int name[] =
{
CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME,
};
char path[MAXPATHLEN];
size_t len;

len = sizeof(path);
if (sysctl(name, __arraycount(name), path, &len, NULL, 0) != -1)
{
entrypointExecutable.Set(path, len);
result = true;
}
else
{
result = false;
}
#elif defined(__sun)
const char *path;
if ((path = getexecname()) == NULL)
{
result = false;
}
else if (*path != '/')
{
char *cwd;
if ((cwd = getcwd(NULL, PATH_MAX)) == NULL)
{
result = false;
}
else
{
entrypointExecutable.Set(cwd, strlen(cwd));
entrypointExecutable.Append('/');
entrypointExecutable.Append(path);

result = true;
free(cwd);
}
}
else
{
entrypointExecutable.Set(path, strlen(path));
result = true;
}
#else

#if HAVE_GETAUXVAL && defined(AT_EXECFN)
const char *execfn = (const char *)getauxval(AT_EXECFN);

if (execfn)
{
entrypointExecutable.Set(execfn, strlen(execfn));
result = true;
}
else
#endif
// On other OSs, return the symlink that will be resolved by GetAbsolutePath
// to fetch the entrypoint EXE absolute path, inclusive of filename.
result = GetAbsolutePath(symlinkEntrypointExecutable, entrypointExecutable);
#endif

return result;
}

/*++
Function:
INIT_ConvertEXEPath
INIT_GetCurrentEXEPath
Abstract:
Check whether the executable path is valid, and convert its type (LPCSTR -> LPWSTR)
Parameters:
LPCSTR exe_name : full path of the current executable
Get the current exe path
Return:
pointer to buffer containing the full path. This buffer must be released
by the caller using free()
Notes :
this function assumes that "exe_name" is in Unix style (no \)
--*/
static LPWSTR INIT_ConvertEXEPath(LPCSTR exe_path)
static LPWSTR INIT_GetCurrentEXEPath()
{
PathCharString real_path;
LPWSTR return_value;
INT return_size;
struct stat theStats;

if (!strchr(exe_path, '/'))
{
ERROR( "The exe path is not fully specified\n" );
return NULL;
}

if (-1 == stat(exe_path, &theStats))
{
ERROR( "The file does not exist\n" );
return NULL;
}

if (!CorUnix::RealPathHelper(exe_path, real_path))
if (!GetEntrypointExecutableAbsolutePath(real_path))
{
ERROR("realpath() failed!\n");
ERROR( "Cannot get current exe path\n" );
return NULL;
}

Expand Down

0 comments on commit 75495b7

Please sign in to comment.