dtoolutil: Overhaul ExecutionEnvironment's dtool path hunting code

The main change here is it uses an array of expected filenames,
which optionally itself feeds off of a compiler definition, rather
than hardcoding the expected filenames straight into the search
code.

The other change is this code is omitted when building statically.
This commit is contained in:
Sam Edwards 2019-06-02 00:43:41 -06:00
parent 83c10d1a0b
commit 2f97b76b42

View File

@ -85,6 +85,44 @@ extern char **GLOBAL_ARGV;
extern int GLOBAL_ARGC;
#endif
// One of the responsibilities of ExecutionEnvironment is to determine the path
// to the binary file that contains itself (this is useful for making other
// components able to read files relative to Panda's installation directory).
// When built statically, this is easy - just use the main executable filename.
// When built shared, ExecutionEnvironment will introspect the memory map of
// the running process to look for dynamic library paths matching this list of
// predetermined filenames (ordered most likely to least likely).
#ifndef LINK_ALL_STATIC
static const char *const libp3dtool_filenames[] = {
#if defined(LIBP3DTOOL_FILENAMES)
// The build system is communicating the expected filename(s) for the
// libp3dtool dynamic library - no guesswork needed.
LIBP3DTOOL_FILENAMES
#elif defined(WIN32_VC)
#ifdef _DEBUG
"libp3dtool_d.dll",
#else
"libp3dtool.dll",
#endif
#elif defined(__APPLE__)
"libp3dtool." PANDA_ABI_VERSION_STR ".dylib",
"libp3dtool.dylib",
#else
"libp3dtool.so." PANDA_ABI_VERSION_STR,
"libp3dtool.so",
#endif
};
#endif /* !LINK_ALL_STATIC */
// Linux with GNU libc does have global argvargc variables, but we can't
// safely access them at stat init time--at least, not in libc5. (It does seem
// to work with glibc2, however.)
@ -546,13 +584,14 @@ read_args() {
// First, we need to fill in _dtool_name. This contains the full path to
// the p3dtool library.
#ifdef WIN32_VC
#ifdef _DEBUG
HMODULE dllhandle = GetModuleHandle("libp3dtool_d.dll");
#else
HMODULE dllhandle = GetModuleHandle("libp3dtool.dll");
#endif
if (dllhandle != 0) {
#ifndef LINK_ALL_STATIC
#if defined(WIN32_VC)
for (const char *filename : libp3dtool_filenames) {
if (!_dtool_name.empty()) break;
HMODULE dllhandle = GetModuleHandle(filename);
if (!dllhandle) continue;
static const DWORD buffer_size = 1024;
wchar_t buffer[buffer_size];
DWORD size = GetModuleFileNameW(dllhandle, buffer, buffer_size);
@ -562,46 +601,44 @@ read_args() {
_dtool_name = tmp;
}
}
#endif
#if defined(__APPLE__)
#elif defined(__APPLE__)
// And on OSX we don't have procselfmaps, but some _dyld_* functions.
if (_dtool_name.empty()) {
uint32_t ic = _dyld_image_count();
for (uint32_t i = 0; i < ic; ++i) {
const char *buffer = _dyld_get_image_name(i);
const char *tail = strrchr(buffer, '/');
if (tail && (strcmp(tail, "/libp3dtool." PANDA_ABI_VERSION_STR ".dylib") == 0
|| strcmp(tail, "/libp3dtool.dylib") == 0)) {
uint32_t ic = _dyld_image_count();
for (uint32_t i = 0; i < ic; ++i) {
if (!_dtool_name.empty()) break;
const char *buffer = _dyld_get_image_name(i);
if (!buffer) continue;
const char *tail = strrchr(buffer, '/');
if (!tail) continue;
for (const char *filename : libp3dtool_filenames) {
if (strcmp(&tail[1], filename) == 0) {
_dtool_name = buffer;
break;
}
}
}
#endif
#if defined(RTLD_DI_ORIGIN)
#elif defined(RTLD_DI_ORIGIN)
// When building with glibc/uClibc, we typically have access to RTLD_DI_ORIGIN in Unix-like operating systems.
char origin[PATH_MAX + 1];
if (_dtool_name.empty()) {
void *dtool_handle = dlopen("libp3dtool.so." PANDA_ABI_VERSION_STR, RTLD_NOW | RTLD_NOLOAD);
for (const char *filename : libp3dtool_filenames) {
if (!_dtool_name.empty()) break;
void *dtool_handle = dlopen(filename, RTLD_NOW | RTLD_NOLOAD);
if (dtool_handle != nullptr && dlinfo(dtool_handle, RTLD_DI_ORIGIN, origin) != -1) {
_dtool_name = origin;
_dtool_name += "/libp3dtool.so." PANDA_ABI_VERSION_STR;
} else {
// Try the version of libp3dtool.so without ABI suffix.
dtool_handle = dlopen("libp3dtool.so", RTLD_NOW | RTLD_NOLOAD);
if (dtool_handle != nullptr && dlinfo(dtool_handle, RTLD_DI_ORIGIN, origin) != -1) {
_dtool_name = origin;
_dtool_name += "/libp3dtool.so";
}
_dtool_name += '/';
_dtool_name += filename;
}
}
#endif
#if !defined(RTLD_DI_ORIGIN) && defined(RTLD_DI_LINKMAP)
#elif defined(RTLD_DI_LINKMAP)
// On platforms without RTLD_DI_ORIGIN, we can use dlinfo with RTLD_DI_LINKMAP to get the origin of a loaded library.
if (_dtool_name.empty()) {
struct link_map *map;
@ -612,12 +649,20 @@ read_args() {
#endif
if (dlinfo(self, RTLD_DI_LINKMAP, &map)) {
while (map != nullptr) {
const char *tail = strrchr(map->l_name, '/');
if (!_dtool_name.empty()) break;
const char *head = strchr(map->l_name, '/');
if (tail && head && (strcmp(tail, "/libp3dtool.so." PANDA_ABI_VERSION_STR) == 0
|| strcmp(tail, "/libp3dtool.so") == 0)) {
_dtool_name = head;
if (!head) continue;
const char *tail = strrchr(head, '/');
if (!tail) continue;
for (const char *filename : libp3dtool_filenames) {
if (strcmp(&tail[1], filename) == 0) {
_dtool_name = head;
break;
}
}
map = map->l_next;
}
}
@ -634,19 +679,27 @@ read_args() {
pifstream maps("/proc/self/maps");
#endif
while (!maps.fail() && !maps.eof()) {
if (!_dtool_name.empty()) break;
char buffer[PATH_MAX];
buffer[0] = 0;
maps.getline(buffer, PATH_MAX);
const char *tail = strrchr(buffer, '/');
const char *head = strchr(buffer, '/');
if (tail && head && (strcmp(tail, "/libp3dtool.so." PANDA_ABI_VERSION_STR) == 0
|| strcmp(tail, "/libp3dtool.so") == 0)) {
_dtool_name = head;
if (!head) continue;
const char *tail = strrchr(head, '/');
if (!tail) continue;
for (const char *filename : libp3dtool_filenames) {
if (strcmp(&tail[1], filename) == 0) {
_dtool_name = head;
break;
}
}
}
maps.close();
}
#endif
#endif /* !LINK_ALL_STATIC */
// Now, we need to fill in _binary_name. This contains the full path to the
// currently running executable.