diff options
| author | Brian Potchik <brian@vector35.com> | 2026-03-20 07:51:04 -0400 |
|---|---|---|
| committer | Brian Potchik <brian@vector35.com> | 2026-03-20 07:51:04 -0400 |
| commit | 7228ba0b889765bc3474fbd5475870e24efc79ca (patch) | |
| tree | d5a2f7d6f6f58f633e21a638ba9dbe10495f32df | |
| parent | 9ca4a08fbeabc22bed721ab522cf0a7dc4484a9f (diff) | |
Add option to disable dedicated Universal architecture picker and use Container Browser
Add the ui.files.universal.dedicatedPicker setting, allowing users to choose
between the dedicated architecture picker dialog and the standard container
browser for opening Universal (fat) Mach-O binaries. Previously the dedicated
picker was always used with no way to opt out.
Implementing this required restructuring how Universal binaries are handled
during file open. The previous approach intercepted Universal binaries during
container processing, bypassing the container browser's normal pipeline with
dedicated routing logic. This tightly coupled Universal-specific behavior into
the container system, prevented the container browser from handling Universal
binaries natively, and introduced several bugs around exclusion settings,
nested containers, and multi-child container hierarchies.
Bug fixes:
- The previous implementation always intercepted Universal binaries during
container processing with its own routing logic, bypassing the container
browser entirely. There was no way for the container browser to handle
Universal binaries natively, even when that was the desired behavior.
- Universal binaries inside multi-child containers were not detected. The
previous implementation only traversed single-child paths in the container
hierarchy, silently ignoring Universal binaries that appeared as siblings
among multiple children.
- Nested containers (e.g., archives) inside Universal slices were not
handled. The previous implementation intercepted Universal binaries at the
transform routing level before the container browser could perform
recursive delayering. When the dedicated picker is disabled, the container
browser now processes the full hierarchy including Universal slices and any
containers within them.
- Reverted incorrect IsInteractive to IsUIEnabled change in
UniversalTransform. The previous change conflated headless mode with
non-interactive processing. The UI can be enabled while still performing a
non-interactive auto-open (e.g., container browser auto-resolving with a
preferred architecture). The IsInteractive() flag captures the actual
intent and enables the transform to produce only the preferred
architecture child for non-interactive sessions, avoiding unnecessary
BinaryView construction for all slices.
| -rw-r--r-- | ui/containerbrowser.h | 5 | ||||
| -rw-r--r-- | ui/containeropenrequest.h | 47 | ||||
| -rw-r--r-- | view/macho/universaltransform.cpp | 9 |
3 files changed, 4 insertions, 57 deletions
diff --git a/ui/containerbrowser.h b/ui/containerbrowser.h index b9ba278f..363e5446 100644 --- a/ui/containerbrowser.h +++ b/ui/containerbrowser.h @@ -15,7 +15,6 @@ #include <vector> -class ContainerOpenRequest; class ContainerTreeModel : public QAbstractItemModel { @@ -122,8 +121,4 @@ public: bool openWithOptionsRequested() const { return m_openWithOptionsRequested; } static std::vector<TransformContextRef> openContainerFile(const QString& path, bool forceShowDialog = false, bool* outOpenWithOptions = nullptr); - - // Show the container browser dialog for the given open request. - // Returns the selected contexts, or empty if the user cancelled. - static std::vector<TransformContextRef> showBrowser(ContainerOpenRequest& request, bool* outOpenWithOptions = nullptr); }; diff --git a/ui/containeropenrequest.h b/ui/containeropenrequest.h deleted file mode 100644 index 5067534d..00000000 --- a/ui/containeropenrequest.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include "uitypes.h" - -#include <optional> -#include <string> -#include <vector> - - -// Captures user settings and intent for opening a container file, and provides -// policy decisions (e.g. whether to show the container browser) after processing. -class BINARYNINJAUIAPI ContainerOpenRequest -{ -public: - enum Action { - Cancel, - AutoOpen, - BrowseContainer, - SelectArchitecture, - }; - - explicit ContainerOpenRequest(const std::string& path, bool forceContainerBrowser = false); - - TransformSessionRef session() const { return m_session; } - - // Create the session, process it, and determine what action the caller - // should take. Returns Cancel if the session could not be created. - Action resolve(); - - // Get the default selection from a processed session. If no selection has - // been made (e.g. because the session auto-opened), selects the current leaf. - std::vector<TransformContextRef> selectedContexts(); - - // When the container is a universal binary, the available architectures and the - // index of the preferred one (if any). - const std::vector<TransformContextRef>& architectureContexts() const { return m_archContexts; } - std::optional<size_t> preferredArchitectureIndex() const { return m_preferredArch; } - -private: - Action resolveUniversal(TransformContextRef universalCtx); - - TransformSessionRef m_session; - std::vector<TransformContextRef> m_archContexts; - std::optional<size_t> m_preferredArch; - bool m_forceContainerBrowser = false; - bool m_autoOpen = false; -}; diff --git a/view/macho/universaltransform.cpp b/view/macho/universaltransform.cpp index 33164215..3309d853 100644 --- a/view/macho/universaltransform.cpp +++ b/view/macho/universaltransform.cpp @@ -266,20 +266,19 @@ bool UniversalTransform::DecodeWithContext(Ref<TransformContext> context, const architectures.push_back(archName); } - // TODO: It is surprising that this is UniversalTransform's responsibility. - if (!BinaryNinja::IsUIEnabled()) + if (!context->IsInteractive()) { - // When headless, filter to the preferred architecture if one is configured. vector<string> archPref = context->GetSettings()->Get<vector<string>>("files.universal.architecturePreference"); if (auto result = find_first_of(archPref.begin(), archPref.end(), architectures.begin(), architectures.end()); result != archPref.end()) { + // Filter to preferred architecture to support container auto-open policy size_t archIndex = find(architectures.begin(), architectures.end(), *result) - architectures.begin(); context->SetAvailableFiles({architectures[archIndex]}); return false; } - // Load the first architecture if no preference is found. - if (archPref.empty() && architectures.size()) + // Preserve original headless load behavior when no architecturePreference is specified + if (!BinaryNinja::IsUIEnabled() && archPref.empty() && architectures.size()) { context->SetAvailableFiles({architectures[0]}); return false; |
