From 820fc25d1d2ff768783e6c2abe24242e9b9d701d Mon Sep 17 00:00:00 2001 From: Charles Giessen Date: Mon, 7 Jun 2021 20:08:16 -0600 Subject: [PATCH] test: Fix framework to work on MacOS Main issue was getting function redireciton to work, dyld-interposer accomplishes that. It required making the shim-library a dylib, but otherwise only required minor changes. Change-Id: I7b7e2c86cc0c0b082f58241a7e146b74af72e792 --- tests/framework/CMakeLists.txt | 4 +- tests/framework/shim/CMakeLists.txt | 9 +-- tests/framework/shim/shim.h | 10 +-- .../shim/{linux_shim.cpp => unix_shim.cpp} | 65 +++++++++++++------ tests/framework/test_environment.cpp | 4 +- tests/framework/test_util.cpp | 2 +- 6 files changed, 60 insertions(+), 34 deletions(-) rename tests/framework/shim/{linux_shim.cpp => unix_shim.cpp} (63%) diff --git a/tests/framework/CMakeLists.txt b/tests/framework/CMakeLists.txt index 2bf55d31..f83163fc 100644 --- a/tests/framework/CMakeLists.txt +++ b/tests/framework/CMakeLists.txt @@ -17,7 +17,7 @@ add_library(testing_framework_util STATIC test_util.cpp test_util.h) target_link_libraries(testing_framework_util PUBLIC Vulkan::Headers) -if(UNIX) +if(UNIX OR APPLE) target_link_libraries(testing_framework_util PUBLIC ${CMAKE_DL_LIBS}) endif() @@ -30,7 +30,7 @@ target_compile_options(testing_framework_util INTERFACE $<$:-DVK_USE_PLATFORM_XLIB_KHR> $<$:-DVK_USE_PLATFORM_WAYLAND_KHR>> ) -if(UNIX AND NOT APPLE) +if(UNIX) target_compile_options(testing_framework_util PUBLIC -fPIC) endif() target_include_directories(testing_framework_util PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/tests/framework/shim/CMakeLists.txt b/tests/framework/shim/CMakeLists.txt index 78bffc83..9bfc8562 100644 --- a/tests/framework/shim/CMakeLists.txt +++ b/tests/framework/shim/CMakeLists.txt @@ -21,10 +21,11 @@ if (WIN32) target_include_directories(shim-library PUBLIC ${CMAKE_SOURCE_DIR}/loader ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) elseif(UNIX) - add_library(shim-library STATIC shim.h linux_shim.cpp) + if(APPLE) + add_library(shim-library SHARED shim.h unix_shim.cpp) + else() + add_library(shim-library STATIC shim.h unix_shim.cpp) + endif() target_link_libraries(shim-library PRIVATE testing_framework_util) target_include_directories(shim-library PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) - -elseif(APPLE) - endif() \ No newline at end of file diff --git a/tests/framework/shim/shim.h b/tests/framework/shim/shim.h index 30614d88..4de9ea6a 100644 --- a/tests/framework/shim/shim.h +++ b/tests/framework/shim/shim.h @@ -148,17 +148,17 @@ struct PlatformShim { }; std::vector parse_env_var_list(std::string const& var); - -// dynamically link on windows -#if defined(WIN32) +extern "C" { +// dynamically link on windows and macos +#if defined(WIN32) || defined(__APPLE__) using PFN_get_platform_shim = PlatformShim& (*)(); #define GET_PLATFORM_SHIM_STR "get_platform_shim" -#elif defined(__linux__) || defined(__APPLE__) +#elif defined(__linux__) // statically link on linux PlatformShim& get_platform_shim(); #endif - +} // Functions which are called by the Test Framework need a definition, but since the shim is a DLL, this // would require loading the functions before using. To get around this, most PlatformShim functions are member // functions, so only the `get_platform_shim()` is needed to be loaded. As a consequence, all the member functions diff --git a/tests/framework/shim/linux_shim.cpp b/tests/framework/shim/unix_shim.cpp similarity index 63% rename from tests/framework/shim/linux_shim.cpp rename to tests/framework/shim/unix_shim.cpp index d4513ad6..2a27c98a 100644 --- a/tests/framework/shim/linux_shim.cpp +++ b/tests/framework/shim/unix_shim.cpp @@ -28,37 +28,53 @@ #include "shim.h" static PlatformShim platform_shim; - +extern "C" { +#if defined(__linux__) PlatformShim& get_platform_shim() { platform_shim = PlatformShim(); return platform_shim; } +#elif defined(__APPLE__) +FRAMEWORK_EXPORT PlatformShim &get_platform_shim() { + platform_shim = PlatformShim(); + return platform_shim; +} +#endif -extern "C" { +// Necessary for MacOS function himming +#if defined(__linux__) +#define OPENDIR_FUNC_NAME opendir +#define ACCESS_FUNC_NAME access +#define FOPEN_FUNC_NAME fopen +#elif defined(__APPLE__) +#define OPENDIR_FUNC_NAME my_opendir +#define ACCESS_FUNC_NAME my_access +#define FOPEN_FUNC_NAME my_fopen +#endif using PFN_OPENDIR = DIR* (*)(const char* path_name); +using PFN_ACCESS = int (*)(const char* pathname, int mode); +using PFN_FOPEN = FILE* (*)(const char* filename, const char* mode); -FRAMEWORK_EXPORT DIR* opendir(const char* path_name) { - static PFN_OPENDIR real_opendir = nullptr; +static PFN_OPENDIR real_opendir = nullptr; +static PFN_ACCESS real_access = nullptr; +static PFN_FOPEN real_fopen = nullptr; + +FRAMEWORK_EXPORT DIR* OPENDIR_FUNC_NAME(const char* path_name) { if (!real_opendir) real_opendir = (PFN_OPENDIR)dlsym(RTLD_NEXT, "opendir"); DIR* dir; if (platform_shim.is_fake_path(path_name)) { auto fake_path_name = platform_shim.get_fake_path(fs::path(path_name)); - //printf("Open Dir %s as %s\n", path_name, fake_path_name.c_str()); dir = real_opendir(fake_path_name.c_str()); } else { - // printf("Open Dir %s\n", path_name); dir = real_opendir(path_name); } return dir; } -using PFN_ACCESS = int (*)(const char* pathname, int mode); - -FRAMEWORK_EXPORT int access(const char* in_pathname, int mode) { - static PFN_ACCESS real_access = nullptr; +FRAMEWORK_EXPORT int ACCESS_FUNC_NAME(const char* in_pathname, int mode) { if (!real_access) real_access = (PFN_ACCESS)dlsym(RTLD_NEXT, "access"); fs::path path{in_pathname}; @@ -69,36 +85,45 @@ FRAMEWORK_EXPORT int access(const char* in_pathname, int mode) { if (platform_shim.is_fake_path(path.parent_path())) { fs::path fake_path = platform_shim.get_fake_path(path.parent_path()); fake_path /= path.filename(); - // printf("ACCESS: in_pathname %s as %s\n", split.path.c_str(), fake_path.c_str()); return real_access(fake_path.c_str(), mode); } return real_access(in_pathname, mode); } -using PFN_FOPEN = FILE* (*)(const char* filename, const char* mode); - -FRAMEWORK_EXPORT FILE* fopen(const char* in_filename, const char* mode) { - static PFN_FOPEN real_fopen = nullptr; +FRAMEWORK_EXPORT FILE* FOPEN_FUNC_NAME(const char* in_filename, const char* mode) { if (!real_fopen) real_fopen = (PFN_FOPEN)dlsym(RTLD_NEXT, "fopen"); - //printf("fopen file %s\n", in_filename); fs::path path{in_filename}; if (!path.has_parent_path()) { return real_fopen(in_filename, mode); } - // printf("path %s as %s\n", split.path.c_str(), split.name.c_str()); - FILE* f_ptr; if (platform_shim.is_fake_path(path.parent_path())) { auto fake_path = platform_shim.get_fake_path(path.parent_path()) / path.filename(); - // printf("fopen %s as %s\n", in_filename, fake_path.c_str()); f_ptr = real_fopen(fake_path.c_str(), mode); } else { - // printf("fopen %s\n", filename); f_ptr = real_fopen(in_filename, mode); } return f_ptr; } + +/* Shiming functions on apple is limited by the linker prefering to not use functions in the + * executable in loaded dylibs. By adding an interposer, we redirect the linker to use our + * version of the function over the real one, thus shimming the system function. + */ +#if defined(__APPLE__) +#define MACOS_ATTRIB __attribute__((section("__DATA,__interpose"))) +#define VOIDP_CAST(_func) reinterpret_cast(&_func) + +struct Interposer { + const void* shim_function; + const void* underlying_function; +}; + +__attribute__((used)) static Interposer _interpose_opendir MACOS_ATTRIB = {VOIDP_CAST(my_opendir), VOIDP_CAST(opendir)}; +__attribute__((used)) static Interposer _interpose_access MACOS_ATTRIB = {VOIDP_CAST(my_access), VOIDP_CAST(access)}; +__attribute__((used)) static Interposer _interpose_fopen MACOS_ATTRIB = {VOIDP_CAST(my_fopen), VOIDP_CAST(fopen)}; +#endif } // extern "C" diff --git a/tests/framework/test_environment.cpp b/tests/framework/test_environment.cpp index 96b49cef..ecb8d031 100644 --- a/tests/framework/test_environment.cpp +++ b/tests/framework/test_environment.cpp @@ -29,12 +29,12 @@ namespace detail { PlatformShimWrapper::PlatformShimWrapper(DebugMode debug_mode) : debug_mode(debug_mode) { -#if defined(WIN32) +#if defined(WIN32) || defined(__APPLE__) shim_library = LibraryWrapper(SHIM_LIBRARY_NAME); auto get_platform_shim_func = shim_library.get_symbol(GET_PLATFORM_SHIM_STR); assert(get_platform_shim_func != NULL && "Must be able to get \"platform_shim\""); platform_shim = &get_platform_shim_func(); -#elif defined(__linux__) || defined(__APPLE__) +#elif defined(__linux__) platform_shim = &get_platform_shim(); #endif platform_shim->setup_override(debug_mode); diff --git a/tests/framework/test_util.cpp b/tests/framework/test_util.cpp index 069c6c8e..9d399531 100644 --- a/tests/framework/test_util.cpp +++ b/tests/framework/test_util.cpp @@ -222,7 +222,7 @@ std::string make_native(std::string const& in_path) { else out += c; } -#elif defined(__linux__) || defined(APPLE) +#elif defined(__linux__) || defined(__APPLE__) for (size_t i = 0; i < in_path.size(); i++) { if (i + 1 < in_path.size() && in_path[i] == '\\' && in_path[i + 1] == '\\') { out += '/';