diff --git a/iree/base/BUILD b/iree/base/BUILD index 32e4a1064da70..e2fc7bc7bcada 100644 --- a/iree/base/BUILD +++ b/iree/base/BUILD @@ -63,26 +63,6 @@ cc_library( # Internal IREE C++ wrappers and utilities #===------------------------------------------------------------------------===# -cc_library( - name = "dynamic_library", - srcs = [ - "dynamic_library_posix.cc", - "dynamic_library_win32.cc", - ], - hdrs = ["dynamic_library.h"], - deps = [ - ":core_headers", - ":logging", - ":status", - ":tracing", - "//build_tools:default_linkopts", - "//iree/base/internal:file_path", - "@com_google_absl//absl/memory", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/types:span", - ], -) - cc_library( name = "flatcc", hdrs = ["flatcc.h"], diff --git a/iree/base/CMakeLists.txt b/iree/base/CMakeLists.txt index 3855042d460c3..71c2c050cd19e 100644 --- a/iree/base/CMakeLists.txt +++ b/iree/base/CMakeLists.txt @@ -48,26 +48,6 @@ iree_cc_library( PUBLIC ) -iree_cc_library( - NAME - dynamic_library - HDRS - "dynamic_library.h" - SRCS - "dynamic_library_posix.cc" - "dynamic_library_win32.cc" - DEPS - ::core_headers - ::logging - ::status - ::tracing - absl::memory - absl::span - absl::strings - iree::base::internal::file_path - PUBLIC -) - iree_cc_library( NAME flatcc diff --git a/iree/base/dynamic_library.h b/iree/base/dynamic_library.h deleted file mode 100644 index 213bb3d80c210..0000000000000 --- a/iree/base/dynamic_library.h +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2020 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef IREE_BASE_DYNAMIC_LIBRARY_H_ -#define IREE_BASE_DYNAMIC_LIBRARY_H_ - -#include -#include - -#include "absl/types/span.h" -#include "iree/base/status.h" - -namespace iree { - -// Dynamic library / shared object cross-platform wrapper class. -// -// Paths searched for libraries are platform and environment-specific. -// In general... -// * On Linux, the LD_LIBRARY_PATH environment variable may be set to a -// colon-separated list of directories to search before the standard set. -// * On Windows, all directories in the PATH environment variable are checked. -// Library file names may be relative to the search paths, or absolute. -// Certain platforms may require the library extension (.so, .dll), or it may -// be optional. If you know the extension, prefer to include it. -// -// Usage: -// static const char* kSearchNames[] = {"libfoo.so"}; -// IREE_ASSIGN_OR_RETURN(library, -// DynamicLibrary::Load(absl::MakeSpan(kSearchNames))); -// void* library_symbol_bar = library->GetSymbol("bar"); -// void* library_symbol_baz = library->GetSymbol("baz"); -class DynamicLibrary { - public: - virtual ~DynamicLibrary() = default; - - // Loads the library at the null-terminated string |search_file_name|. - static Status Load(const char* search_file_name, - std::unique_ptr* out_library) { - return Load(absl::Span({search_file_name}), out_library); - } - // Loads the library at the first name within |search_file_names| found. - static Status Load(absl::Span search_file_names, - std::unique_ptr* out_library); - - // Gets the name of the library file that is loaded. - const std::string& file_name() const { return file_name_; } - - // Loads a debug database (PDB/DWARF/etc) from the given path providing debug - // symbols for this library and attaches it to the symbol store (if active). - virtual void AttachDebugDatabase(const char* database_file_name) {} - - // Gets the address of a symbol with the given name in the loaded library. - // Returns NULL if the symbol could not be found. - virtual void* GetSymbol(const char* symbol_name) const = 0; - template - T GetSymbol(const char* symbol_name) const { - return reinterpret_cast(GetSymbol(symbol_name)); - } - - protected: - // Private constructor, use |Load| factory method instead. - DynamicLibrary(std::string file_name) : file_name_(file_name) {} - - std::string file_name_; -}; - -} // namespace iree - -#endif // IREE_BASE_DYNAMIC_LIBRARY_H_ diff --git a/iree/base/dynamic_library_posix.cc b/iree/base/dynamic_library_posix.cc deleted file mode 100644 index 3027a68a23fb1..0000000000000 --- a/iree/base/dynamic_library_posix.cc +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2020 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "absl/memory/memory.h" -#include "iree/base/dynamic_library.h" -#include "iree/base/target_platform.h" -#include "iree/base/tracing.h" - -#if defined(IREE_PLATFORM_ANDROID) || defined(IREE_PLATFORM_APPLE) || \ - defined(IREE_PLATFORM_LINUX) - -#include - -namespace iree { - -class DynamicLibraryPosix : public DynamicLibrary { - public: - ~DynamicLibraryPosix() override { - // TODO(benvanik): disable if we want to get profiling results. - // Sometimes closing the library can prevent proper symbolization on - // crashes or in sampling profilers. - ::dlclose(library_); - } - - static Status Load(absl::Span search_file_names, - std::unique_ptr* out_library) { - IREE_TRACE_SCOPE0("DynamicLibraryPosix::Load"); - out_library->reset(); - - for (int i = 0; i < search_file_names.size(); ++i) { - void* library = ::dlopen(search_file_names[i], RTLD_LAZY | RTLD_LOCAL); - if (library) { - out_library->reset( - new DynamicLibraryPosix(search_file_names[i], library)); - return OkStatus(); - } - } - return iree_make_status(IREE_STATUS_UNAVAILABLE, - "unable to open dynamic library:'%s'", dlerror()); - } - - void* GetSymbol(const char* symbol_name) const override { - return ::dlsym(library_, symbol_name); - } - - private: - DynamicLibraryPosix(std::string file_name, void* library) - : DynamicLibrary(file_name), library_(library) {} - - void* library_; -}; - -// static -Status DynamicLibrary::Load(absl::Span search_file_names, - std::unique_ptr* out_library) { - return DynamicLibraryPosix::Load(search_file_names, out_library); -} - -} // namespace iree - -#endif // IREE_PLATFORM_* diff --git a/iree/base/dynamic_library_win32.cc b/iree/base/dynamic_library_win32.cc deleted file mode 100644 index 804d793560b54..0000000000000 --- a/iree/base/dynamic_library_win32.cc +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright 2020 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "absl/memory/memory.h" -#include "absl/strings/str_replace.h" -#include "iree/base/dynamic_library.h" -#include "iree/base/internal/file_path.h" -#include "iree/base/target_platform.h" -#include "iree/base/tracing.h" - -#if defined(IREE_PLATFORM_WINDOWS) - -// TODO(benvanik): support PDB overlays when tracy is not enabled too; we'll -// need to rearrange how the dbghelp lock is handled for that (probably moving -// it here and having the tracy code redirect to this). -#if defined(TRACY_ENABLE) -#define IREE_HAVE_DYNAMIC_LIBRARY_PDB_SUPPORT 1 -#pragma warning(disable : 4091) -#include -extern "C" void IREEDbgHelpLock(); -extern "C" void IREEDbgHelpUnlock(); -#endif // TRACY_ENABLE - -namespace iree { - -// We need to match the expected paths from dbghelp exactly or else we'll get -// spurious warnings during module resolution. AFAICT our approach here with -// loading the PDBs directly with a module base/size of the loaded module will -// work regardless but calls like SymRefreshModuleList will still attempt to -// load from system symbol search paths if things don't line up. -static void CanonicalizePath(std::string* path) { - absl::StrReplaceAll({{"/", "\\"}}, path); - absl::StrReplaceAll({{"\\\\", "\\"}}, path); -} - -class DynamicLibraryWin : public DynamicLibrary { - public: - ~DynamicLibraryWin() override { - IREE_TRACE_SCOPE(); - // TODO(benvanik): disable if we want to get profiling results. - // Sometimes closing the library can prevent proper symbolization on - // crashes or in sampling profilers. - ::FreeLibrary(library_); - } - - static Status Load(absl::Span search_file_names, - std::unique_ptr* out_library) { - IREE_TRACE_SCOPE(); - out_library->reset(); - - for (int i = 0; i < search_file_names.size(); ++i) { - HMODULE library = ::LoadLibraryA(search_file_names[i]); - if (library) { - out_library->reset( - new DynamicLibraryWin(search_file_names[i], library)); - return OkStatus(); - } - } - - return iree_make_status( - IREE_STATUS_UNAVAILABLE, - "unable to open dynamic library, not found on search paths"); - } - -#if defined(IREE_HAVE_DYNAMIC_LIBRARY_PDB_SUPPORT) - void AttachDebugDatabase(const char* database_file_name) override { - IREE_TRACE_SCOPE(); - - // Derive the base module name (path stem) for the loaded module. - // For example, the name of 'C:\Dev\foo.dll' would be 'foo'. - // This name is used by dbghelp for listing loaded modules and we want to - // ensure we match the name of the PDB module with the library module. - std::string module_name = file_name_; - size_t last_slash = module_name.find_last_of('\\'); - if (last_slash != std::string::npos) { - module_name = module_name.substr(last_slash + 1); - } - size_t dot = module_name.find_last_of('.'); - if (dot != std::string::npos) { - module_name = module_name.substr(0, dot); - } - - IREEDbgHelpLock(); - - // Useful for debugging; will print search paths and results: - // SymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_DEBUG); - - // Enumerates all loaded modules in the process to extract the module - // base/size parameters we need to overlay the PDB. There's other ways to - // get this (such as registering a LdrDllNotification callback and snooping - // the values during LoadLibrary or using CreateToolhelp32Snapshot), however - // EnumerateLoadedModules is in dbghelp which we are using anyway. - ModuleEnumCallbackState state; - state.module_file_path = file_name_.c_str(); - EnumerateLoadedModules64(GetCurrentProcess(), EnumLoadedModulesCallback, - &state); - - // Load the PDB file and overlay it onto the already-loaded module at the - // address range it got loaded into. - if (state.module_base != 0) { - SymLoadModuleEx(GetCurrentProcess(), NULL, database_file_name, - module_name.c_str(), state.module_base, state.module_size, - NULL, 0); - } - - IREEDbgHelpUnlock(); - } -#endif // IREE_HAVE_DYNAMIC_LIBRARY_PDB_SUPPORT - - void* GetSymbol(const char* symbol_name) const override { - return reinterpret_cast(::GetProcAddress(library_, symbol_name)); - } - - private: - DynamicLibraryWin(std::string file_name, HMODULE library) - : DynamicLibrary(std::move(file_name)), library_(library) { - CanonicalizePath(&file_name_); - } - -#if defined(IREE_HAVE_DYNAMIC_LIBRARY_PDB_SUPPORT) - struct ModuleEnumCallbackState { - const char* module_file_path = NULL; - DWORD64 module_base = 0; - ULONG module_size = 0; - }; - static BOOL EnumLoadedModulesCallback(PCSTR ModuleName, DWORD64 ModuleBase, - ULONG ModuleSize, PVOID UserContext) { - auto* state = reinterpret_cast(UserContext); - if (strcmp(ModuleName, state->module_file_path) != 0) { - return TRUE; // not a match; continue - } - state->module_base = ModuleBase; - state->module_size = ModuleSize; - return FALSE; // match found; stop enumeration - } -#endif // IREE_HAVE_DYNAMIC_LIBRARY_PDB_SUPPORT - - HMODULE library_ = NULL; -}; - -// static -Status DynamicLibrary::Load(absl::Span search_file_names, - std::unique_ptr* out_library) { - return DynamicLibraryWin::Load(search_file_names, out_library); -} - -} // namespace iree - -#endif // IREE_PLATFORM_* diff --git a/iree/base/internal/BUILD b/iree/base/internal/BUILD index efc4dd42237e4..09068948a6e3d 100644 --- a/iree/base/internal/BUILD +++ b/iree/base/internal/BUILD @@ -90,36 +90,29 @@ cc_test( ) cc_library( - name = "file_handle_win32", - srcs = ["file_handle_win32.cc"], - hdrs = ["file_handle_win32.h"], + name = "dynamic_library", + srcs = [ + "dynamic_library_posix.c", + "dynamic_library_win32.c", + ], + hdrs = ["dynamic_library.h"], deps = [ + ":file_path", + ":internal", "//iree/base:core_headers", - "//iree/base:status", - "@com_google_absl//absl/memory", - "@com_google_absl//absl/strings", + "//iree/base:threading", + "//iree/base:tracing", ], ) cc_library( name = "file_io", + srcs = ["file_io.cc"], hdrs = ["file_io.h"], deps = [ + "//iree/base:api", "//iree/base:core_headers", - "//iree/base:status", - "//iree/base/internal:file_io_internal", - "@com_google_absl//absl/memory", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/types:span", - ], -) - -cc_library( - name = "file_io_hdrs", - hdrs = ["file_io.h"], - deps = [ - "//iree/base:status", - "@com_google_absl//absl/strings", + "//iree/base:tracing", ], ) @@ -128,28 +121,8 @@ cc_test( srcs = ["file_io_test.cc"], deps = [ ":file_io", - "//iree/base:logging", - "//iree/base:status", "//iree/testing:gtest", "//iree/testing:gtest_main", - "@com_google_absl//absl/strings", - ], -) - -cc_library( - name = "file_io_internal", - srcs = [ - "file_io_posix.cc", - "file_io_win32.cc", - ], - deps = [ - ":file_handle_win32", - ":file_io_hdrs", - "//iree/base:core_headers", - "//iree/base:status", - "//iree/base:tracing", - "@com_google_absl//absl/memory", - "@com_google_absl//absl/strings", ], ) diff --git a/iree/base/internal/CMakeLists.txt b/iree/base/internal/CMakeLists.txt index 6cf36402ba58c..2ab0ae802b2b6 100644 --- a/iree/base/internal/CMakeLists.txt +++ b/iree/base/internal/CMakeLists.txt @@ -76,16 +76,18 @@ iree_cc_test( iree_cc_library( NAME - file_handle_win32 + dynamic_library HDRS - "file_handle_win32.h" + "dynamic_library.h" SRCS - "file_handle_win32.cc" + "dynamic_library_posix.c" + "dynamic_library_win32.c" DEPS - absl::memory - absl::strings + ::file_path + ::internal iree::base::core_headers - iree::base::status + iree::base::threading + iree::base::tracing PUBLIC ) @@ -94,24 +96,12 @@ iree_cc_library( file_io HDRS "file_io.h" + SRCS + "file_io.cc" DEPS - absl::memory - absl::span - absl::strings + iree::base::api iree::base::core_headers - iree::base::internal::file_io_internal - iree::base::status - PUBLIC -) - -iree_cc_library( - NAME - file_io_hdrs - HDRS - "file_io.h" - DEPS - absl::strings - iree::base::status + iree::base::tracing PUBLIC ) @@ -122,30 +112,10 @@ iree_cc_test( "file_io_test.cc" DEPS ::file_io - absl::strings - iree::base::logging - iree::base::status iree::testing::gtest iree::testing::gtest_main ) -iree_cc_library( - NAME - file_io_internal - SRCS - "file_io_posix.cc" - "file_io_win32.cc" - DEPS - ::file_handle_win32 - ::file_io_hdrs - absl::memory - absl::strings - iree::base::core_headers - iree::base::status - iree::base::tracing - PUBLIC -) - iree_cc_library( NAME file_path diff --git a/iree/base/internal/dynamic_library.h b/iree/base/internal/dynamic_library.h new file mode 100644 index 0000000000000..eff168a966f35 --- /dev/null +++ b/iree/base/internal/dynamic_library.h @@ -0,0 +1,86 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef IREE_BASE_INTERNAL_DYNAMIC_LIBRARY_H_ +#define IREE_BASE_INTERNAL_DYNAMIC_LIBRARY_H_ + +#include "iree/base/api.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Defines the behavior of the dynamic library loader. +enum iree_dynamic_library_flags_e { + IREE_DYNAMIC_LIBRARY_FLAG_NONE = 0u, +}; +typedef uint32_t iree_dynamic_library_flags_t; + +// Dynamic library (aka shared object) cross-platform wrapper. +typedef struct iree_dynamic_library_s iree_dynamic_library_t; + +// Loads a system library using both the system library load paths and the given +// file name. The path may may be absolute or relative. +// +// For process-wide search control the LD_LIBRARY_PATH (Linux) or PATH (Windows) +// is used in addition to the default search path rules of the platform. +iree_status_t iree_dynamic_library_load_from_file( + const char* file_path, iree_dynamic_library_flags_t flags, + iree_allocator_t allocator, iree_dynamic_library_t** out_library); + +// Loads a system library using both the system library load paths and the given +// search path/alternative file names. The paths may may be absolute or +// relative. +// +// For process-wide search control the LD_LIBRARY_PATH (Linux) or PATH (Windows) +// is used in addition to the default search path rules of the platform. +iree_status_t iree_dynamic_library_load_from_files( + iree_host_size_t search_path_count, const char* const* search_paths, + iree_dynamic_library_flags_t flags, iree_allocator_t allocator, + iree_dynamic_library_t** out_library); + +// Opens a dynamic library from a range of bytes in memory. +// |identifier| will be used as the module name in debugging/profiling tools. +// |buffer| must remain live for the lifetime of the library. +iree_status_t iree_dynamic_library_load_from_memory( + iree_string_view_t identifier, iree_const_byte_span_t buffer, + iree_dynamic_library_flags_t flags, iree_allocator_t allocator, + iree_dynamic_library_t** out_library); + +// Retains the given |library| for the caller. +void iree_dynamic_library_retain(iree_dynamic_library_t* library); + +// Releases the given |library| from the caller. +void iree_dynamic_library_release(iree_dynamic_library_t* library); + +// Performs a symbol lookup in the dynamic library exports. +iree_status_t iree_dynamic_library_lookup_symbol( + iree_dynamic_library_t* library, const char* symbol_name, void** out_fn); + +// Loads a debug database (PDB/DWARF/etc) from the given path providing debug +// symbols for this library and attaches it to the symbol store (if active). +iree_status_t iree_dynamic_library_attach_symbols_from_file( + iree_dynamic_library_t* library, const char* file_path); + +// Loads a debug database (PDB/DWARF/etc) from a range of bytes in memory and +// attaches it to the symbol store (if active). |buffer| must remain live for +// the lifetime of the library. +iree_status_t iree_dynamic_library_attach_symbols_from_memory( + iree_dynamic_library_t* library, iree_const_byte_span_t buffer); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // IREE_BASE_INTERNAL_DYNAMIC_LIBRARY_H_ diff --git a/iree/base/internal/dynamic_library_posix.c b/iree/base/internal/dynamic_library_posix.c new file mode 100644 index 0000000000000..252ef4d208482 --- /dev/null +++ b/iree/base/internal/dynamic_library_posix.c @@ -0,0 +1,304 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "iree/base/internal/atomics.h" +#include "iree/base/internal/dynamic_library.h" +#include "iree/base/internal/file_path.h" +#include "iree/base/target_platform.h" +#include "iree/base/tracing.h" + +#if defined(IREE_PLATFORM_ANDROID) || defined(IREE_PLATFORM_APPLE) || \ + defined(IREE_PLATFORM_LINUX) + +#include +#include +#include +#include +#include + +struct iree_dynamic_library_s { + iree_atomic_ref_count_t ref_count; + iree_allocator_t allocator; + + // dlopen shared object handle. + void* handle; + + // 0 or more file paths that were created as part of the loading of the + // library or attaching of symbols from memory. + // + // Each path string is allocated using the |allocator| and freed during + // library deletion. + iree_host_size_t temp_file_count; + char* temp_file_paths[1]; +}; + +// Allocate a new string from |allocator| returned in |out_file_path| containing +// a path to a unique file on the filesystem. +static iree_status_t iree_dynamic_library_make_temp_file_path( + const char* prefix, const char* extension, iree_allocator_t allocator, + char** out_file_path) { + // Query the 'TMPDIR' environment variable to allow users to override the + // path. + const char* tmpdir = getenv("TMPDIR"); + if (!tmpdir) { +#ifdef __ANDROID__ + // Support running Android command-line programs both as regular shell user + // and as root. For the latter, TMPDIR is not defined by default. + tmpdir = "/data/local/tmp"; +#else + tmpdir = "/tmp"; +#endif // __ANDROID__ + } + + // Stamp in a unique file name (replacing XXXXXX in the string). + char temp_path[512]; + if (snprintf(temp_path, sizeof(temp_path), "%s/iree_dylib_XXXXXX", tmpdir) >= + sizeof(temp_path)) { + // NOTE: we could dynamically allocate things, but didn't seem worth it. + return iree_make_status( + IREE_STATUS_INVALID_ARGUMENT, + "TMPDIR name too long (>%zu chars); keep it reasonable", + sizeof(temp_path)); + } + int fd = mkstemp(temp_path); + if (fd < 0) { + return iree_make_status(iree_status_code_from_errno(errno), + "unable to mkstemp file"); + } + + // Allocate storage for the full file path and format it in. + int file_path_length = + snprintf(NULL, 0, "%s_%s.%s", temp_path, prefix, extension); + if (file_path_length < 0) { + return iree_make_status(IREE_STATUS_INVALID_ARGUMENT, + "unable to form temp path string"); + } + IREE_RETURN_IF_ERROR(iree_allocator_malloc( + allocator, file_path_length + /*NUL=*/1, (void**)out_file_path)); + snprintf(*out_file_path, file_path_length + /*NUL=*/1, "%s_%s.%s", temp_path, + prefix, extension); + + // Canonicalize away any double path separators. + iree_file_path_canonicalize(*out_file_path, file_path_length); + + return iree_ok_status(); +} + +// Creates a temp file and writes the |source_data| into it. +// The file path is returned in |out_file_path|. +static iree_status_t iree_dynamic_library_write_temp_file( + iree_const_byte_span_t source_data, const char* prefix, + const char* extension, iree_allocator_t allocator, char** out_file_path) { + IREE_TRACE_ZONE_BEGIN(z0); + + // Reserve a temp file path we can write to. + IREE_RETURN_AND_END_ZONE_IF_ERROR( + z0, iree_dynamic_library_make_temp_file_path(prefix, extension, allocator, + out_file_path)); + + iree_status_t status = iree_ok_status(); + + // Open the file for writing. + FILE* file_handle = fopen(*out_file_path, "wb"); + if (file_handle == NULL) { + status = iree_make_status(iree_status_code_from_errno(errno), + "unable to open file '%s'", *out_file_path); + } + + // Write all file bytes. + if (iree_status_is_ok(status)) { + if (fwrite((char*)source_data.data, source_data.data_length, 1, + file_handle) != 1) { + status = + iree_make_status(iree_status_code_from_errno(errno), + "unable to write file span of %zu bytes to '%s'", + source_data.data_length, *out_file_path); + } + } + + if (file_handle != NULL) { + fclose(file_handle); + file_handle = NULL; + } + if (!iree_status_is_ok(status)) { + iree_allocator_free(allocator, *out_file_path); + } + IREE_TRACE_ZONE_END(z0); + return status; +} + +// Allocates an iree_dynamic_library_t with the given allocator. +static iree_status_t iree_dynamic_library_create( + void* handle, iree_allocator_t allocator, + iree_dynamic_library_t** out_library) { + *out_library = NULL; + + iree_dynamic_library_t* library = NULL; + IREE_RETURN_IF_ERROR( + iree_allocator_malloc(allocator, sizeof(*library), (void**)&library)); + memset(library, 0, sizeof(*library)); + iree_atomic_ref_count_init(&library->ref_count); + library->allocator = allocator; + library->handle = handle; + + *out_library = library; + return iree_ok_status(); +} + +iree_status_t iree_dynamic_library_load_from_file( + const char* file_path, iree_dynamic_library_flags_t flags, + iree_allocator_t allocator, iree_dynamic_library_t** out_library) { + return iree_dynamic_library_load_from_files(1, &file_path, flags, allocator, + out_library); +} + +iree_status_t iree_dynamic_library_load_from_files( + iree_host_size_t search_path_count, const char* const* search_paths, + iree_dynamic_library_flags_t flags, iree_allocator_t allocator, + iree_dynamic_library_t** out_library) { + IREE_TRACE_ZONE_BEGIN(z0); + IREE_ASSERT_ARGUMENT(out_library); + *out_library = NULL; + + // Try to load the module from the set of search paths provided. + void* handle = NULL; + iree_host_size_t i = 0; + for (i = 0; i < search_path_count; ++i) { + handle = dlopen(search_paths[i], RTLD_LAZY | RTLD_LOCAL); + if (handle) break; + } + if (!handle) { + IREE_TRACE_ZONE_END(z0); + return iree_make_status(IREE_STATUS_NOT_FOUND, + "dynamic library not found on any search path"); + } + + iree_dynamic_library_t* library = NULL; + iree_status_t status = + iree_dynamic_library_create(handle, allocator, &library); + + if (iree_status_is_ok(status)) { + *out_library = library; + } else { + dlclose(handle); + } + IREE_TRACE_ZONE_END(z0); + return status; +} + +// TODO(#3845): use dlopen on an fd with either dlopen(/proc/self/fd/NN), +// fdlopen, or android_dlopen_ext to avoid needing to write the file to disk. +// Can fallback to memfd_create + dlopen where available, and fallback from +// that to disk (maybe just windows/mac). +iree_status_t iree_dynamic_library_load_from_memory( + iree_string_view_t identifier, iree_const_byte_span_t buffer, + iree_dynamic_library_flags_t flags, iree_allocator_t allocator, + iree_dynamic_library_t** out_library) { + IREE_TRACE_ZONE_BEGIN(z0); + IREE_ASSERT_ARGUMENT(out_library); + *out_library = NULL; + + // Extract the library to a temp file. + char* temp_path = NULL; + IREE_RETURN_AND_END_ZONE_IF_ERROR( + z0, iree_dynamic_library_write_temp_file(buffer, "mem_", "so", allocator, + &temp_path)); + + // Load using the normal load from file routine. + iree_status_t status = iree_dynamic_library_load_from_file( + temp_path, flags, allocator, out_library); + + if (iree_status_is_ok(status)) { + // Associate the temp path to the library; the temp_path string and the + // backing file will be deleted when the library is closed. + iree_dynamic_library_t* library = *out_library; + library->temp_file_paths[library->temp_file_count++] = temp_path; + } else { + iree_allocator_free(allocator, temp_path); + } + + IREE_TRACE_ZONE_END(z0); + return status; +} + +static void iree_dynamic_library_delete(iree_dynamic_library_t* library) { + iree_allocator_t allocator = library->allocator; + IREE_TRACE_ZONE_BEGIN(z0); + +#if IREE_TRACING_FEATURES & IREE_TRACING_FEATURE_INSTRUMENTATION + // Leak the library when tracing, since the profiler may still be reading it. + // TODO(benvanik): move to an atexit handler instead, verify with ASAN/MSAN + // TODO(scotttodd): Make this compatible with testing: + // two test cases, one for each function in the same executable + // first test case passes, second fails to open the file (already open) +#else + // Close the library first as it may be loaded from one of the temp files we + // are about to delete. + if (library->handle != NULL) { + dlclose(library->handle); + } +#endif // IREE_TRACING_FEATURES & IREE_TRACING_FEATURE_INSTRUMENTATION + + // Cleanup all temp files. + for (iree_host_size_t i = 0; i < library->temp_file_count; ++i) { + char* file_path = library->temp_file_paths[i]; + remove(file_path); + iree_allocator_free(allocator, file_path); + } + + iree_allocator_free(allocator, library); + + IREE_TRACE_ZONE_END(z0); +} + +void iree_dynamic_library_retain(iree_dynamic_library_t* library) { + if (library) { + iree_atomic_ref_count_inc(&library->ref_count); + } +} + +void iree_dynamic_library_release(iree_dynamic_library_t* library) { + if (library && iree_atomic_ref_count_dec(&library->ref_count) == 1) { + iree_dynamic_library_delete(library); + } +} + +iree_status_t iree_dynamic_library_lookup_symbol( + iree_dynamic_library_t* library, const char* symbol_name, void** out_fn) { + IREE_ASSERT_ARGUMENT(library); + IREE_ASSERT_ARGUMENT(symbol_name); + IREE_ASSERT_ARGUMENT(out_fn); + *out_fn = NULL; + void* fn = dlsym(library->handle, symbol_name); + if (!fn) { + return iree_make_status(IREE_STATUS_NOT_FOUND, + "symbol '%s' not found in library", symbol_name); + } + *out_fn = fn; + return iree_ok_status(); +} + +iree_status_t iree_dynamic_library_attach_symbols_from_file( + iree_dynamic_library_t* library, const char* file_path) { + return iree_ok_status(); +} + +iree_status_t iree_dynamic_library_attach_symbols_from_memory( + iree_dynamic_library_t* library, iree_const_byte_span_t buffer) { + return iree_ok_status(); +} + +#endif // IREE_PLATFORM_* diff --git a/iree/base/internal/dynamic_library_win32.c b/iree/base/internal/dynamic_library_win32.c new file mode 100644 index 0000000000000..0f217724a2e2b --- /dev/null +++ b/iree/base/internal/dynamic_library_win32.c @@ -0,0 +1,425 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "iree/base/internal/atomics.h" +#include "iree/base/internal/dynamic_library.h" +#include "iree/base/internal/file_path.h" +#include "iree/base/target_platform.h" +#include "iree/base/threading.h" +#include "iree/base/tracing.h" + +#if defined(IREE_PLATFORM_WINDOWS) + +// TODO(benvanik): support PDB overlays when tracy is not enabled; we'll +// need to rearrange how the dbghelp lock is handled for that (probably moving +// it here and having the tracy code redirect to this). +#if defined(TRACY_ENABLE) +#define IREE_HAVE_DYNAMIC_LIBRARY_PDB_SUPPORT 1 +#pragma warning(disable : 4091) +#include +extern "C" void IREEDbgHelpLock(); +extern "C" void IREEDbgHelpUnlock(); +#endif // TRACY_ENABLE + +struct iree_dynamic_library_s { + iree_atomic_ref_count_t ref_count; + iree_allocator_t allocator; + + // Base module name used as an identifier. When loaded from a file this must + // be the basename for dbghelp to be able to find symbols. + // Owned and allocated as part of the struct upon creation. + // Has NUL terminator for compatibility with Windows APIs. + char* identifier; + + // File path of the loaded module, if loaded from one. + // Owned and allocated as part of the struct upon creation. + // Has NUL terminator for compatibility with Windows APIs. + char* module_path; + + // Windows module handle. + HMODULE module; + + // 0 or more file paths that were created as part of the loading of the + // library or attaching of symbols from memory. + // + // Each path string is allocated using the |allocator| and freed during + // library deletion. + iree_host_size_t temp_file_count; + char* temp_file_paths[2]; +}; + +static iree_once_flag iree_dynamic_library_temp_path_flag_ = + IREE_ONCE_FLAG_INIT; +static char iree_dynamic_library_temp_path_base_[MAX_PATH + 1]; +static void iree_dynamic_library_init_temp_paths(void) { + // Query the temp path from the OS. This can be overridden with the following + // environment variables: [TMP, TEMP, USERPROFILE]. + // + // See: + // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppatha + char temp_path[MAX_PATH]; + DWORD temp_path_length = GetTempPathA(IREE_ARRAYSIZE(temp_path), temp_path); + + // Append the process ID to the path; this is like what _mktemp does but + // without all the hoops. + snprintf(iree_dynamic_library_temp_path_base_, + sizeof(iree_dynamic_library_temp_path_base_), "%s\\iree_dylib_%08X", + temp_path, GetCurrentProcessId()); + + // Canonicalize away any double path separators. + iree_file_path_canonicalize(iree_dynamic_library_temp_path_base_, + strlen(iree_dynamic_library_temp_path_base_)); +} + +// Allocate a new string from |allocator| returned in |out_file_path| containing +// a path to a unique file on the filesystem. +static iree_status_t iree_dynamic_library_make_temp_file_path( + const char* prefix, const char* extension, iree_allocator_t allocator, + char** out_file_path) { + // Ensure the root temp paths are queried/initialized. + iree_call_once(&iree_dynamic_library_temp_path_flag_, + iree_dynamic_library_init_temp_paths); + + // Generate a per-file unique identifier only unique **within** the current + // process. We combine this with the _mktemp path that should be unique to the + // process itself. + static iree_atomic_int32_t next_unique_id = IREE_ATOMIC_VAR_INIT(0); + uint32_t unique_id = (uint32_t)iree_atomic_fetch_add_int32( + &next_unique_id, 1, iree_memory_order_seq_cst); + + // Allocate storage for the full file path and format it in. + int file_path_length = + snprintf(NULL, 0, "%s_%s_%08X.%s", iree_dynamic_library_temp_path_base_, + prefix, unique_id, extension); + if (file_path_length < 0) { + return iree_make_status(IREE_STATUS_INVALID_ARGUMENT, + "unable to form temp path string"); + } + IREE_RETURN_IF_ERROR(iree_allocator_malloc( + allocator, file_path_length + /*NUL=*/1, (void**)out_file_path)); + snprintf(*out_file_path, file_path_length + /*NUL=*/1, "%s_%s_%08X.%s", + iree_dynamic_library_temp_path_base_, prefix, unique_id, extension); + + return iree_ok_status(); +} + +// Creates a temp file and writes the |source_data| into it. +// The file path is returned in |out_file_path|. +static iree_status_t iree_dynamic_library_write_temp_file( + iree_const_byte_span_t source_data, const char* prefix, + const char* extension, iree_allocator_t allocator, char** out_file_path) { + IREE_TRACE_ZONE_BEGIN(z0); + + // Reserve a temp file path we can write to. + IREE_RETURN_AND_END_ZONE_IF_ERROR( + z0, iree_dynamic_library_make_temp_file_path(prefix, extension, allocator, + out_file_path)); + + iree_status_t status = iree_ok_status(); + + // Open the file for writing. + HANDLE file_handle = CreateFileA( + /*lpFileName=*/*out_file_path, /*dwDesiredAccess=*/GENERIC_WRITE, + /*dwShareMode=*/FILE_SHARE_DELETE, /*lpSecurityAttributes=*/NULL, + /*dwCreationDisposition=*/CREATE_ALWAYS, + /*dwFlagsAndAttributes=*/FILE_ATTRIBUTE_TEMPORARY | + FILE_FLAG_DELETE_ON_CLOSE, + /*hTemplateFile=*/NULL); + if (file_handle == INVALID_HANDLE_VALUE) { + status = iree_make_status(iree_status_code_from_win32_error(GetLastError()), + "unable to open file '%s'", *out_file_path); + } + + // Write all file bytes. + if (iree_status_is_ok(status)) { + if (WriteFile(file_handle, source_data.data, (DWORD)source_data.data_length, + NULL, NULL) == FALSE) { + status = + iree_make_status(iree_status_code_from_win32_error(GetLastError()), + "unable to write file span of %zu bytes to '%s'", + source_data.data_length, *out_file_path); + } + } + + if (file_handle != NULL) { + CloseHandle(file_handle); + file_handle = NULL; + } + if (!iree_status_is_ok(status)) { + iree_allocator_free(allocator, *out_file_path); + } + IREE_TRACE_ZONE_END(z0); + return status; +} + +// Allocates an iree_dynamic_library_t with the given allocator. +static iree_status_t iree_dynamic_library_create( + iree_string_view_t identifier, iree_string_view_t module_path, + HMODULE module, iree_allocator_t allocator, + iree_dynamic_library_t** out_library) { + *out_library = NULL; + + iree_dynamic_library_t* library = NULL; + iree_host_size_t total_size = + sizeof(*library) + (identifier.size + 1) + (module_path.size + 1); + IREE_RETURN_IF_ERROR( + iree_allocator_malloc(allocator, total_size, (void**)&library)); + memset(library, 0, total_size); + iree_atomic_ref_count_init(&library->ref_count); + library->allocator = allocator; + library->module = module; + + library->identifier = (char*)library + sizeof(*library); + memcpy(library->identifier, identifier.data, identifier.size); + library->identifier[identifier.size] = 0; // NUL + + library->module_path = library->identifier + (identifier.size + 1); + memcpy(library->module_path, module_path.data, module_path.size); + library->module_path[module_path.size] = 0; // NUL + + *out_library = library; + return iree_ok_status(); +} + +iree_status_t iree_dynamic_library_load_from_file( + const char* file_path, iree_dynamic_library_flags_t flags, + iree_allocator_t allocator, iree_dynamic_library_t** out_library) { + return iree_dynamic_library_load_from_files(1, &file_path, flags, allocator, + out_library); +} + +iree_status_t iree_dynamic_library_load_from_files( + iree_host_size_t search_path_count, const char* const* search_paths, + iree_dynamic_library_flags_t flags, iree_allocator_t allocator, + iree_dynamic_library_t** out_library) { + IREE_TRACE_ZONE_BEGIN(z0); + IREE_ASSERT_ARGUMENT(out_library); + *out_library = NULL; + + // Try to load the module from the set of search paths provided. + HMODULE module = NULL; + iree_host_size_t i = 0; + for (i = 0; i < search_path_count; ++i) { + module = LoadLibraryA(search_paths[i]); + if (module) break; + } + if (!module) { + IREE_TRACE_ZONE_END(z0); + return iree_make_status(IREE_STATUS_NOT_FOUND, + "dynamic library not found on any search path"); + } + + iree_string_view_t file_path = iree_make_cstring_view(search_paths[i]); + iree_string_view_t identifier = iree_file_path_basename(file_path); + + iree_dynamic_library_t* library = NULL; + iree_status_t status = iree_dynamic_library_create( + identifier, file_path, module, allocator, &library); + + if (iree_status_is_ok(status)) { + *out_library = library; + } else { + FreeLibrary(module); + } + IREE_TRACE_ZONE_END(z0); + return status; +} + +iree_status_t iree_dynamic_library_load_from_memory( + iree_string_view_t identifier, iree_const_byte_span_t buffer, + iree_dynamic_library_flags_t flags, iree_allocator_t allocator, + iree_dynamic_library_t** out_library) { + IREE_TRACE_ZONE_BEGIN(z0); + IREE_ASSERT_ARGUMENT(out_library); + *out_library = NULL; + + // Extract the library to a temp file. + char* temp_path = NULL; + iree_status_t status = iree_dynamic_library_write_temp_file( + buffer, "mem_", "dll", allocator, &temp_path); + + if (iree_status_is_ok(status)) { + // Load using the normal load from file routine. + status = iree_dynamic_library_load_from_file(temp_path, flags, allocator, + out_library); + } + if (iree_status_is_ok(status)) { + // Associate the temp path to the library; the temp_path string and the + // backing file will be deleted when the library is closed. + iree_dynamic_library_t* library = *out_library; + library->temp_file_paths[library->temp_file_count++] = temp_path; + } else { + iree_allocator_free(allocator, temp_path); + } + + IREE_TRACE_ZONE_END(z0); + return status; +} + +static void iree_dynamic_library_delete(iree_dynamic_library_t* library) { + iree_allocator_t allocator = library->allocator; + IREE_TRACE_ZONE_BEGIN(z0); + +#if IREE_TRACING_FEATURES & IREE_TRACING_FEATURE_INSTRUMENTATION + // Leak the library when tracing, since the profiler may still be reading it. + // TODO(benvanik): move to an atexit handler instead, verify with ASAN/MSAN + // TODO(scotttodd): Make this compatible with testing: + // two test cases, one for each function in the same executable + // first test case passes, second fails to open the file (already open) +#else + // Close the library first as it may be loaded from one of the temp files we + // are about to delete. + if (library->module != NULL) { + FreeLibrary(library->module); + } +#endif // IREE_TRACING_FEATURES & IREE_TRACING_FEATURE_INSTRUMENTATION + + // Cleanup all temp files. + for (iree_host_size_t i = 0; i < library->temp_file_count; ++i) { + char* file_path = library->temp_file_paths[i]; + DeleteFileA(file_path); + iree_allocator_free(allocator, file_path); + } + + iree_allocator_free(allocator, library); + + IREE_TRACE_ZONE_END(z0); +} + +void iree_dynamic_library_retain(iree_dynamic_library_t* library) { + if (library) { + iree_atomic_ref_count_inc(&library->ref_count); + } +} + +void iree_dynamic_library_release(iree_dynamic_library_t* library) { + if (library && iree_atomic_ref_count_dec(&library->ref_count) == 1) { + iree_dynamic_library_delete(library); + } +} + +iree_status_t iree_dynamic_library_lookup_symbol( + iree_dynamic_library_t* library, const char* symbol_name, void** out_fn) { + IREE_ASSERT_ARGUMENT(library); + IREE_ASSERT_ARGUMENT(symbol_name); + IREE_ASSERT_ARGUMENT(out_fn); + *out_fn = NULL; + void* fn = GetProcAddress(library->module, symbol_name); + if (!fn) { + return iree_make_status(IREE_STATUS_NOT_FOUND, + "symbol '%s' not found in library", symbol_name); + } + *out_fn = fn; + return iree_ok_status(); +} + +#if defined(IREE_HAVE_DYNAMIC_LIBRARY_PDB_SUPPORT) + +typedef struct { + const char* module_path; + DWORD64 module_base; + ULONG module_size; +} ModuleEnumCallbackState; + +static BOOL EnumLoadedModulesCallback(PCSTR ModuleName, DWORD64 ModuleBase, + ULONG ModuleSize, PVOID UserContext) { + ModuleEnumCallbackState* state = (ModuleEnumCallbackState*)UserContext; + if (strcmp(ModuleName, state->module_path) != 0) { + return TRUE; // not a match; continue + } + state->module_base = ModuleBase; + state->module_size = ModuleSize; + return FALSE; // match found; stop enumeration +} + +iree_status_t iree_dynamic_library_attach_symbols_from_file( + iree_dynamic_library_t* library, const char* file_path) { + IREE_ASSERT_ARGUMENT(library); + IREE_TRACE_ZONE_BEGIN(z0); + + IREEDbgHelpLock(); + + // Useful for debugging this logic; will print search paths and results: + // SymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_DEBUG); + + // Enumerates all loaded modules in the process to extract the module + // base/size parameters we need to overlay the PDB. There's other ways to + // get this (such as registering a LdrDllNotification callback and snooping + // the values during LoadLibrary or using CreateToolhelp32Snapshot), however + // EnumerateLoadedModules is in dbghelp which we are using anyway. + ModuleEnumCallbackState state; + memset(&state, 0, sizeof(state)); + state.module_path = library->module_path; + EnumerateLoadedModules64(GetCurrentProcess(), EnumLoadedModulesCallback, + &state); + + // Load the PDB file and overlay it onto the already-loaded module at the + // address range it got loaded into. + if (state.module_base != 0) { + SymLoadModuleEx(GetCurrentProcess(), NULL, file_path, + library->identifier.data, state.module_base, + state.module_size, NULL, 0); + } + + IREEDbgHelpUnlock(); + + IREE_TRACE_ZONE_END(z0); + return iree_ok_status(); +} + +iree_status_t iree_dynamic_library_attach_symbols_from_memory( + iree_dynamic_library_t* library, iree_const_byte_span_t buffer) { + IREE_ASSERT_ARGUMENT(library); + IREE_TRACE_ZONE_BEGIN(z0); + + if (library->temp_file_count + 1 >= + IREE_ARRAYSIZE(library->temp_file_paths)) { + return iree_make_status(IREE_STATUS_RESOURCE_EXHAUSTED, + "too many temp files attached"); + } + + // Extract the library to a temp file. + char* temp_path = NULL; + iree_status_t status = iree_dynamic_library_write_temp_file( + buffer, "mem_", "pdb", library->allocator, &temp_path); + if (iree_status_is_ok(status)) { + // Associate the temp path to the library; the temp_path string and the + // backing file will be deleted when the library is closed. + library->temp_file_paths[library->temp_file_count++] = temp_path; + + // Attempt to attach the extracted temp file to the module. + status = iree_dynamic_library_attach_symbols_from_file(library, temp_path); + } + + IREE_TRACE_ZONE_END(z0); + return status; +} + +#else + +iree_status_t iree_dynamic_library_attach_symbols_from_file( + iree_dynamic_library_t* library, const char* file_path) { + return iree_ok_status(); +} + +iree_status_t iree_dynamic_library_attach_symbols_from_memory( + iree_dynamic_library_t* library, iree_const_byte_span_t buffer) { + return iree_ok_status(); +} + +#endif // IREE_HAVE_DYNAMIC_LIBRARY_PDB_SUPPORT + +#endif // IREE_PLATFORM_WINDOWS diff --git a/iree/base/internal/file_handle_win32.cc b/iree/base/internal/file_handle_win32.cc deleted file mode 100644 index 06f0b7747ed5f..0000000000000 --- a/iree/base/internal/file_handle_win32.cc +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2019 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "iree/base/internal/file_handle_win32.h" - -#include "absl/memory/memory.h" -#include "absl/strings/str_replace.h" -#include "iree/base/target_platform.h" - -#if defined(IREE_PLATFORM_WINDOWS) - -namespace iree { - -static void CanonicalizePath(std::string* path) { - absl::StrReplaceAll({{"/", "\\"}}, path); -} - -// static -Status FileHandle::OpenRead(std::string path, DWORD file_flags, - std::unique_ptr* out_handle) { - out_handle->reset(); - CanonicalizePath(&path); - HANDLE handle = ::CreateFileA( - /*lpFileName=*/path.c_str(), /*dwDesiredAccess=*/GENERIC_READ, - /*dwShareMode=*/FILE_SHARE_READ, /*lpSecurityAttributes=*/nullptr, - /*dwCreationDisposition=*/OPEN_EXISTING, - /*dwFlagsAndAttributes=*/FILE_ATTRIBUTE_NORMAL | file_flags, - /*hTemplateFile=*/nullptr); - if (handle == INVALID_HANDLE_VALUE) { - return iree_make_status(iree_status_code_from_win32_error(GetLastError()), - "unable to open file '%s'", path.c_str()); - } - - BY_HANDLE_FILE_INFORMATION file_info; - if (::GetFileInformationByHandle(handle, &file_info) == FALSE) { - return iree_make_status(iree_status_code_from_win32_error(GetLastError()), - "unable to query file info for %s", path.c_str()); - } - - uint64_t file_size = (static_cast(file_info.nFileSizeHigh) << 32) | - file_info.nFileSizeLow; - *out_handle = absl::make_unique(handle, file_size); - return OkStatus(); -} - -// static -Status FileHandle::OpenWrite(std::string path, DWORD file_flags, - std::unique_ptr* out_handle) { - out_handle->reset(); - CanonicalizePath(&path); - HANDLE handle = ::CreateFileA( - /*lpFileName=*/path.c_str(), /*dwDesiredAccess=*/GENERIC_WRITE, - /*dwShareMode=*/FILE_SHARE_DELETE, /*lpSecurityAttributes=*/nullptr, - /*dwCreationDisposition=*/CREATE_ALWAYS, - /*dwFlagsAndAttributes=*/FILE_ATTRIBUTE_NORMAL | file_flags, - /*hTemplateFile=*/nullptr); - if (handle == INVALID_HANDLE_VALUE) { - return iree_make_status(iree_status_code_from_win32_error(GetLastError()), - "unable to open file '%s'", path.c_str()); - } - *out_handle = absl::make_unique(handle, 0); - return OkStatus(); -} - -FileHandle::~FileHandle() { ::CloseHandle(handle_); } - -} // namespace iree - -#endif // IREE_PLATFORM_WINDOWS diff --git a/iree/base/internal/file_handle_win32.h b/iree/base/internal/file_handle_win32.h deleted file mode 100644 index 3532b599c6b1d..0000000000000 --- a/iree/base/internal/file_handle_win32.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2019 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef IREE_BASE_INTERNAL_FILE_HANDLE_WIN32_H_ -#define IREE_BASE_INTERNAL_FILE_HANDLE_WIN32_H_ - -#include -#include - -#include "absl/memory/memory.h" -#include "absl/strings/string_view.h" -#include "iree/base/status.h" -#include "iree/base/target_platform.h" - -#if defined(IREE_PLATFORM_WINDOWS) - -namespace iree { - -class FileHandle { - public: - static Status OpenRead(std::string path, DWORD file_flags, - std::unique_ptr* out_handle); - static Status OpenWrite(std::string path, DWORD file_flags, - std::unique_ptr* out_handle); - - FileHandle(HANDLE handle, size_t size) : handle_(handle), size_(size) {} - ~FileHandle(); - - absl::string_view path() const { return path_; } - HANDLE handle() const { return handle_; } - size_t size() const { return size_; } - - private: - FileHandle(const FileHandle&) = delete; - FileHandle& operator=(const FileHandle&) = delete; - - std::string path_; - HANDLE handle_; - size_t size_; -}; - -} // namespace iree - -#endif // IREE_PLATFORM_WINDOWS - -#endif // IREE_BASE_INTERNAL_FILE_HANDLE_WIN32_H_ diff --git a/iree/base/internal/file_io.cc b/iree/base/internal/file_io.cc new file mode 100644 index 0000000000000..4190e0df401f7 --- /dev/null +++ b/iree/base/internal/file_io.cc @@ -0,0 +1,105 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "iree/base/internal/file_io.h" + +#include +#include +#include +#include + +#include "iree/base/target_platform.h" +#include "iree/base/tracing.h" + +namespace iree { +namespace file_io { + +iree_status_t FileExists(const char* path) { + IREE_TRACE_ZONE_BEGIN(z0); + struct stat stat_buf; + iree_status_t status = + stat(path, &stat_buf) == 0 + ? iree_ok_status() + : iree_make_status(IREE_STATUS_NOT_FOUND, "'%s'", path); + IREE_TRACE_ZONE_END(z0); + return status; +} + +iree_status_t GetFileContents(const char* path, std::string* out_contents) { + IREE_TRACE_ZONE_BEGIN(z0); + *out_contents = std::string(); + FILE* file = fopen(path, "r"); + if (file == NULL) { + IREE_TRACE_ZONE_END(z0); + return iree_make_status(iree_status_code_from_errno(errno), + "failed to open file '%s'", path); + } + iree_status_t status = iree_ok_status(); + if (fseek(file, 0, SEEK_END) == -1) { + status = iree_make_status(iree_status_code_from_errno(errno), "seek (end)"); + } + size_t file_size = 0; + if (iree_status_is_ok(status)) { + file_size = ftell(file); + if (file_size == -1L) { + status = + iree_make_status(iree_status_code_from_errno(errno), "size query"); + } + } + if (iree_status_is_ok(status)) { + if (fseek(file, 0, SEEK_SET) == -1) { + status = + iree_make_status(iree_status_code_from_errno(errno), "seek (beg)"); + } + } + std::string contents; + if (iree_status_is_ok(status)) { + contents.resize(file_size); + if (fread((char*)contents.data(), file_size, 1, file) != 1) { + status = + iree_make_status(iree_status_code_from_errno(errno), + "unable to read entire file contents of '%s'", path); + } + } + if (iree_status_is_ok(status)) { + *out_contents = std::move(contents); + } + fclose(file); + IREE_TRACE_ZONE_END(z0); + return status; +} + +iree_status_t SetFileContents(const char* path, + iree_const_byte_span_t content) { + IREE_TRACE_ZONE_BEGIN(z0); + FILE* file = fopen(path, "wb"); + if (file == NULL) { + IREE_TRACE_ZONE_END(z0); + return iree_make_status(iree_status_code_from_errno(errno), + "failed to open file '%s'", path); + } + int ret = fwrite((char*)content.data, content.data_length, 1, file); + iree_status_t status = iree_ok_status(); + if (ret != 1) { + status = + iree_make_status(IREE_STATUS_DATA_LOSS, + "unable to write entire file contents of '%s'", path); + } + fclose(file); + IREE_TRACE_ZONE_END(z0); + return status; +} + +} // namespace file_io +} // namespace iree diff --git a/iree/base/internal/file_io.h b/iree/base/internal/file_io.h index 00d53df2cae57..144204cf5d444 100644 --- a/iree/base/internal/file_io.h +++ b/iree/base/internal/file_io.h @@ -17,8 +17,7 @@ #include -#include "absl/strings/string_view.h" -#include "iree/base/status.h" +#include "iree/base/api.h" namespace iree { namespace file_io { @@ -27,33 +26,13 @@ namespace file_io { // // Returns an OK status if the file definitely exists. // Errors can include PermissionDeniedError, NotFoundError, etc. -Status FileExists(const std::string& path); +iree_status_t FileExists(const char* path); // Synchronously reads a file's contents into a string. -Status GetFileContents(const std::string& path, std::string* out_contents); +iree_status_t GetFileContents(const char* path, std::string* out_contents); // Synchronously writes a string into a file, overwriting its contents. -Status SetFileContents(const std::string& path, absl::string_view content); - -// Deletes the file at the provided path. -Status DeleteFile(const std::string& path); - -// Moves a file from 'source_path' to 'destination_path'. -// -// This may simply rename the file, but may fall back to a full copy and delete -// of the original if renaming is not possible (for example when moving between -// physical storage locations). -Status MoveFile(const std::string& source_path, - const std::string& destination_path); - -// Gets a platform and environment-dependent path for temporary files. -std::string GetTempPath(); - -// TODO(#3845): remove this when dylibs no longer need temp files. -// Gets a temporary file name and returns its absolute path. -// The particular path chosen is platform and environment-dependent. -// Unique characters will be automatically inserted after |base_name|. -Status GetTempFile(absl::string_view base_name, std::string* out_path); +iree_status_t SetFileContents(const char* path, iree_const_byte_span_t content); } // namespace file_io } // namespace iree diff --git a/iree/base/internal/file_io_posix.cc b/iree/base/internal/file_io_posix.cc deleted file mode 100644 index 81a04cee6baf6..0000000000000 --- a/iree/base/internal/file_io_posix.cc +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright 2019 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "iree/base/target_platform.h" - -#if defined(IREE_PLATFORM_ANDROID) || defined(IREE_PLATFORM_APPLE) || \ - defined(IREE_PLATFORM_LINUX) - -#include -#include -#include - -#include -#include - -#include "absl/strings/str_cat.h" -#include "iree/base/internal/file_io.h" -#include "iree/base/status.h" -#include "iree/base/tracing.h" - -namespace iree { -namespace file_io { - -Status FileExists(const std::string& path) { - IREE_TRACE_SCOPE0("file_io::FileExists"); - struct stat stat_buf; - return stat(path.c_str(), &stat_buf) == 0 - ? iree_ok_status() - : iree_make_status(IREE_STATUS_NOT_FOUND, "'%s'", path.c_str()); -} - -Status GetFileContents(const std::string& path, std::string* out_contents) { - IREE_TRACE_SCOPE0("file_io::GetFileContents"); - *out_contents = std::string(); - std::unique_ptr file = {std::fopen(path.c_str(), "r"), - +[](FILE* file) { - if (file) fclose(file); - }}; - if (file == nullptr) { - return iree_make_status(iree_status_code_from_errno(errno), - "failed to open file '%s'", path.c_str()); - } - if (std::fseek(file.get(), 0, SEEK_END) == -1) { - return iree_make_status(iree_status_code_from_errno(errno), "seek (end)"); - } - size_t file_size = std::ftell(file.get()); - if (file_size == -1L) { - return iree_make_status(iree_status_code_from_errno(errno), "size query"); - } - if (std::fseek(file.get(), 0, SEEK_SET) == -1) { - return iree_make_status(iree_status_code_from_errno(errno), "seek (beg)"); - } - std::string contents; - contents.resize(file_size); - if (std::fread(const_cast(contents.data()), file_size, 1, - file.get()) != 1) { - return iree_make_status(IREE_STATUS_UNAVAILABLE, - "unable to read entire file contents of '%.*s'", - (int)path.size(), path.data()); - } - *out_contents = std::move(contents); - return OkStatus(); -} - -Status SetFileContents(const std::string& path, absl::string_view content) { - IREE_TRACE_SCOPE0("file_io::SetFileContents"); - std::unique_ptr file = {std::fopen(path.c_str(), "wb"), - +[](FILE* file) { - if (file) fclose(file); - }}; - if (file == nullptr) { - return iree_make_status(iree_status_code_from_errno(errno), - "failed to open file '%s'", path.c_str()); - } - if (std::fwrite(const_cast(content.data()), content.size(), 1, - file.get()) != 1) { - return iree_make_status(IREE_STATUS_UNAVAILABLE, - "unable to write entire file contents of '%.*s'", - (int)path.size(), path.data()); - } - return OkStatus(); -} - -Status DeleteFile(const std::string& path) { - IREE_TRACE_SCOPE0("file_io::DeleteFile"); - if (::remove(path.c_str()) == -1) { - return iree_make_status(iree_status_code_from_errno(errno), - "failed to delete file '%s'", path.c_str()); - } - return OkStatus(); -} - -Status MoveFile(const std::string& source_path, - const std::string& destination_path) { - IREE_TRACE_SCOPE0("file_io::MoveFile"); - if (::rename(source_path.c_str(), destination_path.c_str()) == -1) { - return iree_make_status(iree_status_code_from_errno(errno), - "failed to rename file '%s' to '%s'", - source_path.c_str(), destination_path.c_str()); - } - return OkStatus(); -} - -std::string GetTempPath() { - IREE_TRACE_SCOPE0("file_io::GetTempPath"); - - // TEST_TMPDIR will point to a writeable temp path when running bazel tests. - char* test_tmpdir = getenv("TEST_TMPDIR"); - if (test_tmpdir) { - return test_tmpdir; - } - - char* tmpdir = getenv("TMPDIR"); - if (tmpdir) { - return tmpdir; - } - -#ifdef __ANDROID__ - // Support running Android command-line programs both as regular shell user - // and as root. For the latter, TMPDIR is not defined by default. - return "/data/local/tmp"; -#else - return "/tmp"; -#endif -} - -// TODO(#3845): remove this when dylibs no longer need temp files. -Status GetTempFile(absl::string_view base_name, std::string* out_path) { - IREE_TRACE_SCOPE0("file_io::GetTempFile"); - *out_path = std::string(); - - std::string temp_path = GetTempPath(); - std::string template_path = - temp_path + "/" + std::string(base_name) + "XXXXXX"; - - if (::mkstemp(&template_path[0]) != -1) { - // Should have been modified by mkstemp. - *out_path = std::move(template_path); - return OkStatus(); - } else { - return iree_make_status(iree_status_code_from_errno(errno), - "failed to create temp file with template '%s'", - template_path.c_str()); - } -} - -} // namespace file_io -} // namespace iree - -#endif // IREE_PLATFORM_* diff --git a/iree/base/internal/file_io_test.cc b/iree/base/internal/file_io_test.cc index 37dc1a17bb6d5..03e0c02102755 100644 --- a/iree/base/internal/file_io_test.cc +++ b/iree/base/internal/file_io_test.cc @@ -14,10 +14,6 @@ #include "iree/base/internal/file_io.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/string_view.h" -#include "iree/base/logging.h" -#include "iree/base/status.h" #include "iree/testing/gtest.h" #include "iree/testing/status_matchers.h" @@ -27,82 +23,36 @@ namespace { using ::iree::testing::status::StatusIs; -std::string GetUniquePath(absl::string_view unique_name) { +std::string GetUniquePath(const char* unique_name) { char* test_tmpdir = getenv("TEST_TMPDIR"); + if (!test_tmpdir) { + test_tmpdir = getenv("TMPDIR"); + } + if (!test_tmpdir) { + test_tmpdir = getenv("TEMP"); + } IREE_CHECK(test_tmpdir) << "TEST_TMPDIR not defined"; - return test_tmpdir + std::string("/") + std::string(unique_name) + - "_test.txt"; + return test_tmpdir + std::string("/iree_test_") + unique_name; } -std::string GetUniqueContents(absl::string_view unique_name) { - return absl::StrCat("Test with name ", unique_name, "\n"); +std::string GetUniqueContents(const char* unique_name) { + return std::string("Test with name ") + unique_name + "\n"; } TEST(FileIo, GetSetContents) { - std::string unique_name = "GetSetContents"; - auto path = GetUniquePath(unique_name); - ASSERT_THAT(FileExists(path), StatusIs(StatusCode::kNotFound)); - auto to_write = GetUniqueContents(unique_name); - - IREE_ASSERT_OK(SetFileContents(path, to_write)); + constexpr const char* kUniqueName = "GetSetContents"; + auto path = GetUniquePath(kUniqueName); + ASSERT_THAT(FileExists(path.c_str()), StatusIs(StatusCode::kNotFound)); + auto to_write = GetUniqueContents(kUniqueName); + + IREE_ASSERT_OK(SetFileContents( + path.c_str(), + iree_make_const_byte_span(to_write.data(), to_write.size()))); std::string read; - IREE_ASSERT_OK(GetFileContents(path, &read)); + IREE_ASSERT_OK(GetFileContents(path.c_str(), &read)); EXPECT_EQ(to_write, read); } -TEST(FileIo, SetDeleteExists) { - std::string unique_name = "SetDeleteExists"; - auto path = GetUniquePath(unique_name); - ASSERT_THAT(FileExists(path), StatusIs(StatusCode::kNotFound)); - auto to_write = GetUniqueContents(unique_name); - - IREE_ASSERT_OK(SetFileContents(path, to_write)); - IREE_ASSERT_OK(FileExists(path)); - IREE_ASSERT_OK(DeleteFile(path)); - EXPECT_THAT(FileExists(path), StatusIs(StatusCode::kNotFound)); -} - -TEST(FileIo, MoveFile) { - auto from_path = GetUniquePath("MoveFileFrom"); - auto to_path = GetUniquePath("MoveFileTo"); - ASSERT_THAT(FileExists(from_path), StatusIs(StatusCode::kNotFound)); - ASSERT_THAT(FileExists(to_path), StatusIs(StatusCode::kNotFound)); - auto to_write = GetUniqueContents("MoveFile"); - - IREE_ASSERT_OK(SetFileContents(from_path, to_write)); - IREE_ASSERT_OK(FileExists(from_path)); - IREE_EXPECT_OK(MoveFile(from_path, to_path)); - EXPECT_THAT(FileExists(from_path), StatusIs(StatusCode::kNotFound)); - IREE_EXPECT_OK(FileExists(to_path)); - std::string read; - IREE_ASSERT_OK(GetFileContents(to_path, &read)); - EXPECT_EQ(to_write, read); -} - -TEST(FileIo, GetTempPath) { - auto temp_path = GetTempPath(); - EXPECT_NE("", temp_path); -} - -TEST(FileIo, GetTempFile) { - std::string path1; - IREE_ASSERT_OK(GetTempFile("foo", &path1)); - EXPECT_TRUE(path1.find("foo") != std::string::npos); - - // Should be able to set file contents at the given path. - // Note that the file may or may not exist, depending on the platform, and - // a file must be created at the path before calling GetTempFile again, or - // else the same path may be returned. - auto to_write = GetUniqueContents("GetTempFile"); - IREE_ASSERT_OK(SetFileContents(path1, to_write)); - - // Create another temp file with the same base name, check for a unique path. - std::string path2; - IREE_ASSERT_OK(GetTempFile("foo", &path2)); - EXPECT_TRUE(path2.find("foo") != std::string::npos); - EXPECT_NE(path1, path2); -} - } // namespace } // namespace file_io } // namespace iree diff --git a/iree/base/internal/file_io_win32.cc b/iree/base/internal/file_io_win32.cc deleted file mode 100644 index 2a7cc7b170b97..0000000000000 --- a/iree/base/internal/file_io_win32.cc +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright 2019 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "iree/base/target_platform.h" - -#if defined(IREE_PLATFORM_WINDOWS) - -#include - -#include - -#include "absl/memory/memory.h" -#include "absl/strings/str_cat.h" -#include "iree/base/internal/file_handle_win32.h" -#include "iree/base/internal/file_io.h" -#include "iree/base/target_platform.h" -#include "iree/base/tracing.h" - -namespace iree { -namespace file_io { - -Status FileExists(const std::string& path) { - IREE_TRACE_SCOPE0("file_io::FileExists"); - DWORD attrs = ::GetFileAttributesA(path.c_str()); - if (attrs == INVALID_FILE_ATTRIBUTES) { - return iree_make_status(iree_status_code_from_win32_error(GetLastError()), - "unable to find/access file: %s", path.c_str()); - } - return OkStatus(); -} - -Status GetFileContents(const std::string& path, std::string* out_contents) { - IREE_TRACE_SCOPE0("file_io::GetFileContents"); - *out_contents = std::string(); - std::unique_ptr file; - IREE_RETURN_IF_ERROR( - FileHandle::OpenRead(std::move(path), FILE_FLAG_SEQUENTIAL_SCAN, &file)); - std::string contents; - contents.resize(file->size()); - DWORD bytes_read = 0; - if (::ReadFile(file->handle(), const_cast(contents.data()), - static_cast(contents.size()), &bytes_read, - nullptr) == FALSE) { - return iree_make_status(iree_status_code_from_win32_error(GetLastError()), - "unable to read file span of %zu bytes from '%s'", - contents.size(), path.c_str()); - } else if (bytes_read != file->size()) { - return iree_make_status( - IREE_STATUS_RESOURCE_EXHAUSTED, - "unable to read all %zu bytes from '%.*s' (got %zu)", file->size(), - (int)path.size(), path.data(), bytes_read); - } - *out_contents = contents; - return OkStatus(); -} - -Status SetFileContents(const std::string& path, absl::string_view content) { - IREE_TRACE_SCOPE0("file_io::SetFileContents"); - std::unique_ptr file; - IREE_RETURN_IF_ERROR(FileHandle::OpenWrite(std::move(path), 0, &file)); - if (::WriteFile(file->handle(), content.data(), - static_cast(content.size()), NULL, NULL) == FALSE) { - return iree_make_status(iree_status_code_from_win32_error(GetLastError()), - "unable to write file span of %zu bytes to '%s'", - content.size(), path.c_str()); - } - return OkStatus(); -} - -Status DeleteFile(const std::string& path) { - IREE_TRACE_SCOPE0("file_io::DeleteFile"); - if (::DeleteFileA(path.c_str()) == FALSE) { - return iree_make_status(iree_status_code_from_win32_error(GetLastError()), - "unable to delete/access file: %s", path.c_str()); - } - return OkStatus(); -} - -Status MoveFile(const std::string& source_path, - const std::string& destination_path) { - IREE_TRACE_SCOPE0("file_io::MoveFile"); - if (::MoveFileA(source_path.c_str(), destination_path.c_str()) == FALSE) { - return iree_make_status(iree_status_code_from_win32_error(GetLastError()), - "unable to move file '%s' to '%s'", - source_path.c_str(), destination_path.c_str()); - } - return OkStatus(); -} - -std::string GetTempPath() { - IREE_TRACE_SCOPE0("file_io::GetTempPath"); - - // TEST_TMPDIR will point to a writeable temp path when running bazel tests. - char* test_tmpdir = getenv("TEST_TMPDIR"); - if (test_tmpdir) { - return test_tmpdir; - } - - std::string temp_path(64, '\0'); - for (bool retry_query = true; retry_query;) { - DWORD required_length = - ::GetTempPathA(static_cast(temp_path.size()), &temp_path[0]); - retry_query = required_length > temp_path.size(); - temp_path.resize(required_length); - } - return temp_path; -} - -// TODO(#3845): remove this when dylibs no longer need temp files. -Status GetTempFile(absl::string_view base_name, std::string* out_path) { - IREE_TRACE_SCOPE0("file_io::GetTempFile"); - *out_path = std::string(); - - std::string temp_path = GetTempPath(); - std::string template_path = - temp_path + std::string("\\") + std::string(base_name) + "XXXXXX"; - - if (::_mktemp(&template_path[0]) != nullptr) { - // Should have been modified by _mktemp. - static std::atomic next_id{0}; - template_path += std::to_string(next_id++); - *out_path = std::move(template_path); - return OkStatus(); - } else { - return iree_make_status(iree_status_code_from_win32_error(GetLastError()), - "unable to create temp file with template '%s'", - template_path.c_str()); - } -} - -} // namespace file_io -} // namespace iree - -#endif // IREE_PLATFORM_WINDOWS diff --git a/iree/base/testing/BUILD b/iree/base/testing/BUILD index 9a897f1ac0071..299a3d14e2239 100644 --- a/iree/base/testing/BUILD +++ b/iree/base/testing/BUILD @@ -43,8 +43,7 @@ cc_test( deps = [ ":dynamic_library_test_library", "//iree/base:core_headers", - "//iree/base:dynamic_library", - "//iree/base:status", + "//iree/base/internal:dynamic_library", "//iree/base/internal:file_io", "//iree/testing:gtest", "//iree/testing:gtest_main", diff --git a/iree/base/testing/CMakeLists.txt b/iree/base/testing/CMakeLists.txt index 8b0c9578adc07..6c3a4a2926664 100644 --- a/iree/base/testing/CMakeLists.txt +++ b/iree/base/testing/CMakeLists.txt @@ -50,9 +50,8 @@ iree_cc_test( DEPS ::dynamic_library_test_library iree::base::core_headers - iree::base::dynamic_library + iree::base::internal::dynamic_library iree::base::internal::file_io - iree::base::status iree::testing::gtest iree::testing::gtest_main ) diff --git a/iree/base/testing/dynamic_library_test.cc b/iree/base/testing/dynamic_library_test.cc index 0f831baff0edc..8d8973dca3345 100644 --- a/iree/base/testing/dynamic_library_test.cc +++ b/iree/base/testing/dynamic_library_test.cc @@ -12,12 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "iree/base/dynamic_library.h" +#include "iree/base/internal/dynamic_library.h" #include #include "iree/base/internal/file_io.h" -#include "iree/base/status.h" #include "iree/base/target_platform.h" #include "iree/base/testing/dynamic_library_test_library_embed.h" #include "iree/testing/gtest.h" @@ -32,6 +31,20 @@ static const char* kUnknownName = "library_that_does_not_exist.so"; class DynamicLibraryTest : public ::testing::Test { public: + static std::string GetTempFilename(const char* suffix) { + static int unique_id = 0; + char* test_tmpdir = getenv("TEST_TMPDIR"); + if (!test_tmpdir) { + test_tmpdir = getenv("TMPDIR"); + } + if (!test_tmpdir) { + test_tmpdir = getenv("TEMP"); + } + IREE_CHECK(test_tmpdir) << "TEST_TMPDIR not defined"; + return test_tmpdir + std::string("/iree_test_") + + std::to_string(unique_id++) + suffix; + } + static void SetUpTestCase() { // Making files available to tests, particularly across operating systems // and build tools (Bazel/CMake) is complicated. Rather than include a test @@ -39,19 +52,18 @@ class DynamicLibraryTest : public ::testing::Test { // the file so it's embedded in a C++ module, then write that embedded file // to a platform/test-environment specific temp file for loading. - std::string base_name = "dynamic_library_test_library"; - IREE_ASSERT_OK(file_io::GetTempFile(base_name, &library_temp_path_)); // System APIs for loading dynamic libraries typically require an extension. #if defined(IREE_PLATFORM_WINDOWS) - library_temp_path_ += ".dll"; + static constexpr const char* ext = ".dll"; #else - library_temp_path_ += ".so"; + static constexpr const char* ext = ".so"; #endif + library_temp_path_ = GetTempFilename(ext); const auto* file_toc = dynamic_library_test_library_create(); - absl::string_view file_data(reinterpret_cast(file_toc->data), - file_toc->size); - IREE_ASSERT_OK(file_io::SetFileContents(library_temp_path_, file_data)); + IREE_ASSERT_OK(file_io::SetFileContents( + library_temp_path_.c_str(), + iree_make_const_byte_span(file_toc->data, file_toc->size))); IREE_LOG(INFO) << "Embedded test library written to temp path: " << library_temp_path_; @@ -63,38 +75,62 @@ class DynamicLibraryTest : public ::testing::Test { std::string DynamicLibraryTest::library_temp_path_; TEST_F(DynamicLibraryTest, LoadLibrarySuccess) { - std::unique_ptr library; - IREE_ASSERT_OK(DynamicLibrary::Load(library_temp_path_.c_str(), &library)); + iree_dynamic_library_t* library = NULL; + IREE_ASSERT_OK(iree_dynamic_library_load_from_file( + library_temp_path_.c_str(), IREE_DYNAMIC_LIBRARY_FLAG_NONE, + iree_allocator_system(), &library)); + iree_dynamic_library_release(library); } TEST_F(DynamicLibraryTest, LoadLibraryFailure) { - std::unique_ptr library; - EXPECT_THAT(DynamicLibrary::Load(kUnknownName, &library), - StatusIs(iree::StatusCode::kUnavailable)); + iree_dynamic_library_t* library = NULL; + EXPECT_THAT(iree_dynamic_library_load_from_file( + kUnknownName, IREE_DYNAMIC_LIBRARY_FLAG_NONE, + iree_allocator_system(), &library), + StatusIs(iree::StatusCode::kNotFound)); } TEST_F(DynamicLibraryTest, LoadLibraryTwice) { - std::unique_ptr library1; - IREE_ASSERT_OK(DynamicLibrary::Load(library_temp_path_.c_str(), &library1)); - std::unique_ptr library2; - IREE_ASSERT_OK(DynamicLibrary::Load(library_temp_path_.c_str(), &library2)); + iree_dynamic_library_t* library1 = NULL; + iree_dynamic_library_t* library2 = NULL; + IREE_ASSERT_OK(iree_dynamic_library_load_from_file( + library_temp_path_.c_str(), IREE_DYNAMIC_LIBRARY_FLAG_NONE, + iree_allocator_system(), &library1)); + IREE_ASSERT_OK(iree_dynamic_library_load_from_file( + library_temp_path_.c_str(), IREE_DYNAMIC_LIBRARY_FLAG_NONE, + iree_allocator_system(), &library2)); + iree_dynamic_library_release(library1); + iree_dynamic_library_release(library2); } TEST_F(DynamicLibraryTest, GetSymbolSuccess) { - std::unique_ptr library; - IREE_ASSERT_OK(DynamicLibrary::Load(library_temp_path_.c_str(), &library)); - - auto times_two_fn = library->GetSymbol("times_two"); - ASSERT_NE(nullptr, times_two_fn); - EXPECT_EQ(246, times_two_fn(123)); + iree_dynamic_library_t* library = NULL; + IREE_ASSERT_OK(iree_dynamic_library_load_from_file( + library_temp_path_.c_str(), IREE_DYNAMIC_LIBRARY_FLAG_NONE, + iree_allocator_system(), &library)); + + int (*fn_ptr)(int); + IREE_ASSERT_OK(iree_dynamic_library_lookup_symbol(library, "times_two", + (void**)&fn_ptr)); + ASSERT_NE(nullptr, fn_ptr); + EXPECT_EQ(246, fn_ptr(123)); + + iree_dynamic_library_release(library); } TEST_F(DynamicLibraryTest, GetSymbolFailure) { - std::unique_ptr library; - IREE_ASSERT_OK(DynamicLibrary::Load(library_temp_path_.c_str(), &library)); - - auto unknown_fn = library->GetSymbol("unknown"); - EXPECT_EQ(nullptr, unknown_fn); + iree_dynamic_library_t* library = NULL; + IREE_ASSERT_OK(iree_dynamic_library_load_from_file( + library_temp_path_.c_str(), IREE_DYNAMIC_LIBRARY_FLAG_NONE, + iree_allocator_system(), &library)); + + int (*fn_ptr)(int); + EXPECT_THAT( + iree_dynamic_library_lookup_symbol(library, "unknown", (void**)&fn_ptr), + StatusIs(iree::StatusCode::kNotFound)); + EXPECT_EQ(nullptr, fn_ptr); + + iree_dynamic_library_release(library); } } // namespace diff --git a/iree/base/testing/dynamic_library_test_library.cc b/iree/base/testing/dynamic_library_test_library.cc index 4d99552336d6f..20cde1841c2b0 100644 --- a/iree/base/testing/dynamic_library_test_library.cc +++ b/iree/base/testing/dynamic_library_test_library.cc @@ -13,9 +13,7 @@ // limitations under the License. #ifdef __cplusplus -#define IREE_API_EXPORT extern "C" -#else -#define IREE_API_EXPORT +extern "C" { #endif // __cplusplus #if defined(_WIN32) @@ -24,4 +22,8 @@ #define IREE_SYM_EXPORT __attribute__((visibility("default"))) #endif // _WIN32 -IREE_API_EXPORT int IREE_SYM_EXPORT times_two(int value) { return value * 2; } +int IREE_SYM_EXPORT times_two(int value) { return value * 2; } + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus diff --git a/iree/hal/cuda/BUILD b/iree/hal/cuda/BUILD index b1c1d8846677b..00f80effc3358 100644 --- a/iree/hal/cuda/BUILD +++ b/iree/hal/cuda/BUILD @@ -80,18 +80,18 @@ cc_library( name = "dynamic_symbols", srcs = [ "cuda_headers.h", - "dynamic_symbols.cc", - "dynamic_symbols_tables.h", + "dynamic_symbols.c", ], hdrs = [ "dynamic_symbols.h", ], + textual_hdrs = [ + "dynamic_symbol_tables.h", + ], deps = [ "//iree/base:core_headers", - "//iree/base:dynamic_library", - "//iree/base:status", "//iree/base:tracing", - "@com_google_absl//absl/types:span", + "//iree/base/internal:dynamic_library", "@cuda_headers", ], ) diff --git a/iree/hal/cuda/CMakeLists.txt b/iree/hal/cuda/CMakeLists.txt index 205e34341a45b..967bde4fbfecc 100644 --- a/iree/hal/cuda/CMakeLists.txt +++ b/iree/hal/cuda/CMakeLists.txt @@ -65,16 +65,15 @@ iree_cc_library( dynamic_symbols HDRS "dynamic_symbols.h" + TEXTUAL_HDRS + "dynamic_symbol_tables.h" SRCS "cuda_headers.h" - "dynamic_symbols.cc" - "dynamic_symbols_tables.h" + "dynamic_symbols.c" DEPS - absl::span cuda_headers iree::base::core_headers - iree::base::dynamic_library - iree::base::status + iree::base::internal::dynamic_library iree::base::tracing PUBLIC ) diff --git a/iree/hal/cuda/cuda_driver.c b/iree/hal/cuda/cuda_driver.c index 1f83363079a24..1a1b20f249c35 100644 --- a/iree/hal/cuda/cuda_driver.c +++ b/iree/hal/cuda/cuda_driver.c @@ -61,7 +61,8 @@ static iree_status_t iree_hal_cuda_driver_create_internal( identifier, &driver->identifier, (char*)driver + total_size - identifier.size); driver->default_device_index = options->default_device_index; - iree_status_t status = load_symbols(&driver->syms); + iree_status_t status = + iree_hal_cuda_dynamic_symbols_initialize(host_allocator, &driver->syms); if (iree_status_is_ok(status)) { *out_driver = (iree_hal_driver_t*)driver; } else { @@ -75,7 +76,7 @@ static void iree_hal_cuda_driver_destroy(iree_hal_driver_t* base_driver) { iree_allocator_t host_allocator = driver->host_allocator; IREE_TRACE_ZONE_BEGIN(z0); - unload_symbols(&driver->syms); + iree_hal_cuda_dynamic_symbols_deinitialize(&driver->syms); iree_allocator_free(host_allocator, driver); IREE_TRACE_ZONE_END(z0); diff --git a/iree/hal/cuda/dynamic_symbols_tables.h b/iree/hal/cuda/dynamic_symbol_tables.h similarity index 100% rename from iree/hal/cuda/dynamic_symbols_tables.h rename to iree/hal/cuda/dynamic_symbol_tables.h diff --git a/iree/hal/cuda/dynamic_symbols.c b/iree/hal/cuda/dynamic_symbols.c new file mode 100644 index 0000000000000..c157ae7139972 --- /dev/null +++ b/iree/hal/cuda/dynamic_symbols.c @@ -0,0 +1,65 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "iree/hal/cuda/dynamic_symbols.h" + +#include + +#include "iree/base/internal/dynamic_library.h" +#include "iree/base/target_platform.h" +#include "iree/base/tracing.h" + +static const char* kCUDALoaderSearchNames[] = { +#if defined(IREE_PLATFORM_WINDOWS) + "nvcuda.dll", +#else + "libcuda.so", +#endif +}; + +static iree_status_t iree_hal_cuda_dynamic_symbols_resolve_all( + iree_hal_cuda_dynamic_symbols_t* syms) { +#define CU_PFN_DECL(cudaSymbolName, ...) \ + { \ + static const char* kName = #cudaSymbolName; \ + IREE_RETURN_IF_ERROR(iree_dynamic_library_lookup_symbol( \ + syms->loader_library, kName, (void**)&syms->cudaSymbolName)); \ + } +#include "iree/hal/cuda/dynamic_symbol_tables.h" +#undef CU_PFN_DECL + return iree_ok_status(); +} + +iree_status_t iree_hal_cuda_dynamic_symbols_initialize( + iree_allocator_t allocator, iree_hal_cuda_dynamic_symbols_t* out_syms) { + IREE_TRACE_ZONE_BEGIN(z0); + memset(out_syms, 0, sizeof(*out_syms)); + IREE_RETURN_IF_ERROR(iree_dynamic_library_load_from_files( + IREE_ARRAYSIZE(kCUDALoaderSearchNames), kCUDALoaderSearchNames, + IREE_DYNAMIC_LIBRARY_FLAG_NONE, allocator, &out_syms->loader_library)); + iree_status_t status = iree_hal_cuda_dynamic_symbols_resolve_all(out_syms); + if (!iree_status_is_ok(status)) { + iree_hal_cuda_dynamic_symbols_deinitialize(out_syms); + } + IREE_TRACE_ZONE_END(z0); + return status; +} + +void iree_hal_cuda_dynamic_symbols_deinitialize( + iree_hal_cuda_dynamic_symbols_t* syms) { + IREE_TRACE_ZONE_BEGIN(z0); + iree_dynamic_library_release(syms->loader_library); + memset(syms, 0, sizeof(*syms)); + IREE_TRACE_ZONE_END(z0); +} diff --git a/iree/hal/cuda/dynamic_symbols.cc b/iree/hal/cuda/dynamic_symbols.cc deleted file mode 100644 index df13a4dae7158..0000000000000 --- a/iree/hal/cuda/dynamic_symbols.cc +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2021 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "iree/hal/cuda/dynamic_symbols.h" - -#include - -#include "absl/types/span.h" -#include "iree/base/dynamic_library.h" -#include "iree/base/status.h" -#include "iree/base/target_platform.h" -#include "iree/base/tracing.h" - -static const char* kCUDALoaderSearchNames[] = { -#if defined(IREE_PLATFORM_WINDOWS) - "nvcuda.dll", -#else - "libcuda.so", -#endif -}; - -extern "C" { - -iree_status_t load_symbols(iree_hal_cuda_dynamic_symbols_t* syms) { - std::unique_ptr loader_library; - IREE_RETURN_IF_ERROR(iree::DynamicLibrary::Load( - absl::MakeSpan(kCUDALoaderSearchNames), &loader_library)); - -#define CU_PFN_DECL(cudaSymbolName, ...) \ - { \ - using FuncPtrT = decltype(syms->cudaSymbolName); \ - static const char* kName = #cudaSymbolName; \ - syms->cudaSymbolName = loader_library->GetSymbol(kName); \ - if (!syms->cudaSymbolName) { \ - return iree_make_status(IREE_STATUS_UNAVAILABLE, "symbol not found"); \ - } \ - } - -#include "dynamic_symbols_tables.h" -#undef CU_PFN_DECL - syms->opaque_loader_library_ = (void*)loader_library.release(); - return iree_ok_status(); -} - -void unload_symbols(iree_hal_cuda_dynamic_symbols_t* syms) { - delete (iree::DynamicLibrary*)syms->opaque_loader_library_; -} - -} // extern "C" \ No newline at end of file diff --git a/iree/hal/cuda/dynamic_symbols.h b/iree/hal/cuda/dynamic_symbols.h index 436b32d63a22b..97d9ba1b9a1c3 100644 --- a/iree/hal/cuda/dynamic_symbols.h +++ b/iree/hal/cuda/dynamic_symbols.h @@ -16,25 +16,37 @@ #define IREE_HAL_CUDA_DYNAMIC_SYMBOLS_H_ #include "iree/base/api.h" +#include "iree/base/internal/dynamic_library.h" #include "iree/hal/cuda/cuda_headers.h" #ifdef __cplusplus extern "C" { #endif // __cplusplus -/// DyanmicSymbols allow loading dynamically a subset of CUDA driver API. It -/// loads all the function declared in `dynamic_symbol_tables.def` and fail if -/// any of the symbol is not available. The functions signatures are matching -/// the declarations in `cuda.h`. + +// DynamicSymbols allow loading dynamically a subset of CUDA driver API. It +// loads all the function declared in `dynamic_symbol_tables.def` and fail if +// any of the symbol is not available. The functions signatures are matching +// the declarations in `cuda.h`. typedef struct { + iree_dynamic_library_t* loader_library; + #define CU_PFN_DECL(cudaSymbolName, ...) \ CUresult (*cudaSymbolName)(__VA_ARGS__); -#include "dynamic_symbols_tables.h" +#include "iree/hal/cuda/dynamic_symbol_tables.h" #undef CU_PFN_DECL - void* opaque_loader_library_; } iree_hal_cuda_dynamic_symbols_t; -iree_status_t load_symbols(iree_hal_cuda_dynamic_symbols_t* syms); -void unload_symbols(iree_hal_cuda_dynamic_symbols_t* syms); +// Initializes |out_syms| in-place with dynamically loaded CUDA symbols. +// iree_hal_cuda_dynamic_symbols_deinitialize must be used to release the +// library resources. +iree_status_t iree_hal_cuda_dynamic_symbols_initialize( + iree_allocator_t allocator, iree_hal_cuda_dynamic_symbols_t* out_syms); + +// Deinitializes |syms| by unloading the backing library. All function pointers +// will be invalidated. They _may_ still work if there are other reasons the +// library remains loaded so be careful. +void iree_hal_cuda_dynamic_symbols_deinitialize( + iree_hal_cuda_dynamic_symbols_t* syms); #ifdef __cplusplus } // extern "C" diff --git a/iree/hal/cuda/dynamic_symbols_test.cc b/iree/hal/cuda/dynamic_symbols_test.cc index 5b8681fe203bf..566c06096544d 100644 --- a/iree/hal/cuda/dynamic_symbols_test.cc +++ b/iree/hal/cuda/dynamic_symbols_test.cc @@ -30,7 +30,8 @@ namespace { TEST(DynamicSymbolsTest, CreateFromSystemLoader) { iree_hal_cuda_dynamic_symbols_t symbols; - iree_status_t status = load_symbols(&symbols); + iree_status_t status = iree_hal_cuda_dynamic_symbols_initialize( + iree_allocator_system(), &symbols); if (!iree_status_is_ok(status)) { IREE_LOG(WARNING) << "Symbols cannot be loaded, skipping test."; GTEST_SKIP(); @@ -43,7 +44,8 @@ TEST(DynamicSymbolsTest, CreateFromSystemLoader) { CUdevice device; CUDE_CHECK_ERRORS(symbols.cuDeviceGet(&device, /*ordinal=*/0)); } - unload_symbols(&symbols); + + iree_hal_cuda_dynamic_symbols_deinitialize(&symbols); } } // namespace diff --git a/iree/hal/local/loaders/BUILD b/iree/hal/local/loaders/BUILD index 8c711c3d258ef..618881dac987c 100644 --- a/iree/hal/local/loaders/BUILD +++ b/iree/hal/local/loaders/BUILD @@ -25,7 +25,7 @@ package( cc_library( name = "legacy_library_loader", - srcs = ["legacy_library_loader.cc"], + srcs = ["legacy_library_loader.c"], hdrs = ["legacy_library_loader.h"], defines = [ "IREE_HAL_HAVE_LEGACY_LIBRARY_LOADER=1", @@ -33,11 +33,9 @@ cc_library( deps = [ "//iree/base:api", "//iree/base:core_headers", - "//iree/base:dynamic_library", "//iree/base:flatcc", "//iree/base:tracing", - "//iree/base/internal:file_io", - "//iree/base/internal:file_path", + "//iree/base/internal:dynamic_library", "//iree/hal:api", "//iree/hal/local", "//iree/schemas:dylib_executable_def_c_fbs", diff --git a/iree/hal/local/loaders/CMakeLists.txt b/iree/hal/local/loaders/CMakeLists.txt index 761b566c99709..d4168d44b05d0 100644 --- a/iree/hal/local/loaders/CMakeLists.txt +++ b/iree/hal/local/loaders/CMakeLists.txt @@ -16,14 +16,12 @@ iree_cc_library( HDRS "legacy_library_loader.h" SRCS - "legacy_library_loader.cc" + "legacy_library_loader.c" DEPS iree::base::api iree::base::core_headers - iree::base::dynamic_library iree::base::flatcc - iree::base::internal::file_io - iree::base::internal::file_path + iree::base::internal::dynamic_library iree::base::tracing iree::hal::api iree::hal::local diff --git a/iree/hal/local/loaders/legacy_library_loader.cc b/iree/hal/local/loaders/legacy_library_loader.c similarity index 76% rename from iree/hal/local/loaders/legacy_library_loader.cc rename to iree/hal/local/loaders/legacy_library_loader.c index bebb69b93aec2..136877b070c84 100644 --- a/iree/hal/local/loaders/legacy_library_loader.cc +++ b/iree/hal/local/loaders/legacy_library_loader.c @@ -14,9 +14,7 @@ #include "iree/hal/local/loaders/legacy_library_loader.h" -#include "iree/base/dynamic_library.h" -#include "iree/base/internal/file_io.h" -#include "iree/base/internal/file_path.h" +#include "iree/base/internal/dynamic_library.h" #include "iree/base/target_platform.h" #include "iree/base/tracing.h" #include "iree/hal/local/local_executable.h" @@ -74,13 +72,8 @@ typedef struct { // Flatbuffer definition referencing the executable memory. iree_DyLibExecutableDef_table_t def; - // Temporary files created as part of extraction. - // Strings are allocated from the host allocator. - iree_host_size_t temp_file_count; - iree_string_view_t temp_files[8]; - // Loaded platform dynamic library. - iree::DynamicLibrary* handle; + iree_dynamic_library_t* handle; // Queried metadata from the library. union { @@ -94,45 +87,14 @@ extern const iree_hal_local_executable_vtable_t static iree_status_t iree_hal_legacy_executable_extract_and_load( iree_hal_legacy_executable_t* executable, iree_allocator_t host_allocator) { - // Write the embedded library out to a temp file, since all of the dynamic - // library APIs work with files. We could instead use in-memory files on - // platforms where that is convenient. - // - // TODO(#3845): use dlopen on an fd with either dlopen(/proc/self/fd/NN), - // fdlopen, or android_dlopen_ext to avoid needing to write the file to disk. - // Can fallback to memfd_create + dlopen where available, and fallback from - // that to disk (maybe just windows/mac). - std::string library_temp_path; - IREE_RETURN_IF_ERROR( - iree::file_io::GetTempFile("dylib_executable", &library_temp_path)); - -// Add platform-specific file extensions so opinionated dynamic library -// loaders are more likely to find the file: -#if defined(IREE_PLATFORM_WINDOWS) - library_temp_path += ".dll"; -#else - library_temp_path += ".so"; -#endif // IREE_PLATFORM_WINDOWS - - iree_string_view_t library_temp_file = iree_string_view_empty(); - IREE_RETURN_IF_ERROR( - iree_allocator_clone(host_allocator, - iree_make_const_byte_span(library_temp_path.data(), - library_temp_path.size()), - (void**)&library_temp_file.data)); - library_temp_file.size = library_temp_path.size(); - executable->temp_files[executable->temp_file_count++] = library_temp_file; - flatbuffers_uint8_vec_t embedded_library_vec = iree_DyLibExecutableDef_library_embedded_get(executable->def); - IREE_RETURN_IF_ERROR(iree::file_io::SetFileContents( - library_temp_path, - absl::string_view(reinterpret_cast(embedded_library_vec), - flatbuffers_uint8_vec_len(embedded_library_vec)))); - - std::unique_ptr handle; - IREE_RETURN_IF_ERROR( - iree::DynamicLibrary::Load(library_temp_path.c_str(), &handle)); + IREE_RETURN_IF_ERROR(iree_dynamic_library_load_from_memory( + iree_make_cstring_view("aot"), + iree_make_const_byte_span( + embedded_library_vec, + flatbuffers_uint8_vec_len(embedded_library_vec)), + IREE_DYNAMIC_LIBRARY_FLAG_NONE, host_allocator, &executable->handle)); flatbuffers_string_t debug_database_filename = iree_DyLibExecutableDef_debug_database_filename_get(executable->def); @@ -140,44 +102,22 @@ static iree_status_t iree_hal_legacy_executable_extract_and_load( iree_DyLibExecutableDef_debug_database_embedded_get(executable->def); if (flatbuffers_string_len(debug_database_filename) && flatbuffers_uint8_vec_len(debug_database_embedded_vec)) { - IREE_TRACE_SCOPE0("DyLibExecutable::AttachDebugDatabase"); - iree_string_view_t library_temp_path_sv = iree_make_string_view( - library_temp_path.data(), library_temp_path.size()); - iree_string_view_t debug_database_filename_sv = - iree_make_string_view(debug_database_filename, - flatbuffers_string_len(debug_database_filename)); - char* debug_database_path = NULL; - IREE_RETURN_IF_ERROR(iree_file_path_join( - iree_file_path_dirname(library_temp_path_sv), - debug_database_filename_sv, host_allocator, &debug_database_path)); - iree_string_view_t debug_database_path_sv = - iree_make_cstring_view(debug_database_path); - executable->temp_files[executable->temp_file_count++] = - debug_database_path_sv; - IREE_IGNORE_ERROR(iree::file_io::SetFileContents( - debug_database_path, - absl::string_view( - reinterpret_cast(debug_database_embedded_vec), + IREE_RETURN_IF_ERROR(iree_dynamic_library_attach_symbols_from_memory( + executable->handle, + iree_make_const_byte_span( + debug_database_embedded_vec, flatbuffers_uint8_vec_len(debug_database_embedded_vec)))); - handle->AttachDebugDatabase(debug_database_path); } - - executable->handle = handle.release(); - return iree_ok_status(); } static iree_status_t iree_hal_legacy_executable_query_library( iree_hal_legacy_executable_t* executable) { // Get the exported symbol used to get the library metadata. - iree_hal_executable_library_query_fn_t query_fn = - (iree_hal_executable_library_query_fn_t)executable->handle->GetSymbol( - IREE_HAL_EXECUTABLE_LIBRARY_EXPORT_NAME); - if (!query_fn) { - return iree_make_status( - IREE_STATUS_NOT_FOUND, - "executable metadata query function not found in library"); - } + iree_hal_executable_library_query_fn_t query_fn = NULL; + IREE_RETURN_IF_ERROR(iree_dynamic_library_lookup_symbol( + executable->handle, IREE_HAL_EXECUTABLE_LIBRARY_EXPORT_NAME, + (void**)&query_fn)); // Query for a compatible version of the library. executable->library.header = @@ -286,22 +226,7 @@ static void iree_hal_legacy_executable_destroy( iree_allocator_t host_allocator = executable->base.host_allocator; IREE_TRACE_ZONE_BEGIN(z0); -#if IREE_TRACING_FEATURES & IREE_TRACING_FEATURE_INSTRUMENTATION - // Leak the library when tracing, since the profiler may still be reading it. - // TODO(benvanik): move to an atexit handler instead, verify with ASAN/MSAN - // TODO(scotttodd): Make this compatible with testing: - // two test cases, one for each function in the same executable - // first test case passes, second fails to open the file (already open) -#else - delete executable->handle; -#endif // IREE_TRACING_FEATURES & IREE_TRACING_FEATURE_INSTRUMENTATION - - for (iree_host_size_t i = 0; i < executable->temp_file_count; ++i) { - iree_string_view_t file_path = executable->temp_files[i]; - iree::file_io::DeleteFile(std::string(file_path.data, file_path.size)) - .IgnoreError(); - iree_allocator_free(host_allocator, (void*)file_path.data); - } + iree_dynamic_library_release(executable->handle); iree_hal_local_executable_deinitialize( (iree_hal_local_executable_t*)base_executable); diff --git a/iree/hal/vulkan/BUILD b/iree/hal/vulkan/BUILD index 4c495c8140306..d729186af713a 100644 --- a/iree/hal/vulkan/BUILD +++ b/iree/hal/vulkan/BUILD @@ -121,19 +121,17 @@ cc_library( "vulkan_headers.h", ], hdrs = [ - "dynamic_symbol_tables.h", "dynamic_symbols.h", ], + textual_hdrs = [ + "dynamic_symbol_tables.h", + ], deps = [ "//iree/base:core_headers", - "//iree/base:dynamic_library", "//iree/base:status", "//iree/base:tracing", + "//iree/base/internal:dynamic_library", "//iree/hal/vulkan/util:ref_ptr", - "@com_google_absl//absl/base:core_headers", - "@com_google_absl//absl/memory", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/types:span", "@iree_vulkan_headers//:vulkan_headers", ], ) diff --git a/iree/hal/vulkan/CMakeLists.txt b/iree/hal/vulkan/CMakeLists.txt index 49783de848f42..ae1bd70d02324 100644 --- a/iree/hal/vulkan/CMakeLists.txt +++ b/iree/hal/vulkan/CMakeLists.txt @@ -99,19 +99,16 @@ iree_cc_library( NAME dynamic_symbols HDRS - "dynamic_symbol_tables.h" "dynamic_symbols.h" + TEXTUAL_HDRS + "dynamic_symbol_tables.h" SRCS "dynamic_symbols.cc" "vulkan_headers.h" DEPS Vulkan::Headers - absl::core_headers - absl::memory - absl::span - absl::strings iree::base::core_headers - iree::base::dynamic_library + iree::base::internal::dynamic_library iree::base::status iree::base::tracing iree::hal::vulkan::util::ref_ptr diff --git a/iree/hal/vulkan/dynamic_symbols.cc b/iree/hal/vulkan/dynamic_symbols.cc index 81067c03540ed..11c7be639e90f 100644 --- a/iree/hal/vulkan/dynamic_symbols.cc +++ b/iree/hal/vulkan/dynamic_symbols.cc @@ -16,9 +16,6 @@ #include -#include "absl/memory/memory.h" -#include "absl/strings/str_cat.h" -#include "absl/types/span.h" #include "iree/base/attributes.h" #include "iree/base/status.h" #include "iree/base/target_platform.h" @@ -164,16 +161,24 @@ StatusOr> DynamicSymbols::Create( StatusOr> DynamicSymbols::CreateFromSystemLoader() { IREE_TRACE_SCOPE0("DynamicSymbols::CreateFromSystemLoader"); - std::unique_ptr loader_library; - IREE_RETURN_IF_ERROR(DynamicLibrary::Load( - absl::MakeSpan(kVulkanLoaderSearchNames), &loader_library)); + iree_dynamic_library_t* loader_library = NULL; + IREE_RETURN_IF_ERROR(iree_dynamic_library_load_from_files( + IREE_ARRAYSIZE(kVulkanLoaderSearchNames), kVulkanLoaderSearchNames, + IREE_DYNAMIC_LIBRARY_FLAG_NONE, iree_allocator_system(), + &loader_library)); auto syms = make_ref(); - syms->loader_library_ = std::move(loader_library); - - auto* loader_library_ptr = syms->loader_library_.get(); - IREE_RETURN_IF_ERROR(ResolveFunctions( - syms.get(), [loader_library_ptr](const char* function_name) { - return loader_library_ptr->GetSymbol(function_name); + syms->loader_library_ = loader_library; + + IREE_RETURN_IF_ERROR( + ResolveFunctions(syms.get(), [loader_library](const char* function_name) { + PFN_vkVoidFunction fn = NULL; + iree_status_t status = iree_dynamic_library_lookup_symbol( + loader_library, function_name, (void**)&fn); + if (!iree_status_is_ok(status)) { + IREE_IGNORE_ERROR(status); + return (PFN_vkVoidFunction)NULL; + } + return fn; })); syms->FixupExtensionFunctions(); return syms; @@ -229,7 +234,11 @@ Status DynamicSymbols::LoadFromDevice(VkInstance instance, VkDevice device) { DynamicSymbols::DynamicSymbols() = default; -DynamicSymbols::~DynamicSymbols() = default; +DynamicSymbols::~DynamicSymbols() { + if (loader_library_) { + iree_dynamic_library_release(loader_library_); + } +} void DynamicSymbols::FixupExtensionFunctions() { this->vkGetSemaphoreCounterValue = this->vkGetSemaphoreCounterValue diff --git a/iree/hal/vulkan/dynamic_symbols.h b/iree/hal/vulkan/dynamic_symbols.h index 67cff28101f50..d80e3390cc132 100644 --- a/iree/hal/vulkan/dynamic_symbols.h +++ b/iree/hal/vulkan/dynamic_symbols.h @@ -23,7 +23,7 @@ #include #include -#include "iree/base/dynamic_library.h" +#include "iree/base/internal/dynamic_library.h" #include "iree/base/status.h" #include "iree/hal/vulkan/dynamic_symbol_tables.h" #include "iree/hal/vulkan/util/ref_ptr.h" @@ -124,7 +124,7 @@ struct DynamicSymbols : public RefObject { void FixupExtensionFunctions(); // Optional Vulkan Loader dynamic library. - std::unique_ptr loader_library_; + iree_dynamic_library_t* loader_library_ = nullptr; }; } // namespace vulkan diff --git a/iree/testing/vulkan/iree-run-module-vulkan-gui-main.cc b/iree/testing/vulkan/iree-run-module-vulkan-gui-main.cc index 0ea4501c28d59..29491ba8c1451 100644 --- a/iree/testing/vulkan/iree-run-module-vulkan-gui-main.cc +++ b/iree/testing/vulkan/iree-run-module-vulkan-gui-main.cc @@ -94,7 +94,8 @@ Status GetModuleContentsFromFlags(std::string* out_contents) { *out_contents = std::string{std::istreambuf_iterator(std::cin), std::istreambuf_iterator()}; } else { - IREE_RETURN_IF_ERROR(file_io::GetFileContents(module_file, out_contents)); + IREE_RETURN_IF_ERROR( + file_io::GetFileContents(module_file.c_str(), out_contents)); } return OkStatus(); } diff --git a/iree/tools/BUILD b/iree/tools/BUILD index 3931f8a512e74..ca160705c4b74 100644 --- a/iree/tools/BUILD +++ b/iree/tools/BUILD @@ -73,6 +73,7 @@ cc_binary( name = "iree-dump-module", srcs = ["iree-dump-module-main.cc"], deps = [ + "//iree/base:status", "//iree/base/internal:file_io", "//iree/schemas:bytecode_module_def_c_fbs", ], diff --git a/iree/tools/CMakeLists.txt b/iree/tools/CMakeLists.txt index 550059fd824bb..2c9c3303566a1 100644 --- a/iree/tools/CMakeLists.txt +++ b/iree/tools/CMakeLists.txt @@ -115,6 +115,7 @@ iree_cc_binary( DEPS flatcc::runtime iree::base::internal::file_io + iree::base::status iree::schemas::bytecode_module_def_c_fbs ) diff --git a/iree/tools/iree-benchmark-module-main.cc b/iree/tools/iree-benchmark-module-main.cc index 058dc2b1f6579..a225339e29554 100644 --- a/iree/tools/iree-benchmark-module-main.cc +++ b/iree/tools/iree-benchmark-module-main.cc @@ -121,7 +121,8 @@ Status GetModuleContentsFromFlags(std::string* out_contents) { *out_contents = std::string{std::istreambuf_iterator(std::cin), std::istreambuf_iterator()}; } else { - IREE_RETURN_IF_ERROR(file_io::GetFileContents(module_file, out_contents)); + IREE_RETURN_IF_ERROR( + file_io::GetFileContents(module_file.c_str(), out_contents)); } return OkStatus(); } diff --git a/iree/tools/iree-check-module-main.cc b/iree/tools/iree-check-module-main.cc index 928b066c311eb..1a310b6e6fd49 100644 --- a/iree/tools/iree-check-module-main.cc +++ b/iree/tools/iree-check-module-main.cc @@ -97,7 +97,7 @@ Status Run(std::string module_file_path, int* out_exit_code) { std::istreambuf_iterator()}; } else { IREE_RETURN_IF_ERROR( - file_io::GetFileContents(module_file_path, &module_data)); + file_io::GetFileContents(module_file_path.c_str(), &module_data)); } iree_vm_module_t* input_module = nullptr; diff --git a/iree/tools/iree-dump-module-main.cc b/iree/tools/iree-dump-module-main.cc index d83e429c44a6f..273246a6a9cde 100644 --- a/iree/tools/iree-dump-module-main.cc +++ b/iree/tools/iree-dump-module-main.cc @@ -17,6 +17,7 @@ #include #include "iree/base/internal/file_io.h" +#include "iree/base/status.h" #include "iree/schemas/bytecode_module_def_json_printer.h" // Today we just print to JSON. We could do something more useful (size @@ -31,11 +32,7 @@ extern "C" int main(int argc, char** argv) { return 1; } std::string module_contents; - auto status = iree::file_io::GetFileContents(argv[1], &module_contents); - if (!status.ok()) { - std::cerr << status; - return 1; - } + IREE_CHECK_OK(iree::file_io::GetFileContents(argv[1], &module_contents)); // Print direct to stdout. flatcc_json_printer_t printer; diff --git a/iree/tools/iree-run-module-main.cc b/iree/tools/iree-run-module-main.cc index f770b227bbc99..d4015a3904fe1 100644 --- a/iree/tools/iree-run-module-main.cc +++ b/iree/tools/iree-run-module-main.cc @@ -62,7 +62,8 @@ Status GetModuleContentsFromFlags(std::string* out_contents) { *out_contents = std::string{std::istreambuf_iterator(std::cin), std::istreambuf_iterator()}; } else { - IREE_RETURN_IF_ERROR(file_io::GetFileContents(module_file, out_contents)); + IREE_RETURN_IF_ERROR( + file_io::GetFileContents(module_file.c_str(), out_contents)); } return OkStatus(); } diff --git a/iree/tools/utils/vm_util.cc b/iree/tools/utils/vm_util.cc index fa502ad37233e..296d7fc9151df 100644 --- a/iree/tools/utils/vm_util.cc +++ b/iree/tools/utils/vm_util.cc @@ -182,7 +182,7 @@ Status ParseToVariantListFromFile( iree_hal_allocator_t* allocator, const std::string& filename, iree_vm_list_t** out_list) { std::string contents; - IREE_RETURN_IF_ERROR(file_io::GetFileContents(filename, &contents)); + IREE_RETURN_IF_ERROR(file_io::GetFileContents(filename.c_str(), &contents)); absl::InlinedVector input_views( absl::StrSplit(contents, '\n', absl::SkipEmpty())); return ParseToVariantList(descs, allocator, input_views, out_list);