Path: blob/master/thirdparty/openxr/src/common/platform_utils.hpp
9903 views
// Copyright (c) 2017-2025 The Khronos Group Inc.1// Copyright (c) 2017-2019 Valve Corporation2// Copyright (c) 2017-2019 LunarG, Inc.3//4// SPDX-License-Identifier: Apache-2.0 OR MIT5//6// Initial Authors: Mark Young <[email protected]>, Dave Houlton <[email protected]>7//89#pragma once1011#include "xr_dependencies.h"12#include <string>13#include <stdint.h>14#include <stdlib.h>1516// OpenXR paths and registry key locations17#define OPENXR_RELATIVE_PATH "openxr/"18#define OPENXR_IMPLICIT_API_LAYER_RELATIVE_PATH "/api_layers/implicit.d"19#define OPENXR_EXPLICIT_API_LAYER_RELATIVE_PATH "/api_layers/explicit.d"20#ifdef XR_OS_WINDOWS21#define OPENXR_REGISTRY_LOCATION "SOFTWARE\\Khronos\\OpenXR\\"22#define OPENXR_IMPLICIT_API_LAYER_REGISTRY_LOCATION "\\ApiLayers\\Implicit"23#define OPENXR_EXPLICIT_API_LAYER_REGISTRY_LOCATION "\\ApiLayers\\Explicit"24#endif2526// OpenXR Loader environment variables of interest27#define OPENXR_RUNTIME_JSON_ENV_VAR "XR_RUNTIME_JSON"28#define OPENXR_API_LAYER_PATH_ENV_VAR "XR_API_LAYER_PATH"2930// This is a CMake generated file with #defines for any functions/includes31// that it found present and build-time configuration.32// If you don't have this file, on non-Windows you'll need to define33// one of HAVE_SECURE_GETENV or HAVE___SECURE_GETENV depending on which34// of secure_getenv or __secure_getenv are present35#ifdef OPENXR_HAVE_COMMON_CONFIG36#include "common_config.h"37#endif // OPENXR_HAVE_COMMON_CONFIG3839#if defined(__x86_64__) && defined(__ILP32__)40#define XR_ARCH_ABI "x32"41#elif defined(_M_X64) || defined(__x86_64__)42#define XR_ARCH_ABI "x86_64"43#elif defined(_M_IX86) || defined(__i386__) || defined(_X86_)44#define XR_ARCH_ABI "i686"45#elif (defined(__aarch64__) && defined(__LP64__)) || defined(_M_ARM64)46#define XR_ARCH_ABI "aarch64"47#elif (defined(__ARM_ARCH) && __ARM_ARCH >= 7 && (defined(__ARM_PCS_VFP) || defined(__ANDROID__))) || defined(_M_ARM)48#define XR_ARCH_ABI "armv7a-vfp"49#elif defined(__ARM_ARCH_5TE__) || (defined(__ARM_ARCH) && __ARM_ARCH > 5)50#define XR_ARCH_ABI "armv5te"51#elif defined(__mips64)52#define XR_ARCH_ABI "mips64"53#elif defined(__mips)54#define XR_ARCH_ABI "mips"55#elif defined(__powerpc64__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__56#define XR_ARCH_ABI "ppc64"57#elif defined(__powerpc__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__58#define XR_ARCH_ABI "ppc64el"59#elif defined(__s390x__) || defined(__zarch__)60#define XR_ARCH_ABI "s390x"61#elif defined(__hppa__)62#define XR_ARCH_ABI "hppa"63#elif defined(__alpha__)64#define XR_ARCH_ABI "alpha"65#elif defined(__ia64__) || defined(_M_IA64)66#define XR_ARCH_ABI "ia64"67#elif defined(__m68k__)68#define XR_ARCH_ABI "m68k"69#elif defined(__riscv_xlen) && (__riscv_xlen == 64)70#define XR_ARCH_ABI "riscv64"71#elif defined(__sparc__) && defined(__arch64__)72#define XR_ARCH_ABI "sparc64"73#elif defined(__loongarch64)74#define XR_ARCH_ABI "loongarch64"75#else76#error "No architecture string known!"77#endif7879// Consumers of this file must ensure this function is implemented. For example, the loader will implement this function so that it80// can route messages through the loader's logging system.81void LogPlatformUtilsError(const std::string& message);8283// Environment variables84#if defined(XR_OS_LINUX) || defined(XR_OS_APPLE)8586#include <unistd.h>87#include <fcntl.h>88#include <iostream>89#include <sys/stat.h>9091namespace detail {9293static inline char* ImplGetEnv(const char* name) { return getenv(name); }9495// clang-format off96static inline char* ImplGetSecureEnv(const char* name) {97#ifdef HAVE_SECURE_GETENV98return secure_getenv(name);99#elif defined(HAVE___SECURE_GETENV)100return __secure_getenv(name);101#else102#pragma message( \103"Warning: Falling back to non-secure getenv for environmental" \104"lookups! Consider updating to a different libc.")105106return ImplGetEnv(name);107#endif108}109// clang-format on110111} // namespace detail112113#endif // defined(XR_OS_LINUX) || defined(XR_OS_APPLE)114115#if defined(XR_OS_ANDROID) || defined(XR_OS_APPLE)116117#include <sys/stat.h>118119namespace detail {120121static inline bool ImplTryRuntimeFilename(const char* rt_dir_prefix, uint16_t major_version, std::string& file_name) {122auto decorated_path = rt_dir_prefix + std::to_string(major_version) + "/active_runtime." XR_ARCH_ABI ".json";123auto undecorated_path = rt_dir_prefix + std::to_string(major_version) + "/active_runtime.json";124125struct stat buf {};126if (0 == stat(decorated_path.c_str(), &buf)) {127file_name = decorated_path;128return true;129}130if (0 == stat(undecorated_path.c_str(), &buf)) {131file_name = undecorated_path;132return true;133}134return false;135}136137} // namespace detail138#endif // defined(XR_OS_ANDROID) || defined(XR_OS_APPLE)139#if defined(XR_OS_LINUX)140141static inline std::string PlatformUtilsGetEnv(const char* name) {142auto str = detail::ImplGetEnv(name);143if (str == nullptr) {144return {};145}146return str;147}148149static inline std::string PlatformUtilsGetSecureEnv(const char* name) {150auto str = detail::ImplGetSecureEnv(name);151if (str == nullptr) {152str = detail::ImplGetEnv(name);153if (str != nullptr && !std::string(str).empty()) {154LogPlatformUtilsError(std::string("!!! WARNING !!! Environment variable ") + name +155" is being ignored due to running with secure execution. The value '" + str +156"' will NOT be used.");157}158return {};159}160return str;161}162163static inline bool PlatformUtilsGetEnvSet(const char* name) { return detail::ImplGetEnv(name) != nullptr; }164165#elif defined(XR_OS_APPLE)166167static inline std::string PlatformUtilsGetEnv(const char* name) {168auto str = detail::ImplGetEnv(name);169if (str == nullptr) {170return {};171}172return str;173}174175static inline std::string PlatformUtilsGetSecureEnv(const char* name) {176auto str = detail::ImplGetSecureEnv(name);177if (str == nullptr) {178return {};179}180return str;181}182183static inline bool PlatformUtilsGetEnvSet(const char* name) { return detail::ImplGetEnv(name) != nullptr; }184185static inline bool PlatformGetGlobalRuntimeFileName(uint16_t major_version, std::string& file_name) {186return detail::ImplTryRuntimeFilename("/usr/local/share/openxr/", major_version, file_name);187}188189#elif defined(XR_OS_WINDOWS)190191inline std::wstring utf8_to_wide(const std::string& utf8Text) {192if (utf8Text.empty()) {193return {};194}195196std::wstring wideText;197const int wideLength = ::MultiByteToWideChar(CP_UTF8, 0, utf8Text.data(), (int)utf8Text.size(), nullptr, 0);198if (wideLength == 0) {199LogPlatformUtilsError("utf8_to_wide get size error: " + std::to_string(::GetLastError()));200return {};201}202203// MultiByteToWideChar returns number of chars of the input buffer, regardless of null terminator204wideText.resize(wideLength, 0);205wchar_t* wideString = const_cast<wchar_t*>(wideText.data()); // mutable data() only exists in c++17206const int length = ::MultiByteToWideChar(CP_UTF8, 0, utf8Text.data(), (int)utf8Text.size(), wideString, wideLength);207if (length != wideLength) {208LogPlatformUtilsError("utf8_to_wide convert string error: " + std::to_string(::GetLastError()));209return {};210}211212return wideText;213}214215inline std::string wide_to_utf8(const std::wstring& wideText) {216if (wideText.empty()) {217return {};218}219220std::string narrowText;221int narrowLength = ::WideCharToMultiByte(CP_UTF8, 0, wideText.data(), (int)wideText.size(), nullptr, 0, nullptr, nullptr);222if (narrowLength == 0) {223LogPlatformUtilsError("wide_to_utf8 get size error: " + std::to_string(::GetLastError()));224return {};225}226227// WideCharToMultiByte returns number of chars of the input buffer, regardless of null terminator228narrowText.resize(narrowLength, 0);229char* narrowString = const_cast<char*>(narrowText.data()); // mutable data() only exists in c++17230const int length =231::WideCharToMultiByte(CP_UTF8, 0, wideText.data(), (int)wideText.size(), narrowString, narrowLength, nullptr, nullptr);232if (length != narrowLength) {233LogPlatformUtilsError("wide_to_utf8 convert string error: " + std::to_string(::GetLastError()));234return {};235}236237return narrowText;238}239240// Returns true if the current process has an integrity level > SECURITY_MANDATORY_MEDIUM_RID.241static inline bool IsHighIntegrityLevel() {242// Execute this check once and save the value as a static bool.243static bool isHighIntegrityLevel = ([] {244HANDLE processToken;245if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_QUERY_SOURCE, &processToken)) {246// Maximum possible size of SID_AND_ATTRIBUTES is maximum size of a SID + size of attributes DWORD.247uint8_t mandatoryLabelBuffer[SECURITY_MAX_SID_SIZE + sizeof(DWORD)]{};248DWORD bufferSize;249if (GetTokenInformation(processToken, TokenIntegrityLevel, mandatoryLabelBuffer, sizeof(mandatoryLabelBuffer),250&bufferSize) != 0) {251const auto mandatoryLabel = reinterpret_cast<const TOKEN_MANDATORY_LABEL*>(mandatoryLabelBuffer);252if (mandatoryLabel->Label.Sid != 0) {253const DWORD subAuthorityCount = *GetSidSubAuthorityCount(mandatoryLabel->Label.Sid);254const DWORD integrityLevel = *GetSidSubAuthority(mandatoryLabel->Label.Sid, subAuthorityCount - 1);255CloseHandle(processToken);256return integrityLevel > SECURITY_MANDATORY_MEDIUM_RID;257}258}259260CloseHandle(processToken);261}262263return false;264})();265266return isHighIntegrityLevel;267}268269// Returns true if the given environment variable exists.270// The name is a case-sensitive UTF8 string.271static inline bool PlatformUtilsGetEnvSet(const char* name) {272const std::wstring wname = utf8_to_wide(name);273const DWORD valSize = ::GetEnvironmentVariableW(wname.c_str(), nullptr, 0);274// GetEnvironmentVariable returns 0 when environment variable does not exist or there is an error.275return 0 != valSize;276}277278// Returns the environment variable value for the given name.279// Returns an empty string if the environment variable doesn't exist or if it exists but is empty.280// Use PlatformUtilsGetEnvSet to tell if it exists.281// The name is a case-sensitive UTF8 string.282static inline std::string PlatformUtilsGetEnv(const char* name) {283const std::wstring wname = utf8_to_wide(name);284const DWORD valSize = ::GetEnvironmentVariableW(wname.c_str(), nullptr, 0);285// GetEnvironmentVariable returns 0 when environment variable does not exist or there is an error.286// The size includes the null-terminator, so a size of 1 is means the variable was explicitly set to empty.287if (valSize == 0 || valSize == 1) {288return {};289}290291// GetEnvironmentVariable returns size including null terminator for "query size" call.292std::wstring wValue(valSize, 0);293wchar_t* wValueData = &wValue[0];294295// GetEnvironmentVariable returns string length, excluding null terminator for "get value"296// call if there was enough capacity. Else it returns the required capacity (including null terminator).297const DWORD length = ::GetEnvironmentVariableW(wname.c_str(), wValueData, (DWORD)wValue.size());298if ((length == 0) || (length >= wValue.size())) { // If error or the variable increased length between calls...299LogPlatformUtilsError("GetEnvironmentVariable get value error: " + std::to_string(::GetLastError()));300return {};301}302303wValue.resize(length); // Strip the null terminator.304305return wide_to_utf8(wValue);306}307308// Acts the same as PlatformUtilsGetEnv except returns an empty string if IsHighIntegrityLevel.309static inline std::string PlatformUtilsGetSecureEnv(const char* name) {310// No secure version for Windows so the below integrity check is needed.311const std::string envValue = PlatformUtilsGetEnv(name);312313// Do not allow high integrity processes to act on data that can be controlled by medium integrity processes.314// Specifically, medium integrity processes can set environment variables which could then315// be read by high integrity processes.316if (IsHighIntegrityLevel()) {317if (!envValue.empty()) {318LogPlatformUtilsError(std::string("!!! WARNING !!! Environment variable ") + name +319" is being ignored due to running from an elevated context. The value '" + envValue +320"' will NOT be used.");321}322return {};323}324325return envValue;326}327328#elif defined(XR_OS_ANDROID)329330#include <sys/system_properties.h>331332static inline bool PlatformUtilsGetEnvSet(const char* /* name */) {333// Stub func334return false;335}336337static inline std::string PlatformUtilsGetEnv(const char* /* name */) {338// Stub func339return {};340}341342static inline std::string PlatformUtilsGetSecureEnv(const char* /* name */) {343// Stub func344return {};345}346347// Intended to be only used as a fallback on Android, with a more open, "native" technique used in most cases348static inline bool PlatformGetGlobalRuntimeFileName(uint16_t major_version, std::string& file_name) {349// Prefix for the runtime JSON file name350static const char* rt_dir_prefixes[] = {"/product", "/odm", "/oem", "/vendor", "/system"};351352static const std::string subdir = "/etc/openxr/";353for (const auto prefix : rt_dir_prefixes) {354const std::string rt_dir_prefix = prefix + subdir;355if (detail::ImplTryRuntimeFilename(rt_dir_prefix.c_str(), major_version, file_name)) {356return true;357}358}359360return false;361}362363// Android system properties are sufficiently different from environment variables that we are not re-using364// PlatformUtilsGetEnv for this purpose365static inline std::string PlatformUtilsGetAndroidSystemProperty(const char* name) {366std::string result;367const prop_info* pi = __system_property_find(name);368if (pi == nullptr) {369return {};370}371372#if __ANDROID_API__ >= 26373// use callback to retrieve > 92 character sys prop values, if available374__system_property_read_callback(375pi,376[](void* cookie, const char*, const char* value, uint32_t) {377auto property_value = reinterpret_cast<std::string*>(cookie);378*property_value = value;379},380reinterpret_cast<void*>(&result));381#endif // __ANDROID_API__ >= 26382// fallback to __system_property_get if no value retrieved via callback383if (result.empty()) {384char value[PROP_VALUE_MAX] = {};385if (__system_property_get(name, value) != 0) {386result = value;387}388}389390return result;391}392393#else // Not Linux, Apple, nor Windows394395static inline bool PlatformUtilsGetEnvSet(const char* /* name */) {396// Stub func397return false;398}399400static inline std::string PlatformUtilsGetEnv(const char* /* name */) {401// Stub func402return {};403}404405static inline std::string PlatformUtilsGetSecureEnv(const char* /* name */) {406// Stub func407return {};408}409410static inline bool PlatformGetGlobalRuntimeFileName(uint16_t /* major_version */, std::string const& /* file_name */) {411// Stub func412return false;413}414415#endif416417418