blob: 82afeaa5500bcc338dfc036b7e431b4f5438de76 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <unistd.h>
#include <climits>
#include <cstdio>
#include <cstdlib>
#include <filesystem>
#include <string>
#include <vector>
#ifdef __APPLE__
#include <mach-o/dyld.h>
#endif
#ifndef EXECUTABLE
#error FATAL: EXECUTABLE not defined!
#endif
#ifndef VK_LAYER_PATH
#error Fatal: VK_LAYER_PATH not defined!
#endif
#ifndef VK_LIB_PATH
#error Fatal: VK_LIB_PATH not defined!
#endif
#ifdef __APPLE__
#define LD_LIBRARY_PATH_ENV_NAME "DYLD_LIBRARY_PATH"
#else
#define LD_LIBRARY_PATH_ENV_NAME "LD_LIBRARY_PATH"
#endif
namespace {
std::filesystem::path GetSelfDirectory() {
std::string result;
#if defined(__APPLE__)
// Executable path can have relative references ("..") depending on how the
// app was launched.
uint32_t length = 0;
_NSGetExecutablePath(nullptr, &length);
result.resize(length);
_NSGetExecutablePath(&result[0], &length);
result.resize(length - 1); // Length included terminator.
#elif defined(__linux__)
// The realpath() call below will resolve the symbolic link.
result.assign("/proc/self/exe");
#else
#error Write this for your platform.
#endif
char fullpath[4096];
return std::filesystem::path(realpath(result.c_str(), fullpath)).remove_filename();
}
// Return true iff |*env| is an envp entry for |varname|.
bool EnvironmentHasVarname(const char* env, const char* varname) {
std::string prefix = std::string(varname) + "=";
return !memcmp(prefix.c_str(), env, prefix.size());
}
} // namespace
int main(int argc, char** argv, char** envp) {
auto self_dir = GetSelfDirectory();
auto vk_lib_path = self_dir / VK_LIB_PATH;
auto vk_layer_path = self_dir / VK_LAYER_PATH;
#ifdef VK_ICD_PATH
auto vk_icd_path = self_dir / VK_ICD_PATH;
#endif
auto executable_path = self_dir / EXECUTABLE;
// If LD_LIBRARY_PATH was previously defined, we append prebuilt library path
// to it so that predefined library path will have priority in library lookup.
// Otherwise, we just set it to the prebuilt library and avoid adding a ":"
// prefix to avoid looking up libraries in the current working directory.
const char* ld_library_path_cstr = getenv(LD_LIBRARY_PATH_ENV_NAME);
std::string ld_library_path = ld_library_path_cstr
? std::string(ld_library_path_cstr) + ":" + vk_lib_path.string()
: vk_lib_path.string();
// Set up envp.
std::vector<char*> environment_pointers;
std::vector<std::string> environments = {
std::string(LD_LIBRARY_PATH_ENV_NAME "=") + ld_library_path,
std::string("VK_LAYER_PATH=") + vk_layer_path.string(),
#ifdef VK_ICD_PATH
std::string("VK_ICD_FILENAMES=") + vk_icd_path.string(),
#endif
};
for (const std::string& env : environments) {
environment_pointers.push_back(const_cast<char*>(env.c_str()));
}
// And environment variables from host environment.
for (char** env = envp; *env; env++) {
bool filter_out = EnvironmentHasVarname(*env, LD_LIBRARY_PATH_ENV_NAME) ||
EnvironmentHasVarname(*env, "VK_LAYER_PATH");
#ifdef VK_ICD_PATH
filter_out = filter_out || EnvironmentHasVarname(*env, "VK_ICD_FILENAMES");
#endif
if (!filter_out) {
environment_pointers.push_back(*env);
}
}
environment_pointers.push_back(nullptr);
// Set up argv.
std::vector<char*> arguments;
arguments.push_back(const_cast<char*>(executable_path.c_str()));
for (int i = 1; i < argc; i++) {
arguments.push_back(argv[i]);
}
arguments.push_back(nullptr);
execve(arguments[0], arguments.data(), environment_pointers.data());
// This should be reached only if execve() fails.
perror("execve() fails");
exit(EXIT_FAILURE);
}