From f359caea2f0ca750a42404ca8b116924ea74cb86 Mon Sep 17 00:00:00 2001 From: Peter LaFosse Date: Sun, 31 May 2026 19:57:03 -0400 Subject: [PATCH] Use native filesystem paths across API boundaries Carry paths through the C ABI as BNPath objects containing native platform code units instead of forcing UTF-8 or active-code-page strings. Update the C++, Python, and Rust APIs to preserve filesystem path types until explicit display or interchange boundaries. Centralize printable path formatting, add a C++ IsDatabase path wrapper, and reuse shared path conversion helpers. Add path round-trip coverage for Python FFI and Rust/C++ path-facing APIs, including Windows wide paths and POSIX byte paths. --- arch/x86/arch_x86.cpp | 14 +- binaryninjaapi.cpp | 124 ++++---- binaryninjaapi.h | 271 +++++++++--------- binaryninjacore.h | 257 +++++++++-------- binaryview.cpp | 72 ++--- collaboration.cpp | 30 +- examples/cmdline_disasm/src/disasm.cpp | 11 +- examples/triage/fileinfo.cpp | 6 +- filemetadata.cpp | 65 +++-- interaction.cpp | 115 +++++--- log.cpp | 6 +- pathformathelpers.h | 36 +++ pathhelpers.h | 123 ++++++++ platform.cpp | 39 ++- pluginmanager.cpp | 35 ++- plugins/bntl_utils/src/command.rs | 10 +- plugins/bntl_utils/src/command/diff.rs | 8 +- plugins/bntl_utils/src/process.rs | 6 +- plugins/dwarf/dwarf_export/src/lib.rs | 5 +- plugins/efi_resolver/include/Resolver.h | 8 +- plugins/efi_resolver/src/Resolver.cpp | 26 +- plugins/idb_import/src/commands.rs | 25 +- plugins/svd/src/lib.rs | 3 +- plugins/warp/api/python/_warpcore_template.py | 3 +- plugins/warp/api/python/generator.cpp | 84 +++++- plugins/warp/api/python/warp.py | 44 +-- plugins/warp/api/warp.cpp | 28 +- plugins/warp/api/warp.h | 16 +- plugins/warp/api/warpcore.h | 13 +- plugins/warp/src/plugin/ffi/container.rs | 24 +- plugins/warp/src/plugin/ffi/file.rs | 14 +- plugins/warp/src/plugin/ffi/processor.rs | 29 +- plugins/warp/src/plugin/load.rs | 3 +- plugins/warp/ui/plugin.cpp | 5 +- plugins/warp/ui/shared/processordialog.cpp | 7 +- plugins/warp/ui/shared/source.h | 6 +- project.cpp | 55 ++-- python/binaryview.py | 53 ++-- python/debuginfo.py | 6 +- python/filemetadata.py | 23 +- python/generator.cpp | 168 +++++++++-- python/interaction.py | 153 +++++----- python/log.py | 5 +- python/platform.py | 39 +-- python/pluginmanager.py | 25 +- python/project.py | 22 +- python/scriptingprovider.py | 17 +- python/settings.py | 2 +- python/typecontainer.py | 22 +- python/typeparser.py | 48 ++-- python/workflow.py | 8 +- rust/examples/create_type_library.rs | 3 +- rust/src/architecture.rs | 6 +- rust/src/binary_view.rs | 16 +- rust/src/collaboration/file.rs | 6 +- rust/src/collaboration/project.rs | 3 +- rust/src/collaboration/sync.rs | 12 +- rust/src/data_notification.rs | 6 +- rust/src/file_metadata.rs | 23 +- rust/src/interaction.rs | 27 +- rust/src/interaction/form.rs | 121 ++++++-- rust/src/interaction/handler.rs | 55 ++-- rust/src/lib.rs | 61 ++-- rust/src/platform.rs | 70 +++-- rust/src/project.rs | 17 +- rust/src/project/file.rs | 16 +- rust/src/project/folder.rs | 4 +- rust/src/repository.rs | 10 +- rust/src/repository/manager.rs | 6 +- rust/src/repository/plugin.rs | 6 +- rust/src/string.rs | 122 +++++++- rust/src/types/archive.rs | 13 +- rust/src/types/container.rs | 16 +- rust/src/types/library.rs | 8 +- rust/src/types/parser.rs | 52 ++-- rust/tests/interaction.rs | 14 +- rust/tests/type_parser.rs | 2 +- scriptingprovider.cpp | 21 +- settings.cpp | 32 ++- tempfile.cpp | 10 +- transformcontext.cpp | 6 + transformsession.cpp | 11 +- type.cpp | 14 +- typearchive.cpp | 28 +- typecontainer.cpp | 21 +- typelibrary.cpp | 16 +- typeparser.cpp | 95 +++--- ui/memorymap.h | 2 +- ui/notificationsdispatcher.h | 9 +- ui/typebrowser.h | 4 +- ui/util.h | 5 + view/kernelcache/api/python/generator.cpp | 2 +- view/kernelcache/core/KernelCache.cpp | 5 +- view/kernelcache/core/KernelCache.h | 7 +- .../core/KernelCacheController.cpp | 2 +- view/kernelcache/core/KernelCacheView.cpp | 2 +- view/kernelcache/core/KernelCacheView.h | 13 - view/kernelcache/core/MachO.cpp | 6 +- view/kernelcache/core/MachO.h | 4 +- view/kernelcache/core/Utility.cpp | 26 +- view/kernelcache/core/Utility.h | 9 +- view/kernelcache/core/ffi.cpp | 5 +- view/sharedcache/api/python/generator.cpp | 2 +- view/sharedcache/core/MachO.cpp | 6 +- view/sharedcache/core/MachO.h | 4 +- view/sharedcache/core/MappedFileRegion.cpp | 8 +- view/sharedcache/core/MappedFileRegion.h | 9 +- view/sharedcache/core/SharedCache.cpp | 21 +- view/sharedcache/core/SharedCache.h | 16 +- view/sharedcache/core/SharedCacheBuilder.cpp | 21 +- view/sharedcache/core/SharedCacheBuilder.h | 10 +- .../core/SharedCacheController.cpp | 5 +- view/sharedcache/core/SharedCacheView.cpp | 70 ++--- view/sharedcache/core/SharedCacheView.h | 18 +- view/sharedcache/core/Utility.cpp | 26 +- view/sharedcache/core/Utility.h | 13 +- view/sharedcache/core/ffi.cpp | 7 +- 117 files changed, 2132 insertions(+), 1371 deletions(-) create mode 100644 pathformathelpers.h create mode 100644 pathhelpers.h diff --git a/arch/x86/arch_x86.cpp b/arch/x86/arch_x86.cpp index bdd360bca6..9a404002bb 100644 --- a/arch/x86/arch_x86.cpp +++ b/arch/x86/arch_x86.cpp @@ -4,6 +4,7 @@ #include #include #include "binaryninjaapi.h" +#include "pathhelpers.h" #include "il.h" extern "C" { #include "xed-interface.h" @@ -2625,15 +2626,16 @@ bool X86CommonArchitecture::Assemble(const string& code, uint64_t addr, DataBuff } #ifdef WIN32 - string yasmPath = GetPathRelativeToBundledPluginDirectory("yasm.exe"); + auto yasmPath = GetPathRelativeToBundledPluginDirectory("yasm.exe"); #else - string yasmPath = GetPathRelativeToBundledPluginDirectory("yasm"); + auto yasmPath = GetPathRelativeToBundledPluginDirectory("yasm"); #endif - string inputPath = inputFile->GetPath(); - string outputPath = outputFile->GetPath(); + string yasmPathString = Path::PathToUtf8String(yasmPath); + string inputPath = Path::PathToUtf8String(inputFile->GetPath()); + string outputPath = Path::PathToUtf8String(outputFile->GetPath()); - vector args = vector { yasmPath, "-fbin", "-w", "-Worphan-labels", "-Werror", "-o", outputPath, inputPath }; + vector args = vector { yasmPathString, "-fbin", "-w", "-Worphan-labels", "-Werror", "-o", outputPath, inputPath }; string output; bool ok = ExecuteWorkerProcess(yasmPath, args, DataBuffer(), @@ -2649,7 +2651,7 @@ bool X86CommonArchitecture::Assemble(const string& code, uint64_t addr, DataBuff OR the yasm process return code was nonzero */ if(errors.size() == 0) { - errors = yasmPath + " returned nonzero\n"; + errors = yasmPathString + " returned nonzero\n"; } } else diff --git a/binaryninjaapi.cpp b/binaryninjaapi.cpp index 34a86d413e..44a5dd2634 100644 --- a/binaryninjaapi.cpp +++ b/binaryninjaapi.cpp @@ -19,6 +19,7 @@ // IN THE SOFTWARE. #include "binaryninjaapi.h" +#include "pathhelpers.h" #include using namespace BinaryNinja; @@ -49,134 +50,106 @@ bool BinaryNinja::InitPlugins(bool allowUserPlugins) } -string BinaryNinja::GetBundledPluginDirectory() +filesystem::path BinaryNinja::GetBundledPluginDirectory() { - char* path = BNGetBundledPluginDirectory(); - if (!path) - return string(); - string result = path; - BNFreeString(path); - return result; + return Path::PathFromCore(BNGetBundledPluginDirectory()); } -std::string BinaryNinja::GetBundledScriptPluginDirectory() +filesystem::path BinaryNinja::GetBundledScriptPluginDirectory() { - char* path = BNGetBundledScriptPluginDirectory(); - if (!path) - return string(); - std::string result = path; - BNFreeString(path); - return result; + return Path::PathFromCore(BNGetBundledScriptPluginDirectory()); } -void BinaryNinja::SetBundledPluginDirectory(const string& path) +void BinaryNinja::SetBundledPluginDirectory(const filesystem::path& path) { - BNSetBundledPluginDirectory(path.c_str()); + Path::APIObject corePath(path); + BNSetBundledPluginDirectory(corePath); } -string BinaryNinja::GetUserDirectory(void) +void BinaryNinja::SetBundledScriptPluginDirectory(const filesystem::path& path) { - char* dir = BNGetUserDirectory(); - if (!dir) - return string(); - string result(dir); - BNFreeString(dir); - return result; + Path::APIObject corePath(path); + BNSetBundledScriptPluginDirectory(corePath); } -string BinaryNinja::GetSystemCacheDirectory() +filesystem::path BinaryNinja::GetUserDirectory(void) { - char* dir = BNGetSystemCacheDirectory(); - if (!dir) - return string(); - std::string result(dir); - BNFreeString(dir); - return result; + return Path::PathFromCore(BNGetUserDirectory()); } -string BinaryNinja::GetSettingsFileName() +filesystem::path BinaryNinja::GetSystemCacheDirectory() { - char* dir = BNGetSettingsFileName(); - if (!dir) - return string(); - string result(dir); - BNFreeString(dir); - return result; + return Path::PathFromCore(BNGetSystemCacheDirectory()); } -string BinaryNinja::GetRepositoriesDirectory() +filesystem::path BinaryNinja::GetSettingsFileName() { - char* dir = BNGetRepositoriesDirectory(); - if (!dir) - return string(); - string result(dir); - BNFreeString(dir); - return result; + return Path::PathFromCore(BNGetSettingsFileName()); } -string BinaryNinja::GetInstallDirectory() +filesystem::path BinaryNinja::GetRepositoriesDirectory() { - char* path = BNGetInstallDirectory(); - if (!path) - return string(); - string result = path; - BNFreeString(path); - return result; + return Path::PathFromCore(BNGetRepositoriesDirectory()); } -string BinaryNinja::GetUserPluginDirectory() +filesystem::path BinaryNinja::GetInstallDirectory() { - char* path = BNGetUserPluginDirectory(); - if (!path) - return string(); - string result = path; - BNFreeString(path); - return result; + return Path::PathFromCore(BNGetInstallDirectory()); } -string BinaryNinja::GetPathRelativeToBundledPluginDirectory(const string& rel) +filesystem::path BinaryNinja::GetUserPluginDirectory() { - char* path = BNGetPathRelativeToBundledPluginDirectory(rel.c_str()); + return Path::PathFromCore(BNGetUserPluginDirectory()); +} + + +filesystem::path BinaryNinja::GetPathRelativeToBundledPluginDirectory(const filesystem::path& rel) +{ + Path::APIObject coreRel(rel); + BNPath* path = BNGetPathRelativeToBundledPluginDirectory(coreRel); if (!path) return rel; - string result = path; - BNFreeString(path); - return result; + return Path::PathFromCore(path); } -string BinaryNinja::GetPathRelativeToUserPluginDirectory(const string& rel) +filesystem::path BinaryNinja::GetPathRelativeToUserPluginDirectory(const filesystem::path& rel) { - char* path = BNGetPathRelativeToUserPluginDirectory(rel.c_str()); + Path::APIObject coreRel(rel); + BNPath* path = BNGetPathRelativeToUserPluginDirectory(coreRel); if (!path) return rel; - string result = path; - BNFreeString(path); - return result; + return Path::PathFromCore(path); } -string BinaryNinja::GetPathRelativeToUserDirectory(const string& rel) +filesystem::path BinaryNinja::GetPathRelativeToUserDirectory(const filesystem::path& rel) { - char* path = BNGetPathRelativeToUserDirectory(rel.c_str()); + Path::APIObject coreRel(rel); + BNPath* path = BNGetPathRelativeToUserDirectory(coreRel); if (!path) return rel; - string result = path; - BNFreeString(path); - return result; + return Path::PathFromCore(path); +} + + +bool BinaryNinja::IsDatabase(const filesystem::path& path) +{ + Path::APIObject corePath(path); + return BNIsDatabase(corePath); } -bool BinaryNinja::ExecuteWorkerProcess(const string& path, const vector& args, const DataBuffer& input, +bool BinaryNinja::ExecuteWorkerProcess(const filesystem::path& path, const vector& args, const DataBuffer& input, string& output, string& errors, bool stdoutIsText, bool stderrIsText) { const char** argArray = new const char*[args.size() + 1]; @@ -186,8 +159,9 @@ bool BinaryNinja::ExecuteWorkerProcess(const string& path, const vector& char* outputStr; char* errorStr; + Path::APIObject corePath(path); bool result = BNExecuteWorkerProcess( - path.c_str(), argArray, input.GetBufferObject(), &outputStr, &errorStr, stdoutIsText, stderrIsText); + corePath, argArray, input.GetBufferObject(), &outputStr, &errorStr, stdoutIsText, stderrIsText); output = outputStr; errors = errorStr; diff --git a/binaryninjaapi.h b/binaryninjaapi.h index d81ddb1169..3f6f499772 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -30,12 +30,14 @@ #include "base/compiler.h" #include "binaryninjacore.h" #include "exceptions.h" +#include "pathhelpers.h" #include "json/json.h" #include "rapidjsonwrapper.h" #include "vendor/nlohmann/json.hpp" #include +#include #include #include #include @@ -53,6 +55,8 @@ #include #include #include +#include +#include #include #include #include @@ -1163,7 +1167,7 @@ namespace BinaryNinja { template void LogWithStackTraceF(BNLogLevel level, fmt::format_string format, T&&... args) { - LogWithWithStackTraceFV(level, format, fmt::make_format_args(args...)); + LogWithStackTraceFV(level, format, fmt::make_format_args(args...)); } /*! LogTraceWithStackTraceF only writes text to the error console if the console is set to log level: DebugLog @@ -1294,7 +1298,7 @@ namespace BinaryNinja { \param path Path to log to \param append Optional flag for specifying appending. True = append, False = overwrite. */ - bool LogToFile(BNLogLevel minimumLevel, const std::string& path, bool append = false); + bool LogToFile(BNLogLevel minimumLevel, const std::filesystem::path& path, bool append = false); /*! Close all log files @@ -1982,42 +1986,43 @@ namespace BinaryNinja { std::string EscapeString(const std::string& s); std::string UnescapeString(const std::string& s); - bool PreprocessSource(const std::string& source, const std::string& fileName, std::string& output, - std::string& errors, const std::vector& includeDirs = std::vector()); + bool PreprocessSource(const std::string& source, const std::filesystem::path& fileName, std::string& output, + std::string& errors, const std::vector& includeDirs = std::vector()); void DisablePlugins(); bool IsPluginsEnabled(); bool InitPlugins(bool allowUserPlugins = true); - std::string GetBundledPluginDirectory(); + std::filesystem::path GetBundledPluginDirectory(); /*! Get the directory that script plugins bundled with BinaryNinja are located. * * On non-Apple platforms by default this will be identical to the core plugin directory. * - * @return std::string - Absolute path directory that script plugins bundled with BinaryNinja are located in. + * @return std::filesystem::path - Absolute path directory that script plugins bundled with BinaryNinja are located in. */ - std::string GetBundledScriptPluginDirectory(); - void SetBundledPluginDirectory(const std::string& path); - void SetBundledScriptPluginDirectory(const std::string& path); - std::string GetUserDirectory(); + std::filesystem::path GetBundledScriptPluginDirectory(); + void SetBundledPluginDirectory(const std::filesystem::path& path); + void SetBundledScriptPluginDirectory(const std::filesystem::path& path); + std::filesystem::path GetUserDirectory(); /*! Get the Binary Ninja system cache directory * - * @return std::string - Binary Ninja's cache directory on a given system + * @return std::filesystem::path - Binary Ninja's cache directory on a given system */ - std::string GetSystemCacheDirectory(); + std::filesystem::path GetSystemCacheDirectory(); - std::string GetSettingsFileName(); - std::string GetRepositoriesDirectory(); - std::string GetInstallDirectory(); - std::string GetUserPluginDirectory(); + std::filesystem::path GetSettingsFileName(); + std::filesystem::path GetRepositoriesDirectory(); + std::filesystem::path GetInstallDirectory(); + std::filesystem::path GetUserPluginDirectory(); - std::string GetPathRelativeToBundledPluginDirectory(const std::string& path); - std::string GetPathRelativeToUserPluginDirectory(const std::string& path); - std::string GetPathRelativeToUserDirectory(const std::string& path); + std::filesystem::path GetPathRelativeToBundledPluginDirectory(const std::filesystem::path& path); + std::filesystem::path GetPathRelativeToUserPluginDirectory(const std::filesystem::path& path); + std::filesystem::path GetPathRelativeToUserDirectory(const std::filesystem::path& path); + bool IsDatabase(const std::filesystem::path& path); - bool ExecuteWorkerProcess(const std::string& path, const std::vector& args, const DataBuffer& input, + bool ExecuteWorkerProcess(const std::filesystem::path& path, const std::vector& args, const DataBuffer& input, std::string& output, std::string& errors, bool stdoutIsText = false, bool stderrIsText = true); std::string GetVersionString(); @@ -2363,7 +2368,7 @@ namespace BinaryNinja { being loaded. If the function returns false, it will cancel Load. \return Constructed view, or a nullptr Ref */ - Ref Load(const std::string& filename, bool updateAnalysis = true, const std::string& options = "{}", ProgressFunction progress = {}); + Ref Load(const std::filesystem::path& filename, bool updateAnalysis = true, const std::string& options = "{}", ProgressFunction progress = {}); /*! Open a BinaryView from a raw data buffer, initializing data views and loading settings. @threadmainonly @@ -2416,7 +2421,7 @@ namespace BinaryNinja { */ Ref Load(Ref rawData, bool updateAnalysis = true, const std::string& options = "{}", ProgressFunction progress = {}); - Ref ParseTextFormat(const std::string& filename); + Ref ParseTextFormat(const std::filesystem::path& filename); /*! Deprecated. Use non-metadata version. @@ -2715,7 +2720,7 @@ namespace BinaryNinja { \ingroup interaction - \param[out] result Reference to the string the result will be copied to + \param[out] result Reference to the path the result will be copied to \param[in] prompt Prompt for the input \param[in] title Title for the input popup when used in UI \return Whether a line was successfully received @@ -2782,12 +2787,12 @@ namespace BinaryNinja { \ingroup interaction - \param[out] result Reference to the string the result will be copied to + \param[out] result Reference to the path the result will be copied to \param[in] prompt Prompt for the dialog \param[in] ext Optional, file extension \return Whether a filename was successfully received */ - bool GetOpenFileNameInput(std::string& result, const std::string& prompt, const std::string& ext = ""); + bool GetOpenFileNameInput(std::filesystem::path& result, const std::string& prompt, const std::string& ext = ""); /*! Prompts the user for a file name to save as, optionally providing a file extension and defaultName @@ -2795,26 +2800,27 @@ namespace BinaryNinja { \ingroup interaction - \param[out] result Reference to the string the result will be copied to + \param[out] result Reference to the path the result will be copied to \param[in] prompt Prompt for the dialog \param[in] ext Optional, file extension \param[in] defaultName Optional, default filename \return Whether a filename was successfully received */ - bool GetSaveFileNameInput(std::string& result, const std::string& prompt, const std::string& ext = "", - const std::string& defaultName = ""); + bool GetSaveFileNameInput(std::filesystem::path& result, const std::string& prompt, const std::string& ext = "", + const std::filesystem::path& defaultName = {}); /*! Prompts the user for a directory name to save as, optionally providing a default_name @threadsafe \ingroup interaction - \param[out] result Reference to the string the result will be copied to + \param[out] result Reference to the path the result will be copied to \param[in] prompt Prompt for the dialog \param[in] defaultName Optional, default directory name \return Whether a directory was successfully received */ - bool GetDirectoryNameInput(std::string& result, const std::string& prompt, const std::string& defaultName = ""); + bool GetDirectoryNameInput(std::filesystem::path& result, const std::string& prompt, + const std::filesystem::path& defaultName = {}); /*! Prompts the user for a checkbox input \ingroup interaction @@ -3154,7 +3160,7 @@ namespace BinaryNinja { /*! Path to the TemporaryFile on the filesystem. */ - std::string GetPath() const; + std::filesystem::path GetPath() const; /*! DataBuffer with contents of the file. */ @@ -3630,7 +3636,7 @@ namespace BinaryNinja { bool SetDescription(const std::string& description); Ref GetParent() const; bool SetParent(Ref parent); - bool Export(const std::string& destination, const ProgressFunction& progressCallback = {}) const; + bool Export(const std::filesystem::path& destination, const ProgressFunction& progressCallback = {}) const; std::vector> GetFiles() const; }; @@ -3644,7 +3650,7 @@ namespace BinaryNinja { ProjectFile(BNProjectFile* file); Ref GetProject() const; - std::string GetPathOnDisk() const; + std::filesystem::path GetPathOnDisk() const; std::string GetPathInProject() const; bool ExistsOnDisk() const; std::string GetName() const; @@ -3654,7 +3660,7 @@ namespace BinaryNinja { std::string GetId() const; Ref GetFolder() const; bool SetFolder(Ref folder); - bool Export(const std::string& destination) const; + bool Export(const std::filesystem::path& destination) const; int64_t GetCreationTimestamp() const; bool AddDependency(Ref file); bool RemoveDependency(Ref file); @@ -3676,15 +3682,15 @@ namespace BinaryNinja { public: Project(BNProject* project); - static Ref CreateProject(const std::string& path, const std::string& name); - static Ref OpenProject(const std::string& path); + static Ref CreateProject(const std::filesystem::path& path, const std::string& name); + static Ref OpenProject(const std::filesystem::path& path); static std::vector> GetOpenProjects(); bool Open(); bool Close(); std::string GetId() const; bool IsOpen() const; - std::string GetPath() const; + std::filesystem::path GetPath() const; std::string GetFilePathInProject(const Ref& file) const; std::string GetName() const; bool SetName(const std::string& name); @@ -3695,7 +3701,7 @@ namespace BinaryNinja { bool StoreMetadata(const std::string& key, Ref value); bool RemoveMetadata(const std::string& key); - Ref CreateFolderFromPath(const std::string& path, Ref parent, const std::string& description, + Ref CreateFolderFromPath(const std::filesystem::path& path, Ref parent, const std::string& description, const ProgressFunction& progressCallback = {}); Ref CreateFolder(Ref parent, const std::string& name, const std::string& description); Ref CreateFolderUnsafe(Ref parent, const std::string& name, const std::string& description, const std::string& id); @@ -3704,13 +3710,13 @@ namespace BinaryNinja { bool PushFolder(Ref folder); bool DeleteFolder(Ref folder, const ProgressFunction& progressCallback = {}); - Ref CreateFileFromPath(const std::string& path, Ref folder, const std::string& name, const std::string& description, const ProgressFunction& progressCallback = {}); - Ref CreateFileFromPathUnsafe(const std::string& path, Ref folder, const std::string& name, const std::string& description, const std::string& id, int64_t creationTimestamp, const ProgressFunction& progressCallback = {}); + Ref CreateFileFromPath(const std::filesystem::path& path, Ref folder, const std::string& name, const std::string& description, const ProgressFunction& progressCallback = {}); + Ref CreateFileFromPathUnsafe(const std::filesystem::path& path, Ref folder, const std::string& name, const std::string& description, const std::string& id, int64_t creationTimestamp, const ProgressFunction& progressCallback = {}); Ref CreateFile_(const std::vector& contents, Ref folder, const std::string& name, const std::string& description, const ProgressFunction& progressCallback = {}); Ref CreateFileUnsafe(const std::vector& contents, Ref folder, const std::string& name, const std::string& description, const std::string& id, int64_t creationTimestamp, const ProgressFunction& progressCallback = {}); std::vector> GetFiles() const; Ref GetFileById(const std::string& id) const; - Ref GetFileByPathOnDisk(const std::string& path) const; + Ref GetFileByPathOnDisk(const std::filesystem::path& path) const; std::vector> GetFilesByPathInProject(const std::string& path) const; std::vector> GetFilesInFolder(Ref folder) const; @@ -3776,7 +3782,7 @@ namespace BinaryNinja { { public: FileMetadata(); - FileMetadata(const std::string& filename); + FileMetadata(const std::filesystem::path& filename); FileMetadata(Ref projectFile); FileMetadata(BNFileMetadata* file); @@ -3790,24 +3796,24 @@ namespace BinaryNinja { \return The original name of the binary opened if a bndb, otherwise returns the current filename */ - std::string GetOriginalFilename() const; + std::filesystem::path GetOriginalFilename() const; /*! If the filename is not open in a BNDB, sets the filename for the current file. \param name New name */ - void SetOriginalFilename(const std::string& name); + void SetOriginalFilename(const std::filesystem::path& name); /*! \return The name of the open bndb or binary filename */ - std::string GetFilename() const; + std::filesystem::path GetFilename() const; /*! Set the filename for the current BNDB or binary. \param name Set the filename for the current BNDB or binary. */ - void SetFilename(const std::string& name); + void SetFilename(const std::filesystem::path& name); /*! Get the transform-chain identity for this file in the current session There are three meaningful states: @@ -3839,11 +3845,7 @@ namespace BinaryNinja { \return Whether this FileMetadata represents a derived container entry. */ - bool IsContainerEntry() const - { - std::string virtualPath = GetVirtualPath(); - return !virtualPath.empty() && GetFilename() != virtualPath; - } + bool IsContainerEntry() const; /*! A leaf-shaped human-readable name for UI presentation. Never contains a directory path. Resolution order: @@ -3904,7 +3906,7 @@ namespace BinaryNinja { \param settings Special save options \return Whether the save was successful */ - bool CreateDatabase(const std::string& name, BinaryView* data, Ref settings); + bool CreateDatabase(const std::filesystem::path& name, BinaryView* data, Ref settings); /*! Writes the current database (.bndb) out to the specified file. @@ -3914,7 +3916,7 @@ namespace BinaryNinja { \param settings Special save options \return Whether the save was successful */ - bool CreateDatabase(const std::string& name, BinaryView* data, + bool CreateDatabase(const std::filesystem::path& name, BinaryView* data, const ProgressFunction& progressCallback, Ref settings); /*! Open an existing database from a given path @@ -3922,7 +3924,7 @@ namespace BinaryNinja { \param path Path to the existing database \return The resulting BinaryView, if the load was successful */ - Ref OpenExistingDatabase(const std::string& path); + Ref OpenExistingDatabase(const std::filesystem::path& path); /*! Open an existing database from a given path with a progress callback @@ -3931,8 +3933,8 @@ namespace BinaryNinja { \return The resulting BinaryView, if the load was successful */ Ref OpenExistingDatabase( - const std::string& path, const ProgressFunction& progressCallback); - Ref OpenDatabaseForConfiguration(const std::string& path); + const std::filesystem::path& path, const ProgressFunction& progressCallback); + Ref OpenDatabaseForConfiguration(const std::filesystem::path& path); /*! Save the current database to the already created file. @@ -4177,8 +4179,8 @@ namespace BinaryNinja { static void ExternalLocationUpdatedCallback(void* ctxt, BNBinaryView* data, BNExternalLocation* location); static void ExternalLocationRemovedCallback(void* ctxt, BNBinaryView* data, BNExternalLocation* location); - static void TypeArchiveAttachedCallback(void* ctxt, BNBinaryView* data, const char* id, const char* path); - static void TypeArchiveDetachedCallback(void* ctxt, BNBinaryView* data, const char* id, const char* path); + static void TypeArchiveAttachedCallback(void* ctxt, BNBinaryView* data, const char* id, BNPath* path); + static void TypeArchiveDetachedCallback(void* ctxt, BNBinaryView* data, const char* id, BNPath* path); static void TypeArchiveConnectedCallback(void* ctxt, BNBinaryView* data, BNTypeArchive* archive); static void TypeArchiveDisconnectedCallback(void* ctxt, BNBinaryView* data, BNTypeArchive* archive); @@ -4612,7 +4614,7 @@ namespace BinaryNinja { \param id Id of the attached archive \param path Path on disk of the attached archive */ - virtual void OnTypeArchiveAttached(BinaryView* data, const std::string& id, const std::string& path) + virtual void OnTypeArchiveAttached(BinaryView* data, const std::string& id, const std::filesystem::path& path) { (void)data; (void)id; @@ -4625,7 +4627,7 @@ namespace BinaryNinja { \param id Id of the attached archive \param path Path on disk of the attached archive */ - virtual void OnTypeArchiveDetached(BinaryView* data, const std::string& id, const std::string& path) + virtual void OnTypeArchiveDetached(BinaryView* data, const std::string& id, const std::filesystem::path& path) { (void)data; (void)id; @@ -5884,7 +5886,7 @@ namespace BinaryNinja { \param settings Special save options \return Whether the save was successful */ - bool CreateDatabase(const std::string& path, Ref settings = new SaveSettings()); + bool CreateDatabase(const std::filesystem::path& path, Ref settings = new SaveSettings()); /*! Writes the current database (.bndb) out to the specified file. @@ -5893,7 +5895,7 @@ namespace BinaryNinja { \param settings Special save options \return Whether the save was successful */ - bool CreateDatabase(const std::string& path, + bool CreateDatabase(const std::filesystem::path& path, const ProgressFunction& progressCallback, Ref settings = new SaveSettings()); bool SaveAutoSnapshot(Ref settings = new SaveSettings()); @@ -6225,7 +6227,7 @@ namespace BinaryNinja { \param path destination path and filename of the file to be written \return Whether the save was successful */ - bool Save(const std::string& path); + bool Save(const std::filesystem::path& path); /*! Performs "finalization" on segments added after initial Finalization (performed after an Init() has completed). @@ -7443,7 +7445,7 @@ namespace BinaryNinja { \param[in] importDependencies If Type Library / Type Archive types should be imported during parsing \return Whether parsing was successful */ - bool ParseTypesFromSource(const std::string& text, const std::vector& options, const std::vector& includeDirs, TypeParserResult& result, + bool ParseTypesFromSource(const std::string& text, const std::vector& options, const std::vector& includeDirs, TypeParserResult& result, std::string& errors, const std::set& typesAllowRedefinition = {}, bool importDependencies = true); /*! Type Container for all types (user and auto) in the BinaryView. Any auto types @@ -7623,7 +7625,7 @@ namespace BinaryNinja { \param id Expected id of archive \param path Path to archive */ - Ref AttachTypeArchive(const std::string& id, const std::string& path); + Ref AttachTypeArchive(const std::string& id, const std::filesystem::path& path); /*! Detach from a type archive, breaking all associations to types with the archive \param id Id of archive to detach @@ -7639,13 +7641,13 @@ namespace BinaryNinja { \return All attached archive (id, path) pairs */ - std::unordered_map GetTypeArchives() const; + std::unordered_map GetTypeArchives() const; /*! Look up the path for an attached (but not necessarily connected) type archive by its id \param id Id of archive \return Archive path, if it is attached. Otherwise nullopt. */ - std::optional GetTypeArchivePath(const std::string& id) const; + std::optional GetTypeArchivePath(const std::string& id) const; /*! Get a list of all available type names in all connected archives, and their archive/type id pair \return All type names in a map @@ -8612,7 +8614,7 @@ namespace BinaryNinja { BinaryData(FileMetadata* file); BinaryData(FileMetadata* file, const DataBuffer& data); BinaryData(FileMetadata* file, const void* data, size_t len); - BinaryData(FileMetadata* file, const std::string& path); + BinaryData(FileMetadata* file, const std::filesystem::path& path); BinaryData(FileMetadata* file, FileAccessor* accessor); /*! @@ -8622,7 +8624,7 @@ namespace BinaryNinja { \param path Path to file to open \return Reference to binary data if successful, nullptr reference otherwise */ - static Ref CreateFromFilename(FileMetadata* file, const std::string& path); + static Ref CreateFromFilename(FileMetadata* file, const std::filesystem::path& path); /*! Open a raw file from a given path. @@ -9496,6 +9498,7 @@ namespace BinaryNinja { Ref GetInput() const; std::string GetFileName() const; + std::filesystem::path GetFilePath() const; std::vector GetAvailableTransforms() const; std::string GetTransformName() const; void SetTransformName(const std::string& transformName); @@ -9529,8 +9532,8 @@ namespace BinaryNinja { class TransformSession : public CoreRefCountObject { public: - TransformSession(const std::string& filename, const std::string& options = "{}"); - TransformSession(const std::string& filename, BNTransformSessionMode mode, const std::string& options = "{}"); + TransformSession(const std::filesystem::path& filename, const std::string& options = "{}"); + TransformSession(const std::filesystem::path& filename, BNTransformSessionMode mode, const std::string& options = "{}"); TransformSession(Ref initialView, const std::string& options = "{}"); TransformSession(Ref initialView, BNTransformSessionMode mode, const std::string& options = "{}"); TransformSession(Ref context, BNTransformSessionMode mode, const std::string& options = "{}"); @@ -18498,8 +18501,8 @@ namespace BinaryNinja { { protected: Platform(Architecture* arch, const std::string& name); - Platform(Architecture* arch, const std::string& name, const std::string& typeFile, - const std::vector& includeDirs = std::vector()); + Platform(Architecture* arch, const std::string& name, const std::filesystem::path& typeFile, + const std::vector& includeDirs = std::vector()); static void InitCallback(void *ctxt, BNPlatform*); static void InitViewCallback(void* ctxt, BNBinaryView* view); @@ -18783,10 +18786,10 @@ namespace BinaryNinja { \param autoTypeSource optional source of types if used for automatically generated types \return true on success, false otherwise */ - bool ParseTypesFromSource(const std::string& source, const std::string& fileName, + bool ParseTypesFromSource(const std::string& source, const std::filesystem::path& fileName, std::map>& types, std::map>& variables, std::map>& functions, std::string& errors, - const std::vector& includeDirs = std::vector(), + const std::vector& includeDirs = std::vector(), const std::string& autoTypeSource = ""); /*! Parses the source string and any needed headers searching for them in @@ -18804,9 +18807,9 @@ namespace BinaryNinja { \return true on success, false otherwise \return */ - bool ParseTypesFromSourceFile(const std::string& fileName, std::map>& types, + bool ParseTypesFromSourceFile(const std::filesystem::path& fileName, std::map>& types, std::map>& variables, std::map>& functions, - std::string& errors, const std::vector& includeDirs = std::vector(), + std::string& errors, const std::vector& includeDirs = std::vector(), const std::string& autoTypeSource = ""); }; @@ -18838,17 +18841,17 @@ namespace BinaryNinja { static bool GetOptionTextCallback(void* ctxt, BNTypeParserOption option, const char* value, char** result); static bool PreprocessSourceCallback(void* ctxt, - const char* source, const char* fileName, BNPlatform* platform, + const char* source, BNPath* fileName, BNPlatform* platform, BNTypeContainer* existingTypes, const char* const* options, size_t optionCount, - const char* const* includeDirs, size_t includeDirCount, + BNPath** includeDirs, size_t includeDirCount, char** output, BNTypeParserError** errors, size_t* errorCount ); static bool ParseTypesFromSourceCallback(void* ctxt, - const char* source, const char* fileName, BNPlatform* platform, + const char* source, BNPath* fileName, BNPlatform* platform, BNTypeContainer* existingTypes, const char* const* options, size_t optionCount, - const char* const* includeDirs, size_t includeDirCount, + BNPath** includeDirs, size_t includeDirCount, const char* autoTypeSource, BNTypeParserResult* result, BNTypeParserError** errors, size_t* errorCount ); @@ -18911,11 +18914,11 @@ namespace BinaryNinja { */ virtual bool PreprocessSource( const std::string& source, - const std::string& fileName, + const std::filesystem::path& fileName, Ref platform, std::optional existingTypes, const std::vector& options, - const std::vector& includeDirs, + const std::vector& includeDirs, std::string& output, std::vector& errors ) = 0; @@ -18935,11 +18938,11 @@ namespace BinaryNinja { */ virtual bool ParseTypesFromSource( const std::string& source, - const std::string& fileName, + const std::filesystem::path& fileName, Ref platform, std::optional existingTypes, const std::vector& options, - const std::vector& includeDirs, + const std::vector& includeDirs, const std::string& autoTypeSource, TypeParserResult& result, std::vector& errors @@ -18958,11 +18961,11 @@ namespace BinaryNinja { \return True if parsing was successful */ bool ParseTypesFromSourceFile( - const std::string& fileName, + const std::filesystem::path& fileName, Ref platform, std::optional existingTypes, const std::vector& options, - const std::vector& includeDirs, + const std::vector& includeDirs, const std::string& autoTypeSource, TypeParserResult& result, std::vector& errors @@ -18999,22 +19002,22 @@ namespace BinaryNinja { virtual bool PreprocessSource( const std::string& source, - const std::string& fileName, + const std::filesystem::path& fileName, Ref platform, std::optional existingTypes, const std::vector& options, - const std::vector& includeDirs, + const std::vector& includeDirs, std::string& output, std::vector& errors ) override; virtual bool ParseTypesFromSource( const std::string& source, - const std::string& fileName, + const std::filesystem::path& fileName, Ref platform, std::optional existingTypes, const std::vector& options, - const std::vector& includeDirs, + const std::vector& includeDirs, const std::string& autoTypeSource, TypeParserResult& result, std::vector& errors @@ -19514,7 +19517,7 @@ namespace BinaryNinja { static void DestroyInstanceCallback(void* ctxt); static BNScriptingProviderExecuteResult ExecuteScriptInputCallback(void* ctxt, const char* input); - static BNScriptingProviderExecuteResult ExecuteScriptFromFilenameCallback(void *ctxt, const char* filename); + static BNScriptingProviderExecuteResult ExecuteScriptFromFilenameCallback(void *ctxt, BNPath* filename); static void CancelScriptInputCallback(void* ctxt); static void ReleaseBinaryViewCallback(void* ctxt, BNBinaryView* view); static void SetCurrentBinaryViewCallback(void* ctxt, BNBinaryView* view); @@ -19531,7 +19534,7 @@ namespace BinaryNinja { public: virtual BNScriptingProviderExecuteResult ExecuteScriptInput(const std::string& input) = 0; - virtual BNScriptingProviderExecuteResult ExecuteScriptInputFromFilename(const std::string& filename) = 0; + virtual BNScriptingProviderExecuteResult ExecuteScriptInputFromFilename(const std::filesystem::path& filename) = 0; virtual void CancelScriptInput(); virtual void ReleaseBinaryView(BinaryView* view); virtual void SetCurrentBinaryView(BinaryView* view); @@ -19567,7 +19570,7 @@ namespace BinaryNinja { virtual ~CoreScriptingInstance() {}; virtual BNScriptingProviderExecuteResult ExecuteScriptInput(const std::string& input) override; - virtual BNScriptingProviderExecuteResult ExecuteScriptInputFromFilename(const std::string& filename) override; + virtual BNScriptingProviderExecuteResult ExecuteScriptInputFromFilename(const std::filesystem::path& filename) override; virtual void CancelScriptInput() override; virtual void ReleaseBinaryView(BinaryView* view) override; virtual void SetCurrentBinaryView(BinaryView* view) override; @@ -19594,12 +19597,12 @@ namespace BinaryNinja { ScriptingProvider(BNScriptingProvider* provider); static BNScriptingInstance* CreateInstanceCallback(void* ctxt); - static bool LoadModuleCallback(void* ctxt, const char* repository, const char* module, bool force); + static bool LoadModuleCallback(void* ctxt, BNPath* repository, BNPath* module, bool force); static bool InstallModulesCallback(void* ctxt, const char* modules); public: virtual Ref CreateNewInstance() = 0; - virtual bool LoadModule(const std::string& repository, const std::string& module, bool force) = 0; + virtual bool LoadModule(const std::filesystem::path& repository, const std::filesystem::path& module, bool force) = 0; virtual bool InstallModules(const std::string& modules) = 0; std::string GetName(); @@ -19616,7 +19619,7 @@ namespace BinaryNinja { public: CoreScriptingProvider(BNScriptingProvider* provider); virtual Ref CreateNewInstance() override; - virtual bool LoadModule(const std::string& repository, const std::string& module, bool force) override; + virtual bool LoadModule(const std::filesystem::path& repository, const std::filesystem::path& module, bool force) override; virtual bool InstallModules(const std::string& modules) override; }; @@ -19688,15 +19691,17 @@ namespace BinaryNinja { uint64_t currentAddress; // For AddressFormField std::vector choices; // For ChoiceFormField std::string ext; // For OpenFileNameFormField, SaveFileNameFormField - std::string defaultName; // For SaveFileNameFormField + std::filesystem::path defaultName; // For SaveFileNameFormField, DirectoryNameFormField int64_t intResult; uint64_t addressResult; std::string stringResult; + std::filesystem::path pathResult; // For OpenFileNameFormField, SaveFileNameFormField, DirectoryNameFormField size_t indexResult; bool hasDefault; int64_t intDefault; uint64_t addressDefault; std::string stringDefault; + std::filesystem::path pathDefault; // For OpenFileNameFormField, SaveFileNameFormField, DirectoryNameFormField size_t indexDefault; static FormInputField Label(const std::string& text); @@ -19709,8 +19714,8 @@ namespace BinaryNinja { static FormInputField Choice(const std::string& prompt, const std::vector& choices); static FormInputField OpenFileName(const std::string& prompt, const std::string& ext); static FormInputField SaveFileName( - const std::string& prompt, const std::string& ext, const std::string& defaultName = ""); - static FormInputField DirectoryName(const std::string& prompt, const std::string& defaultName = ""); + const std::string& prompt, const std::string& ext, const std::filesystem::path& defaultName = {}); + static FormInputField DirectoryName(const std::string& prompt, const std::filesystem::path& defaultName = {}); static FormInputField Checkbox(const std::string& prompt, const bool& defaultChoice = false); }; @@ -19766,11 +19771,12 @@ namespace BinaryNinja { const std::vector& choices) = 0; virtual bool GetLargeChoiceInput(size_t& idx, const std::string& prompt, const std::string& title, const std::vector& choices) = 0; - virtual bool GetOpenFileNameInput(std::string& result, const std::string& prompt, const std::string& ext = ""); - virtual bool GetSaveFileNameInput(std::string& result, const std::string& prompt, const std::string& ext = "", - const std::string& defaultName = ""); - virtual bool GetDirectoryNameInput( - std::string& result, const std::string& prompt, const std::string& defaultName = ""); + virtual bool GetOpenFileNameInput( + std::filesystem::path& result, const std::string& prompt, const std::string& ext = ""); + virtual bool GetSaveFileNameInput(std::filesystem::path& result, const std::string& prompt, + const std::string& ext = "", const std::filesystem::path& defaultName = {}); + virtual bool GetDirectoryNameInput(std::filesystem::path& result, const std::string& prompt, + const std::filesystem::path& defaultName = {}); virtual bool GetCheckboxInput( int64_t& result, const std::string& prompt, @@ -19820,7 +19826,7 @@ namespace BinaryNinja { PluginStatus GetPluginStatus() const; std::vector GetApis() const; std::vector GetInstallPlatforms() const; - std::string GetPath() const; + std::filesystem::path GetPath() const; std::string GetSubdir() const; std::string GetDependencies() const; std::string GetPluginDirectory() const; @@ -19873,13 +19879,13 @@ namespace BinaryNinja { public: Repository(BNRepository* repository); std::string GetUrl() const; - std::string GetRepoPath() const; + std::filesystem::path GetRepoPath() const; std::string GetLocalReference() const; std::string GetRemoteReference() const; std::vector> GetPlugins() const; std::string GetPluginDirectory() const; - Ref GetPluginByPath(const std::string& pluginPath); - std::string GetFullPath() const; + Ref GetPluginByPath(const std::filesystem::path& pluginPath); + std::filesystem::path GetFullPath() const; }; /*! @@ -19890,9 +19896,9 @@ namespace BinaryNinja { public: static bool CheckForUpdates(); static std::vector> GetRepositories(); - static Ref GetRepositoryByPath(const std::string& repoName); + static Ref GetRepositoryByPath(const std::filesystem::path& repoName); static bool AddRepository(const std::string& url, // URL to raw plugins.json file - const std::string& repoPath); // Relative path within the repositories directory + const std::filesystem::path& repoPath); // Relative path within the repositories directory Ref GetDefaultRepository(); }; @@ -20010,7 +20016,7 @@ namespace BinaryNinja { \param view a BinaryView object \return True if the load is successful, False otherwise */ - bool LoadSettingsFile(const std::string& fileName, BNSettingsScope scope = SettingsAutoScope, Ref view = nullptr); + bool LoadSettingsFile(const std::filesystem::path& fileName = std::filesystem::path(), BNSettingsScope scope = SettingsAutoScope, Ref view = nullptr); /*! Sets the resource identifier for this \c Settings instance. When accessing setting values at the \c SettingsResourceScope level, the resource identifier is passed along through the backing store interface. @@ -20127,6 +20133,8 @@ namespace BinaryNinja { BNSettingsScope scope = SettingsAutoScope); bool Set(const std::string& key, const std::string& value, Ref view = nullptr, BNSettingsScope scope = SettingsAutoScope); + bool Set(const std::string& key, const std::filesystem::path& value, Ref view = nullptr, + BNSettingsScope scope = SettingsAutoScope); bool Set(const std::string& key, const std::vector& value, Ref view = nullptr, BNSettingsScope scope = SettingsAutoScope); bool SetJson(const std::string& key, const std::string& value, Ref view = nullptr, @@ -20152,6 +20160,7 @@ namespace BinaryNinja { bool Set(const std::string& key, uint64_t value, Ref func, BNSettingsScope scope = SettingsAutoScope); bool Set(const std::string& key, const char* value, Ref func, BNSettingsScope scope = SettingsAutoScope); bool Set(const std::string& key, const std::string& value, Ref func, BNSettingsScope scope = SettingsAutoScope); + bool Set(const std::string& key, const std::filesystem::path& value, Ref func, BNSettingsScope scope = SettingsAutoScope); bool Set(const std::string& key, const std::vector& value, Ref func, BNSettingsScope scope = SettingsAutoScope); bool SetJson(const std::string& key, const std::string& value, Ref func, BNSettingsScope scope = SettingsAutoScope); }; @@ -20175,6 +20184,8 @@ namespace BinaryNinja { template <> std::string Settings::Get(const std::string& key, Ref view, BNSettingsScope* scope); template <> + std::filesystem::path Settings::Get(const std::string& key, Ref view, BNSettingsScope* scope); + template <> std::vector Settings::Get>( const std::string& key, Ref view, BNSettingsScope* scope); /*! \endcond*/ @@ -20190,6 +20201,8 @@ namespace BinaryNinja { template <> std::string Settings::Get(const std::string& key, Ref func, BNSettingsScope* scope); template <> + std::filesystem::path Settings::Get(const std::string& key, Ref func, BNSettingsScope* scope); + template <> std::vector Settings::Get>(const std::string& key, Ref func, BNSettingsScope* scope); typedef BNMetadataType MetadataType; @@ -20875,7 +20888,7 @@ namespace BinaryNinja { \param path \return True if the type library was successfully loaded */ - static Ref LoadFromFile(const std::string& path); + static Ref LoadFromFile(const std::filesystem::path& path); /*! Looks up the first type library found with a matching name. Keep in mind that names are not necessarily unique. @@ -20899,14 +20912,14 @@ namespace BinaryNinja { \param path \return True if the type library was successfully written to the file */ - bool WriteToFile(const std::string& path); + bool WriteToFile(const std::filesystem::path& path); /*! Decompresses the type library to a JSON file \param path \return True if the type library was successfully decompressed */ - bool DecompressToFile(const std::string& path); + bool DecompressToFile(const std::filesystem::path& path); /*! The Architecture this type library is associated with @@ -21190,7 +21203,7 @@ namespace BinaryNinja { \param path Path to type archive file \return Type archive, or nullptr if it could not be loaded. */ - static Ref Open(const std::string& path); + static Ref Open(const std::filesystem::path& path); /*! Create a type archive at the given path. @@ -21198,7 +21211,7 @@ namespace BinaryNinja { \param platform Relevant platform for types in the archive \return Type archive, or nullptr if it could not be loaded. */ - static Ref Create(const std::string& path, Ref platform); + static Ref Create(const std::filesystem::path& path, Ref platform); /*! Create a type archive at the given path with a manually-specified id. @@ -21208,7 +21221,7 @@ namespace BinaryNinja { \param id Assigned id for the type archive \return Type archive, or nullptr if it could not be created. */ - static Ref CreateWithId(const std::string& path, Ref platform, const std::string& id); + static Ref CreateWithId(const std::filesystem::path& path, Ref platform, const std::string& id); /*! Get a reference to the type archive with the known id, if one exists. @@ -21228,7 +21241,7 @@ namespace BinaryNinja { \param path File path \return True if it's a type archive */ - static bool IsTypeArchive(const std::string& path); + static bool IsTypeArchive(const std::filesystem::path& path); /*! Get the unique id associated with this type archive @@ -21240,7 +21253,7 @@ namespace BinaryNinja { \return The path */ - std::string GetPath() const; + std::filesystem::path GetPath() const; /*! Get the associated Platform for a Type Archive @@ -21733,9 +21746,9 @@ namespace BinaryNinja { */ bool ParseTypesFromSource( const std::string& text, - const std::string& fileName, + const std::filesystem::path& fileName, const std::vector& options, - const std::vector& includeDirs, + const std::vector& includeDirs, const std::string& autoTypeSource, bool importDependencies, TypeParserResult& result, @@ -21748,9 +21761,9 @@ namespace BinaryNinja { BN_DEPRECATED("Use ParseTypesFromSource overload with the extra importDependencies param") bool ParseTypesFromSource( const std::string& text, - const std::string& fileName, + const std::filesystem::path& fileName, const std::vector& options, - const std::vector& includeDirs, + const std::vector& includeDirs, const std::string& autoTypeSource, TypeParserResult& result, std::vector& errors @@ -23899,7 +23912,7 @@ namespace BinaryNinja::Collaboration \param project Remote Project \return Default project path */ - std::string DefaultProjectPath(Ref project); + std::filesystem::path DefaultProjectPath(Ref project); /*! Get the default filepath for a remote File. This is based off the Setting for @@ -23908,7 +23921,7 @@ namespace BinaryNinja::Collaboration \param file Remote File \return Default file path */ - std::string DefaultFilePath(Ref file); + std::filesystem::path DefaultFilePath(Ref file); /*! Download a file from its remote, saving all snapshots to a database in the @@ -23919,7 +23932,7 @@ namespace BinaryNinja::Collaboration \return FileContext for opening \throws SyncException If there was an error downloading */ - Ref DownloadFile(Ref file, const std::string& dbPath, ProgressFunction progress = {}); + Ref DownloadFile(Ref file, const std::filesystem::path& dbPath, ProgressFunction progress = {}); /*! Add a snapshot to the id map in a database @@ -24116,7 +24129,7 @@ namespace BinaryNinja::Collaboration \return TypeArchive for using \throws SyncException If there was an error downloading */ - Ref DownloadTypeArchive(Ref file, const std::string& dbPath, ProgressFunction progress = {}); + Ref DownloadTypeArchive(Ref file, const std::filesystem::path& dbPath, ProgressFunction progress = {}); /*! Upload a type archive @@ -24152,7 +24165,7 @@ namespace BinaryNinja::Collaboration */ size_t PullTypeArchive(Ref archive, Ref file, std::function>)> conflictHandler, ProgressFunction progress = {}); - void DownloadDatabaseForFile(Ref file, const std::string& dbPath, bool force, ProgressFunction progress = {}); + void DownloadDatabaseForFile(Ref file, const std::filesystem::path& dbPath, bool force, ProgressFunction progress = {}); /*! Set the remote author of a local snapshot (does not upload) diff --git a/binaryninjacore.h b/binaryninjacore.h index 178312b2a9..7cede75129 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -37,14 +37,14 @@ // Current ABI version for linking to the core. This is incremented any time // there are changes to the API that affect linking, including new functions, // new types, or modifications to existing functions or types. -#define BN_CURRENT_CORE_ABI_VERSION 170 +#define BN_CURRENT_CORE_ABI_VERSION 172 // Minimum ABI version that is supported for loading of plugins. Plugins that // are linked to an ABI version less than this will not be able to load and // will require rebuilding. The minimum version is increased when there are // incompatible changes that break binary compatibility, such as changes to // existing types or functions. -#define BN_MINIMUM_CORE_ABI_VERSION 170 +#define BN_MINIMUM_CORE_ABI_VERSION 172 #ifdef __GNUC__ #ifdef BINARYNINJACORE_LIBRARY @@ -85,6 +85,29 @@ #define __has_extension(x) 0 #endif +#if defined(__clang__) && !defined(BN_TYPE_PARSER) + /*! Pointer ownership is transferred to the caller. */ + #define BN_OWNED __attribute__((annotate("bn_owned"))) + /*! Pointer is borrowed and remains owned by the callee. */ + #define BN_BORROWED __attribute__((annotate("bn_borrowed"))) + /*! Pointer may be null. */ + #define BN_NULLABLE __attribute__((annotate("bn_nullable"))) + /*! Owned pointer must be released with the specified function. */ + #define BN_FREED_BY(func) __attribute__((annotate("bn_freed_by:" #func))) + /*! Pointer length is supplied by the specified count parameter. */ + #define BN_OUT_COUNT(count) __attribute__((annotate("bn_out_count:" #count))) + /*! Native path buffer length is supplied by the specified count parameter. + Counts are measured in BNGetPathFormat() elements, not bytes. */ + #define BN_PATH_ELEMENT_COUNT(count) __attribute__((annotate("bn_path_element_count:" #count))) +#else + #define BN_OWNED + #define BN_BORROWED + #define BN_NULLABLE + #define BN_FREED_BY(func) + #define BN_OUT_COUNT(count) + #define BN_PATH_ELEMENT_COUNT(count) +#endif + // Define attributes for enums based on compiler support #if defined(BN_TYPE_PARSER) #define __BN_ENUM_ATTRIBUTES @@ -261,6 +284,7 @@ extern "C" typedef struct BNFlowGraphLayout BNFlowGraphLayout; typedef struct BNFlowGraphLayoutRequest BNFlowGraphLayoutRequest; typedef struct BNSymbol BNSymbol; + typedef struct BNPath BNPath; typedef struct BNTemporaryFile BNTemporaryFile; typedef struct BNLowLevelILFunction BNLowLevelILFunction; typedef struct BNMediumLevelILFunction BNMediumLevelILFunction; @@ -1834,8 +1858,8 @@ extern "C" void (*externalLocationAdded)(void* ctxt, BNBinaryView* data, BNExternalLocation* location); void (*externalLocationUpdated)(void* ctxt, BNBinaryView* data, BNExternalLocation* location); void (*externalLocationRemoved)(void* ctxt, BNBinaryView* data, BNExternalLocation* location); - void (*typeArchiveAttached)(void* ctxt, BNBinaryView* view, const char* id, const char* path); - void (*typeArchiveDetached)(void* ctxt, BNBinaryView* view, const char* id, const char* path); + void (*typeArchiveAttached)(void* ctxt, BNBinaryView* view, const char* id, BNPath* path); + void (*typeArchiveDetached)(void* ctxt, BNBinaryView* view, const char* id, BNPath* path); void (*typeArchiveConnected)(void* ctxt, BNBinaryView* view, BNTypeArchive* archive); void (*typeArchiveDisconnected)(void* ctxt, BNBinaryView* view, BNTypeArchive* archive); void (*undoEntryAdded)(void* ctxt, BNBinaryView* view, BNUndoEntry* entry); @@ -3223,7 +3247,7 @@ extern "C" void (*externalRefTaken)(void* ctxt); void (*externalRefReleased)(void* ctxt); BNScriptingProviderExecuteResult (*executeScriptInput)(void* ctxt, const char* input); - BNScriptingProviderExecuteResult (*executeScriptInputFromFilename)(void *ctxt, const char* input); + BNScriptingProviderExecuteResult (*executeScriptInputFromFilename)(void *ctxt, BNPath* input); void (*cancelScriptInput)(void* ctxt); void (*releaseBinaryView)(void* ctxt, BNBinaryView* view); void (*setCurrentBinaryView)(void* ctxt, BNBinaryView* view); @@ -3241,7 +3265,7 @@ extern "C" { void* context; BNScriptingInstance* (*createInstance)(void* ctxt); - bool (*loadModule)(void* ctxt, const char* repoPath, const char* pluginPath, bool force); + bool (*loadModule)(void* ctxt, BNPath* repoPath, BNPath* pluginPath, bool force); bool (*installModules)(void* ctxt, const char* modules); } BNScriptingProviderCallbacks; @@ -3265,17 +3289,17 @@ extern "C" void* context; bool (*getOptionText)(void* ctxt, BNTypeParserOption option, const char* value, char** result); bool (*preprocessSource)(void* ctxt, - const char* source, const char* fileName, BNPlatform* platform, + const char* source, BNPath* fileName, BNPlatform* platform, BNTypeContainer* existingTypes, const char* const* options, size_t optionCount, - const char* const* includeDirs, size_t includeDirCount, + BNPath** includeDirs, size_t includeDirCount, char** output, BNTypeParserError** errors, size_t* errorCount ); bool (*parseTypesFromSource)(void* ctxt, - const char* source, const char* fileName, BNPlatform* platform, + const char* source, BNPath* fileName, BNPlatform* platform, BNTypeContainer* existingTypes, const char* const* options, size_t optionCount, - const char* const* includeDirs, size_t includeDirCount, + BNPath** includeDirs, size_t includeDirCount, const char* autoTypeSource, BNTypeParserResult* result, BNTypeParserError** errors, size_t* errorCount ); @@ -3387,16 +3411,18 @@ extern "C" uint64_t currentAddress; // For AddressFormField const char** choices; // For ChoiceFormField size_t count; // For ChoiceFormField - const char* ext; // For OpenFileNameFormField, SaveFileNameFormField - const char* defaultName; // For SaveFileNameFormField + const char* ext; // For OpenFileNameFormField, SaveFileNameFormField + BNPath* defaultName; // For SaveFileNameFormField, DirectoryNameFormField int64_t intResult; uint64_t addressResult; char* stringResult; + BNPath* pathResult; // For OpenFileNameFormField, SaveFileNameFormField, DirectoryNameFormField size_t indexResult; bool hasDefault; int64_t intDefault; uint64_t addressDefault; const char* stringDefault; + BNPath* pathDefault; // For OpenFileNameFormField, SaveFileNameFormField, DirectoryNameFormField size_t indexDefault; } BNFormInputField; @@ -3418,10 +3444,10 @@ extern "C" void* ctxt, size_t* result, const char* prompt, const char* title, const char** choices, size_t count); bool (*getLargeChoiceInput)( void* ctxt, size_t* result, const char* prompt, const char* title, const char** choices, size_t count); - bool (*getOpenFileNameInput)(void* ctxt, char** result, const char* prompt, const char* ext); + bool (*getOpenFileNameInput)(void* ctxt, BNPath** result, const char* prompt, const char* ext); bool (*getSaveFileNameInput)( - void* ctxt, char** result, const char* prompt, const char* ext, const char* defaultName); - bool (*getDirectoryNameInput)(void* ctxt, char** result, const char* prompt, const char* defaultName); + void* ctxt, BNPath** result, const char* prompt, const char* ext, BNPath* defaultName); + bool (*getDirectoryNameInput)(void* ctxt, BNPath** result, const char* prompt, BNPath* defaultName); bool (*getCheckboxInput)(void* ctxt, int64_t* result, const char* prompt, const char* title, const int64_t* defaultChoice); bool (*getFormInput)(void* ctxt, BNFormInputField* fields, size_t count, const char* title); BNMessageBoxButtonResult (*showMessageBox)( @@ -4164,6 +4190,20 @@ extern "C" BINARYNINJACOREAPI char** BNAllocStringList(const char** contents, size_t size); BINARYNINJACOREAPI void BNFreeStringList(char** strs, size_t count); + BN_ENUM(uint8_t, BNPathFormat) + { + BNWindowsUtf16Path, + BNPosixBytesPath + }; + + BINARYNINJACOREAPI BNPathFormat BNGetPathFormat(void); + BINARYNINJACOREAPI BNPath BN_OWNED BN_FREED_BY(BNFreePath)* BNCreatePath( + const void BN_BORROWED BN_NULLABLE BN_PATH_ELEMENT_COUNT(count)* data, size_t count); + BINARYNINJACOREAPI const void BN_BORROWED BN_NULLABLE BN_OUT_COUNT(count) BN_PATH_ELEMENT_COUNT(count)* BNGetPathData( + BNPath BN_BORROWED BN_NULLABLE* path, size_t BN_BORROWED BN_NULLABLE* count); + BINARYNINJACOREAPI void BNFreePath(BNPath BN_OWNED BN_NULLABLE* path); + BINARYNINJACOREAPI void BNFreePathList(BNPath BN_OWNED* BN_OWNED* paths, size_t count); + BINARYNINJACOREAPI void BNShutdown(void); BINARYNINJACOREAPI bool BNIsShutdownRequested(void); @@ -4187,7 +4227,7 @@ extern "C" BINARYNINJACOREAPI bool BNIsUIEnabled(void); BINARYNINJACOREAPI void BNSetLicense(const char* licenseData); - BINARYNINJACOREAPI bool BNIsDatabase(const char* filename); + BINARYNINJACOREAPI bool BNIsDatabase(BNPath* filename); BINARYNINJACOREAPI bool BNIsDatabaseFromData(const void* data, size_t len); BINARYNINJACOREAPI bool BNAuthenticateEnterpriseServerWithToken( @@ -4232,23 +4272,23 @@ extern "C" BINARYNINJACOREAPI void BNDisablePlugins(void); BINARYNINJACOREAPI bool BNIsPluginsEnabled(void); - BINARYNINJACOREAPI char* BNGetInstallDirectory(void); - BINARYNINJACOREAPI char* BNGetBundledPluginDirectory(void); - BINARYNINJACOREAPI char* BNGetBundledScriptPluginDirectory(void); - BINARYNINJACOREAPI void BNSetBundledPluginDirectory(const char* path); - BINARYNINJACOREAPI void BNSetBundledScriptPluginDirectory(const char* path); + BINARYNINJACOREAPI BNPath* BNGetInstallDirectory(void); + BINARYNINJACOREAPI BNPath* BNGetBundledPluginDirectory(void); + BINARYNINJACOREAPI BNPath* BNGetBundledScriptPluginDirectory(void); + BINARYNINJACOREAPI void BNSetBundledPluginDirectory(BNPath* path); + BINARYNINJACOREAPI void BNSetBundledScriptPluginDirectory(BNPath* path); - BINARYNINJACOREAPI char* BNGetUserDirectory(void); - BINARYNINJACOREAPI char* BNGetUserPluginDirectory(void); - BINARYNINJACOREAPI char* BNGetRepositoriesDirectory(void); - BINARYNINJACOREAPI char* BNGetSettingsFileName(void); + BINARYNINJACOREAPI BNPath* BNGetUserDirectory(void); + BINARYNINJACOREAPI BNPath* BNGetUserPluginDirectory(void); + BINARYNINJACOREAPI BNPath* BNGetRepositoriesDirectory(void); + BINARYNINJACOREAPI BNPath* BNGetSettingsFileName(void); BINARYNINJACOREAPI void BNSaveLastRun(void); - BINARYNINJACOREAPI char* BNGetPathRelativeToBundledPluginDirectory(const char* path); - BINARYNINJACOREAPI char* BNGetPathRelativeToUserPluginDirectory(const char* path); - BINARYNINJACOREAPI char* BNGetPathRelativeToUserDirectory(const char* path); + BINARYNINJACOREAPI BNPath* BNGetPathRelativeToBundledPluginDirectory(BNPath* path); + BINARYNINJACOREAPI BNPath* BNGetPathRelativeToUserPluginDirectory(BNPath* path); + BINARYNINJACOREAPI BNPath* BNGetPathRelativeToUserDirectory(BNPath* path); - BINARYNINJACOREAPI bool BNExecuteWorkerProcess(const char* path, const char** args, BNDataBuffer* input, + BINARYNINJACOREAPI bool BNExecuteWorkerProcess(BNPath* path, const char** args, BNDataBuffer* input, char** output, char** error, bool stdoutIsText, bool stderrIsText); BINARYNINJACOREAPI void BNSetCurrentPluginLoadOrder(BNPluginLoadPhase order); @@ -4326,7 +4366,7 @@ extern "C" BINARYNINJACOREAPI void BNLogToStdout(BNLogLevel minimumLevel); BINARYNINJACOREAPI void BNLogToStderr(BNLogLevel minimumLevel); - BINARYNINJACOREAPI bool BNLogToFile(BNLogLevel minimumLevel, const char* path, bool append); + BINARYNINJACOREAPI bool BNLogToFile(BNLogLevel minimumLevel, BNPath* path, bool append); BINARYNINJACOREAPI void BNCloseLogs(void); // Temporary files @@ -4334,7 +4374,7 @@ extern "C" BINARYNINJACOREAPI BNTemporaryFile* BNCreateTemporaryFileWithContents(BNDataBuffer* data); BINARYNINJACOREAPI BNTemporaryFile* BNNewTemporaryFileReference(BNTemporaryFile* file); BINARYNINJACOREAPI void BNFreeTemporaryFile(BNTemporaryFile* file); - BINARYNINJACOREAPI char* BNGetTemporaryFilePath(BNTemporaryFile* file); + BINARYNINJACOREAPI BNPath* BNGetTemporaryFilePath(BNTemporaryFile* file); BINARYNINJACOREAPI BNDataBuffer* BNGetTemporaryFileContents(BNTemporaryFile* file); // Data buffer management @@ -4390,13 +4430,13 @@ extern "C" BINARYNINJACOREAPI bool BNIsBackedByDatabase(BNFileMetadata* file, const char* binaryViewType); - BINARYNINJACOREAPI bool BNCreateDatabase(BNBinaryView* data, const char* path, BNSaveSettings* settings); - BINARYNINJACOREAPI bool BNCreateDatabaseWithProgress(BNBinaryView* data, const char* path, void* ctxt, + BINARYNINJACOREAPI bool BNCreateDatabase(BNBinaryView* data, BNPath* path, BNSaveSettings* settings); + BINARYNINJACOREAPI bool BNCreateDatabaseWithProgress(BNBinaryView* data, BNPath* path, void* ctxt, BNProgressFunction progress, BNSaveSettings* settings); - BINARYNINJACOREAPI BNBinaryView* BNOpenExistingDatabase(BNFileMetadata* file, const char* path); - BINARYNINJACOREAPI BNBinaryView* BNOpenExistingDatabaseWithProgress(BNFileMetadata* file, const char* path, + BINARYNINJACOREAPI BNBinaryView* BNOpenExistingDatabase(BNFileMetadata* file, BNPath* path); + BINARYNINJACOREAPI BNBinaryView* BNOpenExistingDatabaseWithProgress(BNFileMetadata* file, BNPath* path, void* ctxt, BNProgressFunction progress); - BINARYNINJACOREAPI BNBinaryView* BNOpenDatabaseForConfiguration(BNFileMetadata* file, const char* path); + BINARYNINJACOREAPI BNBinaryView* BNOpenDatabaseForConfiguration(BNFileMetadata* file, BNPath* path); BINARYNINJACOREAPI bool BNSaveAutoSnapshot(BNBinaryView* data, BNSaveSettings* settings); BINARYNINJACOREAPI bool BNSaveAutoSnapshotWithProgress(BNBinaryView* data, void* ctxt, BNProgressFunction progress, BNSaveSettings* settings); @@ -4435,13 +4475,13 @@ extern "C" BINARYNINJACOREAPI void BNFreeProject(BNProject* project); BINARYNINJACOREAPI void BNFreeProjectList(BNProject** projects, size_t count); BINARYNINJACOREAPI BNProject** BNGetOpenProjects(size_t* count); - BINARYNINJACOREAPI BNProject* BNCreateProject(const char* path, const char* name); - BINARYNINJACOREAPI BNProject* BNOpenProject(const char* path); + BINARYNINJACOREAPI BNProject* BNCreateProject(BNPath* path, const char* name); + BINARYNINJACOREAPI BNProject* BNOpenProject(BNPath* path); BINARYNINJACOREAPI bool BNProjectOpen(BNProject* project); BINARYNINJACOREAPI bool BNProjectClose(BNProject* project); BINARYNINJACOREAPI char* BNProjectGetId(BNProject* project); BINARYNINJACOREAPI bool BNProjectIsOpen(BNProject* project); - BINARYNINJACOREAPI char* BNProjectGetPath(BNProject* project); + BINARYNINJACOREAPI BNPath* BNProjectGetPath(BNProject* project); BINARYNINJACOREAPI char* BNProjectGetFilePathInProject(BNProject* project, BNProjectFile* file); BINARYNINJACOREAPI char* BNProjectGetName(BNProject* project); BINARYNINJACOREAPI bool BNProjectSetName(BNProject* project, const char* name); @@ -4452,9 +4492,9 @@ extern "C" BINARYNINJACOREAPI bool BNProjectStoreMetadata(BNProject* project, const char* key, BNMetadata* value); BINARYNINJACOREAPI bool BNProjectRemoveMetadata(BNProject* project, const char* key); - BINARYNINJACOREAPI BNProjectFile* BNProjectCreateFileFromPath(BNProject* project, const char* path, BNProjectFolder* folder, const char* name, const char* description, void* ctxt, + BINARYNINJACOREAPI BNProjectFile* BNProjectCreateFileFromPath(BNProject* project, BNPath* path, BNProjectFolder* folder, const char* name, const char* description, void* ctxt, BNProgressFunction progress); - BINARYNINJACOREAPI BNProjectFile* BNProjectCreateFileFromPathUnsafe(BNProject* project, const char* path, BNProjectFolder* folder, const char* name, const char* description, const char* id, int64_t creationTimestamp, void* ctxt, + BINARYNINJACOREAPI BNProjectFile* BNProjectCreateFileFromPathUnsafe(BNProject* project, BNPath* path, BNProjectFolder* folder, const char* name, const char* description, const char* id, int64_t creationTimestamp, void* ctxt, BNProgressFunction progress); BINARYNINJACOREAPI BNProjectFile* BNProjectCreateFile(BNProject* project, const uint8_t* contents, size_t contentsSize, BNProjectFolder* folder, const char* name, const char* description, void* ctxt, BNProgressFunction progress); @@ -4462,14 +4502,14 @@ extern "C" BNProgressFunction progress); BINARYNINJACOREAPI BNProjectFile** BNProjectGetFiles(BNProject* project, size_t* count); BINARYNINJACOREAPI BNProjectFile* BNProjectGetFileById(BNProject* project, const char* id); - BINARYNINJACOREAPI BNProjectFile* BNProjectGetFileByPathOnDisk(BNProject* project, const char* path); + BINARYNINJACOREAPI BNProjectFile* BNProjectGetFileByPathOnDisk(BNProject* project, BNPath* path); BINARYNINJACOREAPI BNProjectFile** BNProjectGetFilesByPathInProject(BNProject* project, const char* path, size_t* count); BINARYNINJACOREAPI BNProjectFile** BNProjectGetFilesInFolder(BNProject* project, BNProjectFolder* folder, size_t* count); BINARYNINJACOREAPI bool BNProjectPushFile(BNProject* project, BNProjectFile* file); BINARYNINJACOREAPI bool BNProjectDeleteFile(BNProject* project, BNProjectFile* file); - BINARYNINJACOREAPI BNProjectFolder* BNProjectCreateFolderFromPath(BNProject* project, const char* path, BNProjectFolder* parent, const char* description, void* ctxt, + BINARYNINJACOREAPI BNProjectFolder* BNProjectCreateFolderFromPath(BNProject* project, BNPath* path, BNProjectFolder* parent, const char* description, void* ctxt, BNProgressFunction progress); BINARYNINJACOREAPI BNProjectFolder* BNProjectCreateFolder(BNProject* project, BNProjectFolder* parent, const char* name, const char* description); BINARYNINJACOREAPI BNProjectFolder* BNProjectCreateFolderUnsafe(BNProject* project, BNProjectFolder* parent, const char* name, const char* description, const char* id); @@ -4488,7 +4528,7 @@ extern "C" BINARYNINJACOREAPI BNProjectFile* BNNewProjectFileReference(BNProjectFile* file); BINARYNINJACOREAPI void BNFreeProjectFile(BNProjectFile* file); BINARYNINJACOREAPI void BNFreeProjectFileList(BNProjectFile** files, size_t count); - BINARYNINJACOREAPI char* BNProjectFileGetPathOnDisk(BNProjectFile* file); + BINARYNINJACOREAPI BNPath* BNProjectFileGetPathOnDisk(BNProjectFile* file); BINARYNINJACOREAPI char* BNProjectFileGetPathInProject(const BNProjectFile* file); BINARYNINJACOREAPI bool BNProjectFileExistsOnDisk(BNProjectFile* file); BINARYNINJACOREAPI char* BNProjectFileGetName(BNProjectFile* file); @@ -4499,7 +4539,7 @@ extern "C" BINARYNINJACOREAPI BNProjectFolder* BNProjectFileGetFolder(BNProjectFile* file); BINARYNINJACOREAPI bool BNProjectFileSetFolder(BNProjectFile* file, BNProjectFolder* folder); BINARYNINJACOREAPI BNProject* BNProjectFileGetProject(BNProjectFile* file); - BINARYNINJACOREAPI bool BNProjectFileExport(BNProjectFile* file, const char* destination); + BINARYNINJACOREAPI bool BNProjectFileExport(BNProjectFile* file, BNPath* destination); BINARYNINJACOREAPI int64_t BNProjectFileGetCreationTimestamp(BNProjectFile* file); BINARYNINJACOREAPI bool BNProjectFileAddDependency(BNProjectFile* file, BNProjectFile* dep); BINARYNINJACOREAPI bool BNProjectFileRemoveDependency(BNProjectFile* file, BNProjectFile* dep); @@ -4519,7 +4559,7 @@ extern "C" BINARYNINJACOREAPI BNProjectFolder* BNProjectFolderGetParent(BNProjectFolder* folder); BINARYNINJACOREAPI bool BNProjectFolderSetParent(BNProjectFolder* folder, BNProjectFolder* parent); BINARYNINJACOREAPI BNProject* BNProjectFolderGetProject(BNProjectFolder* folder); - BINARYNINJACOREAPI bool BNProjectFolderExport(BNProjectFolder* folder, const char* destination, void* ctxt, + BINARYNINJACOREAPI bool BNProjectFolderExport(BNProjectFolder* folder, BNPath* destination, void* ctxt, BNProgressFunction progress); BINARYNINJACOREAPI BNProjectFile** BNProjectFolderGetFiles(BNProjectFolder* folder, size_t* count); @@ -4618,11 +4658,11 @@ extern "C" BINARYNINJACOREAPI bool BNCreateSnapshotedViewWithProgress(BNBinaryView* data, const char* viewName, void* ctxt, BNProgressFunction progress); - BINARYNINJACOREAPI char* BNGetOriginalFilename(BNFileMetadata* file); - BINARYNINJACOREAPI void BNSetOriginalFilename(BNFileMetadata* file, const char* name); + BINARYNINJACOREAPI BNPath* BNGetOriginalFilename(BNFileMetadata* file); + BINARYNINJACOREAPI void BNSetOriginalFilename(BNFileMetadata* file, BNPath* name); - BINARYNINJACOREAPI char* BNGetFilename(BNFileMetadata* file); - BINARYNINJACOREAPI void BNSetFilename(BNFileMetadata* file, const char* name); + BINARYNINJACOREAPI BNPath* BNGetFilename(BNFileMetadata* file); + BINARYNINJACOREAPI void BNSetFilename(BNFileMetadata* file, BNPath* name); BINARYNINJACOREAPI char* BNGetVirtualPath(BNFileMetadata* file); BINARYNINJACOREAPI void BNSetVirtualPath(BNFileMetadata* file, const char* path); @@ -4767,7 +4807,7 @@ extern "C" BINARYNINJACOREAPI bool BNIsExecutableView(BNBinaryView* view); BINARYNINJACOREAPI bool BNSaveToFile(BNBinaryView* view, BNFileAccessor* file); - BINARYNINJACOREAPI bool BNSaveToFilename(BNBinaryView* view, const char* filename); + BINARYNINJACOREAPI bool BNSaveToFilename(BNBinaryView* view, BNPath* filename); BINARYNINJACOREAPI void BNDefineRelocation(BNBinaryView* view, BNArchitecture* arch, BNRelocationInfo* info, uint64_t target, uint64_t reloc); BINARYNINJACOREAPI void BNDefineSymbolRelocation(BNBinaryView* view, BNArchitecture* arch, BNRelocationInfo* info, BNSymbol* target, uint64_t reloc); @@ -4899,7 +4939,7 @@ extern "C" BINARYNINJACOREAPI BNBinaryView* BNCreateBinaryDataView(BNFileMetadata* file); BINARYNINJACOREAPI BNBinaryView* BNCreateBinaryDataViewFromBuffer(BNFileMetadata* file, BNDataBuffer* buf); BINARYNINJACOREAPI BNBinaryView* BNCreateBinaryDataViewFromData(BNFileMetadata* file, const void* data, size_t len); - BINARYNINJACOREAPI BNBinaryView* BNCreateBinaryDataViewFromFilename(BNFileMetadata* file, const char* filename); + BINARYNINJACOREAPI BNBinaryView* BNCreateBinaryDataViewFromFilename(BNFileMetadata* file, BNPath* filename); BINARYNINJACOREAPI BNBinaryView* BNCreateBinaryDataViewFromFile(BNFileMetadata* file, BNFileAccessor* accessor); // Creation of new types of binary views @@ -5047,6 +5087,7 @@ extern "C" BINARYNINJACOREAPI void BNFreeTransformContext(BNTransformContext* context); BINARYNINJACOREAPI BNBinaryView* BNTransformContextGetInput(BNTransformContext* context); BINARYNINJACOREAPI char* BNTransformContextGetFileName(BNTransformContext* context); + BINARYNINJACOREAPI BNPath BN_OWNED BN_FREED_BY(BNFreePath)* BNTransformContextGetFilePath(BNTransformContext* context); BINARYNINJACOREAPI char** BNTransformContextGetAvailableTransforms(BNTransformContext* context, size_t* count); BINARYNINJACOREAPI char* BNTransformContextGetTransformName(BNTransformContext* context); BINARYNINJACOREAPI void BNTransformContextSetTransformName(BNTransformContext* context, const char* transformName); @@ -5078,8 +5119,8 @@ extern "C" BINARYNINJACOREAPI BNSettings* BNTransformContextGetSettings(BNTransformContext* context); // Transform Session - BINARYNINJACOREAPI BNTransformSession* BNCreateTransformSession(const char* filename, const char* options); - BINARYNINJACOREAPI BNTransformSession* BNCreateTransformSessionWithMode(const char* filename, BNTransformSessionMode mode, const char* options); + BINARYNINJACOREAPI BNTransformSession* BNCreateTransformSession(BNPath* filename, const char* options); + BINARYNINJACOREAPI BNTransformSession* BNCreateTransformSessionWithMode(BNPath* filename, BNTransformSessionMode mode, const char* options); BINARYNINJACOREAPI BNTransformSession* BNCreateTransformSessionFromBinaryView(BNBinaryView* initialView, const char* options); BINARYNINJACOREAPI BNTransformSession* BNCreateTransformSessionFromBinaryViewWithMode(BNBinaryView* initialView, BNTransformSessionMode mode, const char* options); BINARYNINJACOREAPI BNTransformSession* BNCreateTransformSessionFromTransformContextWithMode(BNTransformContext* context, BNTransformSessionMode mode, const char* options); @@ -5964,7 +6005,7 @@ extern "C" BINARYNINJACOREAPI bool BNParseTypeString(BNBinaryView* view, const char* text, BNQualifiedNameAndType* result, char** errors, BNQualifiedNameList* typesAllowRedefinition, bool importDepencencies); BINARYNINJACOREAPI bool BNParseTypesString(BNBinaryView* view, const char* text, const char* const* options, size_t optionCount, - const char* const* includeDirs, size_t includeDirCount, BNTypeParserResult* result, char** errors, + BNPath** includeDirs, size_t includeDirCount, BNTypeParserResult* result, char** errors, BNQualifiedNameList* typesAllowRedefinition, bool importDepencencies); BINARYNINJACOREAPI void BNFreeQualifiedNameAndType(BNQualifiedNameAndType* obj); BINARYNINJACOREAPI void BNFreeQualifiedNameAndTypeArray(BNQualifiedNameAndType* obj, size_t count); @@ -6049,9 +6090,9 @@ extern "C" BNTypeParserError** errors, size_t* errorCount ); BINARYNINJACOREAPI bool BNTypeContainerParseTypesFromSource(BNTypeContainer* container, - const char* source, const char* fileName, + const char* source, BNPath* fileName, const char* const* options, size_t optionCount, - const char* const* includeDirs, size_t includeDirCount, + BNPath** includeDirs, size_t includeDirCount, const char* autoTypeSource, bool importDepencencies, BNTypeParserResult* result, BNTypeParserError** errors, size_t* errorCount ); @@ -7016,8 +7057,8 @@ extern "C" BINARYNINJACOREAPI BNTypeLibrary* BNNewTypeLibrary(BNArchitecture* arch, const char* name); BINARYNINJACOREAPI BNTypeLibrary* BNNewTypeLibraryReference(BNTypeLibrary* lib); BINARYNINJACOREAPI BNTypeLibrary* BNDuplicateTypeLibrary(BNTypeLibrary* lib); - BINARYNINJACOREAPI BNTypeLibrary* BNLoadTypeLibraryFromFile(const char* path); - BINARYNINJACOREAPI bool BNTypeLibraryDecompressToFile(BNTypeLibrary* lib, const char* output); + BINARYNINJACOREAPI BNTypeLibrary* BNLoadTypeLibraryFromFile(BNPath* path); + BINARYNINJACOREAPI bool BNTypeLibraryDecompressToFile(BNTypeLibrary* lib, BNPath* output); BINARYNINJACOREAPI void BNFreeTypeLibrary(BNTypeLibrary* lib); BINARYNINJACOREAPI BNTypeLibrary* BNLookupTypeLibraryByName(BNArchitecture* arch, const char* name); @@ -7067,7 +7108,7 @@ extern "C" BINARYNINJACOREAPI BNQualifiedNameAndType* BNGetTypeLibraryNamedObjects(BNTypeLibrary* lib, size_t* count); BINARYNINJACOREAPI BNQualifiedNameAndType* BNGetTypeLibraryNamedTypes(BNTypeLibrary* lib, size_t* count); - BINARYNINJACOREAPI bool BNWriteTypeLibraryToFile(BNTypeLibrary* lib, const char* path); + BINARYNINJACOREAPI bool BNWriteTypeLibraryToFile(BNTypeLibrary* lib, BNPath* path); BINARYNINJACOREAPI void BNAddBinaryViewTypeLibrary(BNBinaryView* view, BNTypeLibrary* lib); BINARYNINJACOREAPI BNTypeLibrary* BNGetBinaryViewTypeLibrary(BNBinaryView* view, const char* name); @@ -7548,11 +7589,11 @@ extern "C" BINARYNINJACOREAPI bool BNCheckForStringAnnotationType(BNBinaryView* view, uint64_t addr, char** value, BNStringType* strType, bool allowShortStrings, bool allowLargeStrings, size_t childWidth); - BINARYNINJACOREAPI BNBinaryView* BNLoadFilename(const char* const filename, const bool updateAnalysis, const char* options, BNProgressFunction progress, void* progressContext); + BINARYNINJACOREAPI BNBinaryView* BNLoadFilename(BNPath* filename, const bool updateAnalysis, const char* options, BNProgressFunction progress, void* progressContext); BINARYNINJACOREAPI BNBinaryView* BNLoadProjectFile(BNProjectFile* projectFile, const bool updateAnalysis, const char* options, BNProgressFunction progress, void* progressContext); BINARYNINJACOREAPI BNBinaryView* BNLoadBinaryView(BNBinaryView* view, const bool updateAnalysis, const char* options, BNProgressFunction progress, void* progressContext); - BINARYNINJACOREAPI BNBinaryView* BNParseTextFormat(const char* filename); + BINARYNINJACOREAPI BNBinaryView* BNParseTextFormat(BNPath* filename); BINARYNINJACOREAPI BNExternalLibrary* BNBinaryViewAddExternalLibrary(BNBinaryView* view, const char* name, BNProjectFile* backingFile, bool isAuto); BINARYNINJACOREAPI void BNBinaryViewRemoveExternalLibrary(BNBinaryView* view, const char* name); @@ -7564,13 +7605,13 @@ extern "C" BINARYNINJACOREAPI BNExternalLocation** BNBinaryViewGetExternalLocations(BNBinaryView* view, size_t* count); // Source code processing - BINARYNINJACOREAPI bool BNPreprocessSource(const char* source, const char* fileName, char** output, char** errors, - const char** includeDirs, size_t includeDirCount); - BINARYNINJACOREAPI bool BNParseTypesFromSource(BNPlatform* platform, const char* source, const char* fileName, - BNTypeParserResult* result, char** errors, const char** includeDirs, size_t includeDirCount, + BINARYNINJACOREAPI bool BNPreprocessSource(const char* source, BNPath* fileName, char** output, char** errors, + BNPath** includeDirs, size_t includeDirCount); + BINARYNINJACOREAPI bool BNParseTypesFromSource(BNPlatform* platform, const char* source, BNPath* fileName, + BNTypeParserResult* result, char** errors, BNPath** includeDirs, size_t includeDirCount, const char* autoTypeSource); - BINARYNINJACOREAPI bool BNParseTypesFromSourceFile(BNPlatform* platform, const char* fileName, - BNTypeParserResult* result, char** errors, const char** includeDirs, size_t includeDirCount, + BINARYNINJACOREAPI bool BNParseTypesFromSourceFile(BNPlatform* platform, BNPath* fileName, + BNTypeParserResult* result, char** errors, BNPath** includeDirs, size_t includeDirCount, const char* autoTypeSource); BINARYNINJACOREAPI BNTypeParser* BNRegisterTypeParser( @@ -7585,17 +7626,17 @@ extern "C" BINARYNINJACOREAPI bool BNGetTypeParserOptionText(BNTypeParser* parser, BNTypeParserOption option, const char* value, char** result); BINARYNINJACOREAPI bool BNTypeParserPreprocessSource(BNTypeParser* parser, - const char* source, const char* fileName, BNPlatform* platform, + const char* source, BNPath* fileName, BNPlatform* platform, BNTypeContainer* existingTypes, const char* const* options, size_t optionCount, - const char* const* includeDirs, size_t includeDirCount, + BNPath** includeDirs, size_t includeDirCount, char** output, BNTypeParserError** errors, size_t* errorCount ); BINARYNINJACOREAPI bool BNTypeParserParseTypesFromSource(BNTypeParser* parser, - const char* source, const char* fileName, BNPlatform* platform, + const char* source, BNPath* fileName, BNPlatform* platform, BNTypeContainer* existingTypes, const char* const* options, size_t optionCount, - const char* const* includeDirs, size_t includeDirCount, + BNPath** includeDirs, size_t includeDirCount, const char* autoTypeSource, BNTypeParserResult* result, BNTypeParserError** errors, size_t* errorCount ); @@ -7850,11 +7891,11 @@ extern "C" // Platforms BINARYNINJACOREAPI BNPlatform* BNCreatePlatform(BNArchitecture* arch, const char* name); BINARYNINJACOREAPI BNPlatform* BNCreatePlatformWithTypes( - BNArchitecture* arch, const char* name, const char* typeFile, const char** includeDirs, size_t includeDirCount); + BNArchitecture* arch, const char* name, BNPath* typeFile, BNPath** includeDirs, size_t includeDirCount); BINARYNINJACOREAPI BNPlatform* BNCreateCustomPlatform(BNArchitecture* arch, const char* name, BNCustomPlatform* impl); BINARYNINJACOREAPI BNPlatform* BNCreateCustomPlatformWithTypes( BNArchitecture* arch, const char* name, BNCustomPlatform* impl, - const char* typeFile, const char** includeDirs, size_t includeDirCount); + BNPath* typeFile, BNPath** includeDirs, size_t includeDirCount); BINARYNINJACOREAPI void BNRegisterPlatform(const char* os, BNPlatform* platform); BINARYNINJACOREAPI BNPlatform* BNNewPlatformReference(BNPlatform* platform); BINARYNINJACOREAPI void BNFreePlatform(BNPlatform* platform); @@ -7992,7 +8033,7 @@ extern "C" BINARYNINJACOREAPI char* BNGetScriptingProviderAPIName(BNScriptingProvider* provider); BINARYNINJACOREAPI BNScriptingInstance* BNCreateScriptingProviderInstance(BNScriptingProvider* provider); BINARYNINJACOREAPI bool BNLoadScriptingProviderModule( - BNScriptingProvider* provider, const char* repository, const char* module, bool force); + BNScriptingProvider* provider, BNPath* repository, BNPath* module, bool force); BINARYNINJACOREAPI bool BNInstallScriptingProviderModules(BNScriptingProvider* provider, const char* modules); BINARYNINJACOREAPI BNScriptingInstance* BNInitScriptingInstance( @@ -8018,7 +8059,7 @@ extern "C" BINARYNINJACOREAPI BNScriptingProviderExecuteResult BNExecuteScriptInput( BNScriptingInstance* instance, const char* input); BINARYNINJACOREAPI BNScriptingProviderExecuteResult BNExecuteScriptInputFromFilename( - BNScriptingInstance* instance, const char* filename); + BNScriptingInstance* instance, BNPath* filename); BINARYNINJACOREAPI void BNCancelScriptInput(BNScriptingInstance* instance); BINARYNINJACOREAPI void BNScriptingInstanceReleaseBinaryView(BNScriptingInstance* instance, BNBinaryView* view); BINARYNINJACOREAPI void BNSetScriptingInstanceCurrentBinaryView(BNScriptingInstance* instance, BNBinaryView* view); @@ -8092,10 +8133,10 @@ extern "C" size_t* result, const char* prompt, const char* title, const char** choices, size_t count); BINARYNINJACOREAPI bool BNGetLargeChoiceInput( size_t* result, const char* prompt, const char* title, const char** choices, size_t count); - BINARYNINJACOREAPI bool BNGetOpenFileNameInput(char** result, const char* prompt, const char* ext); + BINARYNINJACOREAPI bool BNGetOpenFileNameInput(BNPath** result, const char* prompt, const char* ext); BINARYNINJACOREAPI bool BNGetSaveFileNameInput( - char** result, const char* prompt, const char* ext, const char* defaultName); - BINARYNINJACOREAPI bool BNGetDirectoryNameInput(char** result, const char* prompt, const char* defaultName); + BNPath** result, const char* prompt, const char* ext, BNPath* defaultName); + BINARYNINJACOREAPI bool BNGetDirectoryNameInput(BNPath** result, const char* prompt, BNPath* defaultName); BINARYNINJACOREAPI bool BNGetCheckboxInput(int64_t* result, const char* prompt, const char* title, const int64_t* defaultChoice); BINARYNINJACOREAPI bool BNGetFormInput(BNFormInputField* fields, size_t count, const char* title); BINARYNINJACOREAPI void BNFreeFormInputResults(BNFormInputField* fields, size_t count); @@ -8183,7 +8224,7 @@ extern "C" BINARYNINJACOREAPI void BNFreePluginTypes(BNPluginType* r); BINARYNINJACOREAPI BNPlugin* BNNewPluginReference(BNPlugin* r); BINARYNINJACOREAPI void BNFreePlugin(BNPlugin* plugin); - BINARYNINJACOREAPI const char* BNPluginGetPath(BNPlugin* p); + BINARYNINJACOREAPI BNPath* BNPluginGetPath(BNPlugin* p); BINARYNINJACOREAPI const char* BNPluginGetSubdir(BNPlugin* p); BINARYNINJACOREAPI const char* BNPluginGetDependencies(BNPlugin* p); BINARYNINJACOREAPI const char* BNPluginGetLongdescription(BNPlugin* p); @@ -8216,18 +8257,18 @@ extern "C" BINARYNINJACOREAPI BNRepository* BNNewRepositoryReference(BNRepository* r); BINARYNINJACOREAPI void BNFreeRepository(BNRepository* r); BINARYNINJACOREAPI char* BNRepositoryGetUrl(BNRepository* r); - BINARYNINJACOREAPI char* BNRepositoryGetRepoPath(BNRepository* r); + BINARYNINJACOREAPI BNPath* BNRepositoryGetRepoPath(BNRepository* r); BINARYNINJACOREAPI BNPlugin** BNRepositoryGetPlugins(BNRepository* r, size_t* count); BINARYNINJACOREAPI void BNFreeRepositoryPluginList(BNPlugin** r); BINARYNINJACOREAPI void BNRepositoryFreePluginDirectoryList(char** list, size_t count); - BINARYNINJACOREAPI BNPlugin* BNRepositoryGetPluginByPath(BNRepository* r, const char* pluginPath); - BINARYNINJACOREAPI const char* BNRepositoryGetPluginsPath(BNRepository* r); + BINARYNINJACOREAPI BNPlugin* BNRepositoryGetPluginByPath(BNRepository* r, BNPath* pluginPath); + BINARYNINJACOREAPI BNPath* BNRepositoryGetPluginsPath(BNRepository* r); BINARYNINJACOREAPI bool BNRepositoryManagerCheckForUpdates(); BINARYNINJACOREAPI BNRepository** BNRepositoryManagerGetRepositories(size_t* count); BINARYNINJACOREAPI void BNFreeRepositoryManagerRepositoriesList(BNRepository** r); - BINARYNINJACOREAPI bool BNRepositoryManagerAddRepository(const char* url, const char* repoPath); - BINARYNINJACOREAPI BNRepository* BNRepositoryGetRepositoryByPath(const char* repoPath); + BINARYNINJACOREAPI bool BNRepositoryManagerAddRepository(const char* url, BNPath* repoPath); + BINARYNINJACOREAPI BNRepository* BNRepositoryGetRepositoryByPath(BNPath* repoPath); BINARYNINJACOREAPI BNRepository* BNRepositoryManagerGetDefaultRepository(); @@ -8282,29 +8323,13 @@ extern "C" BINARYNINJACOREAPI int BNLlvmServicesDisasmInstruction(const char *triplet, uint8_t *src, int srcLen, uint64_t addr, char *result, size_t resultMaxSize); - // Filesystem functionality - BINARYNINJACOREAPI bool BNDeleteFile(const char* path); - BINARYNINJACOREAPI bool BNDeleteDirectory(const char* path); - BINARYNINJACOREAPI bool BNCreateDirectory(const char* path, bool createSubdirectories); - BINARYNINJACOREAPI bool BNPathExists(const char* path); - BINARYNINJACOREAPI char* BNGetParentPath(const char* path); - BINARYNINJACOREAPI bool BNIsPathDirectory(const char* path); - BINARYNINJACOREAPI bool BNIsPathRegularFile(const char* path); - BINARYNINJACOREAPI bool BNFileSize(const char* path, uint64_t* size); - BINARYNINJACOREAPI bool BNRenameFile(const char* source, const char* dest); - BINARYNINJACOREAPI bool BNCopyFile(const char* source, const char* dest); - BINARYNINJACOREAPI char* BNGetFileName(const char* path); - BINARYNINJACOREAPI char* BNGetFileExtension(const char* path); - BINARYNINJACOREAPI char** BNGetFilePathsInDirectory(const char* path, size_t* count); - BINARYNINJACOREAPI char* BNAppendPath(const char* path, const char* part); - BINARYNINJACOREAPI void BNFreePath(char* path); - BINARYNINJACOREAPI char* BNGetSystemCacheDirectory(); + BINARYNINJACOREAPI BNPath* BNGetSystemCacheDirectory(); // Settings APIs BINARYNINJACOREAPI BNSettings* BNCreateSettings(const char* schemaId); BINARYNINJACOREAPI BNSettings* BNNewSettingsReference(BNSettings* settings); BINARYNINJACOREAPI void BNFreeSettings(BNSettings* settings); - BINARYNINJACOREAPI bool BNLoadSettingsFile(BNSettings* settings, const char* fileName, BNSettingsScope scope, BNBinaryView* view); + BINARYNINJACOREAPI bool BNLoadSettingsFile(BNSettings* settings, BNPath* fileName, BNSettingsScope scope, BNBinaryView* view); BINARYNINJACOREAPI void BNSettingsSetResourceId(BNSettings* settings, const char* resourceId); BINARYNINJACOREAPI bool BNSettingsRegisterGroup(BNSettings* settings, const char* group, const char* title); BINARYNINJACOREAPI bool BNSettingsRegisterSetting(BNSettings* settings, const char* key, const char* properties); @@ -8612,14 +8637,14 @@ extern "C" BINARYNINJACOREAPI BNTypeArchive* BNNewTypeArchiveReference(BNTypeArchive* archive); BINARYNINJACOREAPI void BNFreeTypeArchiveReference(BNTypeArchive* archive); BINARYNINJACOREAPI void BNFreeTypeArchiveList(BNTypeArchive** archives, size_t count); - BINARYNINJACOREAPI BNTypeArchive* BNOpenTypeArchive(const char* path); - BINARYNINJACOREAPI BNTypeArchive* BNCreateTypeArchive(const char* path, BNPlatform* platform); - BINARYNINJACOREAPI BNTypeArchive* BNCreateTypeArchiveWithId(const char* path, BNPlatform* platform, const char* id); + BINARYNINJACOREAPI BNTypeArchive* BNOpenTypeArchive(BNPath* path); + BINARYNINJACOREAPI BNTypeArchive* BNCreateTypeArchive(BNPath* path, BNPlatform* platform); + BINARYNINJACOREAPI BNTypeArchive* BNCreateTypeArchiveWithId(BNPath* path, BNPlatform* platform, const char* id); BINARYNINJACOREAPI BNTypeArchive* BNLookupTypeArchiveById(const char* id); BINARYNINJACOREAPI void BNCloseTypeArchive(BNTypeArchive* archive); - BINARYNINJACOREAPI bool BNIsTypeArchive(const char* path); + BINARYNINJACOREAPI bool BNIsTypeArchive(BNPath* path); BINARYNINJACOREAPI char* BNGetTypeArchiveId(BNTypeArchive* archive); - BINARYNINJACOREAPI char* BNGetTypeArchivePath(BNTypeArchive* archive); + BINARYNINJACOREAPI BNPath* BNGetTypeArchivePath(BNTypeArchive* archive); BINARYNINJACOREAPI BNPlatform* BNGetTypeArchivePlatform(BNTypeArchive* archive); BINARYNINJACOREAPI char* BNGetTypeArchiveCurrentSnapshotId(BNTypeArchive* archive); BINARYNINJACOREAPI void BNSetTypeArchiveCurrentSnapshot(BNTypeArchive* archive, const char* id); @@ -8665,11 +8690,11 @@ extern "C" void* context ); - BINARYNINJACOREAPI BNTypeArchive* BNBinaryViewAttachTypeArchive(BNBinaryView* view, const char* id, const char* path); + BINARYNINJACOREAPI BNTypeArchive* BNBinaryViewAttachTypeArchive(BNBinaryView* view, const char* id, BNPath* path); BINARYNINJACOREAPI bool BNBinaryViewDetachTypeArchive(BNBinaryView* view, const char* id); BINARYNINJACOREAPI BNTypeArchive* BNBinaryViewGetTypeArchive(BNBinaryView* view, const char* id); - BINARYNINJACOREAPI size_t BNBinaryViewGetTypeArchives(BNBinaryView* view, char*** ids, char*** paths); - BINARYNINJACOREAPI char* BNBinaryViewGetTypeArchivePath(BNBinaryView* view, const char* id); + BINARYNINJACOREAPI size_t BNBinaryViewGetTypeArchives(BNBinaryView* view, char*** ids, BNPath*** paths); + BINARYNINJACOREAPI BNPath* BNBinaryViewGetTypeArchivePath(BNBinaryView* view, const char* id); BINARYNINJACOREAPI size_t BNBinaryViewGetTypeArchiveTypeNameList(BNBinaryView* view, BNQualifiedName** names); BINARYNINJACOREAPI size_t BNBinaryViewGetTypeArchiveTypeNames(BNBinaryView* view, BNQualifiedName* name, char*** archiveIds, char*** archiveTypeIds); BINARYNINJACOREAPI size_t BNBinaryViewGetAssociatedTypeArchiveTypes(BNBinaryView* view, char*** typeIds, char*** archiveIds, char*** archiveTypeIds); @@ -8732,9 +8757,9 @@ extern "C" BINARYNINJACOREAPI char* BNCollaborationGetLocalSnapshotFromRemoteTypeArchive(BNCollaborationSnapshot* snapshot, BNTypeArchive* archive); BINARYNINJACOREAPI bool BNCollaborationIsTypeArchiveSnapshotIgnored(BNTypeArchive* archive, const char* snapshot); BINARYNINJACOREAPI bool BNCollaborationSetSnapshotAuthor(BNDatabase* database, BNSnapshot* snapshot, const char* author); - BINARYNINJACOREAPI char* BNCollaborationDefaultProjectPath(BNRemoteProject* project); - BINARYNINJACOREAPI char* BNCollaborationDefaultFilePath(BNRemoteFile* file); - BINARYNINJACOREAPI BNFileMetadata* BNCollaborationDownloadFile(BNRemoteFile* file, const char* dbPath, BNProgressFunction progress, void* progressContext); + BINARYNINJACOREAPI BNPath* BNCollaborationDefaultProjectPath(BNRemoteProject* project); + BINARYNINJACOREAPI BNPath* BNCollaborationDefaultFilePath(BNRemoteFile* file); + BINARYNINJACOREAPI BNFileMetadata* BNCollaborationDownloadFile(BNRemoteFile* file, BNPath* dbPath, BNProgressFunction progress, void* progressContext); BINARYNINJACOREAPI BNRemoteFile* BNCollaborationUploadDatabase(BNFileMetadata* metadata, BNRemoteProject* project, BNRemoteFolder* folder, BNProgressFunction progress, void* progressContext, BNCollaborationNameChangesetFunction nameChangeset, void* nameChangesetContext); BINARYNINJACOREAPI bool BNCollaborationIsCollaborationDatabase(BNDatabase* database); BINARYNINJACOREAPI bool BNCollaborationGetRemoteForLocalDatabase(BNDatabase* database, BNRemote** result); @@ -8743,9 +8768,9 @@ extern "C" BINARYNINJACOREAPI bool BNCollaborationAssignSnapshotMap(BNSnapshot* localSnapshot, BNCollaborationSnapshot* remoteSnapshot); BINARYNINJACOREAPI bool BNCollaborationGetRemoteSnapshotFromLocal(BNSnapshot* snapshot, BNCollaborationSnapshot** result); BINARYNINJACOREAPI bool BNCollaborationGetLocalSnapshotFromRemote(BNCollaborationSnapshot* snapshot, BNDatabase* database, BNSnapshot** result); - BINARYNINJACOREAPI bool BNCollaborationDownloadTypeArchive(BNRemoteFile* file, const char* dbPath, BNProgressFunction progress, void* progressContext, BNTypeArchive** result); + BINARYNINJACOREAPI bool BNCollaborationDownloadTypeArchive(BNRemoteFile* file, BNPath* dbPath, BNProgressFunction progress, void* progressContext, BNTypeArchive** result); BINARYNINJACOREAPI bool BNCollaborationUploadTypeArchive(BNTypeArchive* archive, BNRemoteProject* project, BNRemoteFolder* folder, BNProgressFunction progress, void* progressContext, BNProjectFile* coreFile, BNRemoteFile** result); - BINARYNINJACOREAPI bool BNCollaborationDownloadDatabaseForFile(BNRemoteFile* file, const char* dbPath, bool force, BNProgressFunction progress, void* progressContext); + BINARYNINJACOREAPI bool BNCollaborationDownloadDatabaseForFile(BNRemoteFile* file, BNPath* dbPath, bool force, BNProgressFunction progress, void* progressContext); BINARYNINJACOREAPI BNSnapshot* BNCollaborationMergeSnapshots(BNSnapshot* first, BNSnapshot* second, BNCollaborationAnalysisConflictHandler conflictHandler, void* conflictHandlerCtxt, BNProgressFunction progress, void* progressContext); BINARYNINJACOREAPI bool BNCollaborationPullDatabase(BNDatabase* database, BNRemoteFile* file, size_t* count, BNCollaborationAnalysisConflictHandler conflictHandler, void* conflictHandlerCtxt, BNProgressFunction progress, void* progressContext, BNCollaborationNameChangesetFunction nameChangeset, void* nameChangesetContext); BINARYNINJACOREAPI bool BNCollaborationMergeDatabase(BNDatabase* database, BNCollaborationAnalysisConflictHandler conflictHandler, void* conflictHandlerCtxt, BNProgressFunction progress, void* progressContext); diff --git a/binaryview.cpp b/binaryview.cpp index da6f5b0fe0..0292b6b833 100644 --- a/binaryview.cpp +++ b/binaryview.cpp @@ -22,6 +22,7 @@ #include #include #include "binaryninjaapi.h" +#include "pathhelpers.h" using namespace BinaryNinja; using namespace std; @@ -478,19 +479,19 @@ void BinaryDataNotification::ExternalLocationRemovedCallback(void* ctxt, BNBinar } -void BinaryDataNotification::TypeArchiveAttachedCallback(void* ctxt, BNBinaryView* data, const char* id, const char* path) +void BinaryDataNotification::TypeArchiveAttachedCallback(void* ctxt, BNBinaryView* data, const char* id, BNPath* path) { BinaryDataNotification* notify = (BinaryDataNotification*)ctxt; Ref view = new BinaryView(BNNewViewReference(data)); - notify->OnTypeArchiveAttached(view, id, path); + notify->OnTypeArchiveAttached(view, id, Path::PathFromCoreBorrowed(path)); } -void BinaryDataNotification::TypeArchiveDetachedCallback(void* ctxt, BNBinaryView* data, const char* id, const char* path) +void BinaryDataNotification::TypeArchiveDetachedCallback(void* ctxt, BNBinaryView* data, const char* id, BNPath* path) { BinaryDataNotification* notify = (BinaryDataNotification*)ctxt; Ref view = new BinaryView(BNNewViewReference(data)); - notify->OnTypeArchiveDetached(view, id, path); + notify->OnTypeArchiveDetached(view, id, Path::PathFromCoreBorrowed(path)); } @@ -1670,7 +1671,7 @@ bool BinaryView::IsAnalysisChanged() const } -bool BinaryView::CreateDatabase(const string& path, Ref settings) +bool BinaryView::CreateDatabase(const filesystem::path& path, Ref settings) { auto parent = GetParentView(); if (parent) @@ -1679,7 +1680,7 @@ bool BinaryView::CreateDatabase(const string& path, Ref settings) } -bool BinaryView::CreateDatabase(const string& path, +bool BinaryView::CreateDatabase(const filesystem::path& path, const ProgressFunction& progressCallback, Ref settings) { auto parent = GetParentView(); @@ -1844,9 +1845,10 @@ uint64_t BinaryView::GetEnd() const } -bool BinaryView::Save(const string& path) +bool BinaryView::Save(const filesystem::path& path) { - return BNSaveToFilename(m_object, path.c_str()); + Path::APIObject corePath(path); + return BNSaveToFilename(m_object, corePath); } bool BinaryView::FinalizeNewSegments() @@ -4245,7 +4247,7 @@ bool BinaryView::ParseTypeString(const string& source, map options; - vector includeDirs; + vector includeDirs; bool ok = BNParseTypesString(m_object, source.c_str(), options.data(), options.size(), includeDirs.data(), includeDirs.size(), &result, &errorStr, &typesList, importDependencies); @@ -4278,7 +4280,7 @@ bool BinaryView::ParseTypeString(const string& source, map& options, const vector& includeDirs, +bool BinaryView::ParseTypesFromSource(const string& source, const vector& options, const vector& includeDirs, TypeParserResult& result, string& errors, const std::set& typesAllowRedefinition, bool importDependencies) { BNQualifiedNameList typesList; @@ -4296,10 +4298,7 @@ bool BinaryView::ParseTypesFromSource(const string& source, const vector for (auto& option : options) coreOptions.push_back(option.c_str()); - vector coreIncludeDirs; - coreIncludeDirs.reserve(includeDirs.size()); - for (auto& includeDir : includeDirs) - coreIncludeDirs.push_back(includeDir.c_str()); + Path::APIObjectList coreIncludeDirs(includeDirs); BNTypeParserResult apiResult; char* errorStr = nullptr; @@ -4771,9 +4770,10 @@ std::optional, QualifiedName>> BinaryView::LookupImpo } -Ref BinaryView::AttachTypeArchive(const string& id, const string& path) +Ref BinaryView::AttachTypeArchive(const string& id, const filesystem::path& path) { - BNTypeArchive* archive = BNBinaryViewAttachTypeArchive(m_object, id.c_str(), path.c_str()); + Path::APIObject corePath(path); + BNTypeArchive* archive = BNBinaryViewAttachTypeArchive(m_object, id.c_str(), corePath); if (!archive) return nullptr; return new TypeArchive(archive); @@ -4795,31 +4795,29 @@ Ref BinaryView::GetTypeArchive(const std::string& id) const } -std::unordered_map BinaryView::GetTypeArchives() const +std::unordered_map BinaryView::GetTypeArchives() const { char** ids; - char** paths; + BNPath** paths; size_t count = BNBinaryViewGetTypeArchives(m_object, &ids, &paths); - std::unordered_map result; + std::unordered_map result; for (size_t i = 0; i < count; i ++) { - result.emplace(ids[i], paths[i]); + result.emplace(ids[i], Path::PathFromCoreBorrowed(paths[i])); } BNFreeStringList(ids, count); - BNFreeStringList(paths, count); + BNFreePathList(paths, count); return result; } -std::optional BinaryView::GetTypeArchivePath(const std::string& id) const +std::optional BinaryView::GetTypeArchivePath(const std::string& id) const { - char* result = BNBinaryViewGetTypeArchivePath(m_object, id.c_str()); + BNPath* result = BNBinaryViewGetTypeArchivePath(m_object, id.c_str()); if (!result) return std::nullopt; - std::string cppResult = result; - BNFreeString(result); - return cppResult; + return Path::PathFromCore(result); } @@ -5922,8 +5920,11 @@ BinaryData::BinaryData(FileMetadata* file, const void* data, size_t len) : {} -BinaryData::BinaryData(FileMetadata* file, const string& path) : - BinaryView(BNCreateBinaryDataViewFromFilename(file->GetObject(), path.c_str())) +BinaryData::BinaryData(FileMetadata* file, const filesystem::path& path) : + BinaryView([&]() { + Path::APIObject corePath(path); + return BNCreateBinaryDataViewFromFilename(file->GetObject(), corePath); + }()) {} @@ -5932,10 +5933,11 @@ BinaryData::BinaryData(FileMetadata* file, FileAccessor* accessor) : {} -Ref BinaryData::CreateFromFilename(FileMetadata* file, const std::string& path) +Ref BinaryData::CreateFromFilename(FileMetadata* file, const filesystem::path& path) { // This can fail, and throwing an exception in a c++ ctor is Ugly, so now there's a helper method here - BNBinaryView* handle = BNCreateBinaryDataViewFromFilename(file->GetObject(), path.c_str()); + Path::APIObject corePath(path); + BNBinaryView* handle = BNCreateBinaryDataViewFromFilename(file->GetObject(), corePath); if (!handle) return nullptr; return new BinaryData(handle); @@ -5975,11 +5977,12 @@ Ref BinaryNinja::Load(Ref view, bool updateAnalysis, } -Ref BinaryNinja::Load(const std::string& filename, bool updateAnalysis, const std::string& options, ProgressFunction progress) +Ref BinaryNinja::Load(const filesystem::path& filename, bool updateAnalysis, const std::string& options, ProgressFunction progress) { ProgressContext cb; cb.callback = progress; - BNBinaryView* handle = BNLoadFilename(filename.c_str(), updateAnalysis, options.c_str(), ProgressCallback, &cb); + Path::APIObject coreFilename(filename); + BNBinaryView* handle = BNLoadFilename(coreFilename, updateAnalysis, options.c_str(), ProgressCallback, &cb); if (!handle) return nullptr; return new BinaryView(handle); @@ -6016,9 +6019,10 @@ Ref BinaryNinja::Load(Ref projectFile, bool updateAnaly } -Ref BinaryNinja::ParseTextFormat(const std::string& filename) +Ref BinaryNinja::ParseTextFormat(const filesystem::path& filename) { - BNBinaryView* handle = BNParseTextFormat(filename.c_str()); + Path::APIObject coreFilename(filename); + BNBinaryView* handle = BNParseTextFormat(coreFilename); if (!handle) return nullptr; return new BinaryView(handle); diff --git a/collaboration.cpp b/collaboration.cpp index 19a7168bc4..b01e4ee7c3 100644 --- a/collaboration.cpp +++ b/collaboration.cpp @@ -21,6 +21,7 @@ #include "binaryninjaapi.h" #include "binaryninjacore.h" #include "http.h" +#include "pathhelpers.h" using namespace BinaryNinja; using namespace BinaryNinja::Collaboration; @@ -216,12 +217,13 @@ bool BinaryNinja::Collaboration::IsTypeArchiveSnapshotIgnored(Ref a } -Ref BinaryNinja::Collaboration::DownloadTypeArchive(Ref file, const std::string& dbPath, ProgressFunction progress) +Ref BinaryNinja::Collaboration::DownloadTypeArchive(Ref file, const std::filesystem::path& dbPath, ProgressFunction progress) { ProgressContext pctxt; pctxt.callback = progress; BNTypeArchive* val; - if (!BNCollaborationDownloadTypeArchive(file->m_object, dbPath.c_str(), ProgressCallback, &pctxt, &val)) + Path::APIObject coreDbPath(dbPath); + if (!BNCollaborationDownloadTypeArchive(file->m_object, coreDbPath, ProgressCallback, &pctxt, &val)) throw RemoteException("Failed to download type archive"); if (val == nullptr) @@ -270,12 +272,13 @@ size_t BinaryNinja::Collaboration::PullTypeArchive(Ref archive, Ref } -void BinaryNinja::Collaboration::DownloadDatabaseForFile(Ref file, const std::string& dbPath, bool force, ProgressFunction progress) +void BinaryNinja::Collaboration::DownloadDatabaseForFile(Ref file, const std::filesystem::path& dbPath, bool force, ProgressFunction progress) { ProgressContext pctxt; pctxt.callback = progress; - if (!BNCollaborationDownloadDatabaseForFile(file->m_object, dbPath.c_str(), force, ProgressCallback, &pctxt)) + Path::APIObject coreDbPath(dbPath); + if (!BNCollaborationDownloadDatabaseForFile(file->m_object, coreDbPath, force, ProgressCallback, &pctxt)) throw RemoteException("Failed to download database for file"); } @@ -505,29 +508,24 @@ Ref BinaryNinja::Collaboration::MergeSnapshots(Ref first, Re } -std::string BinaryNinja::Collaboration::DefaultProjectPath(Ref project) +std::filesystem::path BinaryNinja::Collaboration::DefaultProjectPath(Ref project) { - char* path = BNCollaborationDefaultProjectPath(project->m_object); - std::string result = path; - BNFreeString(path); - return result; + return Path::PathFromCore(BNCollaborationDefaultProjectPath(project->m_object)); } -std::string BinaryNinja::Collaboration::DefaultFilePath(Ref file) +std::filesystem::path BinaryNinja::Collaboration::DefaultFilePath(Ref file) { - char* path = BNCollaborationDefaultFilePath(file->m_object); - std::string result = path; - BNFreeString(path); - return result; + return Path::PathFromCore(BNCollaborationDefaultFilePath(file->m_object)); } -Ref BinaryNinja::Collaboration::DownloadFile(Ref file, const std::string& dbPath, ProgressFunction progress) +Ref BinaryNinja::Collaboration::DownloadFile(Ref file, const std::filesystem::path& dbPath, ProgressFunction progress) { ProgressContext pctxt; pctxt.callback = progress; - BNFileMetadata* metadata = BNCollaborationDownloadFile(file->m_object, dbPath.c_str(), ProgressCallback, &pctxt); + Path::APIObject coreDbPath(dbPath); + BNFileMetadata* metadata = BNCollaborationDownloadFile(file->m_object, coreDbPath, ProgressCallback, &pctxt); if (metadata == nullptr) return nullptr; return new FileMetadata(metadata); diff --git a/examples/cmdline_disasm/src/disasm.cpp b/examples/cmdline_disasm/src/disasm.cpp index 58e3a82bc0..99e3070c0a 100644 --- a/examples/cmdline_disasm/src/disasm.cpp +++ b/examples/cmdline_disasm/src/disasm.cpp @@ -7,6 +7,7 @@ #include "binaryninjacore.h" #include "binaryninjaapi.h" +#include "pathhelpers.h" using namespace BinaryNinja; @@ -51,12 +52,12 @@ int main(int ac, char** av) BNInstructionTextToken* ttResult = NULL; size_t ttCount; - char* path_bundled_plugins; - /* plugin path */ - path_bundled_plugins = BNGetBundledPluginDirectory(); - printf("using bundled plugin path: %s\n", path_bundled_plugins); - BNSetBundledPluginDirectory(path_bundled_plugins); + auto path_bundled_plugins = GetBundledPluginDirectory(); + auto path_bundled_plugins_string = Path::PathToUtf8String(path_bundled_plugins); + printf("using bundled plugin path: %s\n", path_bundled_plugins_string.c_str()); + Path::APIObject coreBundledPluginPath(path_bundled_plugins); + BNSetBundledPluginDirectory(coreBundledPluginPath); BNInitPlugins(true); /* parse architecture argument */ diff --git a/examples/triage/fileinfo.cpp b/examples/triage/fileinfo.cpp index 22ce242341..ea69d8fbb1 100644 --- a/examples/triage/fileinfo.cpp +++ b/examples/triage/fileinfo.cpp @@ -3,6 +3,7 @@ #include "fontsettings.h" #include "theme.h" #include "copyablelabel.h" +#include "pathhelpers.h" #include #include #include @@ -140,13 +141,14 @@ FileInfoWidget::FileInfoWidget(QWidget* parent, BinaryViewRef bv, EntropyWidget* const auto file = bv->GetFile(); const auto filePath = file->GetOriginalFilename(); + const auto filePathString = BinaryNinja::Path::PathToUtf8String(filePath); // Calculate max path width as 50% of screen width // Using screen width since the scroll area viewport isn't sized yet during construction const int screenWidth = QGuiApplication::primaryScreen()->availableGeometry().width(); const int maxPathWidth = screenWidth / 2; - this->addCopyableFieldWithElide("Path on disk: ", filePath.c_str(), maxPathWidth); + this->addCopyableFieldWithElide("Path on disk: ", filePathString.c_str(), maxPathWidth); // If triage view is opened from a project, show both actual filepath and path relative to project if (const auto fileProjectRef = file->GetProjectFile()) @@ -187,4 +189,4 @@ void FileInfoWidget::updateEntropy(double avgEntropy) const auto entropyStr = QString::number(avgEntropy, 'f', 6); m_entropyLabel->setText(entropyStr); } -} \ No newline at end of file +} diff --git a/filemetadata.cpp b/filemetadata.cpp index 28384250d8..cbca7c474b 100644 --- a/filemetadata.cpp +++ b/filemetadata.cpp @@ -20,6 +20,7 @@ #include #include "binaryninjaapi.h" #include "binaryninjacore.h" +#include "pathhelpers.h" using namespace BinaryNinja; using namespace Json; @@ -63,17 +64,19 @@ FileMetadata::FileMetadata() } -FileMetadata::FileMetadata(const string& filename) +FileMetadata::FileMetadata(const std::filesystem::path& filename) { m_object = BNCreateFileMetadata(); - BNSetFilename(m_object, filename.c_str()); + Path::APIObject coreFilename(filename); + BNSetFilename(m_object, coreFilename); } FileMetadata::FileMetadata(Ref projectFile) { m_object = BNCreateFileMetadata(); - BNSetFilename(m_object, projectFile->GetPathOnDisk().c_str()); + Path::APIObject corePath(projectFile->GetPathOnDisk()); + BNSetFilename(m_object, corePath); BNSetProjectFile(m_object, projectFile->m_object); } @@ -99,33 +102,36 @@ void FileMetadata::SetNavigationHandler(NavigationHandler* handler) } -string FileMetadata::GetOriginalFilename() const +std::filesystem::path FileMetadata::GetOriginalFilename() const { - char* str = BNGetOriginalFilename(m_object); - string result = str; - BNFreeString(str); - return result; + return Path::PathFromCore(BNGetOriginalFilename(m_object)); } -void FileMetadata::SetOriginalFilename(const string& name) +void FileMetadata::SetOriginalFilename(const std::filesystem::path& name) { - BNSetOriginalFilename(m_object, name.c_str()); + Path::APIObject coreName(name); + BNSetOriginalFilename(m_object, coreName); } -string FileMetadata::GetFilename() const +std::filesystem::path FileMetadata::GetFilename() const { - char* str = BNGetFilename(m_object); - string result = str; - BNFreeString(str); - return result; + return Path::PathFromCore(BNGetFilename(m_object)); +} + + +void FileMetadata::SetFilename(const std::filesystem::path& name) +{ + Path::APIObject coreName(name); + BNSetFilename(m_object, coreName); } -void FileMetadata::SetFilename(const string& name) +bool FileMetadata::IsContainerEntry() const { - BNSetFilename(m_object, name.c_str()); + std::string virtualPath = GetVirtualPath(); + return !virtualPath.empty() && Path::PathToUtf8String(GetFilename()) != virtualPath; } @@ -189,25 +195,28 @@ bool FileMetadata::IsBackedByDatabase(const string& binaryViewType) const } -bool FileMetadata::CreateDatabase(const string& name, BinaryView* data, Ref settings) +bool FileMetadata::CreateDatabase(const filesystem::path& name, BinaryView* data, Ref settings) { - return BNCreateDatabase(data->GetObject(), name.c_str(), settings ? settings->GetObject() : nullptr); + Path::APIObject coreName(name); + return BNCreateDatabase(data->GetObject(), coreName, settings ? settings->GetObject() : nullptr); } -bool FileMetadata::CreateDatabase(const string& name, BinaryView* data, +bool FileMetadata::CreateDatabase(const filesystem::path& name, BinaryView* data, const ProgressFunction& progressCallback, Ref settings) { ProgressContext cb; cb.callback = progressCallback; + Path::APIObject coreName(name); return BNCreateDatabaseWithProgress( - data->GetObject(), name.c_str(), &cb, ProgressCallback, settings ? settings->GetObject() : nullptr); + data->GetObject(), coreName, &cb, ProgressCallback, settings ? settings->GetObject() : nullptr); } -Ref FileMetadata::OpenExistingDatabase(const string& path) +Ref FileMetadata::OpenExistingDatabase(const filesystem::path& path) { - BNBinaryView* data = BNOpenExistingDatabase(m_object, path.c_str()); + Path::APIObject corePath(path); + BNBinaryView* data = BNOpenExistingDatabase(m_object, corePath); if (!data) return nullptr; return new BinaryView(data); @@ -215,20 +224,22 @@ Ref FileMetadata::OpenExistingDatabase(const string& path) Ref FileMetadata::OpenExistingDatabase( - const string& path, const ProgressFunction& progressCallback) + const filesystem::path& path, const ProgressFunction& progressCallback) { ProgressContext cb; cb.callback = progressCallback; - BNBinaryView* data = BNOpenExistingDatabaseWithProgress(m_object, path.c_str(), &cb, ProgressCallback); + Path::APIObject corePath(path); + BNBinaryView* data = BNOpenExistingDatabaseWithProgress(m_object, corePath, &cb, ProgressCallback); if (!data) return nullptr; return new BinaryView(data); } -Ref FileMetadata::OpenDatabaseForConfiguration(const string& path) +Ref FileMetadata::OpenDatabaseForConfiguration(const filesystem::path& path) { - BNBinaryView* data = BNOpenDatabaseForConfiguration(m_object, path.c_str()); + Path::APIObject corePath(path); + BNBinaryView* data = BNOpenDatabaseForConfiguration(m_object, corePath); if (!data) return nullptr; return new BinaryView(data); diff --git a/interaction.cpp b/interaction.cpp index afaa2b4ab9..c421e1ac1e 100644 --- a/interaction.cpp +++ b/interaction.cpp @@ -1,6 +1,7 @@ #include #include #include "binaryninjaapi.h" +#include "pathhelpers.h" using namespace std; using namespace BinaryNinja; @@ -89,7 +90,8 @@ FormInputField FormInputField::OpenFileName(const string& prompt, const string& } -FormInputField FormInputField::SaveFileName(const string& prompt, const string& ext, const string& defaultName) +FormInputField FormInputField::SaveFileName( + const string& prompt, const string& ext, const std::filesystem::path& defaultName) { FormInputField result; result.type = SaveFileNameFormField; @@ -101,7 +103,7 @@ FormInputField FormInputField::SaveFileName(const string& prompt, const string& } -FormInputField FormInputField::DirectoryName(const string& prompt, const string& defaultName) +FormInputField FormInputField::DirectoryName(const string& prompt, const std::filesystem::path& defaultName) { FormInputField result; result.type = DirectoryNameFormField; @@ -182,21 +184,35 @@ bool InteractionHandler::GetAddressInput( } -bool InteractionHandler::GetOpenFileNameInput(string& result, const string& prompt, const string&) +bool InteractionHandler::GetOpenFileNameInput(std::filesystem::path& result, const string& prompt, const string&) { - return GetTextLineInput(result, prompt, "Open File"); + string value; + if (!GetTextLineInput(value, prompt, "Open File")) + return false; + result = Path::Utf8ToPath(value); + return true; } -bool InteractionHandler::GetSaveFileNameInput(string& result, const string& prompt, const string&, const string&) +bool InteractionHandler::GetSaveFileNameInput( + std::filesystem::path& result, const string& prompt, const string&, const std::filesystem::path&) { - return GetTextLineInput(result, prompt, "Save File"); + string value; + if (!GetTextLineInput(value, prompt, "Save File")) + return false; + result = Path::Utf8ToPath(value); + return true; } -bool InteractionHandler::GetDirectoryNameInput(string& result, const string& prompt, const string&) +bool InteractionHandler::GetDirectoryNameInput( + std::filesystem::path& result, const string& prompt, const std::filesystem::path&) { - return GetTextLineInput(result, prompt, "Select Directory"); + string value; + if (!GetTextLineInput(value, prompt, "Select Directory")) + return false; + result = Path::Utf8ToPath(value); + return true; } @@ -295,36 +311,36 @@ static bool GetLargeChoiceInputCallback( } -static bool GetOpenFileNameInputCallback(void* ctxt, char** result, const char* prompt, const char* ext) +static bool GetOpenFileNameInputCallback(void* ctxt, BNPath** result, const char* prompt, const char* ext) { InteractionHandler* handler = (InteractionHandler*)ctxt; - string value; + std::filesystem::path value; if (!handler->GetOpenFileNameInput(value, prompt, ext)) return false; - *result = BNAllocString(value.c_str()); + *result = Path::PathToCore(value); return true; } static bool GetSaveFileNameInputCallback( - void* ctxt, char** result, const char* prompt, const char* ext, const char* defaultName) + void* ctxt, BNPath** result, const char* prompt, const char* ext, BNPath* defaultName) { InteractionHandler* handler = (InteractionHandler*)ctxt; - string value; - if (!handler->GetSaveFileNameInput(value, prompt, ext, defaultName)) + std::filesystem::path value; + if (!handler->GetSaveFileNameInput(value, prompt, ext, Path::PathFromCoreBorrowed(defaultName))) return false; - *result = BNAllocString(value.c_str()); + *result = Path::PathToCore(value); return true; } -static bool GetDirectoryNameInputCallback(void* ctxt, char** result, const char* prompt, const char* defaultName) +static bool GetDirectoryNameInputCallback(void* ctxt, BNPath** result, const char* prompt, BNPath* defaultName) { InteractionHandler* handler = (InteractionHandler*)ctxt; - string value; - if (!handler->GetDirectoryNameInput(value, prompt, defaultName)) + std::filesystem::path value; + if (!handler->GetDirectoryNameInput(value, prompt, Path::PathFromCoreBorrowed(defaultName))) return false; - *result = BNAllocString(value.c_str()); + *result = Path::PathToCore(value); return true; } @@ -374,10 +390,12 @@ static bool GetFormInputCallback(void* ctxt, BNFormInputField* fieldBuf, size_t break; case SaveFileNameFormField: fields.push_back( - FormInputField::SaveFileName(fieldBuf[i].prompt, fieldBuf[i].ext, fieldBuf[i].defaultName)); + FormInputField::SaveFileName(fieldBuf[i].prompt, fieldBuf[i].ext, + Path::PathFromCoreBorrowed(fieldBuf[i].defaultName))); break; case DirectoryNameFormField: - fields.push_back(FormInputField::DirectoryName(fieldBuf[i].prompt, fieldBuf[i].defaultName)); + fields.push_back( + FormInputField::DirectoryName(fieldBuf[i].prompt, Path::PathFromCoreBorrowed(fieldBuf[i].defaultName))); break; case CheckboxFormField: fields.push_back(FormInputField::Checkbox(fieldBuf[i].prompt, fieldBuf[i].intDefault)); @@ -396,7 +414,7 @@ static bool GetFormInputCallback(void* ctxt, BNFormInputField* fieldBuf, size_t case OpenFileNameFormField: case SaveFileNameFormField: case DirectoryNameFormField: - fields.back().stringDefault = fieldBuf[i].stringDefault; + fields.back().pathDefault = Path::PathFromCoreBorrowed(fieldBuf[i].pathDefault); break; case CheckboxFormField: case IntegerFormField: @@ -424,10 +442,12 @@ static bool GetFormInputCallback(void* ctxt, BNFormInputField* fieldBuf, size_t { case TextLineFormField: case MultilineTextFormField: + fieldBuf[i].stringResult = BNAllocString(fields[i].stringResult.c_str()); + break; case OpenFileNameFormField: case SaveFileNameFormField: case DirectoryNameFormField: - fieldBuf[i].stringResult = BNAllocString(fields[i].stringResult.c_str()); + fieldBuf[i].pathResult = Path::PathToCore(fields[i].pathResult); break; case CheckboxFormField: case IntegerFormField: @@ -588,36 +608,36 @@ bool BinaryNinja::GetLargeChoiceInput(size_t& idx, const string& prompt, const s } -bool BinaryNinja::GetOpenFileNameInput(string& result, const string& prompt, const string& ext) +bool BinaryNinja::GetOpenFileNameInput(std::filesystem::path& result, const string& prompt, const string& ext) { - char* value = nullptr; + BNPath* value = nullptr; if (!BNGetOpenFileNameInput(&value, prompt.c_str(), ext.c_str())) return false; - result = value; - BNFreeString(value); + result = Path::PathFromCore(value); return true; } bool BinaryNinja::GetSaveFileNameInput( - string& result, const string& prompt, const string& ext, const string& defaultName) + std::filesystem::path& result, const string& prompt, const string& ext, const std::filesystem::path& defaultName) { - char* value = nullptr; - if (!BNGetSaveFileNameInput(&value, prompt.c_str(), ext.c_str(), defaultName.c_str())) + BNPath* value = nullptr; + Path::APIObject coreDefaultName(defaultName); + if (!BNGetSaveFileNameInput(&value, prompt.c_str(), ext.c_str(), coreDefaultName)) return false; - result = value; - BNFreeString(value); + result = Path::PathFromCore(value); return true; } -bool BinaryNinja::GetDirectoryNameInput(string& result, const string& prompt, const string& defaultName) +bool BinaryNinja::GetDirectoryNameInput( + std::filesystem::path& result, const string& prompt, const std::filesystem::path& defaultName) { - char* value = nullptr; - if (!BNGetDirectoryNameInput(&value, prompt.c_str(), defaultName.c_str())) + BNPath* value = nullptr; + Path::APIObject coreDefaultName(defaultName); + if (!BNGetDirectoryNameInput(&value, prompt.c_str(), coreDefaultName)) return false; - result = value; - BNFreeString(value); + result = Path::PathFromCore(value); return true; } @@ -631,7 +651,11 @@ bool BinaryNinja::GetCheckboxInput(int64_t& result, const std::string& prompt, c bool BinaryNinja::GetFormInput(vector& fields, const string& title) { // Construct field list in core format - BNFormInputField* fieldBuf = new BNFormInputField[fields.size()]; + BNFormInputField* fieldBuf = new BNFormInputField[fields.size()](); + vector defaultNamePaths; + vector pathDefaults; + defaultNamePaths.reserve(fields.size()); + pathDefaults.reserve(fields.size()); for (size_t i = 0; i < fields.size(); i++) { fieldBuf[i].type = fields[i].type; @@ -653,10 +677,12 @@ bool BinaryNinja::GetFormInput(vector& fields, const string& tit break; case SaveFileNameFormField: fieldBuf[i].ext = fields[i].ext.c_str(); - fieldBuf[i].defaultName = fields[i].defaultName.c_str(); + defaultNamePaths.emplace_back(fields[i].defaultName); + fieldBuf[i].defaultName = defaultNamePaths.back(); break; case DirectoryNameFormField: - fieldBuf[i].defaultName = fields[i].defaultName.c_str(); + defaultNamePaths.emplace_back(fields[i].defaultName); + fieldBuf[i].defaultName = defaultNamePaths.back(); break; default: break; @@ -668,10 +694,13 @@ bool BinaryNinja::GetFormInput(vector& fields, const string& tit { case TextLineFormField: case MultilineTextFormField: + fieldBuf[i].stringDefault = fields[i].stringDefault.c_str(); + break; case OpenFileNameFormField: case SaveFileNameFormField: case DirectoryNameFormField: - fieldBuf[i].stringDefault = fields[i].stringDefault.c_str(); + pathDefaults.emplace_back(fields[i].pathDefault); + fieldBuf[i].pathDefault = pathDefaults.back(); break; case CheckboxFormField: fieldBuf[i].intDefault = fields[i].intDefault; @@ -713,10 +742,12 @@ bool BinaryNinja::GetFormInput(vector& fields, const string& tit { case TextLineFormField: case MultilineTextFormField: + fields[i].stringResult = fieldBuf[i].stringResult; + break; case OpenFileNameFormField: case SaveFileNameFormField: case DirectoryNameFormField: - fields[i].stringResult = fieldBuf[i].stringResult; + fields[i].pathResult = Path::PathFromCoreBorrowed(fieldBuf[i].pathResult); break; case CheckboxFormField: case IntegerFormField: diff --git a/log.cpp b/log.cpp index 1f9635d2fa..0452140b6f 100644 --- a/log.cpp +++ b/log.cpp @@ -23,6 +23,7 @@ #include #include #include "binaryninjaapi.h" +#include "pathhelpers.h" using namespace BinaryNinja; using namespace std; @@ -460,9 +461,10 @@ void BinaryNinja::LogToStderr(BNLogLevel minimumLevel) } -bool BinaryNinja::LogToFile(BNLogLevel minimumLevel, const string& path, bool append) +bool BinaryNinja::LogToFile(BNLogLevel minimumLevel, const filesystem::path& path, bool append) { - return BNLogToFile(minimumLevel, path.c_str(), append); + Path::APIObject corePath(path); + return BNLogToFile(minimumLevel, corePath, append); } diff --git a/pathformathelpers.h b/pathformathelpers.h new file mode 100644 index 0000000000..43c6f62d6b --- /dev/null +++ b/pathformathelpers.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include + +namespace BinaryNinjaPathFormat +{ + inline std::string PathToUtf8String(const std::filesystem::path& path) + { + auto value = path.u8string(); + return std::string(reinterpret_cast(value.data()), value.size()); + } + + inline std::string PrintablePath(const std::filesystem::path& path) + { + // TODO: Make diagnostic formatting tolerant of native paths that cannot be represented as valid UTF-8, + // such as Windows paths containing unpaired UTF-16 surrogates. + return PathToUtf8String(path); + } +} + +// fmt/std.h provides its own std::filesystem::path formatter. Keep that disabled in translation units that +// include this header first, so path formatting consistently goes through PrintablePath. +#if !defined(FMT_STD_H_) +#ifndef FMT_CPP_LIB_FILESYSTEM +#define FMT_CPP_LIB_FILESYSTEM 0 +#endif +template<> struct fmt::formatter : fmt::formatter +{ + format_context::iterator format(const std::filesystem::path& path, format_context& ctx) const + { + return fmt::formatter::format(BinaryNinjaPathFormat::PrintablePath(path), ctx); + } +}; +#endif diff --git a/pathhelpers.h b/pathhelpers.h new file mode 100644 index 0000000000..0edbd69d4d --- /dev/null +++ b/pathhelpers.h @@ -0,0 +1,123 @@ +#pragma once + +#include "binaryninjacore.h" +#include "pathformathelpers.h" + +#include +#include +#include +#include +#include + +namespace BinaryNinja::Path +{ + using BinaryNinjaPathFormat::PathToUtf8String; + + // Diagnostic-only path formatting. Use PathToUtf8String for data interchange. + using BinaryNinjaPathFormat::PrintablePath; + + inline std::filesystem::path Utf8ToPath(std::string_view path) + { +#if defined(WIN32) || defined(_WIN32) + std::u8string utf8Path; + utf8Path.reserve(path.size()); + for (char ch : path) + utf8Path.push_back(static_cast(static_cast(ch))); + return std::filesystem::path(utf8Path); +#else + return std::filesystem::path(std::string(path)); +#endif + } + + inline std::filesystem::path PathFromCoreBorrowed(BNPath* path) + { + if (!path) + return {}; + + size_t count = 0; + const void* data = BNGetPathData(path, &count); + if (!data) + return {}; + +#if defined(WIN32) || defined(_WIN32) + return std::filesystem::path( + std::wstring(static_cast(data), static_cast(data) + count)); +#else + return std::filesystem::path( + std::string(static_cast(data), static_cast(data) + count)); +#endif + } + + inline std::string PrintablePath(BNPath* path) + { + return PrintablePath(PathFromCoreBorrowed(path)); + } + + inline BNPath* PathToCore(const std::filesystem::path& path) + { + const auto& native = path.native(); + return BNCreatePath(native.data(), native.size()); + } + + class APIObject + { + struct AdoptObject + {}; + + BNPath* m_path; + + APIObject(BNPath* path, AdoptObject): m_path(path) {} + + public: + explicit APIObject(const std::filesystem::path& path): m_path(PathToCore(path)) {} + ~APIObject() + { + BNFreePath(m_path); + } + APIObject(const APIObject&) = delete; + APIObject& operator=(const APIObject&) = delete; + APIObject(APIObject&& other) noexcept: m_path(std::exchange(other.m_path, nullptr)) {} + APIObject& operator=(APIObject&& other) noexcept + { + if (this != &other) + { + BNFreePath(m_path); + m_path = std::exchange(other.m_path, nullptr); + } + return *this; + } + + static APIObject Adopt(BNPath* path) { return APIObject(path, AdoptObject {}); } + + operator BNPath*() const { return m_path; } + BNPath* get() const { return m_path; } + }; + + inline std::filesystem::path PathFromCore(BNPath* path) + { + APIObject ownedPath = APIObject::Adopt(path); + return PathFromCoreBorrowed(ownedPath.get()); + } + + class APIObjectList + { + std::vector m_objects; + std::vector m_paths; + + public: + template + explicit APIObjectList(const std::vector& paths) + { + m_objects.reserve(paths.size()); + m_paths.reserve(paths.size()); + for (const auto& path : paths) + { + m_objects.emplace_back(path); + m_paths.push_back(m_objects.back().get()); + } + } + + BNPath** data() { return m_paths.data(); } + size_t size() const { return m_paths.size(); } + }; +} diff --git a/platform.cpp b/platform.cpp index 7fbde19472..19453dcb99 100644 --- a/platform.cpp +++ b/platform.cpp @@ -19,6 +19,7 @@ // IN THE SOFTWARE. #include "binaryninjaapi.h" +#include "pathhelpers.h" using namespace std; using namespace BinaryNinja; @@ -51,7 +52,7 @@ Platform::Platform(Architecture* arch, const string& name) } -Platform::Platform(Architecture* arch, const string& name, const string& typeFile, const vector& includeDirs) +Platform::Platform(Architecture* arch, const string& name, const filesystem::path& typeFile, const vector& includeDirs) { BNCustomPlatform plat; plat.context = this; @@ -64,13 +65,11 @@ Platform::Platform(Architecture* arch, const string& name, const string& typeFil plat.adjustTypeParserInput = AdjustTypeParserInputCallback; plat.freeTypeParserInput = FreeTypeParserInputCallback; plat.getFallbackEnabled = GetFallbackEnabledCallback; - const char** includeDirList = new const char*[includeDirs.size()]; - for (size_t i = 0; i < includeDirs.size(); i++) - includeDirList[i] = includeDirs[i].c_str(); + Path::APIObjectList includeDirList(includeDirs); + Path::APIObject coreTypeFile(typeFile); m_object = BNCreateCustomPlatformWithTypes( arch->GetObject(), name.c_str(), &plat, - typeFile.c_str(), includeDirList, includeDirs.size()); - delete[] includeDirList; + coreTypeFile, includeDirList.data(), includeDirList.size()); AddRefForRegistration(); } @@ -775,26 +774,23 @@ string Platform::GetAutoPlatformTypeIdSource() } -bool Platform::ParseTypesFromSource(const string& source, const string& fileName, map>& types, +bool Platform::ParseTypesFromSource(const string& source, const filesystem::path& fileName, map>& types, map>& variables, map>& functions, string& errors, - const vector& includeDirs, const string& autoTypeSource) + const vector& includeDirs, const string& autoTypeSource) { BNTypeParserResult result; char* errorStr; - const char** includeDirList = new const char*[includeDirs.size()]; - - for (size_t i = 0; i < includeDirs.size(); i++) - includeDirList[i] = includeDirs[i].c_str(); + Path::APIObjectList includeDirList(includeDirs); types.clear(); variables.clear(); functions.clear(); - bool ok = BNParseTypesFromSource(m_object, source.c_str(), fileName.c_str(), &result, &errorStr, includeDirList, - includeDirs.size(), autoTypeSource.c_str()); + Path::APIObject coreFileName(fileName); + bool ok = BNParseTypesFromSource(m_object, source.c_str(), coreFileName, &result, &errorStr, includeDirList.data(), + includeDirList.size(), autoTypeSource.c_str()); errors = errorStr; BNFreeString(errorStr); - delete[] includeDirList; if (!ok) return false; @@ -818,26 +814,23 @@ bool Platform::ParseTypesFromSource(const string& source, const string& fileName } -bool Platform::ParseTypesFromSourceFile(const string& fileName, map>& types, +bool Platform::ParseTypesFromSourceFile(const filesystem::path& fileName, map>& types, map>& variables, map>& functions, string& errors, - const vector& includeDirs, const string& autoTypeSource) + const vector& includeDirs, const string& autoTypeSource) { BNTypeParserResult result; char* errorStr; - const char** includeDirList = new const char*[includeDirs.size()]; - - for (size_t i = 0; i < includeDirs.size(); i++) - includeDirList[i] = includeDirs[i].c_str(); + Path::APIObjectList includeDirList(includeDirs); types.clear(); variables.clear(); functions.clear(); + Path::APIObject coreFileName(fileName); bool ok = BNParseTypesFromSourceFile( - m_object, fileName.c_str(), &result, &errorStr, includeDirList, includeDirs.size(), autoTypeSource.c_str()); + m_object, coreFileName, &result, &errorStr, includeDirList.data(), includeDirList.size(), autoTypeSource.c_str()); errors = errorStr; BNFreeString(errorStr); - delete[] includeDirList; if (!ok) return false; diff --git a/pluginmanager.cpp b/pluginmanager.cpp index 57eac9858b..3878be46ce 100644 --- a/pluginmanager.cpp +++ b/pluginmanager.cpp @@ -1,4 +1,5 @@ #include "binaryninjaapi.h" +#include "pathhelpers.h" using namespace BinaryNinja; using namespace std; @@ -12,14 +13,21 @@ using namespace std; return result; \ } while (0) +#define RETURN_PATH(s) \ + do \ + { \ + BNPath* contents = (s); \ + return Path::PathFromCore(contents); \ + } while (0) + Extension::Extension(BNPlugin* plugin) { m_object = plugin; } -string Extension::GetPath() const +filesystem::path Extension::GetPath() const { - RETURN_STRING(BNPluginGetPath(m_object)); + RETURN_PATH(BNPluginGetPath(m_object)); } string Extension::GetSubdir() const @@ -359,9 +367,9 @@ string Repository::GetUrl() const } -string Repository::GetRepoPath() const +filesystem::path Repository::GetRepoPath() const { - RETURN_STRING(BNRepositoryGetRepoPath(m_object)); + RETURN_PATH(BNRepositoryGetRepoPath(m_object)); } @@ -378,17 +386,18 @@ vector> Repository::GetPlugins() const } -Ref Repository::GetPluginByPath(const string& pluginPath) +Ref Repository::GetPluginByPath(const filesystem::path& pluginPath) { - BNPlugin* plugin = BNRepositoryGetPluginByPath(m_object, pluginPath.c_str()); + Path::APIObject corePluginPath(pluginPath); + BNPlugin* plugin = BNRepositoryGetPluginByPath(m_object, corePluginPath); if (!plugin) return nullptr; return new Extension(plugin); } -string Repository::GetFullPath() const +filesystem::path Repository::GetFullPath() const { - RETURN_STRING(BNRepositoryGetPluginsPath(m_object)); + RETURN_PATH(BNRepositoryGetPluginsPath(m_object)); } bool RepositoryManager::CheckForUpdates() @@ -408,14 +417,16 @@ vector> RepositoryManager::GetRepositories() } bool RepositoryManager::AddRepository(const std::string& url, - const std::string& repoPath) // Relative path within the repositories directory + const filesystem::path& repoPath) // Relative path within the repositories directory { - return BNRepositoryManagerAddRepository(url.c_str(), repoPath.c_str()); + Path::APIObject coreRepoPath(repoPath); + return BNRepositoryManagerAddRepository(url.c_str(), coreRepoPath); } -Ref RepositoryManager::GetRepositoryByPath(const std::string& repoPath) +Ref RepositoryManager::GetRepositoryByPath(const filesystem::path& repoPath) { - BNRepository* repo = BNRepositoryGetRepositoryByPath(repoPath.c_str()); + Path::APIObject coreRepoPath(repoPath); + BNRepository* repo = BNRepositoryGetRepositoryByPath(coreRepoPath); if (!repo) return nullptr; return new Repository(repo); diff --git a/plugins/bntl_utils/src/command.rs b/plugins/bntl_utils/src/command.rs index 0ff4b667d8..48b90e2a7a 100644 --- a/plugins/bntl_utils/src/command.rs +++ b/plugins/bntl_utils/src/command.rs @@ -15,15 +15,15 @@ impl OutputDirectoryField { let type_lib_dir = user_directory().join("typelib"); FormInputField::DirectoryName { prompt: "Output Directory".to_string(), - default: Some(type_lib_dir.to_string_lossy().to_string()), + default_name: Some(type_lib_dir.clone()), + default: Some(type_lib_dir), value: None, } } pub fn from_form(form: &Form) -> Option { let field = form.get_field_with_name("Output Directory")?; - let field_value = field.try_value_string()?; - Some(PathBuf::from(field_value)) + field.try_value_path() } } @@ -33,6 +33,7 @@ impl InputDirectoryField { pub fn field() -> FormInputField { FormInputField::DirectoryName { prompt: "Input Directory".to_string(), + default_name: None, default: None, value: None, } @@ -40,7 +41,6 @@ impl InputDirectoryField { pub fn from_form(form: &Form) -> Option { let field = form.get_field_with_name("Input Directory")?; - let field_value = field.try_value_string()?; - Some(PathBuf::from(field_value)) + field.try_value_path() } } diff --git a/plugins/bntl_utils/src/command/diff.rs b/plugins/bntl_utils/src/command/diff.rs index 6cc0fe0a02..fdbdc38e70 100644 --- a/plugins/bntl_utils/src/command/diff.rs +++ b/plugins/bntl_utils/src/command/diff.rs @@ -13,6 +13,7 @@ impl InputFileAField { pub fn field() -> FormInputField { FormInputField::DirectoryName { prompt: "Directory A".to_string(), + default_name: None, default: None, value: None, } @@ -20,8 +21,7 @@ impl InputFileAField { pub fn from_form(form: &Form) -> Option { let field = form.get_field_with_name("Directory A")?; - let field_value = field.try_value_string()?; - Some(PathBuf::from(field_value)) + field.try_value_path() } } @@ -31,6 +31,7 @@ impl InputFileBField { pub fn field() -> FormInputField { FormInputField::DirectoryName { prompt: "Directory B".to_string(), + default_name: None, default: None, value: None, } @@ -38,8 +39,7 @@ impl InputFileBField { pub fn from_form(form: &Form) -> Option { let field = form.get_field_with_name("Directory B")?; - let field_value = field.try_value_string()?; - Some(PathBuf::from(field_value)) + field.try_value_path() } } diff --git a/plugins/bntl_utils/src/process.rs b/plugins/bntl_utils/src/process.rs index 5c9651a473..a98b6ae722 100644 --- a/plugins/bntl_utils/src/process.rs +++ b/plugins/bntl_utils/src/process.rs @@ -970,10 +970,6 @@ impl TypeLibProcessor { let header_contents = std::fs::read_to_string(path).map_err(ProcessingError::FileRead)?; - let file_name = path - .file_name() - .unwrap_or(OsStr::new("source.hpp")) - .to_string_lossy(); // TODO: Allow specifying options? let mut include_dirs = self.include_directories.clone(); // TODO: This will not work for projects, we need to remove this parent call @@ -985,7 +981,7 @@ impl TypeLibProcessor { let parsed_types = parser .parse_types_from_source( &header_contents, - &file_name, + path, &platform, &platform_type_container, &[], diff --git a/plugins/dwarf/dwarf_export/src/lib.rs b/plugins/dwarf/dwarf_export/src/lib.rs index 90a92faacc..df386cdb44 100644 --- a/plugins/dwarf/dwarf_export/src/lib.rs +++ b/plugins/dwarf/dwarf_export/src/lib.rs @@ -18,7 +18,7 @@ use gimli::{ }; use object::{write, Architecture, BinaryFormat, SectionKind}; use std::fs; -use std::path::{Path, PathBuf}; +use std::path::Path; fn export_type( name: String, @@ -646,6 +646,7 @@ fn create_export_form(bv_arch: &str) -> Form { form.add_field(FormInputField::SaveFileName { prompt: "Save Location".to_string(), extension: Some("Debug Files (*.dwo *.debug);;All Files (*)".to_string()), + default_name: None, default: None, value: None, }); @@ -760,7 +761,7 @@ fn export_dwarf(bv: &BinaryView) { }; let save_loc_field = export_form.get_field_with_name("Save Location").unwrap(); - let save_loc_path = PathBuf::from(save_loc_field.try_value_string().unwrap_or_default()); + let save_loc_path = save_loc_field.try_value_path().unwrap_or_default(); let encoding = gimli::Encoding { format: gimli::Format::Dwarf32, diff --git a/plugins/efi_resolver/include/Resolver.h b/plugins/efi_resolver/include/Resolver.h index 91bb18622a..1e3029d6cf 100644 --- a/plugins/efi_resolver/include/Resolver.h +++ b/plugins/efi_resolver/include/Resolver.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -10,6 +11,7 @@ #include "highlevelilinstruction.h" #include "lowlevelilinstruction.h" #include "mediumlevelilinstruction.h" +#include "pathhelpers.h" using namespace BinaryNinja; using namespace std; @@ -30,8 +32,8 @@ class Resolver vector> m_guid_usages; vector> m_variable_usages; - bool parseUserGuidIfExists(const string& filePath); - bool parseProtocolMapping(const string& filePath); + bool parseUserGuidIfExists(const filesystem::path& filePath); + bool parseProtocolMapping(const filesystem::path& filePath); /*! For backward compatibility, if a user saved a bndb with older version Binary Ninja @@ -72,4 +74,4 @@ class Resolver bool defineTypeAtCallsite( Ref func, uint64_t addr, string typeName, int paramIdx, bool followFields = false); vector HighLevelILExprsAt(Ref func, Ref arch, uint64_t addr); -}; \ No newline at end of file +}; diff --git a/plugins/efi_resolver/src/Resolver.cpp b/plugins/efi_resolver/src/Resolver.cpp index e501f35b32..13be1056d3 100644 --- a/plugins/efi_resolver/src/Resolver.cpp +++ b/plugins/efi_resolver/src/Resolver.cpp @@ -40,28 +40,20 @@ string Resolver::nonConflictingLocalName(Ref func, const string& basen return name; } -static string GetBundledEfiPath() +static filesystem::path GetBundledEfiPath() { - string path = GetBundledPluginDirectory(); #if defined(_WIN32) - return path + "\\..\\types\\efi.c"; + return GetBundledPluginDirectory() / ".." / "types" / "efi.c"; #elif defined(__APPLE__) - return path + "/../../Resources/types/efi.c"; + return GetBundledPluginDirectory() / ".." / ".." / "Resources" / "types" / "efi.c"; #else - return path + "/../types/efi.c"; + return GetBundledPluginDirectory() / ".." / "types" / "efi.c"; #endif } -static string GetUserGuidPath() +static filesystem::path GetUserGuidPath() { - string path = GetUserDirectory(); -#if defined(_WIN32) - return path + "\\types\\efi-guids.json"; -#elif defined(__APPLE__) - return path + "/types/efi-guids.json"; -#else - return path + "/types/efi-guids.json"; -#endif + return GetUserDirectory() / "types" / "efi-guids.json"; } static EFI_GUID parseGuid(const string& guidStr) @@ -97,7 +89,7 @@ static EFI_GUID parseGuid(const string& guidStr) return guid; } -bool Resolver::parseProtocolMapping(const string& filePath) +bool Resolver::parseProtocolMapping(const filesystem::path& filePath) { vector> guids; ifstream efiDefs; @@ -105,7 +97,7 @@ bool Resolver::parseProtocolMapping(const string& filePath) m_protocol.clear(); - efiDefs.open(filePath.c_str()); + efiDefs.open(filePath); if (!efiDefs.is_open()) return false; @@ -159,7 +151,7 @@ bool Resolver::parseProtocolMapping(const string& filePath) return true; } -bool Resolver::parseUserGuidIfExists(const string& filePath) +bool Resolver::parseUserGuidIfExists(const filesystem::path& filePath) { ifstream userJson(filePath); if (!userJson.is_open()) diff --git a/plugins/idb_import/src/commands.rs b/plugins/idb_import/src/commands.rs index bc17aa9b4a..bcca523e54 100644 --- a/plugins/idb_import/src/commands.rs +++ b/plugins/idb_import/src/commands.rs @@ -4,8 +4,8 @@ use std::path::PathBuf; pub mod load_file; pub struct LoadFileField { - filter: String, - default: Option, + filter: String, + default: Option, } impl LoadFileField { @@ -17,12 +17,12 @@ impl LoadFileField { } } - pub fn with_default(filter: &str, default: &str) -> Self { - Self { - filter: filter.to_string(), - default: Some(default.to_string()), - } - } + pub fn with_default(filter: &str, default: &str) -> Self { + Self { + filter: filter.to_string(), + default: Some(PathBuf::from(default)), + } + } pub fn field(&self) -> FormInputField { FormInputField::OpenFileName { @@ -34,9 +34,8 @@ impl LoadFileField { } } - pub fn from_form(form: &Form) -> Option { - let field = form.get_field_with_name("File Path")?; - let field_value = field.try_value_string()?; - Some(PathBuf::from(field_value)) - } + pub fn from_form(form: &Form) -> Option { + let field = form.get_field_with_name("File Path")?; + field.try_value_path() + } } diff --git a/plugins/svd/src/lib.rs b/plugins/svd/src/lib.rs index ea37b783d1..c6e1d7a632 100644 --- a/plugins/svd/src/lib.rs +++ b/plugins/svd/src/lib.rs @@ -25,8 +25,7 @@ impl LoadFileField { pub fn from_form(form: &Form) -> Option { let field = form.get_field_with_name("File Path")?; - let field_value = field.try_value_string()?; - Some(PathBuf::from(field_value)) + field.try_value_path() } } diff --git a/plugins/warp/api/python/_warpcore_template.py b/plugins/warp/api/python/_warpcore_template.py index fd923450a0..e1ca259997 100644 --- a/plugins/warp/api/python/_warpcore_template.py +++ b/plugins/warp/api/python/_warpcore_template.py @@ -8,6 +8,8 @@ core = None core_platform = platform.system() +from binaryninja._binaryninjacore import BNPath, BNPathHandle, core_path, path_to_native_path + from binaryninja import Settings if Settings().get_bool("corePlugins.warp"): from binaryninja._binaryninjacore import BNGetBundledPluginDirectory @@ -55,4 +57,3 @@ def pyNativeStr(arg): def free_string(value:ctypes.c_char_p) -> None: BNFreeString(ctypes.cast(value, ctypes.POINTER(ctypes.c_byte))) - diff --git a/plugins/warp/api/python/generator.cpp b/plugins/warp/api/python/generator.cpp index 9e72c2ec9b..2e34daaba9 100644 --- a/plugins/warp/api/python/generator.cpp +++ b/plugins/warp/api/python/generator.cpp @@ -62,6 +62,13 @@ map g_pythonKeywordReplacements = { {"yield", "yield_"}, }; +bool IsPathType(Type* type) +{ + return (type->GetClass() == PointerTypeClass) && + (type->GetChildType()->GetClass() == NamedTypeReferenceClass) && + (type->GetChildType()->GetNamedTypeReference()->GetName().GetString() == "BNPath"); +} + void OutputType(FILE* out, Type* type, bool isReturnType = false, bool isCallback = false) { @@ -125,6 +132,11 @@ void OutputType(FILE* out, Type* type, bool isReturnType = false, bool isCallbac } break; case PointerTypeClass: + if (IsPathType(type)) + { + fprintf(out, "BNPathHandle"); + break; + } if (isCallback || (type->GetChildType()->GetClass() == VoidTypeClass)) { fprintf(out, "ctypes.c_void_p"); @@ -199,6 +211,11 @@ void OutputSwizzledType(FILE* out, Type* type) } break; case PointerTypeClass: + if (IsPathType(type)) + { + fprintf(out, "Optional[os.PathLike]"); + break; + } if (type->GetChildType()->GetClass() == VoidTypeClass) { fprintf(out, "Optional[ctypes.c_void_p]"); @@ -251,7 +268,7 @@ int main(int argc, char* argv[]) auto arch = new CoreArchitecture(BNGetNativeTypeParserArchitecture()); // Enable ephemeral settings - Settings::Instance()->LoadSettingsFile(""); + Settings::Instance()->LoadSettingsFile(); Settings::Instance()->Set("analysis.types.parserName", "ClangTypeParser"); bool ok = arch->GetStandalonePlatform()->ParseTypesFromSourceFile(argv[1], types, vars, funcs, errors); @@ -326,6 +343,11 @@ int main(int argc, char* argv[]) fprintf(out, "from binaryninja._binaryninjacore import BNDataBuffer, BNDataBufferHandle\n"); continue; } + if (name == "BNPath") + { + fprintf(out, "from binaryninja._binaryninjacore import BNPath, BNPathHandle\n"); + continue; + } if (i.second->GetClass() == StructureTypeClass) { fprintf(out, "class %s(ctypes.Structure):\n", name.c_str()); @@ -482,6 +504,7 @@ int main(int argc, char* argv[]) bool stringResult = (i.second->GetChildType()->GetClass() == PointerTypeClass) && (i.second->GetChildType()->GetChildType()->GetWidth() == 1) && (i.second->GetChildType()->GetChildType()->IsSigned()); + bool pathResult = IsPathType(i.second->GetChildType().GetValue()); // Pointer returns will be automatically wrapped to return None on null pointer bool pointerResult = (i.second->GetChildType()->GetClass() == PointerTypeClass); @@ -577,10 +600,10 @@ int main(int argc, char* argv[]) fprintf(out, "\n\t\t) -> "); if (swizzleArgs) { - if (stringResult || pointerResult) + if (stringResult || pathResult || pointerResult) fprintf(out, "Optional["); OutputSwizzledType(out, i.second->GetChildType().GetValue()); - if (stringResult || pointerResult) + if (stringResult || pathResult || pointerResult) fprintf(out, "]"); } else @@ -600,7 +623,11 @@ int main(int argc, char* argv[]) if (argName.empty()) argName = "arg" + to_string(argN); - if (swizzleArgs && (arg.type->GetClass() == PointerTypeClass) && + if (swizzleArgs && IsPathType(arg.type.GetValue())) + { + stringArgFuncCall += string("_path_arg") + to_string(argN) + ", "; + } + else if (swizzleArgs && (arg.type->GetClass() == PointerTypeClass) && (arg.type->GetChildType()->GetClass() == IntegerTypeClass) && (arg.type->GetChildType()->GetWidth() == 1) && (arg.type->GetChildType()->IsSigned())) @@ -617,29 +644,56 @@ int main(int argc, char* argv[]) stringArgFuncCall = stringArgFuncCall.substr(0, stringArgFuncCall.size()-2); stringArgFuncCall += ")"; + argN = 0; + for (auto& arg : i.second->GetParameters()) + { + string argName = arg.name; + if (g_pythonKeywordReplacements.find(argName) != g_pythonKeywordReplacements.end()) + argName = g_pythonKeywordReplacements[argName]; + if (argName.empty()) + argName = "arg" + to_string(argN); + if (swizzleArgs && IsPathType(arg.type.GetValue())) + fprintf(out, "\twith core_path(%s) as _path_arg%zu:\n", argName.c_str(), argN); + argN++; + } + string indent = "\t"; + for (auto& arg : i.second->GetParameters()) + { + if (swizzleArgs && IsPathType(arg.type.GetValue())) + indent += "\t"; + } + if (stringResult) { // Emit wrapper to get Python string and free native memory - fprintf(out, "\tresult = "); + fprintf(out, "%sresult = ", indent.c_str()); + fprintf(out, "%s\n", stringArgFuncCall.c_str()); + fprintf(out, "%sif not result:\n", indent.c_str()); + fprintf(out, "%s\treturn None\n", indent.c_str()); + fprintf(out, "%sstring = str(pyNativeStr(ctypes.cast(result, ctypes.c_char_p).value))\n", indent.c_str()); + fprintf(out, "%sBNFreeString(result)\n", indent.c_str()); + fprintf(out, "%sreturn string\n", indent.c_str()); + } + else if (pathResult) + { + fprintf(out, "%sresult = ", indent.c_str()); fprintf(out, "%s\n", stringArgFuncCall.c_str()); - fprintf(out, "\tif not result:\n"); - fprintf(out, "\t\treturn None\n"); - fprintf(out, "\tstring = str(pyNativeStr(ctypes.cast(result, ctypes.c_char_p).value))\n"); - fprintf(out, "\tBNFreeString(result)\n"); - fprintf(out, "\treturn string\n"); + fprintf(out, "%sif not result:\n", indent.c_str()); + fprintf(out, "%s\treturn None\n", indent.c_str()); + fprintf(out, "%sreturn path_to_native_path(result)\n", indent.c_str()); } else if (pointerResult) { // Emit wrapper to return None on null pointer - fprintf(out, "\tresult = "); + fprintf(out, "%sresult = ", indent.c_str()); fprintf(out, "%s\n", stringArgFuncCall.c_str()); - fprintf(out, "\tif not result:\n"); - fprintf(out, "\t\treturn None\n"); - fprintf(out, "\treturn result\n"); + fprintf(out, "%sif not result:\n", indent.c_str()); + fprintf(out, "%s\treturn None\n", indent.c_str()); + fprintf(out, "%sreturn result\n", indent.c_str()); } else { - fprintf(out, "\treturn "); + fprintf(out, "%sreturn ", indent.c_str()); fprintf(out, "%s\n", stringArgFuncCall.c_str()); } fprintf(out, "\n\n"); diff --git a/plugins/warp/api/python/warp.py b/plugins/warp/api/python/warp.py index b10b2c0991..e49281ea73 100644 --- a/plugins/warp/api/python/warp.py +++ b/plugins/warp/api/python/warp.py @@ -1,5 +1,6 @@ import ctypes import dataclasses +import os import uuid from typing import List, Optional, Union @@ -406,7 +407,7 @@ def sources(self) -> List[Source]: warpcore.BNWARPFreeUUIDList(sources, count.value) return result - def add_source(self, source_path: str) -> Optional[Source]: + def add_source(self, source_path: Union[str, bytes, os.PathLike]) -> Optional[Source]: source = warpcore.BNWARPUUID() if not warpcore.BNWARPContainerAddSource(self.handle, source_path, source): return None @@ -421,7 +422,7 @@ def is_source_uncommitted(self, source: Source) -> bool: def is_source_writable(self, source: Source) -> bool: return warpcore.BNWARPContainerIsSourceWritable(self.handle, source.uuid) - def get_source_path(self, source: Source) -> Optional[str]: + def get_source_path(self, source: Source) -> Optional[os.PathLike]: return warpcore.BNWARPContainerGetSourcePath(self.handle, source.uuid) def add_functions(self, target: WarpTarget, source: Source, functions: List[Function]) -> bool: @@ -565,8 +566,8 @@ def types(self) -> List[WarpType]: return result class WarpFile: - def __init__(self, handle: Union[warpcore.BNWARPFileHandle, str]): - if isinstance(handle, str): + def __init__(self, handle: Union[warpcore.BNWARPFileHandle, str, bytes, os.PathLike]): + if isinstance(handle, (str, bytes, os.PathLike)): self.handle = warpcore.BNWARPNewFileFromPath(handle) else: self.handle = handle @@ -599,21 +600,28 @@ class WarpProcessorState: cancelled: bool = False unprocessed_file_count: int = 0 processed_file_count: int = 0 - analyzing_files: List[str] = dataclasses.field(default_factory=list) - processing_files: List[str] = dataclasses.field(default_factory=list) + analyzing_files: List[os.PathLike] = dataclasses.field(default_factory=list) + processing_files: List[os.PathLike] = dataclasses.field(default_factory=list) @staticmethod def from_api(state: warpcore.BNWARPProcessorState) -> 'WarpProcessorState': + def field(name: str, fallback: str): + if hasattr(state, name): + return getattr(state, name) + return getattr(state, fallback) + analyzing_files = [] processing_files = [] - for i in range(state.analyzing_files_count): - analyzing_files.append(state.analyzing_files[i]) - for i in range(state.processing_files_count): - processing_files.append(state.processing_files[i]) + analyzing_files_raw = field("analyzing_files", "analyzingFiles") + processing_files_raw = field("processing_files", "processingFiles") + for i in range(field("analyzing_files_count", "analyzingFilesCount")): + analyzing_files.append(warpcore.path_to_native_path(analyzing_files_raw[i], free=False)) + for i in range(field("processing_files_count", "processingFilesCount")): + processing_files.append(warpcore.path_to_native_path(processing_files_raw[i], free=False)) return WarpProcessorState( - cancelled=state.cancelled, - unprocessed_file_count=state.unprocessed_file_count, - processed_file_count=state.processed_file_count, + cancelled=field("cancelled", "cancelled"), + unprocessed_file_count=field("unprocessed_file_count", "unprocessedFilesCount"), + processed_file_count=field("processed_file_count", "processedFilesCount"), analyzing_files=analyzing_files, processing_files=processing_files ) @@ -628,7 +636,7 @@ def __del__(self): if self.handle is not None: warpcore.BNWARPFreeProcessor(self.handle) - def add_path(self, path: str): + def add_path(self, path: Union[str, bytes, os.PathLike]): warpcore.BNWARPProcessorAddPath(self.handle, path) def add_project(self, project: Project): @@ -648,8 +656,10 @@ def start(self) -> Optional[WarpFile]: def state(self) -> WarpProcessorState: state_raw = warpcore.BNWARPProcessorGetState(self.handle) - warpcore.BNWARPFreeProcessorState(state_raw) - return WarpProcessorState.from_api(state_raw) + try: + return WarpProcessorState.from_api(state_raw) + finally: + warpcore.BNWARPFreeProcessorState(state_raw) def run_matcher(view: BinaryView): warpcore.BNWARPRunMatcher(view.handle) @@ -682,4 +692,4 @@ def get_basic_block_guid(basic_block: BasicBlock) -> Optional[BasicBlockGUID]: return None return BasicBlockGUID(guid) -# TODO: Magic matched_function, possible_functions \ No newline at end of file +# TODO: Magic matched_function, possible_functions diff --git a/plugins/warp/api/warp.cpp b/plugins/warp/api/warp.cpp index 8c93260276..c593d2256c 100644 --- a/plugins/warp/api/warp.cpp +++ b/plugins/warp/api/warp.cpp @@ -1,5 +1,6 @@ #include "warpcore.h" #include "warp.h" +#include "pathhelpers.h" #include @@ -309,10 +310,11 @@ std::vector Container::GetSources() const return result; } -std::optional Container::AddSource(const std::string &sourcePath) const +std::optional Container::AddSource(const std::filesystem::path& sourcePath) const { Source source; - if (!BNWARPContainerAddSource(m_object, sourcePath.c_str(), source.RawMut())) + BinaryNinja::Path::APIObject corePath(sourcePath); + if (!BNWARPContainerAddSource(m_object, corePath, source.RawMut())) return std::nullopt; return source; } @@ -332,14 +334,12 @@ bool Container::IsSourceWritable(const Source &source) const return BNWARPContainerIsSourceWritable(m_object, source.Raw()); } -std::optional Container::SourcePath(const Source &source) const +std::optional Container::SourcePath(const Source &source) const { - char *rawPath = BNWARPContainerGetSourcePath(m_object, source.Raw()); + BNPath *rawPath = BNWARPContainerGetSourcePath(m_object, source.Raw()); if (!rawPath) return std::nullopt; - std::string path = rawPath; - BNFreeString(rawPath); - return path; + return BinaryNinja::Path::PathFromCore(rawPath); } bool Container::AddFunctions(const Target &target, const Source &source, const std::vector > &functions) const @@ -511,9 +511,10 @@ File::File(BNWARPFile *file) m_object = file; } -Ref File::FromPath(const std::string &path) +Ref File::FromPath(const std::filesystem::path& path) { - BNWARPFile *result = BNWARPNewFileFromPath(path.c_str()); + BinaryNinja::Path::APIObject corePath(path); + BNWARPFile *result = BNWARPNewFileFromPath(corePath); if (!result) return nullptr; return new File(result); @@ -544,10 +545,10 @@ ProcessorState ProcessorState::FromAPIObject(BNWARPProcessorState *state) result.processedFilesCount = state->processedFilesCount; result.analyzingFiles.reserve(state->analyzingFilesCount); for (size_t i = 0; i < state->analyzingFilesCount; ++i) - result.analyzingFiles.emplace_back(state->analyzingFiles[i]); + result.analyzingFiles.emplace_back(BinaryNinja::Path::PathFromCoreBorrowed(state->analyzingFiles[i])); result.processingFiles.reserve(state->processingFilesCount); for (size_t i = 0; i < state->processingFilesCount; ++i) - result.processingFiles.emplace_back(state->processingFiles[i]); + result.processingFiles.emplace_back(BinaryNinja::Path::PathFromCoreBorrowed(state->processingFiles[i])); return result; } @@ -561,9 +562,10 @@ Processor::~Processor() BNWARPFreeProcessor(m_object); } -void Processor::AddPath(const std::string &path) const +void Processor::AddPath(const std::filesystem::path& path) const { - BNWARPProcessorAddPath(m_object, path.c_str()); + BinaryNinja::Path::APIObject corePath(path); + BNWARPProcessorAddPath(m_object, corePath); } void Processor::AddProject(const BinaryNinja::Project &project) const diff --git a/plugins/warp/api/warp.h b/plugins/warp/api/warp.h index 42e8ac703c..c250297ac6 100644 --- a/plugins/warp/api/warp.h +++ b/plugins/warp/api/warp.h @@ -3,6 +3,8 @@ #include #include "warpcore.h" +#include + template class WarpRefCountObject { @@ -402,7 +404,7 @@ namespace Warp { std::vector GetSources() const; - std::optional AddSource(const std::string &sourcePath) const; + std::optional AddSource(const std::filesystem::path& sourcePath) const; bool CommitSource(const Source &source) const; @@ -410,7 +412,7 @@ namespace Warp { bool IsSourceWritable(const Source &source) const; - std::optional SourcePath(const Source &source) const; + std::optional SourcePath(const Source &source) const; bool AddFunctions(const Target &target, const Source &source, const std::vector> &functions) const; @@ -456,7 +458,7 @@ namespace Warp { public: explicit File(BNWARPFile *file); - static Ref FromPath(const std::string &path); + static Ref FromPath(const std::filesystem::path& path); [[nodiscard]] std::vector> GetChunks() const; [[nodiscard]] BinaryNinja::DataBuffer ToDataBuffer() const; @@ -465,8 +467,8 @@ namespace Warp { class ProcessorState { public: - std::vector analyzingFiles; - std::vector processingFiles; + std::vector analyzingFiles; + std::vector processingFiles; bool cancelled; size_t unprocessedFilesCount; size_t processedFilesCount; @@ -485,7 +487,7 @@ namespace Warp { ~Processor(); - void AddPath(const std::string &path) const; + void AddPath(const std::filesystem::path& path) const; void AddProject(const BinaryNinja::Project &project) const; void AddProjectFile(const BinaryNinja::ProjectFile &projectFile) const; void AddBinaryView(const BinaryNinja::BinaryView &view) const; @@ -514,4 +516,4 @@ template<> struct std::hash // TODO: Use the raw bytes instead. return std::hash()(item.ToString()); } -}; \ No newline at end of file +}; diff --git a/plugins/warp/api/warpcore.h b/plugins/warp/api/warpcore.h index c1762ae62c..a322508c96 100644 --- a/plugins/warp/api/warpcore.h +++ b/plugins/warp/api/warpcore.h @@ -49,6 +49,7 @@ extern "C" typedef struct BNSymbol BNSymbol; typedef struct BNType BNType; typedef struct BNDataBuffer BNDataBuffer; + typedef struct BNPath BNPath; typedef struct BNProject BNProject; typedef struct BNProjectFile BNProjectFile; @@ -127,14 +128,14 @@ extern "C" bool cancelled; size_t unprocessedFilesCount; size_t processedFilesCount; - char** analyzingFiles; + BNPath** analyzingFiles; size_t analyzingFilesCount; - char** processingFiles; + BNPath** processingFiles; size_t processingFilesCount; }; WARP_FFI_API BNWARPProcessor* BNWARPNewProcessor(BNWARPProcessorIncludedData includedData, BNWARPProcessorIncludedFunctions includedFunctions, size_t workerCount); - WARP_FFI_API void BNWARPProcessorAddPath(BNWARPProcessor* processor, const char* path); + WARP_FFI_API void BNWARPProcessorAddPath(BNWARPProcessor* processor, BNPath* path); WARP_FFI_API void BNWARPProcessorAddProject(BNWARPProcessor* processor, BNProject* project); WARP_FFI_API void BNWARPProcessorAddProjectFile(BNWARPProcessor* processor, BNProjectFile* projectFile); WARP_FFI_API void BNWARPProcessorAddBinaryView(BNWARPProcessor* processor, BNBinaryView* view); @@ -160,11 +161,11 @@ extern "C" WARP_FFI_API char* BNWARPContainerGetName(BNWARPContainer* container); WARP_FFI_API BNWARPSource* BNWARPContainerGetSources(BNWARPContainer* container, size_t* count); - WARP_FFI_API bool BNWARPContainerAddSource(BNWARPContainer* container, const char* sourcePath, BNWARPSource* result); + WARP_FFI_API bool BNWARPContainerAddSource(BNWARPContainer* container, BNPath* sourcePath, BNWARPSource* result); WARP_FFI_API bool BNWARPContainerCommitSource(BNWARPContainer* container, const BNWARPSource* source); WARP_FFI_API bool BNWARPContainerIsSourceUncommitted(BNWARPContainer* container, const BNWARPSource* source); WARP_FFI_API bool BNWARPContainerIsSourceWritable(BNWARPContainer* container, const BNWARPSource* source); - WARP_FFI_API char* BNWARPContainerGetSourcePath(BNWARPContainer* container, const BNWARPSource* source); + WARP_FFI_API BNPath* BNWARPContainerGetSourcePath(BNWARPContainer* container, const BNWARPSource* source); WARP_FFI_API bool BNWARPContainerAddFunctions(BNWARPContainer* container, const BNWARPTarget* target, const BNWARPSource* source, BNWARPFunction** functions, size_t count); WARP_FFI_API bool BNWARPContainerAddTypes(BNWARPContainer* container, const BNWARPSource* source, BNWARPType** types, size_t count); @@ -232,7 +233,7 @@ extern "C" WARP_FFI_API BNWARPTarget* BNWARPNewTargetReference(BNWARPTarget* target); WARP_FFI_API void BNWARPFreeTargetReference(BNWARPTarget* target); - WARP_FFI_API BNWARPFile* BNWARPNewFileFromPath(const char* path); + WARP_FFI_API BNWARPFile* BNWARPNewFileFromPath(BNPath* path); WARP_FFI_API BNWARPChunk** BNWARPFileGetChunks(BNWARPFile* file, size_t* count); WARP_FFI_API BNDataBuffer* BNWARPFileToDataBuffer(BNWARPFile* file); diff --git a/plugins/warp/src/plugin/ffi/container.rs b/plugins/warp/src/plugin/ffi/container.rs index 09f15d9974..8465237492 100644 --- a/plugins/warp/src/plugin/ffi/container.rs +++ b/plugins/warp/src/plugin/ffi/container.rs @@ -7,11 +7,13 @@ use crate::plugin::ffi::{ BNWARPConstraintGUID, BNWARPContainer, BNWARPFunction, BNWARPFunctionGUID, BNWARPSource, BNWARPTarget, BNWARPType, BNWARPTypeGUID, }; -use binaryninja::string::BnString; +use binaryninja::string::{BnPath, BnString}; +use binaryninjacore_sys::BNPath; use std::collections::HashMap; use std::ffi::{c_char, CStr}; use std::mem::ManuallyDrop; use std::ops::Deref; +use std::path::Path; use std::sync::Arc; use warp::r#type::guid::TypeGUID; @@ -250,7 +252,7 @@ pub unsafe extern "C" fn BNWARPContainerGetSources( #[no_mangle] pub unsafe extern "C" fn BNWARPContainerAddSource( container: *mut BNWARPContainer, - source_path: *const c_char, + source_path: *mut BNPath, result: *mut BNWARPSource, ) -> bool { let arc_container = ManuallyDrop::new(Arc::from_raw(container)); @@ -258,9 +260,10 @@ pub unsafe extern "C" fn BNWARPContainerAddSource( return false; }; - let source_path_cstr = unsafe { CStr::from_ptr(source_path) }; - let source_path_str = source_path_cstr.to_str().unwrap(); - let source_path = SourcePath::new_with_str(source_path_str); + if source_path.is_null() { + return false; + } + let source_path = SourcePath::new(unsafe { BnString::path_buf_from_raw(source_path) }); match container.add_source(source_path) { Ok(source) => { @@ -327,21 +330,20 @@ pub unsafe extern "C" fn BNWARPContainerIsSourceWritable( pub unsafe extern "C" fn BNWARPContainerGetSourcePath( container: *mut BNWARPContainer, source: *const BNWARPSource, -) -> *const c_char { +) -> *mut BNPath { let arc_container = ManuallyDrop::new(Arc::from_raw(container)); let Ok(container) = arc_container.read() else { - return std::ptr::null(); + return std::ptr::null_mut(); }; let source = unsafe { *source }; match container.source_path(&source) { Ok(path) => { - let path = path.to_string(); - // NOTE: Leak the source path to be freed by BNFreeString - BnString::into_raw(path.into()) + let path: &Path = path.as_ref(); + BnPath::into_raw(BnPath::new(path)) } - Err(_) => std::ptr::null(), + Err(_) => std::ptr::null_mut(), } } diff --git a/plugins/warp/src/plugin/ffi/file.rs b/plugins/warp/src/plugin/ffi/file.rs index 3abfe5aaaa..667be3049f 100644 --- a/plugins/warp/src/plugin/ffi/file.rs +++ b/plugins/warp/src/plugin/ffi/file.rs @@ -1,7 +1,7 @@ use crate::plugin::ffi::{BNWARPFunction, BNWARPTarget, BNWARPType}; use binaryninja::data_buffer::DataBuffer; -use binaryninjacore_sys::BNDataBuffer; -use std::ffi::c_char; +use binaryninja::string::BnString; +use binaryninjacore_sys::{BNDataBuffer, BNPath}; use std::mem::ManuallyDrop; use std::sync::Arc; use warp::chunk::ChunkKind; @@ -41,12 +41,12 @@ pub type BNWARPChunk = warp::chunk::Chunk<'static>; // TODO: From bytes as well. #[no_mangle] -pub unsafe extern "C" fn BNWARPNewFileFromPath(path: *mut c_char) -> *mut BNWARPFile { - let path_cstr = unsafe { std::ffi::CStr::from_ptr(path) }; - let Ok(path) = path_cstr.to_str() else { +pub unsafe extern "C" fn BNWARPNewFileFromPath(path: *mut BNPath) -> *mut BNWARPFile { + if path.is_null() { return std::ptr::null_mut(); - }; - let Ok(bytes) = std::fs::read(path) else { + } + let path = unsafe { BnString::path_buf_from_raw(path) }; + let Ok(bytes) = std::fs::read(&path) else { return std::ptr::null_mut(); }; let Some(file) = WarpFile::from_owned_bytes(bytes) else { diff --git a/plugins/warp/src/plugin/ffi/processor.rs b/plugins/warp/src/plugin/ffi/processor.rs index 83b47f5a32..361284cd5d 100644 --- a/plugins/warp/src/plugin/ffi/processor.rs +++ b/plugins/warp/src/plugin/ffi/processor.rs @@ -6,10 +6,9 @@ use crate::processor::{ use binaryninja::binary_view::BinaryView; use binaryninja::project::file::ProjectFile; use binaryninja::project::Project; -use binaryninjacore_sys::{BNBinaryView, BNProject, BNProjectFile}; -use std::ffi::{c_char, CStr, CString}; +use binaryninja::string::{BnPath, BnString}; +use binaryninjacore_sys::{BNBinaryView, BNFreePath, BNPath, BNProject, BNProjectFile}; use std::mem::ManuallyDrop; -use std::path::PathBuf; use std::ptr::NonNull; use std::sync::Arc; use warp::chunk::CompressionType; @@ -21,9 +20,9 @@ pub struct BNWARPProcessorState { cancelled: bool, unprocessed_files_count: usize, processed_files_count: usize, - analyzing_files: *mut *mut c_char, + analyzing_files: *mut *mut BNPath, analyzing_files_count: usize, - processing_files: *mut *mut c_char, + processing_files: *mut *mut BNPath, processing_files_count: usize, } @@ -44,11 +43,13 @@ pub unsafe extern "C" fn BNWARPNewProcessor( #[no_mangle] pub unsafe extern "C" fn BNWARPProcessorAddPath( processor: *mut BNWARPProcessor, - path: *const c_char, + path: *mut BNPath, ) { let mut processor = ManuallyDrop::new(Box::from_raw(processor)); - let path_cstr = unsafe { CStr::from_ptr(path) }; - let path = PathBuf::from(path_cstr.to_str().unwrap()); + if path.is_null() { + return; + } + let path = unsafe { BnString::path_buf_from_raw(path) }; // TODO: Not thread safe. processor.add_entry(WarpFileProcessorEntry::Path(path)); } @@ -140,14 +141,14 @@ pub unsafe extern "C" fn BNWARPProcessorGetState( let raw_analyzing_files: Box<[_]> = analyzing_files .into_iter() - .map(|p| CString::new(p.to_str().unwrap()).unwrap().into_raw()) + .map(|p| BnPath::into_raw(BnPath::new(&p))) .collect(); let raw_analyzing_files_count = raw_analyzing_files.len(); let raw_analyzing_files_ptr = Box::into_raw(raw_analyzing_files); let raw_processing_files: Box<[_]> = processing_files .into_iter() - .map(|p| CString::new(p.to_str().unwrap()).unwrap().into_raw()) + .map(|p| BnPath::into_raw(BnPath::new(&p))) .collect(); let raw_processing_files_count = raw_processing_files.len(); let raw_processing_files_ptr = Box::into_raw(raw_processing_files); @@ -158,9 +159,9 @@ pub unsafe extern "C" fn BNWARPProcessorGetState( .load(std::sync::atomic::Ordering::Relaxed), unprocessed_files_count, processed_files_count, - analyzing_files: raw_analyzing_files_ptr as *mut *mut c_char, + analyzing_files: raw_analyzing_files_ptr as *mut *mut BNPath, analyzing_files_count: raw_analyzing_files_count, - processing_files: raw_processing_files_ptr as *mut *mut c_char, + processing_files: raw_processing_files_ptr as *mut *mut BNPath, processing_files_count: raw_processing_files_count, } } @@ -176,12 +177,12 @@ pub unsafe extern "C" fn BNWARPFreeProcessorState(state: BNWARPProcessorState) { std::ptr::slice_from_raw_parts_mut(state.analyzing_files, state.analyzing_files_count); let a_files_boxed = unsafe { Box::from_raw(a_files_ptr) }; for path in a_files_boxed.iter() { - let _ = CString::from_raw(*path); + unsafe { BNFreePath(*path) }; } let p_files_ptr = std::ptr::slice_from_raw_parts_mut(state.processing_files, state.processing_files_count); let p_files_boxed = unsafe { Box::from_raw(p_files_ptr) }; for path in p_files_boxed.iter() { - let _ = CString::from_raw(*path); + unsafe { BNFreePath(*path) }; } } diff --git a/plugins/warp/src/plugin/load.rs b/plugins/warp/src/plugin/load.rs index 9ec6eaed16..5311ae84ad 100644 --- a/plugins/warp/src/plugin/load.rs +++ b/plugins/warp/src/plugin/load.rs @@ -31,8 +31,7 @@ impl LoadFileField { pub fn from_form(form: &Form) -> Option { let field = form.get_field_with_name("File Path")?; - let field_value = field.try_value_string()?; - Some(PathBuf::from(field_value)) + field.try_value_path() } } diff --git a/plugins/warp/ui/plugin.cpp b/plugins/warp/ui/plugin.cpp index 93900ed9e2..9f1234b039 100644 --- a/plugins/warp/ui/plugin.cpp +++ b/plugins/warp/ui/plugin.cpp @@ -6,6 +6,7 @@ #include "shared/processordialog.h" #include "shared/fetchdialog.h" #include "shared/file.h" +#include "pathhelpers.h" #include #include @@ -267,7 +268,7 @@ void RegisterCommands() dlg->show(); }); RegisterPluginAction("View File", [](const UIActionContext& context) { - std::string path; + std::filesystem::path path; if (!GetOpenFileNameInput(path, "Open WARP File", "*.warp")) return; auto file = Warp::File::FromPath(path); @@ -275,7 +276,7 @@ void RegisterCommands() return; auto* dlg = new QDialog(context.widget); - dlg->setWindowTitle(QString::fromStdString("WARP File: " + path)); + dlg->setWindowTitle(QString::fromStdString("WARP File: " + Path::PrintablePath(path))); dlg->setAttribute(Qt::WA_DeleteOnClose); auto* layout = new QVBoxLayout(dlg); diff --git a/plugins/warp/ui/shared/processordialog.cpp b/plugins/warp/ui/shared/processordialog.cpp index 9e602b79df..8334726ce6 100644 --- a/plugins/warp/ui/shared/processordialog.cpp +++ b/plugins/warp/ui/shared/processordialog.cpp @@ -1,6 +1,7 @@ #include "processordialog.h" #include "commitdialog.h" #include "misc.h" +#include "pathhelpers.h" #include "selectprojectfilesdialog.h" #include @@ -220,7 +221,7 @@ void ProcessorDialog::onAddBinaryView(Ref view) ToProcessEntry item; item.type = ToProcessEntry::ViewMode; item.view = view; - item.displayName = QString("View: %1").arg(QString::fromStdString(view->GetFile()->GetFilename())); + item.displayName = QString("View: %1").arg(QString::fromStdString(Path::PrintablePath(view->GetFile()->GetFilename()))); m_toProcess.push_back(item); m_entryList->addItem(item.displayName); @@ -404,10 +405,10 @@ void ProcessorDialog::onUpdateState() m_stateList->clear(); - auto addToList = [&](const std::vector& files, const QString& prefix) { + auto addToList = [&](const std::vector& files, const QString& prefix) { for (auto it = files.rbegin(); it != files.rend(); ++it) { - auto* item = new QListWidgetItem(prefix + QString::fromStdString(*it)); + auto* item = new QListWidgetItem(prefix + QString::fromStdString(BinaryNinja::Path::PrintablePath(*it))); item->setTextAlignment(Qt::AlignCenter); m_stateList->addItem(item); } diff --git a/plugins/warp/ui/shared/source.h b/plugins/warp/ui/shared/source.h index 7dad933380..41dba08449 100644 --- a/plugins/warp/ui/shared/source.h +++ b/plugins/warp/ui/shared/source.h @@ -8,6 +8,7 @@ #include "theme.h" #include "warp.h" +#include "pathhelpers.h" class WarpSourcesModel final : public QAbstractTableModel { @@ -39,7 +40,8 @@ class WarpSourcesModel final : public QAbstractTableModel for (const auto& src : m_container->GetSources()) { QString guid = QString::fromStdString(src.ToString()); - QString path = QString::fromStdString(m_container->SourcePath(src).value_or(std::string {})); + auto sourcePath = m_container->SourcePath(src); + QString path = sourcePath ? QString::fromStdString(BinaryNinja::Path::PrintablePath(*sourcePath)) : QString {}; bool writable = m_container->IsSourceWritable(src); bool uncommitted = m_container->IsSourceUncommitted(src); m_rows.push_back({guid, path, writable, uncommitted}); @@ -113,4 +115,4 @@ class WarpSourcesView : public QTableView private: WarpSourcesModel* m_model = nullptr; Warp::Ref m_container; -}; \ No newline at end of file +}; diff --git a/project.cpp b/project.cpp index cc7d65de0f..20b7fea244 100644 --- a/project.cpp +++ b/project.cpp @@ -20,6 +20,7 @@ #include #include "binaryninjaapi.h" #include "binaryninjacore.h" +#include "pathhelpers.h" using namespace BinaryNinja; @@ -216,18 +217,20 @@ Project::Project(BNProject* project) } -Ref Project::CreateProject(const std::string& path, const std::string& name) +Ref Project::CreateProject(const std::filesystem::path& path, const std::string& name) { - BNProject* bnproj = BNCreateProject(path.c_str(), name.c_str()); + Path::APIObject corePath(path); + BNProject* bnproj = BNCreateProject(corePath, name.c_str()); if (!bnproj) return nullptr; return new Project(bnproj); } -Ref Project::OpenProject(const std::string& path) +Ref Project::OpenProject(const std::filesystem::path& path) { - BNProject* bnproj = BNOpenProject(path.c_str()); + Path::APIObject corePath(path); + BNProject* bnproj = BNOpenProject(corePath); if (!bnproj) return nullptr; return new Project(bnproj); @@ -277,12 +280,9 @@ bool Project::IsOpen() const } -std::string Project::GetPath() const +std::filesystem::path Project::GetPath() const { - char* path = BNProjectGetPath(m_object); - std::string result = path; - BNFreeString(path); - return result; + return Path::PathFromCore(BNProjectGetPath(m_object)); } std::string Project::GetFilePathInProject(const Ref& file) const @@ -346,12 +346,13 @@ bool Project::RemoveMetadata(const std::string& key) } -Ref Project::CreateFolderFromPath(const std::string& path, Ref parent, const std::string& description, +Ref Project::CreateFolderFromPath(const std::filesystem::path& path, Ref parent, const std::string& description, const ProgressFunction& progressCallback) { ProgressContext cb; cb.callback = progressCallback; - BNProjectFolder* folder = BNProjectCreateFolderFromPath(m_object, path.c_str(), parent ? parent->m_object : nullptr, description.c_str(), &cb, ProgressCallback); + Path::APIObject corePath(path); + BNProjectFolder* folder = BNProjectCreateFolderFromPath(m_object, corePath, parent ? parent->m_object : nullptr, description.c_str(), &cb, ProgressCallback); if (folder == nullptr) return nullptr; return new ProjectFolder(folder); @@ -416,22 +417,24 @@ bool Project::DeleteFolder(Ref folder, const ProgressFunction& pr } -Ref Project::CreateFileFromPath(const std::string& path, Ref folder, const std::string& name, const std::string& description, const ProgressFunction& progressCallback) +Ref Project::CreateFileFromPath(const std::filesystem::path& path, Ref folder, const std::string& name, const std::string& description, const ProgressFunction& progressCallback) { ProgressContext cb; cb.callback = progressCallback; - BNProjectFile* file = BNProjectCreateFileFromPath(m_object, path.c_str(), folder ? folder->m_object : nullptr, name.c_str(), description.c_str(), &cb, ProgressCallback); + Path::APIObject corePath(path); + BNProjectFile* file = BNProjectCreateFileFromPath(m_object, corePath, folder ? folder->m_object : nullptr, name.c_str(), description.c_str(), &cb, ProgressCallback); if (file == nullptr) return nullptr; return new ProjectFile(file); } -Ref Project::CreateFileFromPathUnsafe(const std::string& path, Ref folder, const std::string& name, const std::string& description, const std::string& id, int64_t creationTimestamp, const ProgressFunction& progressCallback) +Ref Project::CreateFileFromPathUnsafe(const std::filesystem::path& path, Ref folder, const std::string& name, const std::string& description, const std::string& id, int64_t creationTimestamp, const ProgressFunction& progressCallback) { ProgressContext cb; cb.callback = progressCallback; - BNProjectFile* file = BNProjectCreateFileFromPathUnsafe(m_object, path.c_str(), folder ? folder->m_object : nullptr, name.c_str(), description.c_str(), id.c_str(), creationTimestamp, &cb, ProgressCallback); + Path::APIObject corePath(path); + BNProjectFile* file = BNProjectCreateFileFromPathUnsafe(m_object, corePath, folder ? folder->m_object : nullptr, name.c_str(), description.c_str(), id.c_str(), creationTimestamp, &cb, ProgressCallback); if (file == nullptr) return nullptr; return new ProjectFile(file); @@ -486,9 +489,10 @@ Ref Project::GetFileById(const std::string& id) const } -Ref Project::GetFileByPathOnDisk(const std::string& path) const +Ref Project::GetFileByPathOnDisk(const std::filesystem::path& path) const { - BNProjectFile* file = BNProjectGetFileByPathOnDisk(m_object, path.c_str()); + Path::APIObject corePath(path); + BNProjectFile* file = BNProjectGetFileByPathOnDisk(m_object, corePath); if (file == nullptr) return nullptr; return new ProjectFile(file); @@ -591,12 +595,9 @@ Ref ProjectFile::GetProject() const } -std::string ProjectFile::GetPathOnDisk() const +std::filesystem::path ProjectFile::GetPathOnDisk() const { - char* path = BNProjectFileGetPathOnDisk(m_object); - std::string result = path; - BNFreeString(path); - return result; + return Path::PathFromCore(BNProjectFileGetPathOnDisk(m_object)); } std::string ProjectFile::GetPathInProject() const @@ -668,9 +669,10 @@ bool ProjectFile::SetFolder(Ref folder) } -bool ProjectFile::Export(const std::string& destination) const +bool ProjectFile::Export(const std::filesystem::path& destination) const { - return BNProjectFileExport(m_object, destination.c_str()); + Path::APIObject coreDestination(destination); + return BNProjectFileExport(m_object, coreDestination); } @@ -788,11 +790,12 @@ bool ProjectFolder::SetParent(Ref parent) } -bool ProjectFolder::Export(const std::string& destination, const ProgressFunction& progressCallback) const +bool ProjectFolder::Export(const std::filesystem::path& destination, const ProgressFunction& progressCallback) const { ProgressContext cb; cb.callback = progressCallback; - return BNProjectFolderExport(m_object, destination.c_str(), &cb, ProgressCallback); + Path::APIObject coreDestination(destination); + return BNProjectFolderExport(m_object, coreDestination, &cb, ProgressCallback); } diff --git a/python/binaryview.py b/python/binaryview.py index 5a63248fe3..edd1e764da 100644 --- a/python/binaryview.py +++ b/python/binaryview.py @@ -25,6 +25,7 @@ import traceback import ctypes import abc +import contextlib import json import pprint import inspect @@ -455,10 +456,10 @@ def component_data_var_added(self, view: 'BinaryView', _component: component.Com def component_data_var_removed(self, view: 'BinaryView', _component: component.Component, var: 'DataVariable'): pass - def type_archive_attached(self, view: 'BinaryView', id: str, path: str): + def type_archive_attached(self, view: 'BinaryView', id: str, path: os.PathLike): pass - def type_archive_detached(self, view: 'BinaryView', id: str, path: str): + def type_archive_detached(self, view: 'BinaryView', id: str, path: os.PathLike): pass def type_archive_connected(self, view: 'BinaryView', archive: 'typearchive.TypeArchive'): @@ -1365,15 +1366,15 @@ def _component_data_variable_removed(self, ctxt, view: core.BNBinaryView, _compo except Exception: log_error_for_exception("Unhandled Python exception in BinaryDataNotificationCallbacks._component_data_variable_removed") - def _type_archive_attached(self, ctxt, view: core.BNBinaryView, id: ctypes.c_char_p, path: ctypes.c_char_p): + def _type_archive_attached(self, ctxt, view: core.BNBinaryView, id: ctypes.c_char_p, path: core.BNPathHandle): try: - self._notify.type_archive_attached(self._view, core.pyNativeStr(id), core.pyNativeStr(path)) + self._notify.type_archive_attached(self._view, core.pyNativeStr(id), core.path_to_native_path(path, free=False)) except Exception: log_error_for_exception("Unhandled Python exception in BinaryDataNotificationCallbacks._type_archive_attached") - def _type_archive_detached(self, ctxt, view: core.BNBinaryView, id: ctypes.c_char_p, path: ctypes.c_char_p): + def _type_archive_detached(self, ctxt, view: core.BNBinaryView, id: ctypes.c_char_p, path: core.BNPathHandle): try: - self._notify.type_archive_detached(self._view, core.pyNativeStr(id), core.pyNativeStr(path)) + self._notify.type_archive_detached(self._view, core.pyNativeStr(id), core.path_to_native_path(path, free=False)) except Exception: log_error_for_exception("Unhandled Python exception in BinaryDataNotificationCallbacks._type_archive_detached") @@ -3465,9 +3466,10 @@ def open(src, file_metadata=None) -> Optional['BinaryView']: file_metadata = filemetadata.FileMetadata() view = core.BNCreateBinaryDataViewFromFile(file_metadata.handle, src._cb) else: + src_path = os.fspath(src) if file_metadata is None: - file_metadata = filemetadata.FileMetadata(str(src)) - view = core.BNCreateBinaryDataViewFromFilename(file_metadata.handle, str(src)) + file_metadata = filemetadata.FileMetadata(src_path) + view = core.BNCreateBinaryDataViewFromFilename(file_metadata.handle, src_path) if view is None: return None return BinaryView(file_metadata=file_metadata, handle=view) @@ -3554,13 +3556,14 @@ def load(source: Union[str, bytes, bytearray, 'databuffer.DataBuffer', 'os.PathL else: progress_cfunc = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.c_ulonglong, ctypes.c_ulonglong)(lambda ctxt, cur, total: progress_func(cur, total)) - if isinstance(source, os.PathLike): - source = str(source) + source_is_pathlike = isinstance(source, os.PathLike) + if source_is_pathlike: + source = os.fspath(source) if isinstance(source, BinaryView): handle = core.BNLoadBinaryView(source.handle, update_analysis, json.dumps(options), progress_cfunc, None) elif isinstance(source, project.ProjectFile): handle = core.BNLoadProjectFile(source._handle, update_analysis, json.dumps(options), progress_cfunc, None) - elif isinstance(source, str): + elif isinstance(source, str) or source_is_pathlike: handle = core.BNLoadFilename(source, update_analysis, json.dumps(options), progress_cfunc, None) elif isinstance(source, bytes) or isinstance(source, bytearray) or isinstance(source, databuffer.DataBuffer): raw_view = BinaryView.new(source) @@ -4093,19 +4096,19 @@ def type_libraries(self) -> List['typelibrary.TypeLibrary']: core.BNFreeTypeLibraryList(libraries, count.value) @property - def attached_type_archives(self) -> Mapping['str', 'str']: + def attached_type_archives(self) -> Mapping['str', os.PathLike]: """All attached type archive ids and paths (read-only)""" ids = ctypes.POINTER(ctypes.c_char_p)() - paths = ctypes.POINTER(ctypes.c_char_p)() + paths = ctypes.POINTER(core.BNPathHandle)() count = core.BNBinaryViewGetTypeArchives(self.handle, ids, paths) result = {} try: for i in range(0, count): - result[core.pyNativeStr(ids[i])] = core.pyNativeStr(paths[i]) + result[core.pyNativeStr(ids[i])] = core.path_to_native_path(paths[i], free=False) return result finally: core.BNFreeStringList(ids, count) - core.BNFreeStringList(paths, count) + core.BNFreePathList(paths, count) @property def connected_type_archives(self) -> List['typearchive.TypeArchive']: @@ -5365,7 +5368,7 @@ def is_offset_readonly_semantics(self, addr: int) -> bool: """ return core.BNIsOffsetReadOnlySemantics(self.handle, addr) - def save(self, dest: Union['fileaccessor.FileAccessor', str]) -> bool: + def save(self, dest: Union['fileaccessor.FileAccessor', str, bytes, os.PathLike]) -> bool: """ ``save`` saves the original binary file to the provided destination ``dest`` along with any modifications. @@ -5377,7 +5380,7 @@ def save(self, dest: Union['fileaccessor.FileAccessor', str]) -> bool: """ if isinstance(dest, fileaccessor.FileAccessor): return core.BNSaveToFile(self.handle, dest._cb) - return core.BNSaveToFilename(self.handle, str(dest)) + return core.BNSaveToFilename(self.handle, os.fspath(dest)) def register_notification(self, notify: BinaryDataNotification) -> None: """ @@ -6774,7 +6777,7 @@ def add_expression_parser_magic_values(self, names: List[str], values: List[int] names_buf = (ctypes.c_char_p * len(names))() for i in range(0, len(names)): - names_buf[i] = names[i].encode('charmap') + names_buf[i] = core.cstr(names[i]) values_buf = (ctypes.c_ulonglong * len(values))() for i in range(0, len(values)): @@ -6796,7 +6799,7 @@ def remove_expression_parser_magic_values(self, names: List[str]) -> None: names_buf = (ctypes.c_char_p * len(names))() for i in range(0, len(names)): - names_buf[i] = names[i].encode('charmap') + names_buf[i] = core.cstr(names[i]) core.BNRemoveExpressionParserMagicValues(self.handle, names_buf, len(names)) @@ -8521,16 +8524,16 @@ def parse_types_from_string(self, text: str, options: Optional[List[str]] = None for (i, s) in enumerate(options): options_cpp[i] = core.cstr(s) - include_dirs_cpp = (ctypes.c_char_p * len(include_dirs))() - for (i, s) in enumerate(include_dirs): - include_dirs_cpp[i] = core.cstr(s) - errors = ctypes.c_char_p() type_list = core.BNQualifiedNameList() type_list.count = 0 - if not core.BNParseTypesString( + with contextlib.ExitStack() as stack: + include_dir_paths = [stack.enter_context(core.core_path(path)) for path in include_dirs] + include_dirs_cpp = (core.BNPathHandle * len(include_dir_paths))(*include_dir_paths) + ok = core.BNParseTypesString( self.handle, text, options_cpp, len(options), include_dirs_cpp, - len(include_dirs), parse, errors, type_list, import_dependencies): + len(include_dir_paths), parse, errors, type_list, import_dependencies) + if not ok: assert errors.value is not None, "core.BNParseTypesString returned errors set to None" error_str = errors.value.decode("utf-8") core.free_string(errors) diff --git a/python/debuginfo.py b/python/debuginfo.py index ec676b7e05..cd1d6c6a60 100644 --- a/python/debuginfo.py +++ b/python/debuginfo.py @@ -515,7 +515,7 @@ def add_type(self, name: str, new_type: '_types.Type', components: Optional[List components = [] component_list = (ctypes.c_char_p * len(components))() for i in range(0, len(components)): - component_list[i] = str(components[i]).encode('charmap') + component_list[i] = core.cstr(str(components[i])) if isinstance(new_type, _types.Type): return core.BNAddDebugType(self.handle, name, new_type.handle, component_list, len(components)) @@ -550,7 +550,7 @@ def add_function(self, new_func: DebugFunctionInfo) -> bool: return NotImplemented component_list = (ctypes.c_char_p * len(components))() for c in range(0, len(components)): - component_list[c] = str(components[c]).encode('charmap') + component_list[c] = core.cstr(str(components[c])) func_info.components = component_list func_info.componentN = len(components) @@ -587,7 +587,7 @@ def add_data_variable(self, address: int, new_type: '_types.Type', name: Optiona components = [] component_list = (ctypes.c_char_p * len(components))() for i in range(0, len(components)): - component_list[i] = str(components[i]).encode('charmap') + component_list[i] = core.cstr(str(components[i])) if isinstance(address, int) and isinstance(new_type, _types.Type): return core.BNAddDebugDataVariable(self.handle, address, new_type.handle, name, component_list, len(components)) diff --git a/python/filemetadata.py b/python/filemetadata.py index de3472e315..37d56772b5 100644 --- a/python/filemetadata.py +++ b/python/filemetadata.py @@ -18,9 +18,10 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. import contextlib +import os import traceback import ctypes -from typing import Any, Callable, Optional, List, Generator +from typing import Any, Callable, Optional, List, Generator, Union # Binary Ninja Components import binaryninja @@ -36,6 +37,7 @@ ProgressFuncType = Callable[[int, int], bool] ViewName = str +AsPath = Union[os.PathLike, str, bytes] class NavigationHandler: @@ -128,7 +130,7 @@ class FileMetadata: _associated_data = {} - def __init__(self, filename: Optional[str] = None, handle: Optional[core.BNFileMetadataHandle] = None): + def __init__(self, filename: Optional[AsPath] = None, handle: Optional[core.BNFileMetadataHandle] = None): if handle is not None: _type = core.BNFileMetadataHandle _handle = ctypes.cast(handle, _type) @@ -136,7 +138,7 @@ def __init__(self, filename: Optional[str] = None, handle: Optional[core.BNFileM binaryninja._init_plugins() _handle = core.BNCreateFileMetadata() if filename is not None: - core.BNSetFilename(_handle, str(filename)) + core.BNSetFilename(_handle, os.fspath(filename)) self._nav: Optional[NavigationHandler] = None assert _handle is not None self.handle = _handle @@ -196,8 +198,8 @@ def original_filename(self) -> str: return core.BNGetOriginalFilename(self.handle) @original_filename.setter - def original_filename(self, value: str) -> None: - core.BNSetOriginalFilename(self.handle, str(value)) + def original_filename(self, value: AsPath) -> None: + core.BNSetOriginalFilename(self.handle, os.fspath(value)) @property def filename(self) -> str: @@ -205,8 +207,8 @@ def filename(self) -> str: return core.BNGetFilename(self.handle) @filename.setter - def filename(self, value: str) -> None: - core.BNSetFilename(self.handle, str(value)) + def filename(self, value: AsPath) -> None: + core.BNSetFilename(self.handle, os.fspath(value)) @property def virtual_path(self) -> str: @@ -619,7 +621,7 @@ def navigate(self, view: ViewName, offset: int) -> bool: return core.BNNavigate(self.handle, str(view), offset) def create_database( - self, filename: str, progress_func: Optional[ProgressFuncType] = None, settings: Optional[SaveSettings] = None + self, filename: AsPath, progress_func: Optional[ProgressFuncType] = None, settings: Optional[SaveSettings] = None ) -> bool: """ ``create_database`` writes the current database (.bndb) out to the specified file. @@ -644,12 +646,13 @@ def create_database( _settings = settings.handle assert self.raw is not None, "BinaryView.create_database called when raw view is None" + filename = os.fspath(filename) if progress_func is None: - return core.BNCreateDatabase(self.raw.handle, str(filename), _settings) + return core.BNCreateDatabase(self.raw.handle, filename, _settings) else: _progress_func = progress_func return core.BNCreateDatabaseWithProgress( - self.raw.handle, str(filename), None, + self.raw.handle, filename, None, ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.c_ulonglong, ctypes.c_ulonglong)(lambda ctxt, cur, total: _progress_func(cur, total)), _settings ) diff --git a/python/generator.cpp b/python/generator.cpp index 82eed91ba3..fc530fe37d 100644 --- a/python/generator.cpp +++ b/python/generator.cpp @@ -64,6 +64,22 @@ map g_pythonKeywordReplacements = { {"yield", "yield_"}, }; +static bool IsPathPointerType(Type* type) +{ + if (type->GetClass() != PointerTypeClass) + return false; + auto child = type->GetChildType(); + return (child->GetClass() == NamedTypeReferenceClass) && + (child->GetNamedTypeReference()->GetName().GetString() == "BNPath"); +} + + +static bool IsRawPathFunction(const string& name) +{ + return (name == "BNCreatePath") || (name == "BNGetPathData") || (name == "BNFreePath") + || (name == "BNFreePathList"); +} + void OutputType(FILE* out, Type* type, bool isReturnType = false, bool isCallback = false, bool isTypeHint = false) { @@ -206,6 +222,11 @@ void OutputSwizzledType(FILE* out, Type* type, bool isTypeHint = false) } break; case PointerTypeClass: + if (IsPathPointerType(type)) + { + fprintf(out, "Optional[os.PathLike]"); + break; + } if (type->GetChildType()->GetClass() == VoidTypeClass) { fprintf(out, "Optional[ctypes.c_void_p]"); @@ -271,7 +292,7 @@ int main(int argc, char* argv[]) auto arch = new CoreArchitecture(BNGetNativeTypeParserArchitecture()); // Enable ephemeral settings - Settings::Instance()->LoadSettingsFile(""); + Settings::Instance()->LoadSettingsFile(); Settings::Instance()->Set("analysis.types.parserName", "ClangTypeParser"); bool ok = arch->GetStandalonePlatform()->ParseTypesFromSourceFile(argv[1], types, vars, funcs, errors); @@ -283,7 +304,7 @@ int main(int argc, char* argv[]) FILE* out = fopen(argv[2], "w"); FILE* enums = fopen(argv[3], "w"); - fprintf(out, "import ctypes, os\n\n"); + fprintf(out, "import ctypes, os, pathlib\n\n"); fprintf(out, "from typing import Optional, AnyStr\n"); fprintf(out, "from .enums import *"); @@ -324,16 +345,80 @@ int main(int argc, char* argv[]) fprintf(out, "def free_string(value:ctypes.c_char_p) -> None:\n"); fprintf(out, " BNFreeString(ctypes.cast(value, ctypes.POINTER(ctypes.c_byte)))\n\n"); + fprintf(out, "def free_path(value) -> None:\n"); + fprintf(out, " BNFreePath(value)\n\n"); + fprintf(out, "class BinaryNinjaPath(type(pathlib.Path())):\n"); + fprintf(out, " def __len__(self):\n"); + fprintf(out, " return len(os.fspath(self))\n"); + fprintf(out, " def __contains__(self, value):\n"); + fprintf(out, " return value in os.fspath(self)\n"); + fprintf(out, " def __getitem__(self, value):\n"); + fprintf(out, " return os.fspath(self)[value]\n"); + fprintf(out, " def __add__(self, value):\n"); + fprintf(out, " return os.fspath(self) + value\n"); + fprintf(out, " def __radd__(self, value):\n"); + fprintf(out, " return value + os.fspath(self)\n"); + fprintf(out, " def __getattr__(self, name):\n"); + fprintf(out, " if name.startswith('_'):\n"); + fprintf(out, " raise AttributeError(name)\n"); + fprintf(out, " return getattr(os.fspath(self), name)\n\n"); + fprintf(out, "class BinaryNinjaEmptyPath(str):\n"); + fprintf(out, " def __new__(cls):\n"); + fprintf(out, " return super().__new__(cls, \"\")\n"); + fprintf(out, " def __fspath__(self):\n"); + fprintf(out, " return \"\"\n\n"); + fprintf(out, "def path_to_core(value):\n"); + fprintf(out, " if value is None:\n"); + fprintf(out, " return None\n"); + fprintf(out, " native = os.fspath(value)\n"); + fprintf(out, " if core_platform == \"Windows\" or core_platform.find(\"CYGWIN_NT\") == 0:\n"); + fprintf(out, " if isinstance(native, bytes):\n"); + fprintf(out, " native = os.fsdecode(native)\n"); + fprintf(out, " buffer = ctypes.create_unicode_buffer(native)\n"); + fprintf(out, " return BNCreatePath(ctypes.cast(buffer, ctypes.c_void_p), len(buffer) - 1)\n"); + fprintf(out, " if isinstance(native, str):\n"); + fprintf(out, " native = os.fsencode(native)\n"); + fprintf(out, " buffer = ctypes.create_string_buffer(native)\n"); + fprintf(out, " return BNCreatePath(ctypes.cast(buffer, ctypes.c_void_p), len(native))\n\n"); + fprintf(out, "class core_path:\n"); + fprintf(out, " def __init__(self, value):\n"); + fprintf(out, " self.value = path_to_core(value)\n"); + fprintf(out, " def __enter__(self):\n"); + fprintf(out, " return self.value\n"); + fprintf(out, " def __exit__(self, exc_type, exc_value, traceback):\n"); + fprintf(out, " if self.value:\n"); + fprintf(out, " BNFreePath(self.value)\n\n"); + fprintf(out, "def path_to_native_path(value, free: bool = True) -> Optional[pathlib.Path]:\n"); + fprintf(out, " if not value:\n"); + fprintf(out, " return None\n"); + fprintf(out, " size = ctypes.c_ulonglong()\n"); + fprintf(out, " data = BNGetPathData(value, size)\n"); + fprintf(out, " try:\n"); + fprintf(out, " if size.value == 0:\n"); + fprintf(out, " return BinaryNinjaEmptyPath()\n"); + fprintf(out, " if not data:\n"); + fprintf(out, " return None\n"); + fprintf(out, " if core_platform == \"Windows\" or core_platform.find(\"CYGWIN_NT\") == 0:\n"); + fprintf(out, " return BinaryNinjaPath(ctypes.wstring_at(data, size.value))\n"); + fprintf(out, " return BinaryNinjaPath(os.fsdecode(ctypes.string_at(data, size.value)))\n"); + fprintf(out, " finally:\n"); + fprintf(out, " if free:\n"); + fprintf(out, " BNFreePath(value)\n\n"); // Create type objects fprintf(out, "# Type definitions\n"); fprintf(out, "BNProgressFunction = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.c_ulonglong, ctypes.c_ulonglong)\n"); + fprintf(out, "class BNPath(ctypes.Structure):\n"); + fprintf(out, " pass\n\n\n"); + fprintf(out, "BNPathHandle = ctypes.POINTER(BNPath)\n\n\n"); for (auto& i : types) { string name; if (i.first.size() != 1) continue; name = i.first[0]; + if (name == "BNPath") + continue; if (i.second->GetClass() == StructureTypeClass) { fprintf(out, "class %s(ctypes.Structure):\n", name.c_str()); @@ -496,15 +581,18 @@ int main(int argc, char* argv[]) && i.second->GetChildType()->GetChildType()->GetClass() == IntegerTypeClass && i.second->GetChildType()->GetChildType()->GetWidth() == 1 && i.second->GetChildType()->GetChildType()->IsSigned(); + bool rawPathFunction = IsRawPathFunction(name); + bool rawPathResult = rawPathFunction && IsPathPointerType(i.second->GetChildType().GetValue()); + bool pathResult = !rawPathResult && IsPathPointerType(i.second->GetChildType().GetValue()); // Pointer returns will be automatically wrapped to return None on null pointer - bool pointerResult = (i.second->GetChildType()->GetClass() == PointerTypeClass); + bool pointerResult = (i.second->GetChildType()->GetClass() == PointerTypeClass) && !pathResult; // Enum returns will automatically cast to the enum type bool enumResult = (i.second->GetChildType()->GetClass() == NamedTypeReferenceClass && i.second->GetChildType()->GetNamedTypeReference()->GetTypeReferenceClass() == EnumNamedTypeClass); - // From python -> C python3 requires str -> str.encode('charmap') + // From python -> C python3 requires str -> bytes. Use UTF-8 to match the C API string convention. bool swizzleArgs = true; - if (name == "BNFreeString") + if ((name == "BNFreeString") || rawPathFunction) swizzleArgs = false; bool callbackConvention = false; @@ -531,10 +619,10 @@ int main(int argc, char* argv[]) for (auto& j : i.second->GetParameters()) { fprintf(out, "\t\t"); - if (name == "BNFreeString") + if ((name == "BNFreeString") || rawPathFunction) { - // BNFreeString expects a pointer to a string allocated by the core, so do not use - // a c_char_p here, as that would be allocated by the Python runtime. This can + // Free functions expect pointers allocated by the core, so do not use a c_char_p + // here, as that would be allocated by the Python runtime. This can // be enforced by outputting like a return value. OutputType(out, j.type.GetValue(), true); } @@ -593,13 +681,19 @@ int main(int argc, char* argv[]) } } fprintf(out, "\n\t\t) -> "); - if (stringResult || pointerResult) + if (stringResult || pointerResult || pathResult) fprintf(out, "Optional['"); - OutputSwizzledType(out, i.second->GetChildType().GetValue(), true); - if (stringResult || pointerResult) + if (pathResult) + fprintf(out, "pathlib.Path"); + else if (rawPathResult) + OutputType(out, i.second->GetChildType().GetValue(), false, false, true); + else + OutputSwizzledType(out, i.second->GetChildType().GetValue(), true); + if (stringResult || pointerResult || pathResult) fprintf(out, "']"); fprintf(out, ":\n"); + vector> pathArgs; string stringArgFuncCall = funcName + "("; size_t argN = 0; for (auto& arg : i.second->GetParameters()) @@ -611,7 +705,13 @@ int main(int argc, char* argv[]) if (argName.empty()) argName = "arg" + to_string(argN); - if (swizzleArgs && (arg.type->GetClass() == PointerTypeClass) + if (swizzleArgs && IsPathPointerType(arg.type.GetValue())) + { + string pathArgName = "_path_arg" + to_string(argN); + pathArgs.emplace_back(argN, argName); + stringArgFuncCall += pathArgName + ", "; + } + else if (swizzleArgs && (arg.type->GetClass() == PointerTypeClass) && (arg.type->GetChildType()->GetClass() == IntegerTypeClass) && (arg.type->GetChildType()->GetWidth() == 1) && (arg.type->GetChildType()->IsSigned())) { @@ -627,37 +727,55 @@ int main(int argc, char* argv[]) stringArgFuncCall = stringArgFuncCall.substr(0, stringArgFuncCall.size() - 2); stringArgFuncCall += ")"; + string indent = "\t"; + if (!pathArgs.empty()) + { + fprintf(out, "\twith "); + for (size_t pathArgIndex = 0; pathArgIndex < pathArgs.size(); pathArgIndex++) + { + if (pathArgIndex > 0) + fprintf(out, ", "); + fprintf(out, "core_path(%s) as _path_arg%zu", pathArgs[pathArgIndex].second.c_str(), pathArgs[pathArgIndex].first); + } + fprintf(out, ":\n"); + indent = "\t\t"; + } + if (stringResult) { // Emit wrapper to get Python string and free native memory - fprintf(out, "\tresult = "); + fprintf(out, "%sresult = ", indent.c_str()); fprintf(out, "%s\n", stringArgFuncCall.c_str()); - fprintf(out, "\tcasted = ctypes.cast(result, ctypes.c_char_p).value\n"); - fprintf(out, "\tif casted is None:\n"); - fprintf(out, "\t\treturn None\n"); - fprintf(out, "\tstring = str(pyNativeStr(casted))\n"); - fprintf(out, "\tBNFreeString(result)\n"); - fprintf(out, "\treturn string\n"); + fprintf(out, "%scasted = ctypes.cast(result, ctypes.c_char_p).value\n", indent.c_str()); + fprintf(out, "%sif casted is None:\n", indent.c_str()); + fprintf(out, "%s\treturn None\n", indent.c_str()); + fprintf(out, "%sstring = str(pyNativeStr(casted))\n", indent.c_str()); + fprintf(out, "%sBNFreeString(result)\n", indent.c_str()); + fprintf(out, "%sreturn string\n", indent.c_str()); + } + else if (pathResult) + { + fprintf(out, "%sreturn path_to_native_path(%s)\n", indent.c_str(), stringArgFuncCall.c_str()); } else if (pointerResult) { // Emit wrapper to return None on null pointer - fprintf(out, "\tresult = "); + fprintf(out, "%sresult = ", indent.c_str()); fprintf(out, "%s\n", stringArgFuncCall.c_str()); - fprintf(out, "\tif not result:\n"); - fprintf(out, "\t\treturn None\n"); - fprintf(out, "\treturn result\n"); + fprintf(out, "%sif not result:\n", indent.c_str()); + fprintf(out, "%s\treturn None\n", indent.c_str()); + fprintf(out, "%sreturn result\n", indent.c_str()); } else if (enumResult) { // Emit wrapper to cast result to enum type - fprintf(out, "\treturn "); + fprintf(out, "%sreturn ", indent.c_str()); OutputSwizzledType(out, i.second->GetChildType().GetValue()); fprintf(out, "(%s)", stringArgFuncCall.c_str()); } else { - fprintf(out, "\treturn "); + fprintf(out, "%sreturn ", indent.c_str()); fprintf(out, "%s\n", stringArgFuncCall.c_str()); } fprintf(out, "\n\n"); diff --git a/python/interaction.py b/python/interaction.py index 02e75a71f2..e584f4f9a8 100644 --- a/python/interaction.py +++ b/python/interaction.py @@ -19,6 +19,7 @@ # IN THE SOFTWARE. import ctypes +import pathlib import traceback import webbrowser from typing import Optional, Callable, List @@ -32,6 +33,21 @@ from . import mainthread +def _track_core_path(obj, value): + path = core.path_to_core(value) + if not hasattr(obj, "_core_paths"): + obj._core_paths = [] + obj._core_paths.append(path) + return path + + +def _free_tracked_core_paths(obj): + for path in getattr(obj, "_core_paths", []): + if path: + core.BNFreePath(path) + obj._core_paths = [] + + class LabelField: """ ``LabelField`` adds a text label to the display. @@ -282,7 +298,7 @@ def _fill_core_struct(self, value): value.prompt = self._prompt choice_buf = (ctypes.c_char_p * len(self._choices))() for i in range(0, len(self._choices)): - choice_buf[i] = self._choices[i].encode('charmap') + choice_buf[i] = core.cstr(self._choices[i]) value.choices = choice_buf value.count = len(self._choices) value.hasDefault = self._default is not None @@ -330,9 +346,9 @@ def result(self, value: int): class OpenFileNameField: """ - ``OpenFileNameField`` prompts the user to specify a file name to open. Result is stored in self.result as a string. + ``OpenFileNameField`` prompts the user to specify a file name to open. Result is stored in self.result as a path. """ - def __init__(self, prompt: str, ext: str = "", default: Optional[str] = None): + def __init__(self, prompt: str, ext: str = "", default = None): self._prompt = prompt self._ext = ext self._default = default @@ -344,13 +360,16 @@ def _fill_core_struct(self, value): value.ext = self._ext value.hasDefault = self._default is not None if self._default is not None: - value.stringDefault = self._default + value.pathDefault = _track_core_path(self, self._default) def _fill_core_result(self, value): - value.stringResult = core.BNAllocString(str(self.result)) + value.pathResult = core.path_to_core(self.result) def _get_result(self, value): - self._result = value.stringResult + self._result = core.path_to_native_path(value.pathResult, free=False) + + def _free_core_struct(self): + _free_tracked_core_paths(self) @property def prompt(self): @@ -379,29 +398,32 @@ def result(self, value): class SaveFileNameField: """ - ``SaveFileNameField`` prompts the user to specify a file name to save. Result is stored in self.result as a string. + ``SaveFileNameField`` prompts the user to specify a file name to save. Result is stored in self.result as a path. """ - def __init__(self, prompt: str, ext: str = "", default_name: str = "", default: Optional[str] = None): + def __init__(self, prompt: str, ext: str = "", default_name = "", default = None): self._prompt = prompt self._ext = ext self._default_name = default_name self._default = default - self._result: Optional[str] = None + self._result = None def _fill_core_struct(self, value): value.type = FormInputFieldType.SaveFileNameFormField value.prompt = self._prompt value.ext = self._ext - value.defaultName = self._default_name + value.defaultName = _track_core_path(self, self._default_name) value.hasDefault = self._default is not None if self._default is not None: - value.stringDefault = self._default + value.pathDefault = _track_core_path(self, self._default) def _fill_core_result(self, value): - value.stringResult = core.BNAllocString(str(self._result)) + value.pathResult = core.path_to_core(self._result) def _get_result(self, value): - self._result = value.stringResult + self._result = core.path_to_native_path(value.pathResult, free=False) + + def _free_core_struct(self): + _free_tracked_core_paths(self) @property def prompt(self): @@ -432,16 +454,16 @@ def result(self): return self._result @result.setter - def result(self, value: Optional[str]): + def result(self, value): self._result = value class DirectoryNameField: """ ``DirectoryNameField`` prompts the user to specify a directory name to open. Result is stored in self.result as - a string. + a path. """ - def __init__(self, prompt: str, default_name: str = "", default: Optional[str] = None): + def __init__(self, prompt: str, default_name = "", default = None): self._prompt = prompt self._default_name = default_name self._default = default @@ -450,16 +472,19 @@ def __init__(self, prompt: str, default_name: str = "", default: Optional[str] = def _fill_core_struct(self, value): value.type = FormInputFieldType.DirectoryNameFormField value.prompt = self._prompt - value.defaultName = self._default_name + value.defaultName = _track_core_path(self, self._default_name) value.hasDefault = self._default is not None if self._default is not None: - value.stringDefault = self._default + value.pathDefault = _track_core_path(self, self._default) def _fill_core_result(self, value): - value.stringResult = core.BNAllocString(str(self._result)) + value.pathResult = core.path_to_core(self._result) def _get_result(self, value): - self._result = value.stringResult + self._result = core.path_to_native_path(value.pathResult, free=False) + + def _free_core_struct(self): + _free_tracked_core_paths(self) @property def prompt(self): @@ -676,27 +701,27 @@ def _get_open_filename_input(self, ctxt, result, prompt, ext): value = self.get_open_filename_input(prompt, ext) if value is None: return False - result[0] = core.BNAllocString(str(value)) + result[0] = core.path_to_core(value) return True except Exception: log_error_for_exception("Unhandled Python exception in InteractionHandler._get_open_filename_input") def _get_save_filename_input(self, ctxt, result, prompt, ext, default_name): try: - value = self.get_save_filename_input(prompt, ext, default_name) + value = self.get_save_filename_input(prompt, ext, core.path_to_native_path(default_name, free=False)) if value is None: return False - result[0] = core.BNAllocString(str(value)) + result[0] = core.path_to_core(value) return True except Exception: log_error_for_exception("Unhandled Python exception in InteractionHandler._get_save_filename_input") def _get_directory_name_input(self, ctxt, result, prompt, default_name): try: - value = self.get_directory_name_input(prompt, default_name) + value = self.get_directory_name_input(prompt, core.path_to_native_path(default_name, free=False)) if value is None: return False - result[0] = core.BNAllocString(str(value)) + result[0] = core.path_to_core(value) return True except Exception: log_error_for_exception("Unhandled Python exception in InteractionHandler._get_directory_name_input") @@ -758,21 +783,21 @@ def _get_form_input(self, ctxt, fields, count, title): field_objs.append( OpenFileNameField( fields[i].prompt, fields[i].ext, - default=fields[i].stringDefault if fields[i].hasDefault else None + default=core.path_to_native_path(fields[i].pathDefault, free=False) if fields[i].hasDefault else None ) ) elif fields[i].type == FormInputFieldType.SaveFileNameFormField: field_objs.append( SaveFileNameField( - fields[i].prompt, fields[i].ext, fields[i].defaultName, - default=fields[i].stringDefault if fields[i].hasDefault else None + fields[i].prompt, fields[i].ext, core.path_to_native_path(fields[i].defaultName, free=False), + default=core.path_to_native_path(fields[i].pathDefault, free=False) if fields[i].hasDefault else None ) ) elif fields[i].type == FormInputFieldType.DirectoryNameFormField: field_objs.append( DirectoryNameField( - fields[i].prompt, fields[i].defaultName, - default=fields[i].stringDefault if fields[i].hasDefault else None + fields[i].prompt, core.path_to_native_path(fields[i].defaultName, free=False), + default=core.path_to_native_path(fields[i].pathDefault, free=False) if fields[i].hasDefault else None ) ) elif fields[i].type == FormInputFieldType.CheckboxFormField: @@ -1308,7 +1333,7 @@ def get_choice_input(prompt, title, choices): """ choice_buf = (ctypes.c_char_p * len(choices))() for i in range(0, len(choices)): - choice_buf[i] = str(choices[i]).encode('charmap') + choice_buf[i] = core.cstr(str(choices[i])) value = ctypes.c_ulonglong() if not core.BNGetChoiceInput(value, prompt, title, choice_buf, len(choices)): return None @@ -1332,14 +1357,14 @@ def get_large_choice_input(prompt, title, choices): """ choice_buf = (ctypes.c_char_p * len(choices))() for i in range(0, len(choices)): - choice_buf[i] = str(choices[i]).encode('charmap') + choice_buf[i] = core.cstr(str(choices[i])) value = ctypes.c_ulonglong() if not core.BNGetLargeChoiceInput(value, prompt, title, choice_buf, len(choices)): return None return value.value -def get_open_filename_input(prompt: str, ext: str = "") -> Optional[str]: +def get_open_filename_input(prompt: str, ext: str = "") -> Optional[pathlib.Path]: """ ``get_open_filename_input`` prompts the user for a file name to open @@ -1352,6 +1377,7 @@ def get_open_filename_input(prompt: str, ext: str = "") -> Optional[str]: :param str prompt: Prompt to display. :param str ext: Optional, file extension + :rtype: pathlib.Path or None :Example: >>> get_open_filename_input("filename:", "*.py") 'test.py' @@ -1364,26 +1390,24 @@ def get_open_filename_input(prompt: str, ext: str = "") -> Optional[str]: >>> get_open_filename_input("filename:", "Executables (*.exe *.com);;Python Files (*.py);;All Files (*)") 'foo.exe' """ - value = ctypes.c_char_p() - if not core.BNGetOpenFileNameInput(value, prompt, ext): + value = core.BNPathHandle() + if not core.BNGetOpenFileNameInput(ctypes.byref(value), prompt, ext): return None - result = value.value - assert result is not None - core.free_string(value) - return result.decode("utf-8") + return core.path_to_native_path(value) -def get_save_filename_input(prompt: str, ext: str = "", default_name: str = "") -> Optional[str]: +def get_save_filename_input(prompt: str, ext: str = "", default_name: str = ""): """ ``get_save_filename_input`` prompts the user for a file name to save as, optionally providing a file extension and \ - default_name + default_name. .. note:: This API function differently on the command-line vs the UI. In the UI a pop-up is used. On the command-line \ a simple text prompt is used. The UI uses the native window pop-up for file selection. :param str prompt: Prompt to display. :param str ext: Optional, file extension - :param str default_name: Optional, default file name. + :param default_name: Optional, path-like default file name. + :rtype: pathlib.Path or None :Example: >>> get_save_filename_input("filename:", "*.py", "test.py") filename: test.py @@ -1401,36 +1425,30 @@ def get_save_filename_input(prompt: str, ext: str = "", default_name: str = "") filename: foo.exe 'foo.exe' """ - value = ctypes.c_char_p() - if not core.BNGetSaveFileNameInput(value, prompt, ext, default_name): + value = core.BNPathHandle() + if not core.BNGetSaveFileNameInput(ctypes.byref(value), prompt, ext, default_name): return None - result = value.value - assert result is not None - core.free_string(value) - return result.decode("utf-8") + return core.path_to_native_path(value) -def get_directory_name_input(prompt: str, default_name: str = ""): +def get_directory_name_input(prompt: str, default_name: str = "") -> Optional[pathlib.Path]: """ ``get_directory_name_input`` prompts the user for a directory name to save as, optionally providing a default_name .. note:: This API function differently on the command-line vs the UI. In the UI a pop-up is used. On the command-line a simple text prompt is used. The UI uses the native window pop-up for file selection. :param str prompt: Prompt to display. - :param str default_name: Optional, default directory name. - :rtype: str + :param default_name: Optional, path-like default directory name. + :rtype: pathlib.Path or None :Example: >>> get_directory_name_input("prompt") prompt dirname 'dirname' """ - value = ctypes.c_char_p() - if not core.BNGetDirectoryNameInput(value, prompt, default_name): + value = core.BNPathHandle() + if not core.BNGetDirectoryNameInput(ctypes.byref(value), prompt, default_name): return None - result = value.value - assert result is not None - core.free_string(value) - return result.decode("utf-8") + return core.path_to_native_path(value) def get_checkbox_input(prompt: str, title: str, default: bool = False): """ @@ -1507,13 +1525,20 @@ def get_form_input(fields, title): SeparatorField()._fill_core_struct(value[i]) else: fields[i]._fill_core_struct(value[i]) - if not core.BNGetFormInput(value, len(fields), title): - return False - for i in range(0, len(fields)): - if not (isinstance(fields[i], str) or (fields[i] is None)): - fields[i]._get_result(value[i]) - core.BNFreeFormInputResults(value, len(fields)) - return True + try: + if not core.BNGetFormInput(value, len(fields), title): + return False + try: + for i in range(0, len(fields)): + if not (isinstance(fields[i], str) or (fields[i] is None)): + fields[i]._get_result(value[i]) + finally: + core.BNFreeFormInputResults(value, len(fields)) + return True + finally: + for field in fields: + if not (isinstance(field, str) or (field is None)) and hasattr(field, "_free_core_struct"): + field._free_core_struct() def show_message_box(title, text, buttons=MessageBoxButtonSet.OKButtonSet, icon=MessageBoxIcon.InformationIcon): diff --git a/python/log.py b/python/log.py index 7347b07752..09f7c80a40 100644 --- a/python/log.py +++ b/python/log.py @@ -23,6 +23,7 @@ # Binary Ninja components from . import _binaryninjacore as core from .enums import LogLevel +import os import threading import traceback @@ -342,7 +343,7 @@ def log_to_stderr(min_level: LogLevel): core.BNLogToStderr(min_level) -def log_to_file(min_level: LogLevel, path: str, append: bool=False): +def log_to_file(min_level: LogLevel, path: Union[str, bytes, os.PathLike], append: bool=False): """ ``log_to_file`` redirects minimum log level to a file named ``path``, optionally appending rather than overwriting. @@ -351,7 +352,7 @@ def log_to_file(min_level: LogLevel, path: str, append: bool=False): :param bool append: optional flag for specifying appending. True = append, False = overwrite. :rtype: None """ - core.BNLogToFile(min_level, str(path), append) + core.BNLogToFile(min_level, os.fspath(path), append) def close_logs(): diff --git a/python/platform.py b/python/platform.py index 2e8ad846c2..9f710e661f 100644 --- a/python/platform.py +++ b/python/platform.py @@ -19,6 +19,7 @@ # IN THE SOFTWARE. import os +import contextlib import ctypes import traceback import warnings @@ -139,13 +140,12 @@ def __init__(self, arch: Optional['architecture.Architecture'] = None, handle=No _handle = core.BNCreateCustomPlatform(arch.handle, self.__class__.name, self._cb) assert _handle is not None else: - dir_buf = (ctypes.c_char_p * len(self.__class__.type_include_dirs))() - for (i, dir) in enumerate(self.__class__.type_include_dirs): - dir_buf[i] = dir.encode('charmap') - _handle = core.BNCreateCustomPlatformWithTypes( - arch.handle, self.__class__.name, self._cb, self.__class__.type_file_path, dir_buf, - len(self.__class__.type_include_dirs) - ) + with contextlib.ExitStack() as stack: + dir_paths = [stack.enter_context(core.core_path(dir)) for dir in self.__class__.type_include_dirs] + dir_buf = (core.BNPathHandle * len(dir_paths))(*dir_paths) + _handle = core.BNCreateCustomPlatformWithTypes( + arch.handle, self.__class__.name, self._cb, self.__class__.type_file_path, dir_buf, len(dir_paths) + ) assert _handle is not None self.__class__._registered_platforms.append(self) else: @@ -704,14 +704,14 @@ def parse_types_from_source( raise AttributeError("Source must be a string") if include_dirs is None: include_dirs = [] - dir_buf = (ctypes.c_char_p * len(include_dirs))() - for i in range(0, len(include_dirs)): - dir_buf[i] = include_dirs[i].encode('charmap') parse = core.BNTypeParserResult() errors = ctypes.c_char_p() - result = core.BNParseTypesFromSource( - self.handle, source, filename, parse, errors, dir_buf, len(include_dirs), auto_type_source - ) + with contextlib.ExitStack() as stack: + dir_paths = [stack.enter_context(core.core_path(dir)) for dir in include_dirs] + dir_buf = (core.BNPathHandle * len(dir_paths))(*dir_paths) + result = core.BNParseTypesFromSource( + self.handle, source, filename, parse, errors, dir_buf, len(dir_paths), auto_type_source + ) assert errors.value is not None, "core.BNParseTypesFromSource returned errors set to None" error_str = errors.value.decode("utf-8") core.free_string(errors) @@ -754,18 +754,19 @@ def parse_types_from_source_file(self, filename, include_dirs: Optional[List[str {'bar': }}, '') >>> """ + filename = os.fspath(filename) if not (isinstance(filename, str) and os.path.isfile(filename) and os.access(filename, os.R_OK)): raise AttributeError("File {} doesn't exist or isn't readable".format(filename)) if include_dirs is None: include_dirs = [] - dir_buf = (ctypes.c_char_p * len(include_dirs))() - for i in range(0, len(include_dirs)): - dir_buf[i] = include_dirs[i].encode('charmap') parse = core.BNTypeParserResult() errors = ctypes.c_char_p() - result = core.BNParseTypesFromSourceFile( - self.handle, filename, parse, errors, dir_buf, len(include_dirs), auto_type_source - ) + with contextlib.ExitStack() as stack: + dir_paths = [stack.enter_context(core.core_path(dir)) for dir in include_dirs] + dir_buf = (core.BNPathHandle * len(dir_paths))(*dir_paths) + result = core.BNParseTypesFromSourceFile( + self.handle, filename, parse, errors, dir_buf, len(dir_paths), auto_type_source + ) assert errors.value is not None, "core.BNParseTypesFromSourceFile returned errors set to None" error_str = errors.value.decode("utf-8") core.free_string(errors) diff --git a/python/pluginmanager.py b/python/pluginmanager.py index 8ce1ac5906..3db2096d9d 100644 --- a/python/pluginmanager.py +++ b/python/pluginmanager.py @@ -20,9 +20,10 @@ import ctypes import json +import os from dataclasses import dataclass from datetime import datetime, date -from typing import List, Dict, Optional +from typing import List, Dict, Optional, Union import binaryninja from . import _binaryninjacore as core @@ -64,7 +65,7 @@ def __repr__(self): return f"<{self.path} {'installed' if self.installed else 'not-installed'}/{'enabled' if self.enabled else 'disabled'}>" @property - def path(self) -> str: + def path(self) -> os.PathLike: """Relative path from the base of the repository to the actual plugin""" result = core.BNPluginGetPath(self.handle) assert result is not None, "core.BNPluginGetPath returned None" @@ -390,9 +391,10 @@ def __del__(self) -> None: def __repr__(self) -> str: return f"" - def __getitem__(self, plugin_path: str): + def __getitem__(self, plugin_path: Union[str, os.PathLike]): + plugin_path = os.fspath(plugin_path) for plugin in self.plugins: - if plugin_path == plugin.path: + if plugin_path == os.fspath(plugin.path): return plugin raise KeyError() @@ -404,14 +406,14 @@ def url(self) -> str: return result @property - def path(self) -> str: + def path(self) -> os.PathLike: """String local path to store the given plugin repository""" result = core.BNRepositoryGetRepoPath(self.handle) assert result is not None return result @property - def full_path(self) -> str: + def full_path(self) -> os.PathLike: """String full path the repository""" result = core.BNRepositoryGetPluginsPath(self.handle) assert result is not None @@ -443,9 +445,10 @@ class RepositoryManager: def __init__(self): binaryninja._init_plugins() - def __getitem__(self, repo_path: str) -> Repository: + def __getitem__(self, repo_path: Union[str, os.PathLike]) -> Repository: + repo_path = os.fspath(repo_path) for repo in self.repositories: - if repo_path == repo.path: + if repo_path == os.fspath(repo.path): return repo raise KeyError() @@ -486,7 +489,7 @@ def default_repository(self) -> Repository: assert repo_handle_ref is not None, "core.BNNewRepositoryReference returned None" return Repository(repo_handle_ref) - def add_repository(self, url: Optional[str] = None, repopath: Optional[str] = None) -> bool: + def add_repository(self, url: Optional[str] = None, repopath: Optional[Union[str, os.PathLike]] = None) -> bool: """ ``add_repository`` adds a new plugin repository for the manager to track. @@ -507,7 +510,7 @@ def add_repository(self, url: Optional[str] = None, repopath: Optional[str] = No >>> mgr.check_for_updates() >>> """ - if not isinstance(url, str) or not isinstance(repopath, str): - raise ValueError("Expected url or repopath to be of type str.") + if not isinstance(url, str) or not isinstance(repopath, (str, os.PathLike)): + raise ValueError("Expected url to be a str and repopath to be path-like.") return core.BNRepositoryManagerAddRepository(url, repopath) diff --git a/python/project.py b/python/project.py index fc274ac237..e4dd442ab4 100644 --- a/python/project.py +++ b/python/project.py @@ -21,7 +21,7 @@ import ctypes from contextlib import contextmanager -from os import PathLike +from os import PathLike, fspath from typing import Any, Callable, List, Optional, Union import binaryninja @@ -31,7 +31,7 @@ ProgressFuncType = Callable[[int, int], bool] -AsPath = Union[PathLike, str] +AsPath = Union[PathLike, str, bytes] #TODO: notifications @@ -188,7 +188,7 @@ def export(self, dest: AsPath) -> bool: :param dest: Destination path for the exported contents :return: True if the export succeeded, False otherwise """ - return core.BNProjectFileExport(self._handle, str(dest)) + return core.BNProjectFileExport(self._handle, fspath(dest)) def get_path_on_disk(self) -> Optional[str]: """ @@ -381,7 +381,7 @@ def export(self, dest: AsPath, progress_func: ProgressFuncType = _nop) -> bool: :param progress_func: Progress function that will be called as contents are exporting :return: True if the export succeeded, False otherwise """ - return core.BNProjectFolderExport(self._handle, str(dest), None, _wrap_progress(progress_func)) + return core.BNProjectFolderExport(self._handle, fspath(dest), None, _wrap_progress(progress_func)) @property def files(self) -> List['ProjectFile']: @@ -434,7 +434,7 @@ def open_project(path: AsPath) -> 'Project': :raises ProjectException: If there was an error opening the project """ binaryninja._init_plugins() - project_handle = core.BNOpenProject(str(path)) + project_handle = core.BNOpenProject(fspath(path)) if project_handle is None: raise ProjectException("Failed to open project") return Project(handle=project_handle) @@ -450,7 +450,7 @@ def create_project(path: AsPath, name: str) -> 'Project': :raises ProjectException: If there was an error creating the project """ binaryninja._init_plugins() - project_handle = core.BNCreateProject(str(path), name) + project_handle = core.BNCreateProject(fspath(path), name) if project_handle is None: raise ProjectException("Failed to create project") return Project(handle=project_handle) @@ -593,7 +593,7 @@ def remove_metadata(self, key: str) -> bool: """ return core.BNProjectRemoveMetadata(self._handle, key) - def create_folder_from_path(self, path: Union[PathLike, str], parent: Optional[ProjectFolder] = None, description: str = "", progress_func: ProgressFuncType = _nop) -> ProjectFolder: + def create_folder_from_path(self, path: AsPath, parent: Optional[ProjectFolder] = None, description: str = "", progress_func: ProgressFuncType = _nop) -> ProjectFolder: """ Recursively create files and folders in the project from a path on disk @@ -606,7 +606,7 @@ def create_folder_from_path(self, path: Union[PathLike, str], parent: Optional[P parent_handle = parent._handle if parent is not None else None folder_handle = core.BNProjectCreateFolderFromPath( project=self._handle, - path=str(path), + path=fspath(path), parent=parent_handle, description=description, ctxt=None, @@ -698,7 +698,7 @@ def create_file_from_path(self, path: AsPath, folder: Optional[ProjectFolder], n folder_handle = folder._handle if folder is not None else None file_handle = core.BNProjectCreateFileFromPath( project=self._handle, - path=str(path), + path=fspath(path), folder=folder_handle, name=name, description=description, @@ -775,14 +775,14 @@ def get_file_by_id(self, id: str) -> Optional[ProjectFile]: file = ProjectFile(handle) return file - def get_file_by_path_on_disk(self, path: str) -> Optional[ProjectFile]: + def get_file_by_path_on_disk(self, path: AsPath) -> Optional[ProjectFile]: """ Retrieve a file in the project by its path on disk :param path: Path of the file on the disk :return: File with the requested path or None """ - handle = core.BNProjectGetFileByPathOnDisk(self._handle, path) + handle = core.BNProjectGetFileByPathOnDisk(self._handle, fspath(path)) if handle is None: return None file = ProjectFile(handle) diff --git a/python/scriptingprovider.py b/python/scriptingprovider.py index b1f83ec451..7a545bfae4 100644 --- a/python/scriptingprovider.py +++ b/python/scriptingprovider.py @@ -187,7 +187,7 @@ def _execute_script_input(self, ctxt, text): def _execute_script_input_from_filename(self, ctxt, filename): try: - return self.perform_execute_script_input_from_filename(filename) + return self.perform_execute_script_input_from_filename(core.path_to_native_path(filename, free=False)) except Exception: logger.log_error_for_exception("Unhandled Python exception in ScriptingInstance._execute_script_input_from_filename") return ScriptingProviderExecuteResult.InvalidScriptInput @@ -499,7 +499,7 @@ def create_instance(self) -> Optional[ScriptingInstance]: return None return ScriptingInstance(self, handle=result) - def _load_module(self, ctx, repo_path: bytes, plugin_path: bytes, force: bool) -> bool: + def _load_module(self, ctx, repo_path, plugin_path, force: bool) -> bool: return False def _install_modules(self, ctx, modules: bytes) -> bool: @@ -1004,8 +1004,7 @@ def perform_execute_script_input(self, text): @abc.abstractmethod def perform_execute_script_input_from_filename(self, filename): - if isinstance(filename, bytes): - filename = filename.decode("utf-8") + filename_for_compile = os.fspath(filename) if not os.path.exists(filename) and os.path.isfile(filename): return ScriptingProviderExecuteResult.InvalidScriptInput # TODO: maybe this isn't the best result to use? try: @@ -1018,8 +1017,8 @@ def perform_execute_script_input_from_filename(self, filename): if len(file_contents) == 0: return ScriptingProviderExecuteResult.SuccessfulScriptExecution - _code = code.compile_command(file_contents.decode('utf-8'), filename, 'exec') - self.interpreter.locals['__file__'] = filename + _code = code.compile_command(file_contents.decode('utf-8'), filename_for_compile, 'exec') + self.interpreter.locals['__file__'] = filename_for_compile self.interpreter.locals['__name__'] = '__main__' self.interpreter.execute(_code) @@ -1138,9 +1137,9 @@ def _python_bin(self) -> Optional[str]: python_bin, status = self._get_executable_for_libpython(python_lib, python_bin_override, python_env=python_env) return python_bin - def _load_module(self, ctx, _repo_path: bytes, _module: bytes, force: bool): - repo_path = _repo_path.decode("utf-8") - module = _module.decode("utf-8") + def _load_module(self, ctx, _repo_path, _module, force: bool): + repo_path = core.path_to_native_path(_repo_path, free=False) + module = core.path_to_native_path(_module, free=False) try: repo = RepositoryManager()[repo_path] plugin = repo[module] diff --git a/python/settings.py b/python/settings.py index dc5d635b21..fa72391a74 100644 --- a/python/settings.py +++ b/python/settings.py @@ -584,7 +584,7 @@ def set_string_list(self, key: str, value: List[str], resource: Optional[Union[' length.value = len(value) string_list = (ctypes.c_char_p * len(value))() for i in range(len(value)): - string_list[i] = value[i].encode('charmap') + string_list[i] = core.cstr(value[i]) return core.BNSettingsSetStringList(self.handle, view_handle, func_handle, scope, key, string_list, length) def set_json(self, key: str, value: str, resource: Optional[Union['binaryview.BinaryView', 'function.Function']] = None, scope: 'SettingsScope' = SettingsScope.SettingsAutoScope) -> bool: diff --git a/python/typecontainer.py b/python/typecontainer.py index 6b3fe891d5..b44755b8b1 100644 --- a/python/typecontainer.py +++ b/python/typecontainer.py @@ -19,6 +19,7 @@ # IN THE SOFTWARE. import ctypes +import contextlib from typing import Optional, Mapping, Callable, List, Tuple # Binary Ninja components @@ -399,20 +400,19 @@ def parse_types_from_source(self, source: str, file_name: str, for (i, s) in enumerate(options): options_cpp[i] = core.cstr(s) - include_dirs_cpp = (ctypes.c_char_p * len(include_dirs))() - for (i, s) in enumerate(include_dirs): - include_dirs_cpp[i] = core.cstr(s) - result_cpp = core.BNTypeParserResult() errors_cpp = ctypes.POINTER(core.BNTypeParserError)() error_count = ctypes.c_size_t() - success = core.BNTypeContainerParseTypesFromSource( - self.handle, source, file_name, - options_cpp, len(options), - include_dirs_cpp, len(include_dirs), auto_type_source, import_dependencies, - result_cpp, errors_cpp, error_count - ) + with contextlib.ExitStack() as stack: + include_dir_paths = [stack.enter_context(core.core_path(path)) for path in include_dirs] + include_dirs_cpp = (core.BNPathHandle * len(include_dir_paths))(*include_dir_paths) + success = core.BNTypeContainerParseTypesFromSource( + self.handle, source, file_name, + options_cpp, len(options), + include_dirs_cpp, len(include_dir_paths), auto_type_source, import_dependencies, + result_cpp, errors_cpp, error_count + ) if success: result = typeparser.TypeParserResult._from_core_struct(result_cpp) @@ -426,5 +426,3 @@ def parse_types_from_source(self, source: str, file_name: str, core.BNFreeTypeParserErrors(errors_cpp, error_count.value) return result, errors - - diff --git a/python/typeparser.py b/python/typeparser.py index 4d251db3af..1b45eb55d7 100644 --- a/python/typeparser.py +++ b/python/typeparser.py @@ -19,8 +19,10 @@ # IN THE SOFTWARE. import abc +import contextlib import ctypes import dataclasses +import os from json import dumps from typing import List, Tuple, Optional, Dict, Any @@ -282,7 +284,7 @@ def _preprocess_source( ) -> bool: try: source_py = core.pyNativeStr(source) - file_name_py = core.pyNativeStr(fileName) + file_name_py = core.path_to_native_path(fileName, free=False) platform_py = platform.CorePlatform._from_cache(handle=core.BNNewPlatformReference(platform_)) existing_types_py = None @@ -295,7 +297,7 @@ def _preprocess_source( include_dirs_py = [] for i in range(includeDirCount): - include_dirs_py.append(core.pyNativeStr(includeDirs[i])) + include_dirs_py.append(core.path_to_native_path(includeDirs[i], free=False)) (output_py, errors_py) = self.preprocess_source( source_py, file_name_py, platform_py, existing_types_py, options_py, @@ -326,7 +328,7 @@ def _parse_types_from_source( ) -> bool: try: source_py = core.pyNativeStr(source) - file_name_py = core.pyNativeStr(fileName) + file_name_py = core.path_to_native_path(fileName, free=False) platform_py = platform.CorePlatform._from_cache(handle=core.BNNewPlatformReference(platform_)) existing_types_py = None @@ -339,7 +341,7 @@ def _parse_types_from_source( include_dirs_py = [] for i in range(includeDirCount): - include_dirs_py.append(core.pyNativeStr(includeDirs[i])) + include_dirs_py.append(core.path_to_native_path(includeDirs[i], free=False)) auto_type_source = core.pyNativeStr(autoTypeSource) or "" @@ -539,20 +541,19 @@ def preprocess_source( for (i, s) in enumerate(options): options_cpp[i] = core.cstr(s) - include_dirs_cpp = (ctypes.c_char_p * len(include_dirs))() - for (i, s) in enumerate(include_dirs): - include_dirs_cpp[i] = core.cstr(s) - output_cpp = ctypes.c_char_p() errors_cpp = ctypes.POINTER(core.BNTypeParserError)() error_count = ctypes.c_size_t() - success = core.BNTypeParserPreprocessSource( - self.handle, source, file_name, platform.handle, - existing_types_cpp.handle if existing_types_cpp is not None else None, - options_cpp, len(options), include_dirs_cpp, len(include_dirs), - output_cpp, errors_cpp, error_count - ) + with contextlib.ExitStack() as stack: + include_dir_paths = [stack.enter_context(core.core_path(path)) for path in include_dirs] + include_dirs_cpp = (core.BNPathHandle * len(include_dir_paths))(*include_dir_paths) + success = core.BNTypeParserPreprocessSource( + self.handle, source, file_name, platform.handle, + existing_types_cpp.handle if existing_types_cpp is not None else None, + options_cpp, len(options), include_dirs_cpp, len(include_dir_paths), + output_cpp, errors_cpp, error_count + ) if success: output = core.pyNativeStr(output_cpp.value) @@ -591,20 +592,19 @@ def parse_types_from_source( for (i, s) in enumerate(options): options_cpp[i] = core.cstr(s) - include_dirs_cpp = (ctypes.c_char_p * len(include_dirs))() - for (i, s) in enumerate(include_dirs): - include_dirs_cpp[i] = core.cstr(s) - result_cpp = core.BNTypeParserResult() errors_cpp = ctypes.POINTER(core.BNTypeParserError)() error_count = ctypes.c_size_t() - success = core.BNTypeParserParseTypesFromSource( - self.handle, source, file_name, platform.handle, - existing_types_cpp.handle if existing_types_cpp is not None else None, - options_cpp, len(options), include_dirs_cpp, len(include_dirs), - auto_type_source, result_cpp, errors_cpp, error_count - ) + with contextlib.ExitStack() as stack: + include_dir_paths = [stack.enter_context(core.core_path(path)) for path in include_dirs] + include_dirs_cpp = (core.BNPathHandle * len(include_dir_paths))(*include_dir_paths) + success = core.BNTypeParserParseTypesFromSource( + self.handle, source, file_name, platform.handle, + existing_types_cpp.handle if existing_types_cpp is not None else None, + options_cpp, len(options), include_dirs_cpp, len(include_dir_paths), + auto_type_source, result_cpp, errors_cpp, error_count + ) if success: result = TypeParserResult._from_core_struct(result_cpp) diff --git a/python/workflow.py b/python/workflow.py index 1d85e7d09b..1acd144987 100644 --- a/python/workflow.py +++ b/python/workflow.py @@ -718,7 +718,7 @@ def register_activity(self, activity: Activity, subactivities: List[ActivityType return None input_list = (ctypes.c_char_p * len(subactivities))() for i in range(0, len(subactivities)): - input_list[i] = str(subactivities[i]).encode('charmap') + input_list[i] = core.cstr(str(subactivities[i])) handle = core.BNWorkflowRegisterActivity(self.handle, activity.handle, input_list, len(subactivities)) if handle is None: return None @@ -818,7 +818,7 @@ def assign_subactivities(self, activity: Activity, activities: Union[List[str], activities = [activities] input_list = (ctypes.c_char_p * len(activities))() for i in range(0, len(activities)): - input_list[i] = str(activities[i]).encode('charmap') + input_list[i] = core.cstr(str(activities[i])) return core.BNWorkflowAssignSubactivities(self.handle, str(activity), input_list, len(activities)) def clear(self) -> bool: @@ -843,7 +843,7 @@ def insert(self, activity: ActivityType, activities: Union[List[str], str]) -> b activities = [activities] input_list = (ctypes.c_char_p * len(activities))() for i in range(0, len(activities)): - input_list[i] = str(activities[i]).encode('charmap') + input_list[i] = core.cstr(str(activities[i])) return core.BNWorkflowInsert(self.handle, str(activity), input_list, len(activities)) def insert_after(self, activity: ActivityType, activities: Union[List[str], str]) -> bool: @@ -859,7 +859,7 @@ def insert_after(self, activity: ActivityType, activities: Union[List[str], str] activities = [activities] input_list = (ctypes.c_char_p * len(activities))() for i in range(0, len(activities)): - input_list[i] = str(activities[i]).encode('charmap') + input_list[i] = core.cstr(str(activities[i])) return core.BNWorkflowInsertAfter(self.handle, str(activity), input_list, len(activities)) def remove(self, activity: ActivityType) -> bool: diff --git a/rust/examples/create_type_library.rs b/rust/examples/create_type_library.rs index 14c387a4a3..fe4724c2c4 100644 --- a/rust/examples/create_type_library.rs +++ b/rust/examples/create_type_library.rs @@ -10,7 +10,6 @@ fn main() { let header_path_str = std::env::args().nth(1).expect("No header provided"); let header_path = std::path::Path::new(&header_path_str); - let header_name = header_path.file_stem().unwrap().to_str().unwrap(); let type_lib_plat_str = std::env::args().nth(2).expect("No type library provided"); let type_lib_path_str = std::env::args().nth(3).expect("No type library provided"); let type_lib_path = std::path::Path::new(&type_lib_path_str); @@ -31,7 +30,7 @@ fn main() { let parsed_types = parser .parse_types_from_source( &header_contents, - header_name, + header_path, &type_lib_plat, &plat_type_container, &[], diff --git a/rust/src/architecture.rs b/rust/src/architecture.rs index df2e66b74d..d96dc34b5f 100644 --- a/rust/src/architecture.rs +++ b/rust/src/architecture.rs @@ -1776,7 +1776,11 @@ where }; let mut ctx = unsafe { - FunctionLifterContext::from_raw_with_arch(function, context, Some(*custom_arch.as_ref())) + FunctionLifterContext::from_raw_with_arch( + function, + context, + Some(*custom_arch.as_ref()), + ) }; custom_arch.lift_function(llil, &mut ctx) } diff --git a/rust/src/binary_view.rs b/rust/src/binary_view.rs index cda76ef291..a8b73f58fc 100644 --- a/rust/src/binary_view.rs +++ b/rust/src/binary_view.rs @@ -713,9 +713,8 @@ impl BinaryView { if !meta.file_path().exists() { return Err(()); } - let file = meta.file_path().to_cstr(); - let handle = - unsafe { BNCreateBinaryDataViewFromFilename(meta.handle, file.as_ptr() as *mut _) }; + let file = BnPath::new(&meta.file_path()); + let handle = unsafe { BNCreateBinaryDataViewFromFilename(meta.handle, file.as_ptr()) }; if handle.is_null() { return Err(()); } @@ -770,8 +769,8 @@ impl BinaryView { /// To avoid the above issue, use [`crate::main_thread::execute_on_main_thread_and_wait`] to verify there /// are no queued up main thread actions. pub fn save_to_path(&self, file_path: impl AsRef) -> bool { - let file = file_path.as_ref().to_cstr(); - unsafe { BNSaveToFilename(self.handle, file.as_ptr() as *mut _) } + let file = BnPath::new(file_path.as_ref()); + unsafe { BNSaveToFilename(self.handle, file.as_ptr()) } } /// Save the original binary file to the provided [`FileAccessor`] along with any modifications. @@ -2942,12 +2941,12 @@ impl BinaryView { /// Using the returned id you can retrieve the [`TypeArchive`] with [`BinaryView::type_archive_by_id`]. pub fn attached_type_archives(&self) -> Vec { let mut ids: *mut *mut c_char = std::ptr::null_mut(); - let mut paths: *mut *mut c_char = std::ptr::null_mut(); + let mut paths: *mut *mut BNPath = std::ptr::null_mut(); let count = unsafe { BNBinaryViewGetTypeArchives(self.handle, &mut ids, &mut paths) }; // We discard the path here, you can retrieve it later with [`BinaryView::type_archive_path_by_id`]. // This is so we can simplify the return type which will commonly just want to query through to the type // archive itself. - let _path_list = unsafe { Array::::new(paths, count, ()) }; + let _path_list = unsafe { Array::::new(paths, count, ()) }; let id_list = unsafe { Array::::new(ids, count, ()) }; id_list .into_iter() @@ -2972,8 +2971,7 @@ impl BinaryView { if result.is_null() { return None; } - let path_str = unsafe { BnString::into_string(result) }; - Some(PathBuf::from(path_str)) + Some(unsafe { BnString::into_path_buf(result) }) } pub fn deref_return_value_named_type_references( diff --git a/rust/src/collaboration/file.rs b/rust/src/collaboration/file.rs index 84860dd828..1a621492fa 100644 --- a/rust/src/collaboration/file.rs +++ b/rust/src/collaboration/file.rs @@ -1,6 +1,6 @@ use std::ffi::c_void; use std::fmt::{Debug, Formatter}; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::ptr::NonNull; use std::time::SystemTime; @@ -221,10 +221,10 @@ impl RemoteFile { /// Get the default filepath for a remote File. This is based off the Setting for /// collaboration.directory, the file's id, the file's project's id, and the file's /// remote's id. - pub fn default_path(&self) -> String { + pub fn default_path(&self) -> PathBuf { let result = unsafe { BNCollaborationDefaultFilePath(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::into_string(result) } + unsafe { BnString::into_path_buf(result) } } /// If the file has pulled the snapshots yet diff --git a/rust/src/collaboration/project.rs b/rust/src/collaboration/project.rs index 8ea7c028b5..7615e0f287 100644 --- a/rust/src/collaboration/project.rs +++ b/rust/src/collaboration/project.rs @@ -793,8 +793,7 @@ impl RemoteProject { /// project's remote's id. pub fn default_project_path(&self) -> PathBuf { let result = unsafe { BNCollaborationDefaultProjectPath(self.handle.as_ptr()) }; - let result_str = unsafe { BnString::into_string(result) }; - PathBuf::from(result_str) + unsafe { BnString::into_path_buf(result) } } /// Upload a file, with database, to the remote under the given project diff --git a/rust/src/collaboration/sync.rs b/rust/src/collaboration/sync.rs index 1bf9c010bb..7197cc4a23 100644 --- a/rust/src/collaboration/sync.rs +++ b/rust/src/collaboration/sync.rs @@ -12,7 +12,7 @@ use crate::file_metadata::FileMetadata; use crate::progress::{NoProgressCallback, ProgressCallback}; use crate::project::file::ProjectFile; use crate::rc::Ref; -use crate::string::{raw_to_string, BnString, IntoCStr}; +use crate::string::{raw_to_string, BnPath, BnString, IntoCStr}; use crate::types::archive::{TypeArchive, TypeArchiveMergeConflict}; /// Get the default directory path for a remote Project. This is based off the Setting for @@ -21,7 +21,7 @@ pub fn default_project_path(project: &RemoteProject) -> Result { let result = unsafe { BNCollaborationDefaultProjectPath(project.handle.as_ptr()) }; let success = !result.is_null(); success - .then(|| PathBuf::from(unsafe { BnString::into_string(result) })) + .then(|| unsafe { BnString::into_path_buf(result) }) .ok_or(()) } @@ -32,7 +32,7 @@ pub fn default_file_path(file: &RemoteFile) -> Result { let result = unsafe { BNCollaborationDefaultFilePath(file.handle.as_ptr()) }; let success = !result.is_null(); success - .then(|| PathBuf::from(unsafe { BnString::into_string(result) })) + .then(|| unsafe { BnString::into_path_buf(result) }) .ok_or(()) } @@ -56,7 +56,7 @@ pub fn download_file_with_progress( db_path: &Path, mut progress: F, ) -> Result, ()> { - let db_path = db_path.to_cstr(); + let db_path = BnPath::new(db_path); let result = unsafe { BNCollaborationDownloadFile( file.handle.as_ptr(), @@ -228,7 +228,7 @@ pub fn download_database_with_progress( where PC: ProgressCallback, { - let db_path = location.to_cstr(); + let db_path = BnPath::new(location); let success = unsafe { BNCollaborationDownloadDatabaseForFile( file.handle.as_ptr(), @@ -699,7 +699,7 @@ pub fn download_type_archive_with_progress( mut progress: PC, ) -> Result>, ()> { let mut value = std::ptr::null_mut(); - let db_path = location.to_cstr(); + let db_path = BnPath::new(location); let success = unsafe { BNCollaborationDownloadTypeArchive( file.handle.as_ptr(), diff --git a/rust/src/data_notification.rs b/rust/src/data_notification.rs index d01a5ed32a..1c135284bc 100644 --- a/rust/src/data_notification.rs +++ b/rust/src/data_notification.rs @@ -1,6 +1,7 @@ //! Receive notifications for many types of core events. use std::ffi::{c_char, c_void, CStr}; +use std::path::PathBuf; use std::ptr::NonNull; use binaryninjacore_sys::*; @@ -13,6 +14,7 @@ use crate::function::Function; use crate::rc::Ref; use crate::section::Section; use crate::segment::Segment; +use crate::string::BnString; use crate::symbol::Symbol; use crate::tags::{TagReference, TagType}; use crate::types::{QualifiedName, Type, TypeArchive}; @@ -378,12 +380,12 @@ trait_handler! { typeArchiveAttached => type_archive_attached( view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view), id: *const c_char: &str = CStr::from_ptr(id).to_str().unwrap(), - path: *const c_char: &[u8] = CStr::from_ptr(path).to_bytes(), + path: *mut BNPath: PathBuf = unsafe { BnString::path_buf_from_raw(path) }, ), typeArchiveDetached => type_archive_detached( view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view), id: *const c_char: &str = CStr::from_ptr(id).to_str().unwrap(), - path: *const c_char: &[u8] = CStr::from_ptr(path).to_bytes(), + path: *mut BNPath: PathBuf = unsafe { BnString::path_buf_from_raw(path) }, ), typeArchiveConnected => type_archive_connected( view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view), diff --git a/rust/src/file_metadata.rs b/rust/src/file_metadata.rs index 064d1fe93d..ec596ff558 100644 --- a/rust/src/file_metadata.rs +++ b/rust/src/file_metadata.rs @@ -160,7 +160,7 @@ impl FileMetadata { pub fn file_path(&self) -> PathBuf { unsafe { let raw = BNGetFilename(self.handle); - PathBuf::from(BnString::into_string(raw)) + BnString::into_path_buf(raw) } } @@ -170,7 +170,7 @@ impl FileMetadata { /// /// This should always be a valid path. pub(crate) fn set_file_path(&self, name: &Path) { - let name = name.to_cstr(); + let name = BnPath::new(name); unsafe { BNSetFilename(self.handle, name.as_ptr()); } @@ -220,7 +220,7 @@ impl FileMetadata { pub fn original_file_path(&self) -> Option { let raw_name = unsafe { let raw = BNGetOriginalFilename(self.handle); - PathBuf::from(BnString::into_string(raw)) + BnString::into_path_buf(raw) }; // If the original file path is empty, or the original file path is pointing to the same file // as the database itself, we know the original file path does not exist. @@ -236,7 +236,7 @@ impl FileMetadata { /// Set the original file path inside the database. Useful if it has since been cleared from the /// database, or you have moved the original file. pub fn set_original_file_path(&self, path: &Path) { - let name = path.to_cstr(); + let name = BnPath::new(path); unsafe { BNSetOriginalFilename(self.handle, name.as_ptr()); } @@ -246,7 +246,7 @@ impl FileMetadata { /// may be sensitive information you wish to not share with others. pub fn clear_original_file_path(&self) { unsafe { - BNSetOriginalFilename(self.handle, std::ptr::null()); + BNSetOriginalFilename(self.handle, std::ptr::null_mut()); } } @@ -527,11 +527,11 @@ impl FileMetadata { ) -> bool { // Databases are created with the root view (Raw). let raw_view = self.raw_view(); - let file_path = file_path.as_ref().to_cstr(); + let file_path = BnPath::new(file_path.as_ref()); unsafe { BNCreateDatabaseWithProgress( raw_view.handle, - file_path.as_ptr() as *mut _, + file_path.as_ptr(), &mut progress as *mut P as *mut c_void, Some(P::cb_progress_callback), settings.handle, @@ -551,10 +551,9 @@ impl FileMetadata { // TODO: Deprecate this function? Does not seem to do anything different than `open_database`. pub fn open_database_for_configuration(&self, file: &Path) -> Result, ()> { - let file = file.to_cstr(); + let file = BnPath::new(file); unsafe { - let bv = - BNOpenDatabaseForConfiguration(self.handle, file.as_ref().as_ptr() as *const _); + let bv = BNOpenDatabaseForConfiguration(self.handle, file.as_ptr()); if bv.is_null() { Err(()) @@ -566,7 +565,7 @@ impl FileMetadata { // TODO: How this relates to `BNLoadFilename`? pub fn open_database(&self, file: &Path) -> Result, ()> { - let file = file.to_cstr(); + let file = BnPath::new(file); let view = unsafe { BNOpenExistingDatabase(self.handle, file.as_ptr()) }; if view.is_null() { @@ -582,7 +581,7 @@ impl FileMetadata { file: &Path, mut progress: P, ) -> Result, ()> { - let file = file.to_cstr(); + let file = BnPath::new(file); let view = unsafe { BNOpenExistingDatabaseWithProgress( diff --git a/rust/src/interaction.rs b/rust/src/interaction.rs index 5fbb91f984..40fed43c83 100644 --- a/rust/src/interaction.rs +++ b/rust/src/interaction.rs @@ -15,11 +15,11 @@ //! Interfaces for asking the user for information: forms, opening files, etc. use std::ffi::{c_char, c_void}; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use binaryninjacore_sys::*; -use crate::string::{BnString, IntoCStr}; +use crate::string::{BnPath, BnString, IntoCStr}; pub mod form; pub mod handler; @@ -126,7 +126,7 @@ pub fn get_large_choice_input(prompt: &str, title: &str, choices: &[&str]) -> Op } pub fn get_open_filename_input(prompt: &str, extension: &str) -> Option { - let mut value: *mut c_char = std::ptr::null_mut(); + let mut value: *mut BNPath = std::ptr::null_mut(); let prompt = prompt.to_cstr(); let extension = extension.to_cstr(); @@ -135,20 +135,19 @@ pub fn get_open_filename_input(prompt: &str, extension: &str) -> Option return None; } - let path = unsafe { BnString::into_string(value) }; - Some(PathBuf::from(path)) + Some(unsafe { BnString::into_path_buf(value) }) } pub fn get_save_filename_input( prompt: &str, extension: &str, - default_name: &str, + default_name: impl AsRef, ) -> Option { - let mut value: *mut c_char = std::ptr::null_mut(); + let mut value: *mut BNPath = std::ptr::null_mut(); let prompt = prompt.to_cstr(); let extension = extension.to_cstr(); - let default_name = default_name.to_cstr(); + let default_name = BnPath::new(default_name.as_ref()); let result = unsafe { BNGetSaveFileNameInput( &mut value, @@ -161,23 +160,21 @@ pub fn get_save_filename_input( return None; } - let path = unsafe { BnString::into_string(value) }; - Some(PathBuf::from(path)) + Some(unsafe { BnString::into_path_buf(value) }) } -pub fn get_directory_name_input(prompt: &str, default_name: &str) -> Option { - let mut value: *mut c_char = std::ptr::null_mut(); +pub fn get_directory_name_input(prompt: &str, default_name: impl AsRef) -> Option { + let mut value: *mut BNPath = std::ptr::null_mut(); let prompt = prompt.to_cstr(); - let default_name = default_name.to_cstr(); + let default_name = BnPath::new(default_name.as_ref()); let result = unsafe { BNGetDirectoryNameInput(&mut value, prompt.as_ptr(), default_name.as_ptr()) }; if !result { return None; } - let path = unsafe { BnString::into_string(value) }; - Some(PathBuf::from(path)) + Some(unsafe { BnString::into_path_buf(value) }) } pub fn show_message_box( diff --git a/rust/src/interaction/form.rs b/rust/src/interaction/form.rs index 4cf9f86301..c326a4299d 100644 --- a/rust/src/interaction/form.rs +++ b/rust/src/interaction/form.rs @@ -1,10 +1,11 @@ use std::ffi::c_char; +use std::path::PathBuf; use binaryninjacore_sys::*; use crate::binary_view::BinaryView; use crate::rc::{Array, Ref}; -use crate::string::{raw_to_string, strings_to_string_list, BnString, IntoCStr}; +use crate::string::{raw_to_string, strings_to_string_list, BnPath, BnString, IntoCStr}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct Form { @@ -94,20 +95,22 @@ pub enum FormInputField { prompt: String, /// File extension to filter on. extension: Option, - default: Option, - value: Option, + default: Option, + value: Option, }, SaveFileName { prompt: String, /// File extension to filter on. extension: Option, - default: Option, - value: Option, + default_name: Option, + default: Option, + value: Option, }, DirectoryName { prompt: String, - default: Option, - value: Option, + default_name: Option, + default: Option, + value: Option, }, Checkbox { prompt: String, @@ -123,12 +126,13 @@ impl FormInputField { false => Some(unsafe { BinaryView::from_raw(value.view) }.to_owned()), true => None, }; - let name_default = value - .hasDefault - .then_some(raw_to_string(value.defaultName).unwrap_or_default()); let string_default = value .hasDefault .then_some(raw_to_string(value.stringDefault).unwrap_or_default()); + let path_default = (value.hasDefault && !value.pathDefault.is_null()) + .then(|| unsafe { BnString::path_buf_from_raw(value.pathDefault) }); + let name_default = (!value.defaultName.is_null()) + .then(|| unsafe { BnString::path_buf_from_raw(value.defaultName) }); let int_default = value.hasDefault.then_some(value.intDefault); let address_default = value.hasDefault.then_some(value.addressDefault); let index_default = value.hasDefault.then_some(value.indexDefault); @@ -136,6 +140,8 @@ impl FormInputField { let extension = raw_to_string(value.ext); let current_address = value.currentAddress; let string_result = raw_to_string(value.stringResult); + let path_result = (!value.pathResult.is_null()) + .then(|| unsafe { BnString::path_buf_from_raw(value.pathResult) }); let int_result = value.intResult; let address_result = value.addressResult; let index_result = value.indexResult; @@ -173,19 +179,21 @@ impl FormInputField { BNFormInputFieldType::OpenFileNameFormField => Self::OpenFileName { prompt, extension, - default: string_default, - value: string_result, + default: path_default, + value: path_result, }, BNFormInputFieldType::SaveFileNameFormField => Self::SaveFileName { prompt, extension, - default: name_default, - value: string_result, + default_name: name_default, + default: path_default, + value: path_result, }, BNFormInputFieldType::DirectoryNameFormField => Self::DirectoryName { prompt, - default: name_default, - value: string_result, + default_name: name_default, + default: path_default, + value: path_result, }, BNFormInputFieldType::CheckboxFormField => Self::Checkbox { prompt, @@ -205,8 +213,16 @@ impl FormInputField { let bn_prompt = BnString::new(self.try_prompt().unwrap_or_default()); let bn_extension = BnString::new(self.try_extension().unwrap_or_default()); let bn_default_string = BnString::new(self.try_default_string().unwrap_or_default()); - let bn_default_name = BnString::new(self.try_default_name().unwrap_or_default()); let bn_value_string = BnString::new(self.try_value_string().unwrap_or_default()); + let bn_default_name = self + .try_default_name_path() + .map(|path| BnPath::new(path.as_path())); + let bn_path_default = self + .try_default_path() + .map(|path| BnPath::new(path.as_path())); + let bn_value_path = self + .try_value_path() + .map(|path| BnPath::new(path.as_path())); // Expected to be freed by [`FormInputField::free_raw`]. BNFormInputField { type_: self.as_type(), @@ -223,26 +239,36 @@ impl FormInputField { // NOTE: `count` is the length of the `choices` array. count: self.try_choices().unwrap_or_default().len(), ext: BnString::into_raw(bn_extension), - defaultName: BnString::into_raw(bn_default_name), + defaultName: bn_default_name + .map(BnPath::into_raw) + .unwrap_or_else(std::ptr::null_mut), intResult: self.try_value_int().unwrap_or_default(), addressResult: self.try_value_address().unwrap_or_default(), stringResult: BnString::into_raw(bn_value_string), + pathResult: bn_value_path + .map(BnPath::into_raw) + .unwrap_or_else(std::ptr::null_mut), indexResult: self.try_value_index().unwrap_or_default(), hasDefault: self.try_has_default().unwrap_or_default(), intDefault: self.try_default_int().unwrap_or_default(), addressDefault: self.try_default_address().unwrap_or_default(), stringDefault: BnString::into_raw(bn_default_string), + pathDefault: bn_path_default + .map(BnPath::into_raw) + .unwrap_or_else(std::ptr::null_mut), indexDefault: self.try_default_index().unwrap_or_default(), } } pub fn free_raw(value: BNFormInputField) { unsafe { - BnString::free_raw(value.defaultName as *mut c_char); + BNFreePath(value.defaultName); BnString::free_raw(value.prompt as *mut c_char); BnString::free_raw(value.ext as *mut c_char); BnString::free_raw(value.stringDefault as *mut c_char); BnString::free_raw(value.stringResult); + BNFreePath(value.pathDefault); + BNFreePath(value.pathResult); // TODO: Would like access to a `Array::free_raw` or something. Array::::new(value.choices as *mut *mut c_char, value.count, ()); // Free the view ref if provided. @@ -252,6 +278,43 @@ impl FormInputField { } } + pub fn update_raw_result(&self, value: &mut BNFormInputField) { + unsafe { + BnString::free_raw(value.stringResult); + value.stringResult = std::ptr::null_mut(); + if !value.pathResult.is_null() { + BNFreePath(value.pathResult); + value.pathResult = std::ptr::null_mut(); + } + } + + match value.type_ { + BNFormInputFieldType::TextLineFormField + | BNFormInputFieldType::MultilineTextFormField => { + value.stringResult = + BnString::into_raw(BnString::new(self.try_value_string().unwrap_or_default())); + } + BNFormInputFieldType::IntegerFormField | BNFormInputFieldType::CheckboxFormField => { + value.intResult = self.try_value_int().unwrap_or_default(); + } + BNFormInputFieldType::AddressFormField => { + value.addressResult = self.try_value_address().unwrap_or_default(); + } + BNFormInputFieldType::ChoiceFormField => { + value.indexResult = self.try_value_index().unwrap_or_default(); + } + BNFormInputFieldType::OpenFileNameFormField + | BNFormInputFieldType::SaveFileNameFormField + | BNFormInputFieldType::DirectoryNameFormField => { + value.pathResult = self + .try_value_path() + .map(|path| BnPath::into_raw(BnPath::new(path.as_path()))) + .unwrap_or_else(std::ptr::null_mut); + } + BNFormInputFieldType::LabelFormField | BNFormInputFieldType::SeparatorFormField => {} + } + } + pub fn as_type(&self) -> BNFormInputFieldType { match self { FormInputField::Label { .. } => BNFormInputFieldType::LabelFormField, @@ -338,10 +401,10 @@ impl FormInputField { } /// Mapping to the [`BNFormInputField::defaultName`] field. - pub fn try_default_name(&self) -> Option { + pub fn try_default_name_path(&self) -> Option { match self { - Self::SaveFileName { default, .. } => default.clone(), - Self::DirectoryName { default, .. } => default.clone(), + Self::SaveFileName { default_name, .. } => default_name.clone(), + Self::DirectoryName { default_name, .. } => default_name.clone(), _ => None, } } @@ -368,6 +431,13 @@ impl FormInputField { match self { FormInputField::TextLine { default, .. } => default.clone(), FormInputField::MultilineText { default, .. } => default.clone(), + _ => None, + } + } + + /// Mapping to the [`BNFormInputField::pathDefault`] field. + pub fn try_default_path(&self) -> Option { + match self { FormInputField::OpenFileName { default, .. } => default.clone(), FormInputField::SaveFileName { default, .. } => default.clone(), FormInputField::DirectoryName { default, .. } => default.clone(), @@ -405,6 +475,13 @@ impl FormInputField { match self { FormInputField::TextLine { value, .. } => value.clone(), FormInputField::MultilineText { value, .. } => value.clone(), + _ => None, + } + } + + /// Mapping to the [`BNFormInputField::pathResult`] field. + pub fn try_value_path(&self) -> Option { + match self { FormInputField::OpenFileName { value, .. } => value.clone(), FormInputField::SaveFileName { value, .. } => value.clone(), FormInputField::DirectoryName { value, .. } => value.clone(), diff --git a/rust/src/interaction/handler.rs b/rust/src/interaction/handler.rs index d4734d3444..9ac21790b1 100644 --- a/rust/src/interaction/handler.rs +++ b/rust/src/interaction/handler.rs @@ -1,4 +1,5 @@ use std::ffi::{c_char, c_void, CStr}; +use std::path::PathBuf; use std::ptr; use binaryninjacore_sys::*; @@ -8,7 +9,7 @@ use crate::flowgraph::FlowGraph; use crate::interaction::form::{Form, FormInputField}; use crate::interaction::report::{Report, ReportCollection}; use crate::interaction::{MessageBoxButtonResult, MessageBoxButtonSet, MessageBoxIcon}; -use crate::string::{raw_to_string, BnString}; +use crate::string::{raw_to_string, BnPath, BnString}; pub fn register_interaction_handler(custom: R) { let leak_custom = Box::leak(Box::new(custom)); @@ -192,7 +193,7 @@ pub trait InteractionHandler: Sync + Send + 'static { &mut self, prompt: &str, extension: Option, - ) -> Option { + ) -> Option { let mut form = Form::new(prompt.to_owned()); form.add_field(FormInputField::OpenFileName { prompt: prompt.to_string(), @@ -204,19 +205,20 @@ pub trait InteractionHandler: Sync + Send + 'static { return None; } form.get_field_with_name(prompt) - .and_then(|f| f.try_value_string()) + .and_then(|f| f.try_value_path()) } fn get_save_file_name_input( &mut self, prompt: &str, extension: Option, - default: Option, - ) -> Option { + default: Option, + ) -> Option { let mut form = Form::new(prompt.to_owned()); form.add_field(FormInputField::SaveFileName { prompt: prompt.to_string(), extension, + default_name: default.clone(), default, value: None, }); @@ -224,17 +226,18 @@ pub trait InteractionHandler: Sync + Send + 'static { return None; } form.get_field_with_name(prompt) - .and_then(|f| f.try_value_string()) + .and_then(|f| f.try_value_path()) } fn get_directory_name_input( &mut self, prompt: &str, - default: Option, - ) -> Option { + default: Option, + ) -> Option { let mut form = Form::new(prompt.to_owned()); form.add_field(FormInputField::DirectoryName { prompt: prompt.to_string(), + default_name: default.clone(), default, value: None, }); @@ -242,7 +245,7 @@ pub trait InteractionHandler: Sync + Send + 'static { return None; } form.get_field_with_name(prompt) - .and_then(|f| f.try_value_string()) + .and_then(|f| f.try_value_path()) } fn get_checkbox_input( @@ -502,7 +505,7 @@ unsafe extern "C" fn cb_get_large_choice_input( unsafe extern "C" fn cb_get_open_file_name_input( ctxt: *mut c_void, - result_ffi: *mut *mut c_char, + result_ffi: *mut *mut BNPath, prompt: *const c_char, ext: *const c_char, ) -> bool { @@ -512,7 +515,7 @@ unsafe extern "C" fn cb_get_open_file_name_input( let result = (*ctxt).get_open_file_name_input(&prompt, ext.map(|x| x.to_string_lossy().to_string())); if let Some(result) = result { - unsafe { *result_ffi = BnString::into_raw(BnString::new(result)) }; + unsafe { *result_ffi = BnPath::into_raw(BnPath::new(result.as_path())) }; true } else { unsafe { *result_ffi = ptr::null_mut() }; @@ -522,18 +525,22 @@ unsafe extern "C" fn cb_get_open_file_name_input( unsafe extern "C" fn cb_get_save_file_name_input( ctxt: *mut c_void, - result_ffi: *mut *mut c_char, + result_ffi: *mut *mut BNPath, prompt: *const c_char, ext: *const c_char, - default_name: *const c_char, + default_name: *mut BNPath, ) -> bool { let ctxt = ctxt as *mut R; let prompt = raw_to_string(prompt).unwrap(); let ext = raw_to_string(ext); - let default_name = raw_to_string(default_name); + let default_name = if default_name.is_null() { + None + } else { + Some(BnString::path_buf_from_raw(default_name)) + }; let result = (*ctxt).get_save_file_name_input(&prompt, ext, default_name); if let Some(result) = result { - unsafe { *result_ffi = BnString::into_raw(BnString::new(result)) }; + unsafe { *result_ffi = BnPath::into_raw(BnPath::new(result.as_path())) }; true } else { unsafe { *result_ffi = ptr::null_mut() }; @@ -543,16 +550,20 @@ unsafe extern "C" fn cb_get_save_file_name_input( unsafe extern "C" fn cb_get_directory_name_input( ctxt: *mut c_void, - result_ffi: *mut *mut c_char, + result_ffi: *mut *mut BNPath, prompt: *const c_char, - default_name: *const c_char, + default_name: *mut BNPath, ) -> bool { let ctxt = ctxt as *mut R; let prompt = raw_to_string(prompt).unwrap(); - let default_name = raw_to_string(default_name); + let default_name = if default_name.is_null() { + None + } else { + Some(BnString::path_buf_from_raw(default_name)) + }; let result = (*ctxt).get_directory_name_input(&prompt, default_name); if let Some(result) = result { - unsafe { *result_ffi = BnString::into_raw(BnString::new(result)) }; + unsafe { *result_ffi = BnPath::into_raw(BnPath::new(result.as_path())) }; true } else { unsafe { *result_ffi = ptr::null_mut() }; @@ -596,13 +607,13 @@ unsafe extern "C" fn cb_get_form_input( let title = raw_to_string(title).unwrap(); let mut form = Form::new_with_fields(title, fields); let results = (*ctxt).get_form_input(&mut form); - // Update the fields with the new values. Freeing the old ones. + // The incoming fields are borrowed from the caller. Only result fields are + // callback-owned outputs, so leave prompts/defaults/choices/view untouched. raw_fields .iter_mut() .enumerate() .for_each(|(idx, raw_field)| { - FormInputField::free_raw(*raw_field); - *raw_field = form.fields[idx].into_raw(); + form.fields[idx].update_raw_result(raw_field); }); results } diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 62ee776e2a..829f313f4d 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -100,9 +100,9 @@ use std::collections::HashMap; use std::ffi::{c_char, c_void, CStr}; use std::fmt::{Display, Formatter}; use std::path::{Path, PathBuf}; -use string::BnString; use string::IntoCStr; use string::IntoJson; +use string::{BnPath, BnString}; use crate::project::file::ProjectFile; pub use binaryninjacore_sys::BNDataFlowQueryOption as DataFlowQueryOption; @@ -124,11 +124,11 @@ pub fn load_with_progress( file_path: impl AsRef, mut progress: P, ) -> Option> { - let file_path = file_path.as_ref().to_cstr(); + let file_path = BnPath::new(file_path.as_ref()); let options = c""; let handle = unsafe { BNLoadFilename( - file_path.as_ptr() as *mut _, + file_path.as_ptr(), true, options.as_ptr() as *mut c_char, Some(P::cb_progress_callback), @@ -189,7 +189,7 @@ where O: IntoJson, P: ProgressCallback, { - let file_path = file_path.as_ref().to_cstr(); + let file_path = BnPath::new(file_path.as_ref()); let options_or_default = if let Some(opt) = options { opt.get_json_string() .ok()? @@ -201,7 +201,7 @@ where }; let handle = unsafe { BNLoadFilename( - file_path.as_ptr() as *mut _, + file_path.as_ptr(), update_analysis_and_wait, options_or_default.as_ptr() as *mut c_char, Some(P::cb_progress_callback), @@ -313,55 +313,50 @@ where } pub fn install_directory() -> PathBuf { - let install_dir_ptr: *mut c_char = unsafe { BNGetInstallDirectory() }; + let install_dir_ptr = unsafe { BNGetInstallDirectory() }; assert!(!install_dir_ptr.is_null()); - let install_dir_str = unsafe { BnString::into_string(install_dir_ptr) }; - PathBuf::from(install_dir_str) + unsafe { BnString::into_path_buf(install_dir_ptr) } } pub fn bundled_plugin_directory() -> Result { - let s: *mut c_char = unsafe { BNGetBundledPluginDirectory() }; + let s = unsafe { BNGetBundledPluginDirectory() }; if s.is_null() { return Err(()); } - Ok(PathBuf::from(unsafe { BnString::into_string(s) })) + Ok(unsafe { BnString::into_path_buf(s) }) } pub fn set_bundled_plugin_directory(new_dir: impl AsRef) { - let new_dir = new_dir.as_ref().to_cstr(); + let new_dir = BnPath::new(new_dir.as_ref()); unsafe { BNSetBundledPluginDirectory(new_dir.as_ptr()) }; } pub fn user_directory() -> PathBuf { - let user_dir_ptr: *mut c_char = unsafe { BNGetUserDirectory() }; + let user_dir_ptr = unsafe { BNGetUserDirectory() }; assert!(!user_dir_ptr.is_null()); - let user_dir_str = unsafe { BnString::into_string(user_dir_ptr) }; - PathBuf::from(user_dir_str) + unsafe { BnString::into_path_buf(user_dir_ptr) } } pub fn user_plugin_directory() -> Result { - let s: *mut c_char = unsafe { BNGetUserPluginDirectory() }; + let s = unsafe { BNGetUserPluginDirectory() }; if s.is_null() { return Err(()); } - let user_plugin_dir_str = unsafe { BnString::into_string(s) }; - Ok(PathBuf::from(user_plugin_dir_str)) + Ok(unsafe { BnString::into_path_buf(s) }) } pub fn repositories_directory() -> Result { - let s: *mut c_char = unsafe { BNGetRepositoriesDirectory() }; + let s = unsafe { BNGetRepositoriesDirectory() }; if s.is_null() { return Err(()); } - let repo_dir_str = unsafe { BnString::into_string(s) }; - Ok(PathBuf::from(repo_dir_str)) + Ok(unsafe { BnString::into_path_buf(s) }) } pub fn settings_file_path() -> PathBuf { - let settings_file_name_ptr: *mut c_char = unsafe { BNGetSettingsFileName() }; + let settings_file_name_ptr = unsafe { BNGetSettingsFileName() }; assert!(!settings_file_name_ptr.is_null()); - let settings_file_path_str = unsafe { BnString::into_string(settings_file_name_ptr) }; - PathBuf::from(settings_file_path_str) + unsafe { BnString::into_path_buf(settings_file_name_ptr) } } /// Write the installation directory of the currently running core instance to disk. @@ -372,30 +367,30 @@ pub fn save_last_run() { } pub fn path_relative_to_bundled_plugin_directory(path: impl AsRef) -> Result { - let path_raw = path.as_ref().to_cstr(); - let s: *mut c_char = unsafe { BNGetPathRelativeToBundledPluginDirectory(path_raw.as_ptr()) }; + let path_raw = BnPath::new(path.as_ref()); + let s = unsafe { BNGetPathRelativeToBundledPluginDirectory(path_raw.as_ptr()) }; if s.is_null() { return Err(()); } - Ok(PathBuf::from(unsafe { BnString::into_string(s) })) + Ok(unsafe { BnString::into_path_buf(s) }) } pub fn path_relative_to_user_plugin_directory(path: impl AsRef) -> Result { - let path_raw = path.as_ref().to_cstr(); - let s: *mut c_char = unsafe { BNGetPathRelativeToUserPluginDirectory(path_raw.as_ptr()) }; + let path_raw = BnPath::new(path.as_ref()); + let s = unsafe { BNGetPathRelativeToUserPluginDirectory(path_raw.as_ptr()) }; if s.is_null() { return Err(()); } - Ok(PathBuf::from(unsafe { BnString::into_string(s) })) + Ok(unsafe { BnString::into_path_buf(s) }) } pub fn path_relative_to_user_directory(path: impl AsRef) -> Result { - let path_raw = path.as_ref().to_cstr(); - let s: *mut c_char = unsafe { BNGetPathRelativeToUserDirectory(path_raw.as_ptr()) }; + let path_raw = BnPath::new(path.as_ref()); + let s = unsafe { BNGetPathRelativeToUserDirectory(path_raw.as_ptr()) }; if s.is_null() { return Err(()); } - Ok(PathBuf::from(unsafe { BnString::into_string(s) })) + Ok(unsafe { BnString::into_path_buf(s) }) } /// Returns if the running thread is the "main thread" @@ -572,7 +567,7 @@ pub fn is_ui_enabled() -> bool { } pub fn is_database(file: &Path) -> bool { - let filename = file.to_cstr(); + let filename = BnPath::new(file); unsafe { BNIsDatabase(filename.as_ptr()) } } diff --git a/rust/src/platform.rs b/rust/src/platform.rs index 9de125a1c3..9ee8bacb65 100644 --- a/rust/src/platform.rs +++ b/rust/src/platform.rs @@ -26,8 +26,9 @@ use crate::{ }; use binaryninjacore_sys::*; use std::fmt::Debug; +use std::path::Path; use std::ptr::NonNull; -use std::{borrow::Borrow, ffi, ptr}; +use std::{borrow::Borrow, ptr}; /// A platform describes the target [`CoreArchitecture`] and platform-specific information such as /// the calling conventions and generic types (think `HRESULT` on Windows). @@ -302,22 +303,31 @@ impl Platform { pub fn preprocess_source( &self, source: &str, - file_name: &str, - include_dirs: &[BnString], + file_name: &impl AsRef, + include_dirs: &[impl AsRef], ) -> Result { let source_cstr = BnString::new(source); - let file_name_cstr = BnString::new(file_name); + let file_name_display = file_name.as_ref().to_string_lossy().to_string(); + let file_name = BnPath::new(file_name.as_ref()); + let include_dir_paths: Vec = include_dirs + .iter() + .map(|include_dir| BnPath::new(include_dir.as_ref())) + .collect(); + let include_dir_ptrs: Vec<*mut BNPath> = include_dir_paths + .iter() + .map(|include_dir| include_dir.as_ptr()) + .collect(); let mut result = ptr::null_mut(); let mut error_string = ptr::null_mut(); let success = unsafe { BNPreprocessSource( source_cstr.as_ptr(), - file_name_cstr.as_ptr(), + file_name.as_ptr(), &mut result, &mut error_string, - include_dirs.as_ptr() as *mut *const ffi::c_char, - include_dirs.len(), + include_dir_ptrs.as_ptr() as *mut *mut BNPath, + include_dir_ptrs.len(), ) }; @@ -329,7 +339,7 @@ impl Platform { Err(TypeParserError::new( TypeParserErrorSeverity::FatalSeverity, unsafe { BnString::into_string(error_string) }, - file_name.to_string(), + file_name_display, 0, 0, )) @@ -340,12 +350,20 @@ impl Platform { pub fn parse_types_from_source( &self, src: &str, - filename: &str, - include_dirs: &[BnString], + filename: &impl AsRef, + include_dirs: &[impl AsRef], auto_type_source: &str, ) -> Result { let source_cstr = BnString::new(src); - let file_name_cstr = BnString::new(filename); + let file_name = BnPath::new(filename.as_ref()); + let include_dir_paths: Vec = include_dirs + .iter() + .map(|include_dir| BnPath::new(include_dir.as_ref())) + .collect(); + let include_dir_ptrs: Vec<*mut BNPath> = include_dir_paths + .iter() + .map(|include_dir| include_dir.as_ptr()) + .collect(); let auto_type_source = BnString::new(auto_type_source); let mut raw_result = BNTypeParserResult::default(); @@ -354,11 +372,11 @@ impl Platform { BNParseTypesFromSource( self.handle, source_cstr.as_ptr(), - file_name_cstr.as_ptr(), + file_name.as_ptr(), &mut raw_result, &mut error_string, - include_dirs.as_ptr() as *mut *const ffi::c_char, - include_dirs.len(), + include_dir_ptrs.as_ptr() as *mut *mut BNPath, + include_dir_ptrs.len(), auto_type_source.as_ptr(), ) }; @@ -373,7 +391,7 @@ impl Platform { Err(TypeParserError::new( TypeParserErrorSeverity::FatalSeverity, unsafe { BnString::into_string(error_string) }, - filename.to_string(), + filename.as_ref().to_string_lossy().to_string(), 0, 0, )) @@ -383,11 +401,19 @@ impl Platform { // TODO: Documentation, specifically how this differs from the TypeParser impl pub fn parse_types_from_source_file( &self, - filename: &str, - include_dirs: &[BnString], + filename: impl AsRef, + include_dirs: &[impl AsRef], auto_type_source: &str, ) -> Result { - let file_name_cstr = BnString::new(filename); + let file_name = BnPath::new(filename.as_ref()); + let include_dir_paths: Vec = include_dirs + .iter() + .map(|include_dir| BnPath::new(include_dir.as_ref())) + .collect(); + let include_dir_ptrs: Vec<*mut BNPath> = include_dir_paths + .iter() + .map(|include_dir| include_dir.as_ptr()) + .collect(); let auto_type_source = BnString::new(auto_type_source); let mut raw_result = BNTypeParserResult::default(); @@ -395,11 +421,11 @@ impl Platform { let success = unsafe { BNParseTypesFromSourceFile( self.handle, - file_name_cstr.as_ptr(), + file_name.as_ptr(), &mut raw_result, &mut error_string, - include_dirs.as_ptr() as *mut *const ffi::c_char, - include_dirs.len(), + include_dir_ptrs.as_ptr() as *mut *mut BNPath, + include_dir_ptrs.len(), auto_type_source.as_ptr(), ) }; @@ -414,7 +440,7 @@ impl Platform { Err(TypeParserError::new( TypeParserErrorSeverity::FatalSeverity, unsafe { BnString::into_string(error_string) }, - filename.to_string(), + filename.as_ref().display().to_string(), 0, 0, )) diff --git a/rust/src/project.rs b/rust/src/project.rs index 7188ed59d6..d758a9535d 100644 --- a/rust/src/project.rs +++ b/rust/src/project.rs @@ -14,7 +14,7 @@ use crate::progress::{NoProgressCallback, ProgressCallback}; use crate::project::file::ProjectFile; use crate::project::folder::ProjectFolder; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; -use crate::string::{BnString, IntoCStr}; +use crate::string::{BnPath, BnString, IntoCStr}; pub mod file; pub mod folder; @@ -45,7 +45,7 @@ impl Project { /// * `path` - Path to the project directory (.bnpr) /// * `name` - Name of the new project pub fn create(path: impl AsRef, name: &str) -> Option> { - let path_raw = path.as_ref().to_cstr(); + let path_raw = BnPath::new(path.as_ref()); let name_raw = name.to_cstr(); let handle = unsafe { BNCreateProject(path_raw.as_ptr(), name_raw.as_ptr()) }; NonNull::new(handle).map(|h| unsafe { Self::ref_from_raw(h) }) @@ -55,7 +55,7 @@ impl Project { /// /// * `path` - Path to the project directory (.bnpr) or project metadata file (.bnpm) pub fn open_project(path: impl AsRef) -> Option> { - let path_raw = path.as_ref().to_cstr(); + let path_raw = BnPath::new(path.as_ref()); let handle = unsafe { BNOpenProject(path_raw.as_ptr()) }; NonNull::new(handle).map(|h| unsafe { Self::ref_from_raw(h) }) } @@ -90,8 +90,7 @@ impl Project { /// Get the path on disk for the project pub fn path(&self) -> PathBuf { - let path_str = unsafe { BnString::into_string(BNProjectGetPath(self.handle.as_ptr())) }; - PathBuf::from(path_str) + unsafe { BnString::into_path_buf(BNProjectGetPath(self.handle.as_ptr())) } } /// Get the name of the project @@ -173,7 +172,7 @@ impl Project { where PC: ProgressCallback, { - let path_raw = path.as_ref().to_cstr(); + let path_raw = BnPath::new(path.as_ref()); let description_raw = description.to_cstr(); let parent_ptr = parent.map(|p| p.handle.as_ptr()).unwrap_or(null_mut()); @@ -338,7 +337,7 @@ impl Project { where PC: ProgressCallback, { - let path_raw = path.as_ref().to_cstr(); + let path_raw = BnPath::new(path.as_ref()); let name_raw = name.to_cstr(); let description_raw = description.to_cstr(); let folder_ptr = folder.map(|p| p.handle.as_ptr()).unwrap_or(null_mut()); @@ -408,7 +407,7 @@ impl Project { where PC: ProgressCallback, { - let path_raw = path.as_ref().to_cstr(); + let path_raw = BnPath::new(path.as_ref()); let name_raw = name.to_cstr(); let description_raw = description.to_cstr(); let id_raw = id.to_cstr(); @@ -574,7 +573,7 @@ impl Project { /// Retrieve a file in the project by the `path` on disk pub fn file_by_path(&self, path: &Path) -> Option> { - let path_raw = path.to_cstr(); + let path_raw = BnPath::new(path); let result = unsafe { BNProjectGetFileByPathOnDisk(self.handle.as_ptr(), path_raw.as_ptr()) }; let handle = NonNull::new(result)?; diff --git a/rust/src/project/file.rs b/rust/src/project/file.rs index d016d377d4..f691e8f4b3 100644 --- a/rust/src/project/file.rs +++ b/rust/src/project/file.rs @@ -1,6 +1,6 @@ use crate::project::{systime_from_bntime, Project, ProjectFolder}; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; -use crate::string::{BnString, IntoCStr}; +use crate::string::{BnPath, BnString, IntoCStr}; use binaryninjacore_sys::{ BNFreeProjectFile, BNFreeProjectFileList, BNNewProjectFileReference, BNProjectFile, BNProjectFileAddDependency, BNProjectFileExistsOnDisk, BNProjectFileExport, @@ -44,16 +44,16 @@ impl ProjectFile { if !self.exists_on_disk() { return None; } - let path_str = - unsafe { BnString::into_string(BNProjectFileGetPathOnDisk(self.handle.as_ptr())) }; - Some(PathBuf::from(path_str)) + Some(unsafe { BnString::into_path_buf(BNProjectFileGetPathOnDisk(self.handle.as_ptr())) }) } /// Get the path in the project to this file's contents pub fn path_in_project(&self) -> PathBuf { - let path_str = - unsafe { BnString::into_string(BNProjectFileGetPathInProject(self.handle.as_ptr())) }; - PathBuf::from(path_str) + unsafe { + PathBuf::from(BnString::into_string(BNProjectFileGetPathInProject( + self.handle.as_ptr(), + ))) + } } /// Check if this file's contents exist on disk @@ -110,7 +110,7 @@ impl ProjectFile { /// /// * `dest` - Destination file path for the exported contents, passing a directory will append the file name. pub fn export(&self, dest: &Path) -> bool { - let dest_raw = dest.to_cstr(); + let dest_raw = BnPath::new(dest); unsafe { BNProjectFileExport(self.handle.as_ptr(), dest_raw.as_ptr()) } } diff --git a/rust/src/project/folder.rs b/rust/src/project/folder.rs index e14b84a804..c4f6f60825 100644 --- a/rust/src/project/folder.rs +++ b/rust/src/project/folder.rs @@ -2,7 +2,7 @@ use crate::progress::{NoProgressCallback, ProgressCallback}; use crate::project::file::ProjectFile; use crate::project::Project; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; -use crate::string::{BnString, IntoCStr}; +use crate::string::{BnPath, BnString, IntoCStr}; use binaryninjacore_sys::{ BNFreeProjectFolder, BNFreeProjectFolderList, BNNewProjectFolderReference, BNProjectFolder, BNProjectFolderExport, BNProjectFolderGetDescription, BNProjectFolderGetFiles, @@ -92,7 +92,7 @@ impl ProjectFolder { where P: ProgressCallback, { - let dest_raw = dest.to_cstr(); + let dest_raw = BnPath::new(dest); unsafe { BNProjectFolderExport( self.handle.as_ptr(), diff --git a/rust/src/repository.rs b/rust/src/repository.rs index 18946eac2f..888616f114 100644 --- a/rust/src/repository.rs +++ b/rust/src/repository.rs @@ -11,7 +11,7 @@ use std::ptr::NonNull; use binaryninjacore_sys::*; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; -use crate::string::{BnString, IntoCStr}; +use crate::string::{BnPath, BnString}; pub use manager::RepositoryManager; pub use plugin::{Extension, ExtensionVersion, ExtensionVersionPlatform}; @@ -44,8 +44,7 @@ impl Repository { pub fn path(&self) -> PathBuf { let result = unsafe { BNRepositoryGetRepoPath(self.handle.as_ptr()) }; assert!(!result.is_null()); - let result_str = unsafe { BnString::into_string(result as *mut c_char) }; - PathBuf::from(result_str) + unsafe { BnString::into_path_buf(result) } } /// List of RepoPlugin objects contained within this repository @@ -57,7 +56,7 @@ impl Repository { } pub fn plugin_by_path(&self, path: &Path) -> Option> { - let path = path.to_cstr(); + let path = BnPath::new(path); let result = unsafe { BNRepositoryGetPluginByPath(self.handle.as_ptr(), path.as_ptr()) }; NonNull::new(result).map(|h| unsafe { Extension::ref_from_raw(h) }) } @@ -66,8 +65,7 @@ impl Repository { pub fn full_path(&self) -> PathBuf { let result = unsafe { BNRepositoryGetPluginsPath(self.handle.as_ptr()) }; assert!(!result.is_null()); - let result_str = unsafe { BnString::into_string(result as *mut c_char) }; - PathBuf::from(result_str) + unsafe { BnString::into_path_buf(result) } } } diff --git a/rust/src/repository/manager.rs b/rust/src/repository/manager.rs index d20539c42b..f533745621 100644 --- a/rust/src/repository/manager.rs +++ b/rust/src/repository/manager.rs @@ -1,6 +1,6 @@ use crate::rc::{Array, Ref}; use crate::repository::Repository; -use crate::string::IntoCStr; +use crate::string::{BnPath, IntoCStr}; use binaryninjacore_sys::{ BNRepositoryGetRepositoryByPath, BNRepositoryManagerAddRepository, BNRepositoryManagerCheckForUpdates, BNRepositoryManagerGetDefaultRepository, @@ -41,12 +41,12 @@ impl RepositoryManager { /// Returns true if the repository was successfully added, false otherwise. pub fn add_repository(url: &str, repository_path: &Path) -> bool { let url = url.to_cstr(); - let repo_path = repository_path.to_cstr(); + let repo_path = BnPath::new(repository_path); unsafe { BNRepositoryManagerAddRepository(url.as_ptr(), repo_path.as_ptr()) } } pub fn repository_by_path(path: &Path) -> Option { - let path = path.to_cstr(); + let path = BnPath::new(path); let result = unsafe { BNRepositoryGetRepositoryByPath(path.as_ptr()) }; NonNull::new(result).map(|raw| unsafe { Repository::from_raw(raw) }) } diff --git a/rust/src/repository/plugin.rs b/rust/src/repository/plugin.rs index 9a8dba913c..b6421ac1b5 100644 --- a/rust/src/repository/plugin.rs +++ b/rust/src/repository/plugin.rs @@ -180,16 +180,14 @@ impl Extension { pub fn path(&self) -> PathBuf { let result = unsafe { BNPluginGetPath(self.handle.as_ptr()) }; assert!(!result.is_null()); - let result_str = unsafe { BnString::into_string(result as *mut c_char) }; - PathBuf::from(result_str) + unsafe { BnString::into_path_buf(result) } } /// Optional sub-directory the plugin code lives in as a relative path from the plugin root pub fn subdir(&self) -> PathBuf { let result = unsafe { BNPluginGetSubdir(self.handle.as_ptr()) }; assert!(!result.is_null()); - let result_str = unsafe { BnString::into_string(result as *mut c_char) }; - PathBuf::from(result_str) + unsafe { PathBuf::from(BnString::into_string(result as *mut c_char)) } } /// Dependencies required for installing this plugin diff --git a/rust/src/string.rs b/rust/src/string.rs index 001fc3e3a5..d0066dee5b 100644 --- a/rust/src/string.rs +++ b/rust/src/string.rs @@ -22,6 +22,7 @@ use std::hash::{Hash, Hasher}; use std::mem; use std::ops::Deref; use std::path::{Path, PathBuf}; +use std::ptr::NonNull; use crate::rc::*; use crate::types::QualifiedName; @@ -66,6 +67,70 @@ pub struct BnString { raw: *mut c_char, } +#[repr(transparent)] +pub struct BnPath { + raw: NonNull, +} + +impl BnPath { + pub fn new(path: &Path) -> Self { + let raw = unsafe { + #[cfg(windows)] + { + use std::os::windows::ffi::OsStrExt; + let path_data: Vec = path.as_os_str().encode_wide().collect(); + BNCreatePath(path_data.as_ptr() as *const _, path_data.len()) + } + #[cfg(unix)] + { + use std::os::unix::ffi::OsStrExt; + let path_data = path.as_os_str().as_bytes(); + BNCreatePath(path_data.as_ptr() as *const _, path_data.len()) + } + #[cfg(not(any(windows, unix)))] + { + let path_data = path.to_string_lossy(); + BNCreatePath(path_data.as_bytes().as_ptr() as *const _, path_data.len()) + } + }; + Self { + raw: NonNull::new(raw).expect("core failed to allocate path"), + } + } + + pub fn as_ptr(&self) -> *mut BNPath { + self.raw.as_ptr() + } + + pub fn into_raw(value: Self) -> *mut BNPath { + let res = value.raw.as_ptr(); + mem::forget(value); + res + } +} + +impl Drop for BnPath { + fn drop(&mut self) { + unsafe { BNFreePath(self.raw.as_ptr()) }; + } +} + +impl CoreArrayProvider for BnPath { + type Raw = *mut BNPath; + type Context = (); + type Wrapped<'a> = PathBuf; +} + +unsafe impl CoreArrayProviderInner for BnPath { + unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { + BNFreePathList(raw, count); + } + + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { + unsafe { BnString::path_buf_from_raw(*raw) } + } +} + impl BnString { pub fn new(s: impl IntoCStr) -> Self { let raw = s.to_cstr(); @@ -79,6 +144,44 @@ impl BnString { Self::from_raw(raw).to_string_lossy().to_string() } + /// Take an owned core path and convert it to [`PathBuf`]. + /// + /// Core paths are UTF-16 on Windows and native bytes on Unix. + pub unsafe fn path_buf_from_raw(raw: *mut BNPath) -> PathBuf { + let mut size = 0usize; + let data = BNGetPathData(raw, &mut size); + if data.is_null() { + PathBuf::new() + } else { + #[cfg(windows)] + { + use std::os::windows::ffi::OsStringExt; + let path_data = std::slice::from_raw_parts(data as *const u16, size); + PathBuf::from(std::ffi::OsString::from_wide(path_data)) + } + #[cfg(unix)] + { + use std::os::unix::ffi::OsStringExt; + let path_data = std::slice::from_raw_parts(data as *const u8, size); + PathBuf::from(std::ffi::OsString::from_vec(path_data.to_vec())) + } + #[cfg(not(any(windows, unix)))] + { + let path_data = std::slice::from_raw_parts(data as *const u8, size); + PathBuf::from(String::from_utf8_lossy(path_data).into_owned()) + } + } + } + + /// Take an owned core path and convert it to [`PathBuf`]. + /// + /// Core paths are UTF-16 on Windows and native bytes on Unix. + pub unsafe fn into_path_buf(raw: *mut BNPath) -> PathBuf { + let path = unsafe { Self::path_buf_from_raw(raw) }; + BNFreePath(raw); + path + } + /// Construct a BnString from an owned const char* allocated by [`BNAllocString`]. pub(crate) unsafe fn from_raw(raw: *mut c_char) -> Self { Self { raw } @@ -282,8 +385,25 @@ impl IntoCStr for &Path { type Result = CString; fn to_cstr(self) -> Self::Result { - CString::new(self.as_os_str().as_encoded_bytes()) + #[cfg(windows)] + { + CString::new( + self.to_str() + .expect("can't pass paths with unpaired surrogates to core!"), + ) .expect("can't pass paths with internal nul bytes to core!") + } + #[cfg(unix)] + { + use std::os::unix::ffi::OsStrExt; + CString::new(self.as_os_str().as_bytes()) + .expect("can't pass paths with internal nul bytes to core!") + } + #[cfg(not(any(windows, unix)))] + { + CString::new(self.to_string_lossy().as_bytes()) + .expect("can't pass paths with internal nul bytes to core!") + } } } diff --git a/rust/src/types/archive.rs b/rust/src/types/archive.rs index 15f4cbfeb4..ba9c1b522b 100644 --- a/rust/src/types/archive.rs +++ b/rust/src/types/archive.rs @@ -10,7 +10,7 @@ use crate::data_buffer::DataBuffer; use crate::metadata::Metadata; use crate::platform::Platform; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; -use crate::string::{raw_to_string, BnString, IntoCStr}; +use crate::string::{raw_to_string, BnPath, BnString, IntoCStr}; use crate::types::{ QualifiedName, QualifiedNameAndType, QualifiedNameTypeAndId, Type, TypeContainer, }; @@ -84,7 +84,7 @@ impl TypeArchive { /// Open the Type Archive at the given path, if it exists. pub fn open(path: impl AsRef) -> Option> { - let raw_path = path.as_ref().to_cstr(); + let raw_path = BnPath::new(path.as_ref()); let handle = unsafe { BNOpenTypeArchive(raw_path.as_ptr()) }; NonNull::new(handle).map(|handle| unsafe { TypeArchive::ref_from_raw(handle) }) } @@ -93,7 +93,7 @@ impl TypeArchive { /// /// If the file has already been created and is not a valid type archive this will return `None`. pub fn create(path: impl AsRef, platform: &Platform) -> Option> { - let raw_path = path.as_ref().to_cstr(); + let raw_path = BnPath::new(path.as_ref()); let handle = unsafe { BNCreateTypeArchive(raw_path.as_ptr(), platform.handle) }; NonNull::new(handle).map(|handle| unsafe { TypeArchive::ref_from_raw(handle) }) } @@ -106,7 +106,7 @@ impl TypeArchive { id: &TypeArchiveId, platform: &Platform, ) -> Option> { - let raw_path = path.as_ref().to_cstr(); + let raw_path = BnPath::new(path.as_ref()); let id = id.0.as_str().to_cstr(); let handle = unsafe { BNCreateTypeArchiveWithId(raw_path.as_ptr(), platform.handle, id.as_ptr()) }; @@ -124,8 +124,7 @@ impl TypeArchive { pub fn path(&self) -> Option { let result = unsafe { BNGetTypeArchivePath(self.handle.as_ptr()) }; assert!(!result.is_null()); - let path_str = unsafe { BnString::into_string(result) }; - Some(PathBuf::from(path_str)) + Some(unsafe { BnString::into_path_buf(result) }) } /// Get the guid for a Type Archive @@ -668,7 +667,7 @@ impl TypeArchive { /// Determine if `file` is a Type Archive pub fn is_type_archive(file: &Path) -> bool { - let file = file.to_cstr(); + let file = BnPath::new(file); unsafe { BNIsTypeArchive(file.as_ptr()) } } diff --git a/rust/src/types/container.rs b/rust/src/types/container.rs index 5096ac78c0..17a88e64cb 100644 --- a/rust/src/types/container.rs +++ b/rust/src/types/container.rs @@ -11,12 +11,13 @@ use crate::platform::Platform; use crate::progress::{NoProgressCallback, ProgressCallback}; use crate::rc::{Array, Ref}; -use crate::string::{raw_to_string, BnString, IntoCStr}; +use crate::string::{raw_to_string, BnPath, BnString, IntoCStr}; use crate::types::{QualifiedName, QualifiedNameAndType, Type, TypeParserError, TypeParserResult}; use binaryninjacore_sys::*; use std::collections::HashMap; use std::ffi::{c_char, c_void}; use std::fmt::{Debug, Formatter}; +use std::path::Path; use std::ptr::NonNull; pub type TypeContainerType = BNTypeContainerType; @@ -327,7 +328,7 @@ impl TypeContainer { pub fn parse_types_from_source( &self, source: &str, - filename: &str, + filename: &impl AsRef, options: O, include_directories: I, auto_type_source: &str, @@ -335,17 +336,18 @@ impl TypeContainer { ) -> Result> where O: IntoIterator, - I: IntoIterator, + I: IntoIterator, + I::Item: AsRef, { let source = source.to_cstr(); - let filename = filename.to_cstr(); + let filename = BnPath::new(filename.as_ref()); let options: Vec<_> = options.into_iter().map(|o| o.to_cstr()).collect(); let options_raw: Vec<*const c_char> = options.iter().map(|o| o.as_ptr()).collect(); let include_directories: Vec<_> = include_directories .into_iter() - .map(|d| d.to_cstr()) + .map(|d| BnPath::new(d.as_ref())) .collect(); - let include_directories_raw: Vec<*const c_char> = + let include_directories_raw: Vec<*mut BNPath> = include_directories.iter().map(|d| d.as_ptr()).collect(); let auto_type_source = auto_type_source.to_cstr(); let mut raw_result = BNTypeParserResult::default(); @@ -358,7 +360,7 @@ impl TypeContainer { filename.as_ptr(), options_raw.as_ptr(), options_raw.len(), - include_directories_raw.as_ptr(), + include_directories_raw.as_ptr() as *mut *mut BNPath, include_directories_raw.len(), auto_type_source.as_ptr(), import_dependencies, diff --git a/rust/src/types/library.rs b/rust/src/types/library.rs index 9b543362ab..682ee6c332 100644 --- a/rust/src/types/library.rs +++ b/rust/src/types/library.rs @@ -5,7 +5,7 @@ use crate::{ metadata::Metadata, platform::Platform, rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref}, - string::{BnString, IntoCStr}, + string::{BnPath, BnString, IntoCStr}, types::{QualifiedName, QualifiedNameAndType, Type}, }; use binaryninjacore_sys::*; @@ -74,7 +74,7 @@ impl TypeLibrary { /// /// The returned type library cannot be modified. pub fn load_from_file(path: &Path) -> Option> { - let path = path.to_cstr(); + let path = BnPath::new(path); let handle = unsafe { BNLoadTypeLibraryFromFile(path.as_ptr()) }; NonNull::new(handle).map(|h| unsafe { TypeLibrary::ref_from_raw(h) }) } @@ -83,13 +83,13 @@ impl TypeLibrary { /// /// The path must be writable, and the parent directory must exist. pub fn write_to_file(&self, path: &Path) -> bool { - let path = path.to_cstr(); + let path = BnPath::new(path); unsafe { BNWriteTypeLibraryToFile(self.as_raw(), path.as_ptr()) } } /// Decompresses the type library file to a JSON file at the given `output_path`. pub fn decompress_to_file(&self, output_path: &Path) -> bool { - let path = output_path.to_cstr(); + let path = BnPath::new(output_path); unsafe { BNTypeLibraryDecompressToFile(self.handle.as_ptr(), path.as_ptr()) } } diff --git a/rust/src/types/parser.rs b/rust/src/types/parser.rs index 9bcdf41aa9..5f7559d541 100644 --- a/rust/src/types/parser.rs +++ b/rust/src/types/parser.rs @@ -2,12 +2,12 @@ use binaryninjacore_sys::*; use std::ffi::{c_char, c_void}; use std::fmt::Debug; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::ptr::NonNull; use crate::platform::Platform; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref}; -use crate::string::{raw_to_string, BnString, IntoCStr}; +use crate::string::{raw_to_string, BnPath, BnString, IntoCStr}; use crate::types::{QualifiedName, QualifiedNameAndType, Type, TypeContainer}; pub type TypeParserErrorSeverity = BNTypeParserErrorSeverity; @@ -80,21 +80,21 @@ impl TypeParser for CoreTypeParser { fn preprocess_source( &self, source: &str, - file_name: &str, + file_name: &Path, platform: &Platform, existing_types: &TypeContainer, options: &[String], include_directories: &[PathBuf], ) -> Result> { let source_cstr = BnString::new(source); - let file_name_cstr = BnString::new(file_name); + let file_name = BnPath::new(file_name); let options: Vec<_> = options.iter().map(|o| o.to_cstr()).collect(); let options_raw: Vec<*const c_char> = options.iter().map(|o| o.as_ptr()).collect(); let include_directories: Vec<_> = include_directories .iter() - .map(|d| d.clone().to_cstr()) + .map(|d| BnPath::new(d.as_path())) .collect(); - let include_directories_raw: Vec<*const c_char> = + let include_directories_raw: Vec<*mut BNPath> = include_directories.iter().map(|d| d.as_ptr()).collect(); let mut result = std::ptr::null_mut(); let mut errors = std::ptr::null_mut(); @@ -103,12 +103,12 @@ impl TypeParser for CoreTypeParser { BNTypeParserPreprocessSource( self.handle.as_ptr(), source_cstr.as_ptr(), - file_name_cstr.as_ptr(), + file_name.as_ptr(), platform.handle, existing_types.handle.as_ptr(), options_raw.as_ptr(), options_raw.len(), - include_directories_raw.as_ptr(), + include_directories_raw.as_ptr() as *mut *mut BNPath, include_directories_raw.len(), &mut result, &mut errors, @@ -128,7 +128,7 @@ impl TypeParser for CoreTypeParser { fn parse_types_from_source( &self, source: &str, - file_name: &str, + file_name: &Path, platform: &Platform, existing_types: &TypeContainer, options: &[String], @@ -136,14 +136,14 @@ impl TypeParser for CoreTypeParser { auto_type_source: &str, ) -> Result> { let source_cstr = BnString::new(source); - let file_name_cstr = BnString::new(file_name); + let file_name = BnPath::new(file_name); let options: Vec<_> = options.iter().map(|o| o.to_cstr()).collect(); let options_raw: Vec<*const c_char> = options.iter().map(|o| o.as_ptr()).collect(); let include_directories: Vec<_> = include_directories .iter() - .map(|d| d.clone().to_cstr()) + .map(|d| BnPath::new(d.as_path())) .collect(); - let include_directories_raw: Vec<*const c_char> = + let include_directories_raw: Vec<*mut BNPath> = include_directories.iter().map(|d| d.as_ptr()).collect(); let auto_type_source = BnString::new(auto_type_source); let mut raw_result = BNTypeParserResult::default(); @@ -153,12 +153,12 @@ impl TypeParser for CoreTypeParser { BNTypeParserParseTypesFromSource( self.handle.as_ptr(), source_cstr.as_ptr(), - file_name_cstr.as_ptr(), + file_name.as_ptr(), platform.handle, existing_types.handle.as_ptr(), options_raw.as_ptr(), options_raw.len(), - include_directories_raw.as_ptr(), + include_directories_raw.as_ptr() as *mut *mut BNPath, include_directories_raw.len(), auto_type_source.as_ptr(), &mut raw_result, @@ -235,7 +235,7 @@ pub trait TypeParser { fn preprocess_source( &self, source: &str, - file_name: &str, + file_name: &Path, platform: &Platform, existing_types: &TypeContainer, options: &[String], @@ -254,7 +254,7 @@ pub trait TypeParser { fn parse_types_from_source( &self, source: &str, - file_name: &str, + file_name: &Path, platform: &Platform, existing_types: &TypeContainer, options: &[String], @@ -534,12 +534,12 @@ unsafe extern "C" fn cb_get_option_text( unsafe extern "C" fn cb_preprocess_source( ctxt: *mut c_void, source: *const c_char, - file_name: *const c_char, + file_name: *mut BNPath, platform: *mut BNPlatform, existing_types: *mut BNTypeContainer, options: *const *const c_char, option_count: usize, - include_dirs: *const *const c_char, + include_dirs: *mut *mut BNPath, include_dir_count: usize, result: *mut *mut c_char, errors: *mut *mut BNTypeParserError, @@ -557,11 +557,13 @@ unsafe extern "C" fn cb_preprocess_source( let includes_raw = unsafe { std::slice::from_raw_parts(include_dirs, include_dir_count) }; let includes: Vec<_> = includes_raw .iter() - .filter_map(|&r| Some(PathBuf::from(raw_to_string(r)?))) + .filter(|&&r| !r.is_null()) + .map(|&r| unsafe { BnString::path_buf_from_raw(r) }) .collect(); + let file_name = unsafe { BnString::path_buf_from_raw(file_name) }; match ctxt.preprocess_source( &raw_to_string(source).unwrap(), - &raw_to_string(file_name).unwrap(), + file_name.as_path(), &platform, &existing_types, &options, @@ -593,12 +595,12 @@ unsafe extern "C" fn cb_preprocess_source( unsafe extern "C" fn cb_parse_types_from_source( ctxt: *mut c_void, source: *const c_char, - file_name: *const c_char, + file_name: *mut BNPath, platform: *mut BNPlatform, existing_types: *mut BNTypeContainer, options: *const *const c_char, option_count: usize, - include_dirs: *const *const c_char, + include_dirs: *mut *mut BNPath, include_dir_count: usize, auto_type_source: *const c_char, result: *mut BNTypeParserResult, @@ -617,11 +619,13 @@ unsafe extern "C" fn cb_parse_types_from_source( let includes_raw = unsafe { std::slice::from_raw_parts(include_dirs, include_dir_count) }; let includes: Vec<_> = includes_raw .iter() - .filter_map(|&r| Some(PathBuf::from(raw_to_string(r)?))) + .filter(|&&r| !r.is_null()) + .map(|&r| unsafe { BnString::path_buf_from_raw(r) }) .collect(); + let file_name = unsafe { BnString::path_buf_from_raw(file_name) }; match ctxt.parse_types_from_source( &raw_to_string(source).unwrap(), - &raw_to_string(file_name).unwrap(), + file_name.as_path(), &platform, &existing_types, &options, diff --git a/rust/tests/interaction.rs b/rust/tests/interaction.rs index 2f38241cd8..b29bbc1280 100644 --- a/rust/tests/interaction.rs +++ b/rust/tests/interaction.rs @@ -96,8 +96,11 @@ impl InteractionHandler for MyInteractionHandler { default, .. } => { - let new_value = format!("example{}", default.clone().unwrap_or_default(),); - *value = Some(new_value); + let suffix = default + .as_ref() + .map(|path| path.as_os_str().to_string_lossy()) + .unwrap_or_default(); + *value = Some(PathBuf::from(format!("example{}", suffix))); true } _ => false, @@ -129,14 +132,15 @@ fn test_get_directory_default() { let mut my_form = Form::new("get_dir_default"); my_form.add_field(FormInputField::DirectoryName { prompt: "get_dir_default".to_string(), - default: Some("_default".to_string()), + default_name: None, + default: Some(PathBuf::from("_default")), value: None, }); assert_eq!(my_form.prompt(), true); assert_eq!( - my_form.fields[0].try_value_string(), - Some("example_default".to_string()) + my_form.fields[0].try_value_path(), + Some(PathBuf::from("example_default")) ) } diff --git a/rust/tests/type_parser.rs b/rust/tests/type_parser.rs index 64771404d2..8cbde5a627 100644 --- a/rust/tests/type_parser.rs +++ b/rust/tests/type_parser.rs @@ -47,7 +47,7 @@ fn test_string_to_types() { let parsed_type = parser .parse_types_from_source( TEST_TYPES, - "test_file.h", + std::path::Path::new("test_file.h"), &platform, &plat_type_container, &[], diff --git a/scriptingprovider.cpp b/scriptingprovider.cpp index 95726d6dc5..016da46875 100644 --- a/scriptingprovider.cpp +++ b/scriptingprovider.cpp @@ -1,4 +1,5 @@ #include "binaryninjaapi.h" +#include "pathhelpers.h" using namespace BinaryNinja; using namespace std; @@ -99,10 +100,10 @@ BNScriptingProviderExecuteResult ScriptingInstance::ExecuteScriptInputCallback(v } -BNScriptingProviderExecuteResult ScriptingInstance::ExecuteScriptFromFilenameCallback(void* ctxt, const char* filename) +BNScriptingProviderExecuteResult ScriptingInstance::ExecuteScriptFromFilenameCallback(void* ctxt, BNPath* filename) { CallbackRef instance(ctxt); - return instance->ExecuteScriptInputFromFilename(filename); + return instance->ExecuteScriptInputFromFilename(BinaryNinja::Path::PathFromCoreBorrowed(filename)); } @@ -310,9 +311,10 @@ BNScriptingProviderExecuteResult CoreScriptingInstance::ExecuteScriptInput(const return BNExecuteScriptInput(m_object, input.c_str()); } -BNScriptingProviderExecuteResult CoreScriptingInstance::ExecuteScriptInputFromFilename(const string& filename) +BNScriptingProviderExecuteResult CoreScriptingInstance::ExecuteScriptInputFromFilename(const filesystem::path& filename) { - return BNExecuteScriptInputFromFilename(m_object, filename.c_str()); + BinaryNinja::Path::APIObject coreFilename(filename); + return BNExecuteScriptInputFromFilename(m_object, coreFilename); } void CoreScriptingInstance::CancelScriptInput() @@ -409,10 +411,11 @@ BNScriptingInstance* ScriptingProvider::CreateInstanceCallback(void* ctxt) } -bool ScriptingProvider::LoadModuleCallback(void* ctxt, const char* repository, const char* module, bool force) +bool ScriptingProvider::LoadModuleCallback(void* ctxt, BNPath* repository, BNPath* module, bool force) { ScriptingProvider* provider = (ScriptingProvider*)ctxt; - return BNLoadScriptingProviderModule(provider->GetObject(), repository, module, force); + return provider->LoadModule( + BinaryNinja::Path::PathFromCoreBorrowed(repository), BinaryNinja::Path::PathFromCoreBorrowed(module), force); } @@ -496,9 +499,11 @@ Ref CoreScriptingProvider::CreateNewInstance() } -bool CoreScriptingProvider::LoadModule(const std::string& repository, const std::string& module, bool force) +bool CoreScriptingProvider::LoadModule(const filesystem::path& repository, const filesystem::path& module, bool force) { - return BNLoadScriptingProviderModule(m_object, repository.c_str(), module.c_str(), force); + BinaryNinja::Path::APIObject coreRepository(repository); + BinaryNinja::Path::APIObject coreModule(module); + return BNLoadScriptingProviderModule(m_object, coreRepository, coreModule, force); } diff --git a/settings.cpp b/settings.cpp index d06e309b23..a3559b4db3 100644 --- a/settings.cpp +++ b/settings.cpp @@ -1,4 +1,5 @@ #include "binaryninjaapi.h" +#include "pathhelpers.h" #include using namespace BinaryNinja; @@ -27,9 +28,10 @@ Ref Settings::Instance(const std::string& instanceId) } -bool Settings::LoadSettingsFile(const string& fileName, BNSettingsScope scope, Ref view) +bool Settings::LoadSettingsFile(const std::filesystem::path& fileName, BNSettingsScope scope, Ref view) { - return BNLoadSettingsFile(m_object, fileName.c_str(), scope, view ? view->GetObject() : nullptr); + Path::APIObject coreFileName(fileName); + return BNLoadSettingsFile(m_object, coreFileName, scope, view ? view->GetObject() : nullptr); } @@ -258,6 +260,13 @@ string Settings::Get(const string& key, Ref view, BNSettings } +template <> +std::filesystem::path Settings::Get(const string& key, Ref view, BNSettingsScope* scope) +{ + return Path::Utf8ToPath(Get(key, view, scope)); +} + + template <> vector Settings::Get>(const string& key, Ref view, BNSettingsScope* scope) { @@ -326,6 +335,12 @@ bool Settings::Set(const string& key, const string& value, Ref view, } +bool Settings::Set(const string& key, const std::filesystem::path& value, Ref view, BNSettingsScope scope) +{ + return Set(key, Path::PathToUtf8String(value), view, scope); +} + + bool Settings::Set(const string& key, const vector& value, Ref view, BNSettingsScope scope) { char** buffer = new char*[value.size()]; @@ -422,6 +437,13 @@ string Settings::Get(const string& key, Ref func, BNSettingsSc } +template <> +std::filesystem::path Settings::Get(const string& key, Ref func, BNSettingsScope* scope) +{ + return Path::Utf8ToPath(Get(key, func, scope)); +} + + template <> vector Settings::Get>(const string& key, Ref func, BNSettingsScope* scope) { @@ -490,6 +512,12 @@ bool Settings::Set(const string& key, const string& value, Ref func, B } +bool Settings::Set(const string& key, const std::filesystem::path& value, Ref func, BNSettingsScope scope) +{ + return Set(key, Path::PathToUtf8String(value), func, scope); +} + + bool Settings::Set(const string& key, const vector& value, Ref func, BNSettingsScope scope) { char** buffer = new char*[value.size()]; diff --git a/tempfile.cpp b/tempfile.cpp index a59febe118..896a5522dc 100644 --- a/tempfile.cpp +++ b/tempfile.cpp @@ -19,6 +19,7 @@ // IN THE SOFTWARE. #include "binaryninjaapi.h" +#include "pathhelpers.h" using namespace BinaryNinja; using namespace std; @@ -49,15 +50,12 @@ TemporaryFile::TemporaryFile(BNTemporaryFile* file) } -string TemporaryFile::GetPath() const +filesystem::path TemporaryFile::GetPath() const { if (!m_object) - return string(); + return {}; - char* str = BNGetTemporaryFilePath(m_object); - string result = str; - BNFreeString(str); - return result; + return Path::PathFromCore(BNGetTemporaryFilePath(m_object)); } diff --git a/transformcontext.cpp b/transformcontext.cpp index 2cdb2a2c23..47efce2292 100644 --- a/transformcontext.cpp +++ b/transformcontext.cpp @@ -33,6 +33,12 @@ string TransformContext::GetFileName() const } +std::filesystem::path TransformContext::GetFilePath() const +{ + return Path::PathFromCore(BNTransformContextGetFilePath(m_object)); +} + + vector TransformContext::GetAvailableTransforms() const { size_t count; diff --git a/transformsession.cpp b/transformsession.cpp index 2165039c9a..1cea606018 100644 --- a/transformsession.cpp +++ b/transformsession.cpp @@ -1,18 +1,21 @@ #include "binaryninjaapi.h" +#include "pathhelpers.h" using namespace BinaryNinja; using namespace std; -TransformSession::TransformSession(const string& filename, const string& options) +TransformSession::TransformSession(const filesystem::path& filename, const string& options) { - m_object = BNCreateTransformSession(filename.c_str(), options.c_str()); + Path::APIObject coreFilename(filename); + m_object = BNCreateTransformSession(coreFilename, options.c_str()); } -TransformSession::TransformSession(const string& filename, BNTransformSessionMode mode, const string& options) +TransformSession::TransformSession(const filesystem::path& filename, BNTransformSessionMode mode, const string& options) { - m_object = BNCreateTransformSessionWithMode(filename.c_str(), mode, options.c_str()); + Path::APIObject coreFilename(filename); + m_object = BNCreateTransformSessionWithMode(coreFilename, mode, options.c_str()); } diff --git a/type.cpp b/type.cpp index ff4f8eb02d..376fc72cab 100644 --- a/type.cpp +++ b/type.cpp @@ -19,6 +19,7 @@ // IN THE SOFTWARE. #include "binaryninjaapi.h" +#include "pathhelpers.h" #include using namespace BinaryNinja; @@ -3672,24 +3673,21 @@ EnumerationBuilder& EnumerationBuilder::ReplaceMember(size_t idx, const string& bool BinaryNinja::PreprocessSource( - const string& source, const string& fileName, string& output, string& errors, const vector& includeDirs) + const string& source, const filesystem::path& fileName, string& output, string& errors, const vector& includeDirs) { char* outStr; char* errorStr; - const char** includeDirList = new const char*[includeDirs.size()]; + Path::APIObjectList includeDirList(includeDirs); - for (size_t i = 0; i < includeDirs.size(); i++) - includeDirList[i] = includeDirs[i].c_str(); - - bool result = - BNPreprocessSource(source.c_str(), fileName.c_str(), &outStr, &errorStr, includeDirList, includeDirs.size()); + Path::APIObject coreFileName(fileName); + bool result = BNPreprocessSource( + source.c_str(), coreFileName, &outStr, &errorStr, includeDirList.data(), includeDirList.size()); output = outStr; errors = errorStr; BNFreeString(outStr); BNFreeString(errorStr); - delete[] includeDirList; return result; } diff --git a/typearchive.cpp b/typearchive.cpp index 79671abc16..ed0ec3d500 100644 --- a/typearchive.cpp +++ b/typearchive.cpp @@ -19,6 +19,7 @@ // IN THE SOFTWARE. #include "binaryninjaapi.h" +#include "pathhelpers.h" using namespace BinaryNinja; @@ -77,27 +78,30 @@ TypeArchive::TypeArchive(BNTypeArchive* archive) } -Ref TypeArchive::Open(const std::string& path) +Ref TypeArchive::Open(const std::filesystem::path& path) { - BNTypeArchive* handle = BNOpenTypeArchive(path.c_str()); + Path::APIObject corePath(path); + BNTypeArchive* handle = BNOpenTypeArchive(corePath); if (!handle) return nullptr; return new TypeArchive(handle); } -Ref TypeArchive::Create(const std::string& path, Ref platform) +Ref TypeArchive::Create(const std::filesystem::path& path, Ref platform) { - BNTypeArchive* handle = BNCreateTypeArchive(path.c_str(), platform->GetObject()); + Path::APIObject corePath(path); + BNTypeArchive* handle = BNCreateTypeArchive(corePath, platform->GetObject()); if (!handle) return nullptr; return new TypeArchive(handle); } -Ref TypeArchive::CreateWithId(const std::string& path, Ref platform, const std::string& id) +Ref TypeArchive::CreateWithId(const std::filesystem::path& path, Ref platform, const std::string& id) { - BNTypeArchive* handle = BNCreateTypeArchiveWithId(path.c_str(), platform->GetObject(), id.c_str()); + Path::APIObject corePath(path); + BNTypeArchive* handle = BNCreateTypeArchiveWithId(corePath, platform->GetObject(), id.c_str()); if (!handle) return nullptr; return new TypeArchive(handle); @@ -119,9 +123,10 @@ void TypeArchive::Close(Ref archive) } -bool TypeArchive::IsTypeArchive(const std::string& path) +bool TypeArchive::IsTypeArchive(const std::filesystem::path& path) { - return BNIsTypeArchive(path.c_str()); + Path::APIObject corePath(path); + return BNIsTypeArchive(corePath); } @@ -134,12 +139,9 @@ std::string TypeArchive::GetId() const } -std::string TypeArchive::GetPath() const +std::filesystem::path TypeArchive::GetPath() const { - char* str = BNGetTypeArchivePath(m_object); - std::string result(str); - BNFreeString(str); - return result; + return Path::PathFromCore(BNGetTypeArchivePath(m_object)); } diff --git a/typecontainer.cpp b/typecontainer.cpp index cbff21ee01..76fc8b2234 100644 --- a/typecontainer.cpp +++ b/typecontainer.cpp @@ -19,6 +19,7 @@ // IN THE SOFTWARE. #include "binaryninjaapi.h" +#include "pathhelpers.h" using namespace BinaryNinja; @@ -402,9 +403,9 @@ bool TypeContainer::ParseTypeString( bool TypeContainer::ParseTypesFromSource( const std::string& text, - const std::string& fileName, + const std::filesystem::path& fileName, const std::vector& options, - const std::vector& includeDirs, + const std::vector& includeDirs, const std::string& autoTypeSource, bool importDependencies, BinaryNinja::TypeParserResult& result, @@ -416,22 +417,18 @@ bool TypeContainer::ParseTypesFromSource( { apiOptions[i] = options[i].c_str(); } - const char** apiIncludeDirs = new const char*[includeDirs.size()]; - for (size_t i = 0; i < includeDirs.size(); ++i) - { - apiIncludeDirs[i] = includeDirs[i].c_str(); - } + Path::APIObjectList apiIncludeDirs(includeDirs); BNTypeParserResult apiResult; BNTypeParserError* apiErrors; size_t errorCount; - auto success = BNTypeContainerParseTypesFromSource(m_object, text.c_str(), fileName.c_str(), - apiOptions, options.size(), apiIncludeDirs, includeDirs.size(), autoTypeSource.c_str(), importDependencies, + Path::APIObject apiFileName(fileName); + auto success = BNTypeContainerParseTypesFromSource(m_object, text.c_str(), apiFileName, + apiOptions, options.size(), apiIncludeDirs.data(), apiIncludeDirs.size(), autoTypeSource.c_str(), importDependencies, &apiResult, &apiErrors, &errorCount); delete [] apiOptions; - delete [] apiIncludeDirs; for (size_t j = 0; j < errorCount; j ++) { @@ -487,9 +484,9 @@ bool TypeContainer::ParseTypesFromSource( bool TypeContainer::ParseTypesFromSource( const std::string& text, - const std::string& fileName, + const std::filesystem::path& fileName, const std::vector& options, - const std::vector& includeDirs, + const std::vector& includeDirs, const std::string& autoTypeSource, BinaryNinja::TypeParserResult& result, std::vector& errors diff --git a/typelibrary.cpp b/typelibrary.cpp index bea52459f7..64b583096a 100644 --- a/typelibrary.cpp +++ b/typelibrary.cpp @@ -1,4 +1,5 @@ #include "binaryninjaapi.h" +#include "pathhelpers.h" using namespace BinaryNinja; @@ -14,15 +15,17 @@ TypeLibrary::TypeLibrary(Ref arch, const std::string& name) } -bool TypeLibrary::DecompressToFile(const std::string& path) +bool TypeLibrary::DecompressToFile(const std::filesystem::path& path) { - return BNTypeLibraryDecompressToFile(m_object, path.c_str()); + Path::APIObject corePath(path); + return BNTypeLibraryDecompressToFile(m_object, corePath); } -Ref TypeLibrary::LoadFromFile(const std::string& path) +Ref TypeLibrary::LoadFromFile(const std::filesystem::path& path) { - return new TypeLibrary(BNLoadTypeLibraryFromFile(path.c_str())); + Path::APIObject corePath(path); + return new TypeLibrary(BNLoadTypeLibraryFromFile(corePath)); } @@ -38,9 +41,10 @@ Ref TypeLibrary::LookupByGuid(Ref arch, const std::st } -bool TypeLibrary::WriteToFile(const std::string& path) +bool TypeLibrary::WriteToFile(const std::filesystem::path& path) { - return BNWriteTypeLibraryToFile(m_object, path.c_str()); + Path::APIObject corePath(path); + return BNWriteTypeLibraryToFile(m_object, corePath); } diff --git a/typeparser.cpp b/typeparser.cpp index 18ecac0849..75fd708c63 100644 --- a/typeparser.cpp +++ b/typeparser.cpp @@ -1,5 +1,7 @@ #include "binaryninjaapi.h" +#include "pathhelpers.h" #include +#include using namespace BinaryNinja; using namespace std; @@ -134,10 +136,10 @@ bool TypeParser::GetOptionTextCallback(void* ctxt, BNTypeParserOption option, co bool TypeParser::PreprocessSourceCallback(void* ctxt, - const char* source, const char* fileName, BNPlatform* platform, + const char* source, BNPath* fileName, BNPlatform* platform, BNTypeContainer* existingTypes, const char* const* options, size_t optionCount, - const char* const* includeDirs, size_t includeDirCount, + BNPath** includeDirs, size_t includeDirCount, char** output, BNTypeParserError** errors, size_t* errorCount ) { @@ -150,18 +152,18 @@ bool TypeParser::PreprocessSourceCallback(void* ctxt, optionsCpp.push_back(options[i]); } - vector includeDirsCpp; + vector includeDirsCpp; includeDirsCpp.reserve(includeDirCount); for (size_t i = 0; i < includeDirCount; i ++) { - includeDirsCpp.push_back(includeDirs[i]); + includeDirsCpp.push_back(Path::PathFromCoreBorrowed(includeDirs[i])); } std::string outputCpp; vector errorsCpp; bool success = parser->PreprocessSource( source, - fileName, + Path::PathFromCoreBorrowed(fileName), new CorePlatform(platform), TypeContainer{BNDuplicateTypeContainer(existingTypes)}, optionsCpp, @@ -195,10 +197,10 @@ bool TypeParser::PreprocessSourceCallback(void* ctxt, bool TypeParser::ParseTypesFromSourceCallback(void* ctxt, - const char* source, const char* fileName, BNPlatform* platform, + const char* source, BNPath* fileName, BNPlatform* platform, BNTypeContainer* existingTypes, const char* const* options, size_t optionCount, - const char* const* includeDirs, size_t includeDirCount, + BNPath** includeDirs, size_t includeDirCount, const char* autoTypeSource, BNTypeParserResult* result, BNTypeParserError** errors, size_t* errorCount ) @@ -212,18 +214,18 @@ bool TypeParser::ParseTypesFromSourceCallback(void* ctxt, optionsCpp.push_back(options[i]); } - vector includeDirsCpp; + vector includeDirsCpp; includeDirsCpp.reserve(includeDirCount); for (size_t i = 0; i < includeDirCount; i ++) { - includeDirsCpp.push_back(includeDirs[i]); + includeDirsCpp.push_back(Path::PathFromCoreBorrowed(includeDirs[i])); } TypeParserResult resultCpp; vector errorsCpp; bool success = parser->ParseTypesFromSource( source, - fileName, + Path::PathFromCoreBorrowed(fileName), new CorePlatform(platform), TypeContainer{BNDuplicateTypeContainer(existingTypes)}, optionsCpp, @@ -359,49 +361,38 @@ void TypeParser::FreeErrorListCallback(void* ctxt, BNTypeParserError* errors, si } -bool TypeParser::ParseTypesFromSourceFile(const string& fileName, Ref platform, +bool TypeParser::ParseTypesFromSourceFile(const filesystem::path& fileName, Ref platform, std::optional existingTypes, const vector& options, - const vector& includeDirs, const string& autoTypeSource, TypeParserResult& result, + const vector& includeDirs, const string& autoTypeSource, TypeParserResult& result, vector& errors) { - if (!fs::is_regular_file(fileName)) + auto fileNameString = Path::PathToUtf8String(fileName); + + std::error_code ec; + if (!fs::is_regular_file(fileName, ec) || ec) { - errors.push_back(TypeParserError(FatalSeverity, string("error: argument '") + fileName + "' is not a file")); + errors.push_back(TypeParserError(FatalSeverity, string("error: argument '") + fileNameString + "' is not a file")); return false; } // Read file contents, then parse them - FILE* fp = fopen(fileName.c_str(), "rb"); + ifstream fp(fileName, ios::binary); if (!fp) { - errors.push_back(TypeParserError(FatalSeverity, string("file '") + fileName + "' not found")); - return false; - } - - fseek(fp, 0, SEEK_END); - long size = ftell(fp); - if(size == -1) - { - errors.push_back(TypeParserError(FatalSeverity, string("error: unable to open '") + fileName)); + errors.push_back(TypeParserError(FatalSeverity, string("file '") + fileNameString + "' not found")); return false; } - fseek(fp, 0, SEEK_SET); - char* data = new char[size + 2]; - if (fread(data, 1, size, fp) != (size_t)size) + string data((istreambuf_iterator(fp)), istreambuf_iterator()); + if (!fp.eof() && fp.fail()) { - errors.push_back(TypeParserError(FatalSeverity, string("error: file '") + fileName + "' could not be read")); - delete[] data; - fclose(fp); + errors.push_back(TypeParserError(FatalSeverity, string("error: file '") + fileNameString + "' could not be read")); return false; } - data[size++] = '\n'; - data[size] = 0; - fclose(fp); + data.push_back('\n'); - bool ok = ParseTypesFromSource(data, fileName, platform, existingTypes, options, includeDirs, autoTypeSource, result, errors); - delete[] data; - return ok; + return ParseTypesFromSource( + data, fileName, platform, existingTypes, options, includeDirs, autoTypeSource, result, errors); } @@ -422,9 +413,9 @@ bool CoreTypeParser::GetOptionText(BNTypeParserOption option, std::string value, } -bool CoreTypeParser::PreprocessSource(const std::string& source, const std::string& fileName, +bool CoreTypeParser::PreprocessSource(const std::string& source, const filesystem::path& fileName, Ref platform, std::optional existingTypes, - const std::vector& options, const std::vector& includeDirs, + const std::vector& options, const std::vector& includeDirs, std::string& output, std::vector& errors) { BNTypeContainer* apiExistingTypes = (existingTypes.has_value() ? existingTypes->GetObject() : nullptr); @@ -434,23 +425,19 @@ bool CoreTypeParser::PreprocessSource(const std::string& source, const std::stri { apiOptions[i] = options[i].c_str(); } - const char** apiIncludeDirs = new const char*[includeDirs.size()]; - for (size_t i = 0; i < includeDirs.size(); ++i) - { - apiIncludeDirs[i] = includeDirs[i].c_str(); - } + Path::APIObjectList apiIncludeDirs(includeDirs); char* apiOutput; BNTypeParserError* apiErrors; size_t errorCount; - auto success = BNTypeParserPreprocessSource(m_object, source.c_str(), fileName.c_str(), + Path::APIObject apiFileName(fileName); + auto success = BNTypeParserPreprocessSource(m_object, source.c_str(), apiFileName, platform->GetObject(), apiExistingTypes, - apiOptions, options.size(), apiIncludeDirs, includeDirs.size(), &apiOutput, + apiOptions, options.size(), apiIncludeDirs.data(), apiIncludeDirs.size(), &apiOutput, &apiErrors, &errorCount); delete [] apiOptions; - delete [] apiIncludeDirs; for (size_t j = 0; j < errorCount; j ++) { @@ -475,9 +462,9 @@ bool CoreTypeParser::PreprocessSource(const std::string& source, const std::stri } -bool CoreTypeParser::ParseTypesFromSource(const std::string& source, const std::string& fileName, +bool CoreTypeParser::ParseTypesFromSource(const std::string& source, const filesystem::path& fileName, Ref platform, std::optional existingTypes, - const std::vector& options, const std::vector& includeDirs, + const std::vector& options, const std::vector& includeDirs, const std::string& autoTypeSource, TypeParserResult& result, std::vector& errors) { BNTypeContainer* apiExistingTypes = (existingTypes.has_value() ? existingTypes->GetObject() : nullptr); @@ -487,23 +474,19 @@ bool CoreTypeParser::ParseTypesFromSource(const std::string& source, const std:: { apiOptions[i] = options[i].c_str(); } - const char** apiIncludeDirs = new const char*[includeDirs.size()]; - for (size_t i = 0; i < includeDirs.size(); ++i) - { - apiIncludeDirs[i] = includeDirs[i].c_str(); - } + Path::APIObjectList apiIncludeDirs(includeDirs); BNTypeParserResult apiResult; BNTypeParserError* apiErrors; size_t errorCount; - auto success = BNTypeParserParseTypesFromSource(m_object, source.c_str(), fileName.c_str(), + Path::APIObject apiFileName(fileName); + auto success = BNTypeParserParseTypesFromSource(m_object, source.c_str(), apiFileName, platform->GetObject(), apiExistingTypes, - apiOptions, options.size(), apiIncludeDirs, includeDirs.size(), autoTypeSource.c_str(), &apiResult, + apiOptions, options.size(), apiIncludeDirs.data(), apiIncludeDirs.size(), autoTypeSource.c_str(), &apiResult, &apiErrors, &errorCount); delete [] apiOptions; - delete [] apiIncludeDirs; for (size_t j = 0; j < errorCount; j ++) { diff --git a/ui/memorymap.h b/ui/memorymap.h index 56898482a2..bbc1828951 100644 --- a/ui/memorymap.h +++ b/ui/memorymap.h @@ -66,7 +66,7 @@ class BINARYNINJAUIAPI MemoryRegionDialog : public QDialog BinaryViewRef m_data; SegmentRef m_segment; - std::optional m_filePath; + std::optional m_filePath; bool m_nameManuallyEdited = false; void SelectFile(); diff --git a/ui/notificationsdispatcher.h b/ui/notificationsdispatcher.h index 0507352505..6cb74a580d 100644 --- a/ui/notificationsdispatcher.h +++ b/ui/notificationsdispatcher.h @@ -112,10 +112,10 @@ class NotificationEvent struct TypeArchiveInfo { std::string id; - std::string path; + std::filesystem::path path; TypeArchiveInfo() {}; - TypeArchiveInfo(const std::string& id, const std::string& path) : id(id), path(path) {}; + TypeArchiveInfo(const std::string& id, const std::filesystem::path& path) : id(id), path(path) {}; }; private: @@ -294,8 +294,8 @@ class NotificationsWorker: public QThread, public BinaryNinja::BinaryDataNotific void OnExternalLocationRemoved(BinaryNinja::BinaryView* data, BinaryNinja::ExternalLocation* location) override; void OnExternalLocationUpdated(BinaryNinja::BinaryView* data, BinaryNinja::ExternalLocation* location) override; - void OnTypeArchiveAttached(BinaryNinja::BinaryView* data, const std::string& id, const std::string& path) override; - void OnTypeArchiveDetached(BinaryNinja::BinaryView* data, const std::string& id, const std::string& path) override; + void OnTypeArchiveAttached(BinaryNinja::BinaryView* data, const std::string& id, const std::filesystem::path& path) override; + void OnTypeArchiveDetached(BinaryNinja::BinaryView* data, const std::string& id, const std::filesystem::path& path) override; void OnTypeArchiveConnected(BinaryNinja::BinaryView* data, BinaryNinja::TypeArchive* archive) override; void OnTypeArchiveDisconnected(BinaryNinja::BinaryView* data, BinaryNinja::TypeArchive* archive) override; @@ -321,4 +321,3 @@ class NotificationsDispatcher: public QObject void asyncRefresh() { m_worker->asyncRefresh(); } }; - diff --git a/ui/typebrowser.h b/ui/typebrowser.h index e5972c7323..e4b413e2fd 100644 --- a/ui/typebrowser.h +++ b/ui/typebrowser.h @@ -306,8 +306,8 @@ class BINARYNINJAUIAPI TypeBrowserModel : public QAbstractItemModel, public Bina void OnTypeRenamed(TypeArchiveRef archive, const std::string& id, const BinaryNinja::QualifiedName& oldName, const BinaryNinja::QualifiedName& newName) override; void OnTypeDeleted(TypeArchiveRef archive, const std::string& id, TypeRef definition) override; - void OnTypeArchiveAttached(BinaryNinja::BinaryView* data, const std::string& id, const std::string& path) override; - void OnTypeArchiveDetached(BinaryNinja::BinaryView* data, const std::string& id, const std::string& path) override; + void OnTypeArchiveAttached(BinaryNinja::BinaryView* data, const std::string& id, const std::filesystem::path& path) override; + void OnTypeArchiveDetached(BinaryNinja::BinaryView* data, const std::string& id, const std::filesystem::path& path) override; void OnTypeArchiveConnected(BinaryNinja::BinaryView* data, BinaryNinja::TypeArchive* archive) override; void OnTypeArchiveDisconnected(BinaryNinja::BinaryView* data, BinaryNinja::TypeArchive* archive) override; diff --git a/ui/util.h b/ui/util.h index 57fbdde420..8c5987d9d6 100644 --- a/ui/util.h +++ b/ui/util.h @@ -9,6 +9,7 @@ #include #include +#include #include /*! @@ -37,6 +38,10 @@ std::optional BINARYNINJAUIAPI getPossibleValueSe std::optional BINARYNINJAUIAPI getPossibleValueSetForILToken(View* view, HighlightTokenState token); std::optional BINARYNINJAUIAPI getAddressOfILTokenExpr(View* view, HighlightTokenState token); void BINARYNINJAUIAPI setCallStackAdjustment(QWidget* parent, FunctionRef func, ArchitectureRef arch, uint64_t instrAddress); +QString BINARYNINJAUIAPI PathToQString(const std::filesystem::path& path); +std::filesystem::path BINARYNINJAUIAPI PathFromQString(const QString& path); +bool BINARYNINJAUIAPI IsDatabasePath(const std::filesystem::path& path); +bool BINARYNINJAUIAPI IsDatabasePath(const QString& path); // Resolve the address of the call instruction that the user is currently on, // given the active function, architecture, IL view, highlight, and cursor diff --git a/view/kernelcache/api/python/generator.cpp b/view/kernelcache/api/python/generator.cpp index 81463b5183..499ca61b41 100644 --- a/view/kernelcache/api/python/generator.cpp +++ b/view/kernelcache/api/python/generator.cpp @@ -251,7 +251,7 @@ int main(int argc, char* argv[]) auto arch = new CoreArchitecture(BNGetNativeTypeParserArchitecture()); // Enable ephemeral settings - Settings::Instance()->LoadSettingsFile(""); + Settings::Instance()->LoadSettingsFile(); Settings::Instance()->Set("analysis.types.parserName", "ClangTypeParser"); bool ok = arch->GetStandalonePlatform()->ParseTypesFromSourceFile(argv[1], types, vars, funcs, errors); diff --git a/view/kernelcache/core/KernelCache.cpp b/view/kernelcache/core/KernelCache.cpp index 6ff88ad1fa..88967efb69 100644 --- a/view/kernelcache/core/KernelCache.cpp +++ b/view/kernelcache/core/KernelCache.cpp @@ -51,7 +51,7 @@ void KernelCache::AddSymbols(std::vector&& symbols) m_symbols.insert({symbol.address, std::move(symbol)}); } -bool KernelCache::ProcessEntryImage(Ref bv, const std::string& path, const fileset_entry_command& info) +bool KernelCache::ProcessEntryImage(Ref bv, const std::filesystem::path& path, const fileset_entry_command& info) { auto imageHeader = KernelCacheMachOHeader::ParseHeaderForAddress(bv, info.vmaddr, info.fileoff, path); if (!imageHeader.has_value()) @@ -402,8 +402,9 @@ std::optional KernelCache::GetImageContaining(const uint64_t address std::optional KernelCache::GetImageWithName(const std::string& name) const { + auto imagePath = ImagePathFromString(name); for (const auto& [address, image] : m_images) - if (image.path == name) + if (image.path == imagePath) return image; return std::nullopt; } diff --git a/view/kernelcache/core/KernelCache.h b/view/kernelcache/core/KernelCache.h index 3897422597..033e13a87e 100644 --- a/view/kernelcache/core/KernelCache.h +++ b/view/kernelcache/core/KernelCache.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include "binaryninjaapi.h" @@ -74,7 +75,7 @@ struct CacheImage { uint64_t headerFileAddress; uint64_t headerVirtualAddress; - std::string path; + std::filesystem::path path; // A list to the start of memory regions associated with the image. // This lets us load all regions for a given image easily. std::vector regions; @@ -90,7 +91,7 @@ struct CacheImage CacheImage& operator=(CacheImage&& other) noexcept = default; // Get the file name from the path. - std::string GetName() const { return BaseFileName(path); } + std::string GetName() const { return ImageNameFromPath(path); } // Get the names of the dependencies. std::vector GetDependencies() const; @@ -120,7 +121,7 @@ class KernelCache public: - bool ProcessEntryImage(BinaryNinja::Ref bv, const std::string& path, const BinaryNinja::fileset_entry_command& info); + bool ProcessEntryImage(BinaryNinja::Ref bv, const std::filesystem::path& path, const BinaryNinja::fileset_entry_command& info); KernelCache() = default; explicit KernelCache(uint64_t addressSize); diff --git a/view/kernelcache/core/KernelCacheController.cpp b/view/kernelcache/core/KernelCacheController.cpp index 753c28ad06..6bd616ab08 100644 --- a/view/kernelcache/core/KernelCacheController.cpp +++ b/view/kernelcache/core/KernelCacheController.cpp @@ -176,7 +176,7 @@ bool KernelCacheController::ApplyImage(BinaryView& view, const CacheImage& image m_loadedImages.insert(image.headerVirtualAddress); - m_logger->LogInfoF("Loaded image: '{}'", image.path); + m_logger->LogInfoF("Loaded image: '{}'", ImagePathToUtf8String(image.path)); // TODO: This needs to be done in a "database save" callback. // NOTE: We store on the parent view because hilariously, the view metadata is not available in view init. diff --git a/view/kernelcache/core/KernelCacheView.cpp b/view/kernelcache/core/KernelCacheView.cpp index 82f3ce7f62..08f1b1345e 100644 --- a/view/kernelcache/core/KernelCacheView.cpp +++ b/view/kernelcache/core/KernelCacheView.cpp @@ -801,7 +801,7 @@ bool KernelCacheView::InitController() reader.Read(&fileset_entry, sizeof(fileset_entry_command)); reader.Seek(offset + fileset_entry.nameEntryOffsetFromBaseOfCommand); auto name = reader.ReadCString(1000); - kernelCache.ProcessEntryImage(this, name, fileset_entry); + kernelCache.ProcessEntryImage(this, ImagePathFromString(name), fileset_entry); } if (cmd == LC_DYLD_CHAINED_FIXUPS) diff --git a/view/kernelcache/core/KernelCacheView.h b/view/kernelcache/core/KernelCacheView.h index bb2515e444..9de6a229b9 100644 --- a/view/kernelcache/core/KernelCacheView.h +++ b/view/kernelcache/core/KernelCacheView.h @@ -22,19 +22,6 @@ class KernelCacheView : public BinaryNinja::BinaryView // Initialized the shared cache controller for this view. This is what allows us to load images and regions. bool InitController(); - void SetPrimaryFileName(std::string primaryFileName); - - // Logs the secondary file name to `m_secondaryFileNames`, see the note on the field about usage. - void LogSecondaryFileName(std::string associatedFileName); - - // Get the path to the primary file. - std::optional GetPrimaryFilePath(); - - // Get the metadata for saving the state of the shared cache. - BinaryNinja::Ref GetMetadata() const; - - void LoadMetadata(const BinaryNinja::Metadata& metadata); - virtual bool PerformIsExecutable() const override { return true; } }; diff --git a/view/kernelcache/core/MachO.cpp b/view/kernelcache/core/MachO.cpp index 01b5da7448..641f85a950 100644 --- a/view/kernelcache/core/MachO.cpp +++ b/view/kernelcache/core/MachO.cpp @@ -27,14 +27,14 @@ std::vector KernelCacheMachOHeader::ReadFunctionTable(Ref } std::optional KernelCacheMachOHeader::ParseHeaderForAddress( - Ref bv, uint64_t vmAddress, uint64_t fileAddress, const std::string& imagePath) + Ref bv, uint64_t vmAddress, uint64_t fileAddress, const std::filesystem::path& imagePath) { KernelCacheMachOHeader header; header.textBase = vmAddress; - header.installName = imagePath; + header.installName = ImagePathToUtf8String(imagePath); // The identifierPrefix is used for the display of the image name in the sections and segments. - header.identifierPrefix = BaseFileName(imagePath); + header.identifierPrefix = ImageNameFromPath(imagePath); std::string errorMsg; BinaryReader reader(bv->GetParentView()); diff --git a/view/kernelcache/core/MachO.h b/view/kernelcache/core/MachO.h index 8c2a77093f..30fd1c24b1 100644 --- a/view/kernelcache/core/MachO.h +++ b/view/kernelcache/core/MachO.h @@ -1,5 +1,7 @@ #pragma once +#include + // TODO: Including this adds a bunch of binary ninja specific stuff :ugh: #include "view/macho/machoview.h" @@ -62,7 +64,7 @@ struct KernelCacheMachOHeader bool relocatable = false; static std::optional ParseHeaderForAddress( - BinaryNinja::Ref bv, uint64_t vmAddress, uint64_t fileAddress, const std::string& imagePath); + BinaryNinja::Ref bv, uint64_t vmAddress, uint64_t fileAddress, const std::filesystem::path& imagePath); std::vector ReadSymbolTable(BinaryNinja::Ref bv, const TableInfo &symbolInfo, const TableInfo &stringInfo) const; diff --git a/view/kernelcache/core/Utility.cpp b/view/kernelcache/core/Utility.cpp index 920d13e839..108736ce99 100644 --- a/view/kernelcache/core/Utility.cpp +++ b/view/kernelcache/core/Utility.cpp @@ -4,6 +4,25 @@ using namespace BinaryNinja; +std::filesystem::path ImagePathFromString(std::string_view imagePath) +{ +#if defined(WIN32) || defined(_WIN32) + std::u8string utf8Path; + utf8Path.reserve(imagePath.size()); + for (char ch : imagePath) + utf8Path.push_back(static_cast(static_cast(ch))); + return std::filesystem::path(utf8Path, std::filesystem::path::generic_format); +#else + return std::filesystem::path(std::string(imagePath), std::filesystem::path::generic_format); +#endif +} + +std::string ImagePathToUtf8String(const std::filesystem::path& imagePath) +{ + auto value = imagePath.generic_u8string(); + return std::string(reinterpret_cast(value.data()), value.size()); +} + int64_t readSLEB128(const uint8_t*& current, const uint8_t* end) { uint8_t cur; @@ -116,12 +135,9 @@ void ApplySymbol(Ref view, Ref typeLib, Ref sym } } -std::string BaseFileName(const std::string& path) +std::string ImageNameFromPath(const std::filesystem::path& imagePath) { - auto lastSlashPos = path.find_last_of("/\\"); - if (lastSlashPos != std::string::npos) - return path.substr(lastSlashPos + 1); - return path; + return ImagePathToUtf8String(imagePath.filename()); } bool IsSameFolderForFile(Ref a, Ref b) diff --git a/view/kernelcache/core/Utility.h b/view/kernelcache/core/Utility.h index 6d0bf0a0c3..75db3909f3 100644 --- a/view/kernelcache/core/Utility.h +++ b/view/kernelcache/core/Utility.h @@ -1,7 +1,9 @@ #pragma once #include +#include #include +#include #include "binaryninjaapi.h" @@ -42,9 +44,10 @@ uint64_t readValidULEB128(const uint8_t*& current, const uint8_t* end); void ApplySymbol(BinaryNinja::Ref view, BinaryNinja::Ref typeLib, BinaryNinja::Ref symbol, BinaryNinja::Ref type = nullptr); -// Returns the "image name" for a given path. -// /blah/foo/bar/libObjCThing.dylib -> libObjCThing.dylib -std::string BaseFileName(const std::string& path); +// Mach-O image paths are stored in the file format as UTF-8-ish POSIX paths, not native filesystem paths. +std::filesystem::path ImagePathFromString(std::string_view imagePath); +std::string ImagePathToUtf8String(const std::filesystem::path& imagePath); +std::string ImageNameFromPath(const std::filesystem::path& imagePath); bool IsSameFolderForFile(BinaryNinja::Ref a, BinaryNinja::Ref b); bool IsSameFolder(BinaryNinja::Ref a, BinaryNinja::Ref b); diff --git a/view/kernelcache/core/ffi.cpp b/view/kernelcache/core/ffi.cpp index d712689485..b2d6fb9369 100644 --- a/view/kernelcache/core/ffi.cpp +++ b/view/kernelcache/core/ffi.cpp @@ -7,7 +7,8 @@ using namespace BinaryNinja::KC; BNKernelCacheImage ImageToApi(const CacheImage& image) { BNKernelCacheImage apiImage; - apiImage.name = BNAllocString(image.path.c_str()); + auto path = ImagePathToUtf8String(image.path); + apiImage.name = BNAllocString(path.c_str()); apiImage.headerVirtualAddress = image.headerVirtualAddress; apiImage.headerFileAddress = image.headerFileAddress; return apiImage; @@ -16,7 +17,7 @@ BNKernelCacheImage ImageToApi(const CacheImage& image) CacheImage ImageFromApi(const BNKernelCacheImage& image) { CacheImage apiImage; - apiImage.path = image.name; + apiImage.path = ImagePathFromString(image.name); apiImage.headerVirtualAddress = image.headerVirtualAddress; apiImage.headerFileAddress = image.headerFileAddress; apiImage.header = nullptr; diff --git a/view/sharedcache/api/python/generator.cpp b/view/sharedcache/api/python/generator.cpp index 062f2e2753..9ec105eecf 100644 --- a/view/sharedcache/api/python/generator.cpp +++ b/view/sharedcache/api/python/generator.cpp @@ -251,7 +251,7 @@ int main(int argc, char* argv[]) auto arch = new CoreArchitecture(BNGetNativeTypeParserArchitecture()); // Enable ephemeral settings - Settings::Instance()->LoadSettingsFile(""); + Settings::Instance()->LoadSettingsFile(); Settings::Instance()->Set("analysis.types.parserName", "ClangTypeParser"); bool ok = arch->GetStandalonePlatform()->ParseTypesFromSourceFile(argv[1], types, vars, funcs, errors); diff --git a/view/sharedcache/core/MachO.cpp b/view/sharedcache/core/MachO.cpp index 7afcbd8d0a..3a7405c03f 100644 --- a/view/sharedcache/core/MachO.cpp +++ b/view/sharedcache/core/MachO.cpp @@ -32,7 +32,7 @@ std::vector SharedCacheMachOHeader::ReadFunctionTable(VirtualMemory& v } std::optional SharedCacheMachOHeader::ParseHeaderForAddress( - std::shared_ptr vm, uint64_t address, const std::string& imagePath) + std::shared_ptr vm, uint64_t address, const std::filesystem::path& imagePath) { // Sanity check to make sure that the header is mapped. // This should really only fail if we didn't grab all the required entries. @@ -42,9 +42,9 @@ std::optional SharedCacheMachOHeader::ParseHeaderForAddr SharedCacheMachOHeader header; header.textBase = address; - header.installName = imagePath; + header.installName = ImagePathToUtf8String(imagePath); // The identifierPrefix is used for the display of the image name in the sections and segments. - header.identifierPrefix = BaseFileName(imagePath); + header.identifierPrefix = ImageNameFromPath(imagePath); std::string errorMsg; VirtualMemoryReader reader(vm); diff --git a/view/sharedcache/core/MachO.h b/view/sharedcache/core/MachO.h index 950e5a46c4..445eb291ad 100644 --- a/view/sharedcache/core/MachO.h +++ b/view/sharedcache/core/MachO.h @@ -2,6 +2,8 @@ #include "VirtualMemory.h" +#include + // TODO: Including this adds a bunch of binary ninja specific stuff :ugh: #include "view/macho/machoview.h" @@ -68,7 +70,7 @@ struct SharedCacheMachOHeader uint64_t GetLinkEditFileBase() const { return linkeditSegment.vmaddr - linkeditSegment.fileoff; }; static std::optional ParseHeaderForAddress( - std::shared_ptr vm, uint64_t address, const std::string& imagePath); + std::shared_ptr vm, uint64_t address, const std::filesystem::path& imagePath); std::vector ReadSymbolTable(VirtualMemory& vm, const TableInfo &symbolInfo, const TableInfo &stringInfo, BNSymbolBinding bindingOverride = NoBinding) const; diff --git a/view/sharedcache/core/MappedFileRegion.cpp b/view/sharedcache/core/MappedFileRegion.cpp index 8c130610be..eca6b7681f 100644 --- a/view/sharedcache/core/MappedFileRegion.cpp +++ b/view/sharedcache/core/MappedFileRegion.cpp @@ -9,13 +9,13 @@ #endif -MappedFileRegion::MappedFileRegion(PrivateTag, uint8_t* data, size_t length, std::string path) +MappedFileRegion::MappedFileRegion(PrivateTag, uint8_t* data, size_t length, std::filesystem::path path) : m_data(data), m_length(length), m_path(std::move(path)) {} #ifndef _MSC_VER -std::shared_ptr MappedFileRegion::Open(const std::string& path) +std::shared_ptr MappedFileRegion::Open(const std::filesystem::path& path) { std::unique_ptr fd(fopen(path.c_str(), "r"), fclose); if (!fd) @@ -54,9 +54,9 @@ MappedFileRegion::~MappedFileRegion() #else // _MSC_VER -std::shared_ptr MappedFileRegion::Open(const std::string& path) +std::shared_ptr MappedFileRegion::Open(const std::filesystem::path& path) { - HANDLE hFile = CreateFile(path.c_str(), + HANDLE hFile = CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, diff --git a/view/sharedcache/core/MappedFileRegion.h b/view/sharedcache/core/MappedFileRegion.h index 7cbf2100cd..2fd9dc8764 100644 --- a/view/sharedcache/core/MappedFileRegion.h +++ b/view/sharedcache/core/MappedFileRegion.h @@ -2,6 +2,7 @@ #include "binaryninjaapi.h" +#include #include #include #include @@ -22,7 +23,7 @@ class MappedFileRegion { uint8_t* m_data = nullptr; size_t m_length = 0; - std::string m_path; + std::filesystem::path m_path; std::once_flag m_slidOnce; MappedFileRegion(const MappedFileRegion&) = delete; @@ -33,14 +34,14 @@ class MappedFileRegion struct PrivateTag {}; public: - MappedFileRegion(PrivateTag, uint8_t* data, size_t length, std::string path); + MappedFileRegion(PrivateTag, uint8_t* data, size_t length, std::filesystem::path path); ~MappedFileRegion(); // Opens file, mmaps with MAP_PRIVATE, closes fd immediately. // Returns nullptr on failure. - static std::shared_ptr Open(const std::string& path); + static std::shared_ptr Open(const std::filesystem::path& path); - const std::string& Path() const { return m_path; } + const std::filesystem::path& Path() const { return m_path; } size_t Length() const { return m_length; } // Run `fn` exactly once. All callers block until the work is complete. diff --git a/view/sharedcache/core/SharedCache.cpp b/view/sharedcache/core/SharedCache.cpp index f9761968b9..57aecba1b3 100644 --- a/view/sharedcache/core/SharedCache.cpp +++ b/view/sharedcache/core/SharedCache.cpp @@ -32,7 +32,7 @@ std::vector CacheImage::GetDependencies() const return {}; } -CacheEntry::CacheEntry(std::string filePath, std::string fileName, CacheEntryType type, dyld_cache_header header, +CacheEntry::CacheEntry(std::filesystem::path filePath, std::string fileName, CacheEntryType type, dyld_cache_header header, std::vector mappings, std::vector> images, std::shared_ptr file) { @@ -45,21 +45,21 @@ CacheEntry::CacheEntry(std::string filePath, std::string fileName, CacheEntryTyp m_file = std::move(file); } -CacheEntry CacheEntry::FromFile(const std::string& filePath, const std::string& fileName, CacheEntryType type) +CacheEntry CacheEntry::FromFile(const std::filesystem::path& filePath, const std::string& fileName, CacheEntryType type) { auto file = MappedFileRegion::Open(filePath); if (!file) - throw std::runtime_error(fmt::format("Failed to open cache file: '{}'", filePath)); + throw std::runtime_error(fmt::format("Failed to open cache file: '{}'", PrintablePath(filePath))); // TODO: Pull this out into another function so we can do IsValidDSCFile or something. // We first want to make sure that the base file is dyld. // All entries must start with "dyld". if (file->Length() < 4) - throw std::runtime_error(fmt::format("File too small to be a shared cache: '{}'", filePath)); + throw std::runtime_error(fmt::format("File too small to be a shared cache: '{}'", PrintablePath(filePath))); DataBuffer sig = file->ReadBuffer(0, 4); const char* magic = static_cast(sig.GetData()); if (strncmp(magic, "dyld", 4) != 0) - throw std::runtime_error(fmt::format("File does not start with 'dyld': '{}'", filePath)); + throw std::runtime_error(fmt::format("File does not start with 'dyld': '{}'", PrintablePath(filePath))); // Read the header, this _should_ be compatible with all known DSC formats. // Mason: the above is not true! https://github.com/Vector35/binaryninja-api/issues/6073 @@ -78,7 +78,7 @@ CacheEntry CacheEntry::FromFile(const std::string& filePath, const std::string& // Cancel adding the entry if we have an invalid mapping. if (currentMapping.fileOffset + currentMapping.size > file->Length()) - throw std::runtime_error(fmt::format("Invalid mapping in shared cache entry: '{}'", filePath)); + throw std::runtime_error(fmt::format("Invalid mapping in shared cache entry: '{}'", PrintablePath(filePath))); // TODO: Check initProt to make sure its in the range of expected values. @@ -254,7 +254,7 @@ void SharedCache::AddEntry(CacheEntry entry) m_entries.push_back(std::move(entry)); } -bool SharedCache::ProcessEntryImage(const std::string& path, const dyld_cache_image_info& info) +bool SharedCache::ProcessEntryImage(const std::filesystem::path& path, const dyld_cache_image_info& info) { auto imageHeader = SharedCacheMachOHeader::ParseHeaderForAddress(m_vm, info.address, path); if (!imageHeader.has_value()) @@ -321,7 +321,7 @@ bool SharedCache::ProcessEntryImage(const std::string& path, const dyld_cache_im void SharedCache::ProcessEntryImages(const CacheEntry& entry) { for (const auto& [imagePath, imageInfo] : entry.GetImages()) - ProcessEntryImage(imagePath, imageInfo); + ProcessEntryImage(ImagePathFromString(imagePath), imageInfo); } // At this point all relevant mapping should be loaded in the virtual memory. @@ -335,7 +335,7 @@ void SharedCache::ProcessEntryRegions(const CacheEntry& entry) auto branchPoolIdxAddr = *entry.GetMappedAddress(entryHeader.branchPoolsOffset) + (i * m_vm->GetAddressSize()); auto branchPoolAddr = m_vm->ReadPointer(branchPoolIdxAddr); auto branchHeader = SharedCacheMachOHeader::ParseHeaderForAddress( - m_vm, branchPoolAddr, fmt::format("dyld_shared_cache_branch_islands_{}", i)); + m_vm, branchPoolAddr, ImagePathFromString(fmt::format("dyld_shared_cache_branch_islands_{}", i))); // Stop processing branch pools if a header fails to parse. if (!branchHeader.has_value()) break; @@ -498,8 +498,9 @@ std::optional SharedCache::GetImageContaining(const uint64_t address std::optional SharedCache::GetImageWithName(const std::string& name) const { + auto imagePath = ImagePathFromString(name); for (const auto& [address, image] : m_images) - if (image.path == name) + if (image.path == imagePath) return image; return std::nullopt; } diff --git a/view/sharedcache/core/SharedCache.h b/view/sharedcache/core/SharedCache.h index 8ecd8f1de7..5a47fd4d1f 100644 --- a/view/sharedcache/core/SharedCache.h +++ b/view/sharedcache/core/SharedCache.h @@ -1,11 +1,13 @@ #pragma once +#include #include #include #include #include "binaryninjaapi.h" #include "MachO.h" +#include "Utility.h" #include "VirtualMemory.h" struct CacheSymbol @@ -81,7 +83,7 @@ struct CacheRegion struct CacheImage { uint64_t headerAddress; - std::string path; + std::filesystem::path path; // A list to the start of memory regions associated with the image. // This lets us load all regions for a given image easily. std::vector regionStarts; @@ -97,7 +99,7 @@ struct CacheImage CacheImage& operator=(CacheImage&& other) noexcept = default; // Get the file name from the path. - std::string GetName() const { return BaseFileName(path); } + std::string GetName() const { return ImageNameFromPath(path); } // Get the names of the dependencies. std::vector GetDependencies() const; @@ -119,7 +121,7 @@ enum class CacheEntryType class CacheEntry { CacheEntryType m_type; - std::string m_filePath; + std::filesystem::path m_filePath; std::string m_fileName; dyld_cache_header m_header {}; // Mappings tell us _where_ to map the regions within the flat address space. @@ -131,7 +133,7 @@ class CacheEntry std::shared_ptr m_file; public: - CacheEntry(std::string filePath, std::string fileName, CacheEntryType type, dyld_cache_header header, + CacheEntry(std::filesystem::path filePath, std::string fileName, CacheEntryType type, dyld_cache_header header, std::vector mappings, std::vector> images, std::shared_ptr file); @@ -142,7 +144,7 @@ class CacheEntry CacheEntry& operator=(CacheEntry&&) = default; // Construct a cache entry from the file on disk. - static CacheEntry FromFile(const std::string& filePath, const std::string& fileName, CacheEntryType type); + static CacheEntry FromFile(const std::filesystem::path& filePath, const std::string& fileName, CacheEntryType type); const std::shared_ptr& GetFile() const { return m_file; } @@ -156,7 +158,7 @@ class CacheEntry CacheEntryType GetType() const { return m_type; } // Ex. "/myuser/mypath/dyld_shared_cache_arm64e" - const std::string& GetFilePath() const { return m_filePath; } + const std::filesystem::path& GetFilePath() const { return m_filePath; } // Ex. "dyld_shared_cache_arm64e" const std::string GetFileName() const { return m_fileName; } const dyld_cache_header& GetHeader() const { return m_header; } @@ -197,7 +199,7 @@ class SharedCache std::optional m_localSymbolsEntry; std::shared_ptr m_localSymbolsVM; - bool ProcessEntryImage(const std::string& path, const dyld_cache_image_info& info); + bool ProcessEntryImage(const std::filesystem::path& path, const dyld_cache_image_info& info); // Add a region known not to overlap with another, otherwise use AddRegion. // returns whether the region was inserted. diff --git a/view/sharedcache/core/SharedCacheBuilder.cpp b/view/sharedcache/core/SharedCacheBuilder.cpp index 639b5bc40f..1aa37b5db7 100644 --- a/view/sharedcache/core/SharedCacheBuilder.cpp +++ b/view/sharedcache/core/SharedCacheBuilder.cpp @@ -1,4 +1,5 @@ #include +#include #include "SharedCacheBuilder.h" using namespace BinaryNinja; @@ -7,7 +8,7 @@ SharedCacheBuilder::SharedCacheBuilder(Ref view) { m_view = std::move(view); m_logger = new Logger("SharedCache.Builder", m_view->GetFile()->GetSessionId()); - m_primaryFileName = BaseFileName(m_view->GetFile()->GetOriginalFilename()); + m_primaryFileName = PrintablePath(m_view->GetFile()->GetOriginalFilename().filename()); m_cache = SharedCache(m_view->GetAddressSize()); m_processedFiles = {}; } @@ -20,7 +21,7 @@ SharedCache SharedCacheBuilder::Finalize() } bool SharedCacheBuilder::AddFile( - const std::string& filePath, const std::string& fileName, const CacheEntryType cacheType) + const std::filesystem::path& filePath, const std::string& fileName, const CacheEntryType cacheType) { // Skip already processed files. if (auto [_, inserted] = m_processedFiles.insert(filePath); !inserted) @@ -48,22 +49,23 @@ bool SharedCacheBuilder::AddFile( catch (const std::exception& e) { // Just return false so the view init can continue. - m_logger->LogErrorForExceptionF(e, "Failed to add file '{}': {}", fileName, e.what()); + m_logger->LogErrorForExceptionF(e, "Failed to add file '{}': {}", filePath, e.what()); return false; } return true; } -size_t SharedCacheBuilder::AddDirectory(const std::string& directoryPath) +size_t SharedCacheBuilder::AddDirectory(const std::filesystem::path& directoryPath) { // Filters then attempts to process a single directory entry as a shared cache file. auto processDirEntry = [&](const std::filesystem::directory_entry& entry) { - const auto currentFilePath = entry.path().string(); - const auto currentFileName = BaseFileName(currentFilePath); + const auto currentFilePath = entry.path(); + const auto currentFileName = PrintablePath(currentFilePath.filename()); // Skip non-files. - if (!entry.is_regular_file()) + std::error_code ec; + if (!entry.is_regular_file(ec) || ec) return false; // Ok, we are now _sure_ that this file _might_ be a part of the cache, lets try and process it! @@ -73,8 +75,9 @@ size_t SharedCacheBuilder::AddDirectory(const std::string& directoryPath) // TODO: This is ugly. size_t added = 0; // Locate all possible related entry files and add them to the cache. - for (const auto& entry : std::filesystem::directory_iterator(directoryPath)) - if (processDirEntry(entry)) + std::error_code ec; + for (std::filesystem::directory_iterator entry(directoryPath, ec), end; !ec && entry != end; entry.increment(ec)) + if (processDirEntry(*entry)) added++; return added; } diff --git a/view/sharedcache/core/SharedCacheBuilder.h b/view/sharedcache/core/SharedCacheBuilder.h index 4a3ed1c4d1..881344fa9e 100644 --- a/view/sharedcache/core/SharedCacheBuilder.h +++ b/view/sharedcache/core/SharedCacheBuilder.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "binaryninjaapi.h" #include "SharedCache.h" @@ -14,7 +16,7 @@ class SharedCacheBuilder SharedCache m_cache; // List of already processedFiles so we skip adding them again. - std::set m_processedFiles; + std::set m_processedFiles; // The base file name (i.e. "dyld_shared_cache_arm64e"), this is used to filter out non-relevant files. std::string m_primaryFileName; @@ -22,7 +24,7 @@ class SharedCacheBuilder explicit SharedCacheBuilder(BinaryNinja::Ref view); SharedCache& GetCache() { return m_cache; }; - std::set GetProcessedFiles() { return m_processedFiles; }; + std::set GetProcessedFiles() { return m_processedFiles; }; std::string GetPrimaryFileName() { return m_primaryFileName; }; // Set the base file name used when filtering in `AddFile`. @@ -34,10 +36,10 @@ class SharedCacheBuilder // Tries to add the file to the shared cache, if the file has already been processed or is not valid // then false will be returned, true if the file was added to the shared cache. A file can only be added once. bool AddFile( - const std::string& filePath, const std::string& fileName, CacheEntryType cacheType = CacheEntryType::Secondary); + const std::filesystem::path& filePath, const std::string& fileName, CacheEntryType cacheType = CacheEntryType::Secondary); // Process a directory on the file system. - size_t AddDirectory(const std::string& directoryPath); + size_t AddDirectory(const std::filesystem::path& directoryPath); // Process a directory in a project. size_t AddProjectFolder(BinaryNinja::Ref folder); diff --git a/view/sharedcache/core/SharedCacheController.cpp b/view/sharedcache/core/SharedCacheController.cpp index 8ed011bd39..957f6151ea 100644 --- a/view/sharedcache/core/SharedCacheController.cpp +++ b/view/sharedcache/core/SharedCacheController.cpp @@ -1,6 +1,7 @@ #include "SharedCacheController.h" #include "MachOProcessor.h" #include "ObjC.h" +#include "Utility.h" using namespace BinaryNinja; using namespace BinaryNinja::DSC; @@ -40,7 +41,7 @@ void DeleteController(const FileMetadata& file) controller->m_refs.load()); controllers.erase(it); - LogDebugF("Deleted SharedCacheController for view {:?}", file.GetFilename().c_str()); + LogDebugF("Deleted SharedCacheController for view {:?}", file.GetFilename()); } } @@ -238,7 +239,7 @@ bool SharedCacheController::ApplyImage(BinaryView& view, const CacheImage& image m_loadedImages.insert(image.headerAddress); - m_logger->LogInfoF("Loaded image: '{}'", image.path); + m_logger->LogInfoF("Loaded image: '{}'", ImagePathToUtf8String(image.path)); // TODO: This needs to be done in a "database save" callback. // NOTE: We store on the parent view because hilariously, the view metadata is not available in view init. diff --git a/view/sharedcache/core/SharedCacheView.cpp b/view/sharedcache/core/SharedCacheView.cpp index f3d60e2ea1..6c1377124f 100644 --- a/view/sharedcache/core/SharedCacheView.cpp +++ b/view/sharedcache/core/SharedCacheView.cpp @@ -4,19 +4,20 @@ #include "SharedCacheController.h" #include "SharedCacheBuilder.h" +#include "Utility.h" using namespace BinaryNinja; using namespace BinaryNinja::DSC; static const char* VIEW_METADATA_KEY = "shared_cache_view"; -static bool IsBndbPath(const std::string& path) +static bool IsBndbPath(const std::filesystem::path& path) { - return std::filesystem::path(path).extension() == ".bndb"; + return path.extension() == ".bndb"; } -static bool IsUsablePrimaryCachePath(const std::string& path) +static bool IsUsablePrimaryCachePath(const std::filesystem::path& path) { std::error_code ec; return !path.empty() && !IsBndbPath(path) && std::filesystem::exists(path, ec) @@ -24,22 +25,24 @@ static bool IsUsablePrimaryCachePath(const std::string& path) } -static std::string PathRelativeTo(const std::string& path, const std::string& basePath) +static std::filesystem::path PathRelativeTo( + const std::filesystem::path& path, const std::filesystem::path& basePath) { std::error_code ec; auto relativePath = std::filesystem::relative(path, basePath, ec); - auto relativePathString = relativePath.generic_string(); - if (ec || relativePath.empty() || relativePathString == ".." || relativePathString.find("../") == 0) + auto firstPart = relativePath.begin(); + if (ec || relativePath.empty() || (firstPart != relativePath.end() && *firstPart == "..")) return path; - return relativePath.string(); + return relativePath; } -static std::string ResolveRelativePath(const std::string& path, const std::string& basePath) +static std::filesystem::path ResolveRelativePath(const std::string& path, const std::filesystem::path& basePath) { - if (path.empty() || std::filesystem::path(path).is_absolute()) - return path; - return (std::filesystem::path(basePath) / path).string(); + auto pathObj = Utf8ToPath(path); + if (pathObj.empty() || pathObj.is_absolute()) + return pathObj; + return basePath / pathObj; } SharedCacheViewType::SharedCacheViewType() : BinaryViewType(VIEW_NAME, VIEW_NAME) {} @@ -190,7 +193,7 @@ SharedCacheView::SharedCacheView(const std::string& typeName, BinaryView* data, { // By default, we will assume the primary file name from the original file path. // This is subject to be overridden via `LoadMetadata`. - m_primaryFileName = BaseFileName(GetFile()->GetOriginalFilename()); + m_primaryFileName = PrintablePath(GetFile()->GetOriginalFilename().filename()); // Load up the metadata from the parent view (because our metadata is hilariously not available during view init) if (const auto metadata = GetParentView()->QueryMetadata(VIEW_METADATA_KEY)) @@ -869,7 +872,7 @@ bool SharedCacheView::InitController() m_logger->LogError("Failed to get primary file path!"); return false; } - std::string primaryFileDir = std::filesystem::path(*primaryFilePath).parent_path().string(); + std::filesystem::path primaryFileDir = primaryFilePath->parent_path(); // If the primary file is in the current project, use its project folder to discover related cache files. // Otherwise, fall back to scanning the resolved primary file's directory on disk. @@ -1052,14 +1055,15 @@ void SharedCacheView::LogSecondaryFileName(std::string secondaryFileName) } -std::string SharedCacheView::StorePrimaryProjectFile(ProjectFile* projectFile) +std::filesystem::path SharedCacheView::StorePrimaryProjectFile(ProjectFile* projectFile) { SetPrimaryFileLocation(projectFile->GetPathInProject(), projectFile->GetName()); return projectFile->GetPathOnDisk(); } -void SharedCacheView::StorePrimaryFilePath(const std::string& path, Project* project, const std::string& databaseDir) +void SharedCacheView::StorePrimaryFilePath( + const std::filesystem::path& path, Project* project, const std::filesystem::path& databaseDir) { if (project) { @@ -1072,15 +1076,16 @@ void SharedCacheView::StorePrimaryFilePath(const std::string& path, Project* pro m_logger->LogWarnF( "Primary shared cache file '{}' is outside the current project. Add the shared cache files to the project to avoid selecting them on future opens.", path); - SetPrimaryFileName(BaseFileName(path)); + SetPrimaryFileName(PrintablePath(path.filename())); return; } - SetPrimaryFileLocation(PathRelativeTo(path, databaseDir), BaseFileName(path)); + SetPrimaryFileLocation(PathToUtf8String(PathRelativeTo(path, databaseDir)), PrintablePath(path.filename())); } -std::optional SharedCacheView::ResolveProjectFilePath(Project* project, const std::string& projectPath) +std::optional SharedCacheView::ResolveProjectFilePath( + Project* project, const std::string& projectPath) { if (!project || projectPath.empty()) return std::nullopt; @@ -1093,7 +1098,7 @@ std::optional SharedCacheView::ResolveProjectFilePath(Project* proj m_logger->LogErrorF( "Multiple project files match primary shared cache path '{}'. Provide loader.dsc.primaryFilePath with an unambiguous project path.", projectPath); - return std::string(); + return std::filesystem::path(); } if (matches.size() == 1) return StorePrimaryProjectFile(matches[0]); @@ -1108,7 +1113,7 @@ std::optional SharedCacheView::ResolveProjectFilePath(Project* proj } -std::optional SharedCacheView::ResolveUniqueProjectFileName(Project* project) +std::optional SharedCacheView::ResolveUniqueProjectFileName(Project* project) { if (!project || m_primaryFileName.empty()) return std::nullopt; @@ -1133,14 +1138,15 @@ std::optional SharedCacheView::ResolveUniqueProjectFileName(Project m_logger->LogErrorF( "Multiple project files are named '{}': {}. Provide loader.dsc.primaryFilePath with the project path to the correct primary shared cache file.", m_primaryFileName, paths); - return std::string(); + return std::filesystem::path(); } return StorePrimaryProjectFile(matches[0]); } -std::optional SharedCacheView::ResolveMetadataPrimaryFilePath(Project* project, const std::string& databaseDir) +std::optional SharedCacheView::ResolveMetadataPrimaryFilePath( + Project* project, const std::filesystem::path& databaseDir) { if (m_primaryFilePath.empty()) return std::nullopt; @@ -1155,7 +1161,7 @@ std::optional SharedCacheView::ResolveMetadataPrimaryFilePath(Proje } -std::optional SharedCacheView::PromptForPrimaryFile() +std::optional SharedCacheView::PromptForPrimaryFile() { if (!IsUIEnabled()) { @@ -1169,7 +1175,7 @@ std::optional SharedCacheView::PromptForPrimaryFile() "Binary Ninja needs the original primary dyld shared cache file to reopen this database. " "Select the primary dyld_shared_cache file, not another .bndb database.", OKButtonSet, InformationIcon); - std::string newPrimaryFilePath; + std::filesystem::path newPrimaryFilePath; std::string prompt = "Select primary shared cache file"; if (!m_primaryFileName.empty()) prompt += " '" + m_primaryFileName + "'"; @@ -1194,10 +1200,10 @@ std::optional SharedCacheView::PromptForPrimaryFile() } -std::optional SharedCacheView::GetPrimaryFilePath() +std::optional SharedCacheView::GetPrimaryFilePath() { auto viewFile = GetFile(); - auto databaseDir = std::filesystem::path(viewFile->GetFilename()).parent_path().string(); + auto databaseDir = viewFile->GetFilename().parent_path(); auto currentProjectFile = viewFile->GetProjectFile(); Ref project = nullptr; if (currentProjectFile) @@ -1229,7 +1235,7 @@ std::optional SharedCacheView::GetPrimaryFilePath() } else { - SetPrimaryFileName(BaseFileName(resolvedConfiguredPath)); + SetPrimaryFileName(PrintablePath(resolvedConfiguredPath.filename())); return resolvedConfiguredPath; } } @@ -1251,13 +1257,13 @@ std::optional SharedCacheView::GetPrimaryFilePath() std::vector candidateNames; if (!m_primaryFileName.empty()) candidateNames.push_back(m_primaryFileName); - auto originalBaseName = BaseFileName(primaryFilePath); + auto originalBaseName = PrintablePath(primaryFilePath.filename()); if (!originalBaseName.empty() && originalBaseName != m_primaryFileName) candidateNames.push_back(originalBaseName); for (const auto& candidateName : candidateNames) { - auto candidatePath = (std::filesystem::path(databaseDir) / candidateName).string(); + auto candidatePath = databaseDir / Utf8ToPath(candidateName); if (IsUsablePrimaryCachePath(candidatePath)) { primaryFilePath = candidatePath; @@ -1318,7 +1324,7 @@ std::optional SharedCacheView::GetPrimaryFilePath() return *uniqueProjectPath; } - if (IsUsablePrimaryCachePath(primaryFilePath) && BaseFileName(primaryFilePath) == m_primaryFileName) + if (IsUsablePrimaryCachePath(primaryFilePath) && PrintablePath(primaryFilePath.filename()) == m_primaryFileName) return primaryFilePath; // 6. If automatic project resolution failed, ask the user in UI mode. Headless callers must provide @@ -1326,7 +1332,7 @@ std::optional SharedCacheView::GetPrimaryFilePath() auto promptedPrimaryFilePath = PromptForPrimaryFile(); if (!promptedPrimaryFilePath) return std::nullopt; - std::string newPrimaryFilePath = *promptedPrimaryFilePath; + std::filesystem::path newPrimaryFilePath = *promptedPrimaryFilePath; // Persist a project-relative path when the selected file is in the project. External selections are // allowed as an escape hatch, but only the basename is stored so local absolute paths are not synced. @@ -1334,7 +1340,7 @@ std::optional SharedCacheView::GetPrimaryFilePath() if (primaryProjectFile) SetPrimaryFileLocation(primaryProjectFile->GetPathInProject(), primaryProjectFile->GetName()); else - SetPrimaryFileName(BaseFileName(newPrimaryFilePath)); + SetPrimaryFileName(PrintablePath(newPrimaryFilePath.filename())); return newPrimaryFilePath; } diff --git a/view/sharedcache/core/SharedCacheView.h b/view/sharedcache/core/SharedCacheView.h index 7ad2ba9a1f..6e8ed69575 100644 --- a/view/sharedcache/core/SharedCacheView.h +++ b/view/sharedcache/core/SharedCacheView.h @@ -6,6 +6,7 @@ #define SHAREDCACHE_DSCVIEW_H #include +#include class SharedCacheView : public BinaryNinja::BinaryView { @@ -21,12 +22,15 @@ class SharedCacheView : public BinaryNinja::BinaryView // NOTE: Currently this is just used to alert the user to supposed missing files. std::set m_secondaryFileNames; - std::string StorePrimaryProjectFile(BinaryNinja::ProjectFile* projectFile); - void StorePrimaryFilePath(const std::string& path, BinaryNinja::Project* project, const std::string& databaseDir); - std::optional ResolveProjectFilePath(BinaryNinja::Project* project, const std::string& projectPath); - std::optional ResolveUniqueProjectFileName(BinaryNinja::Project* project); - std::optional ResolveMetadataPrimaryFilePath(BinaryNinja::Project* project, const std::string& databaseDir); - std::optional PromptForPrimaryFile(); + std::filesystem::path StorePrimaryProjectFile(BinaryNinja::ProjectFile* projectFile); + void StorePrimaryFilePath( + const std::filesystem::path& path, BinaryNinja::Project* project, const std::filesystem::path& databaseDir); + std::optional ResolveProjectFilePath( + BinaryNinja::Project* project, const std::string& projectPath); + std::optional ResolveUniqueProjectFileName(BinaryNinja::Project* project); + std::optional ResolveMetadataPrimaryFilePath( + BinaryNinja::Project* project, const std::filesystem::path& databaseDir); + std::optional PromptForPrimaryFile(); public: SharedCacheView(const std::string& typeName, BinaryView* data, bool parseOnly = false); @@ -46,7 +50,7 @@ class SharedCacheView : public BinaryNinja::BinaryView void LogSecondaryFileName(std::string associatedFileName); // Get the path to the primary file. - std::optional GetPrimaryFilePath(); + std::optional GetPrimaryFilePath(); // Get the metadata for saving the state of the shared cache. BinaryNinja::Ref GetMetadata() const; diff --git a/view/sharedcache/core/Utility.cpp b/view/sharedcache/core/Utility.cpp index 9ae2c3b42b..1bb774512d 100644 --- a/view/sharedcache/core/Utility.cpp +++ b/view/sharedcache/core/Utility.cpp @@ -4,6 +4,25 @@ using namespace BinaryNinja; +std::filesystem::path ImagePathFromString(std::string_view imagePath) +{ +#if defined(WIN32) || defined(_WIN32) + std::u8string utf8Path; + utf8Path.reserve(imagePath.size()); + for (char ch : imagePath) + utf8Path.push_back(static_cast(static_cast(ch))); + return std::filesystem::path(utf8Path, std::filesystem::path::generic_format); +#else + return std::filesystem::path(std::string(imagePath), std::filesystem::path::generic_format); +#endif +} + +std::string ImagePathToUtf8String(const std::filesystem::path& imagePath) +{ + auto value = imagePath.generic_u8string(); + return std::string(reinterpret_cast(value.data()), value.size()); +} + BNSegmentFlag SegmentFlagsFromMachOProtections(int initProt, int maxProt) { uint32_t flags = 0; @@ -135,12 +154,9 @@ void ApplySymbol(Ref view, Ref typeLib, Ref sym } } -std::string BaseFileName(const std::string& path) +std::string ImageNameFromPath(const std::filesystem::path& imagePath) { - auto lastSlashPos = path.find_last_of("/\\"); - if (lastSlashPos != std::string::npos) - return path.substr(lastSlashPos + 1); - return path; + return ImagePathToUtf8String(imagePath.filename()); } bool IsSameFolderForFile(Ref a, Ref b) diff --git a/view/sharedcache/core/Utility.h b/view/sharedcache/core/Utility.h index 5664fd9b5f..e559222297 100644 --- a/view/sharedcache/core/Utility.h +++ b/view/sharedcache/core/Utility.h @@ -1,7 +1,9 @@ #pragma once #include +#include #include +#include #include "binaryninjaapi.h" @@ -36,13 +38,18 @@ uint64_t readValidULEB128(const uint8_t*& current, const uint8_t* end); void ApplySymbol(BinaryNinja::Ref view, BinaryNinja::Ref typeLib, BinaryNinja::Ref symbol, BinaryNinja::Ref type = nullptr); -// Returns the "image name" for a given path. -// /blah/foo/bar/libObjCThing.dylib -> libObjCThing.dylib -std::string BaseFileName(const std::string& path); +// Mach-O image paths are stored in the file format as UTF-8-ish POSIX paths, not native filesystem paths. +std::filesystem::path ImagePathFromString(std::string_view imagePath); +std::string ImagePathToUtf8String(const std::filesystem::path& imagePath); +std::string ImageNameFromPath(const std::filesystem::path& imagePath); bool IsSameFolderForFile(BinaryNinja::Ref a, BinaryNinja::Ref b); bool IsSameFolder(BinaryNinja::Ref a, BinaryNinja::Ref b); +using BinaryNinja::Path::PathToUtf8String; +using BinaryNinja::Path::PrintablePath; +using BinaryNinja::Path::Utf8ToPath; + // Represents a range of addresses [start, end). // Note that `end` is not included within the range. struct AddressRange diff --git a/view/sharedcache/core/ffi.cpp b/view/sharedcache/core/ffi.cpp index 7ad10f9bb6..483fa7adc6 100644 --- a/view/sharedcache/core/ffi.cpp +++ b/view/sharedcache/core/ffi.cpp @@ -7,7 +7,8 @@ using namespace BinaryNinja::DSC; BNSharedCacheImage ImageToApi(const CacheImage& image) { BNSharedCacheImage apiImage; - apiImage.name = BNAllocStringWithLength(image.path.c_str(), image.path.size()); + auto path = ImagePathToUtf8String(image.path); + apiImage.name = BNAllocStringWithLength(path.c_str(), path.size()); apiImage.headerAddress = image.headerAddress; apiImage.regionStartCount = image.regionStarts.size(); uint64_t* regionStarts = new uint64_t[image.regionStarts.size()]; @@ -20,7 +21,7 @@ BNSharedCacheImage ImageToApi(const CacheImage& image) CacheImage ImageFromApi(const BNSharedCacheImage& image) { CacheImage apiImage; - apiImage.path = image.name; + apiImage.path = ImagePathFromString(image.name); apiImage.headerAddress = image.headerAddress; apiImage.regionStarts.reserve(image.regionStartCount); for (size_t i = 0; i < image.regionStartCount; i++) @@ -135,7 +136,7 @@ BNSharedCacheMappingInfo MappingToApi(const dyld_cache_mapping_info& mapping) BNSharedCacheEntry EntryToApi(const CacheEntry& entry) { BNSharedCacheEntry apiEntry; - auto path = entry.GetFilePath(); + auto path = Path::PathToUtf8String(entry.GetFilePath()); auto name = entry.GetFileName(); apiEntry.path = BNAllocStringWithLength(path.c_str(), path.size()); apiEntry.name = BNAllocStringWithLength(name.c_str(), name.size());