summaryrefslogtreecommitdiff
path: root/view/macho
diff options
context:
space:
mode:
authorBrian Potchik <brian@vector35.com>2026-02-17 07:20:51 -0500
committerBrian Potchik <brian@vector35.com>2026-02-17 07:20:51 -0500
commit56da69aa61654521424cf1af87bcce41a1c0df01 (patch)
tree5ee87934d47933b48c8e9858a7b5e1c3deaf62d7 /view/macho
parentda19d7e633872ad1c4ba5155e714062ba5c8e5ae (diff)
Handle Universal binaries through container transform system.
Diffstat (limited to 'view/macho')
-rw-r--r--view/macho/machoview.cpp4
-rw-r--r--view/macho/universaltransform.cpp372
-rw-r--r--view/macho/universaltransform.h24
-rw-r--r--view/macho/universalview.cpp140
-rw-r--r--view/macho/universalview.h3
5 files changed, 404 insertions, 139 deletions
diff --git a/view/macho/machoview.cpp b/view/macho/machoview.cpp
index a6e45d4b..f742a1c5 100644
--- a/view/macho/machoview.cpp
+++ b/view/macho/machoview.cpp
@@ -4,6 +4,7 @@
#include "fatmachoview.h"
#include "lowlevelilinstruction.h"
#include "rapidjsonwrapper.h"
+#include "universaltransform.h"
#include "universalview.h"
#include <algorithm>
@@ -1952,7 +1953,7 @@ bool MachoView::InitializeHeader(MachOHeader& header, bool isMainHeader, uint64_
return true;
bool is64Bit;
- string archName = UniversalViewType::ArchitectureToString(m_archId, 0, is64Bit);
+ string archName = UniversalTransform::ArchitectureToString(m_archId, 0, is64Bit);
if (!archName.empty())
{
#ifdef DEMO_EDITION
@@ -4004,6 +4005,7 @@ extern "C"
InitMachoViewType();
InitFatMachoViewType();
InitUniversalViewType();
+ InitUniversalTransform();
return true;
}
}
diff --git a/view/macho/universaltransform.cpp b/view/macho/universaltransform.cpp
new file mode 100644
index 00000000..3309d853
--- /dev/null
+++ b/view/macho/universaltransform.cpp
@@ -0,0 +1,372 @@
+#include "universaltransform.h"
+#include "machoview.h"
+#include <cstring>
+
+using namespace BinaryNinja;
+using namespace std;
+
+
+const map<pair<cpu_type_t, cpu_subtype_t>, string>& UniversalTransform::GetArchitectures()
+{
+ static map<pair<cpu_type_t, cpu_subtype_t>, string> g_cpuArchNames =
+ {
+ {{MACHO_CPU_TYPE_VAX, 0}, "vax"},
+ {{MACHO_CPU_TYPE_MC680x0, 0}, "mc680x0"},
+ {{MACHO_CPU_TYPE_X86, 0}, "x86"},
+ {{MACHO_CPU_TYPE_X86, MACHO_CPU_SUBTYPE_X86_ALL}, "x86"},
+ {{MACHO_CPU_TYPE_X86, MACHO_CPU_SUBTYPE_X86_ARCH1}, "x86 (Arch1)"},
+ {{MACHO_CPU_TYPE_X86_64, 0}, "x86_64"},
+ {{MACHO_CPU_TYPE_X86_64, MACHO_CPU_SUBTYPE_X86_64_ALL}, "x86_64"},
+ {{MACHO_CPU_TYPE_X86_64, MACHO_CPU_SUBTYPE_X86_64_H}, "x86_64 (Haswell)"},
+ {{MACHO_CPU_TYPE_MIPS, 0}, "mips"},
+ {{MACHO_CPU_TYPE_MC98000, 0}, "mc98000"},
+ {{MACHO_CPU_TYPE_HPPA, 0}, "hppa"},
+ {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_ALL}, "arm"},
+ {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V4T}, "armv4t"},
+ {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V6}, "armv6"},
+ {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V5TEJ}, "armv5tej"},
+ {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_XSCALE}, "arm (XScale)"},
+ {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V7}, "armv7"},
+ {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V7F}, "armv7f"},
+ {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V7S}, "armv7s"},
+ {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V7K}, "armv7k"},
+ {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V8}, "armv8"},
+ {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V6M}, "armv6m"},
+ {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V7M}, "armv7m"},
+ {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V7EM}, "armv7em"},
+ {{MACHO_CPU_TYPE_ARM64, MACHO_CPU_SUBTYPE_ARM64_ALL}, "arm64"},
+ {{MACHO_CPU_TYPE_ARM64, MACHO_CPU_SUBTYPE_ARM64_V8}, "arm64v8"},
+ {{MACHO_CPU_TYPE_ARM64, MACHO_CPU_SUBTYPE_ARM64E}, "arm64e"},
+ {{MACHO_CPU_TYPE_ARM64_32, MACHO_CPU_SUBTYPE_ARM64_32_ALL}, "arm64_32"},
+ {{MACHO_CPU_TYPE_ARM64_32, MACHO_CPU_SUBTYPE_ARM64_32_V8}, "arm64_32v8"},
+ {{MACHO_CPU_TYPE_MC88000, 0}, "mc88000"},
+ {{MACHO_CPU_TYPE_SPARC, 0}, "sparc"},
+ {{MACHO_CPU_TYPE_I860, 0}, "i860"},
+ {{MACHO_CPU_TYPE_ALPHA, 0}, "alpha"},
+ {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_ALL}, "ppc"},
+ {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_601}, "ppc601"},
+ {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_602}, "ppc602"},
+ {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_603}, "ppc603"},
+ {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_603e}, "ppc603e"},
+ {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_603ev}, "ppc603ev"},
+ {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_604}, "ppc604"},
+ {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_604e}, "ppc604e"},
+ {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_620}, "ppc620"},
+ {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_750}, "ppc750"},
+ {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_7400}, "ppc7400"},
+ {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_7450}, "ppc7450"},
+ {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_970}, "ppc970"},
+ {{MACHO_CPU_TYPE_POWERPC64, 0}, "ppc64"}
+ };
+
+ return g_cpuArchNames;
+}
+
+
+string UniversalTransform::ArchitectureToString(cpu_type_t cpuType, cpu_subtype_t cpuSubType, bool& is64Bit)
+{
+ const map<pair<cpu_type_t, cpu_subtype_t>, string>& cpuArchNames = UniversalTransform::GetArchitectures();
+
+ switch(cpuType)
+ {
+ case MACHO_CPU_TYPE_X86_64:
+ case MACHO_CPU_TYPE_ARM64:
+ case MACHO_CPU_TYPE_ARM64_32:
+ case MACHO_CPU_TYPE_POWERPC64:
+ is64Bit = true;
+ break;
+ default:
+ is64Bit = false;
+ break;
+ }
+
+ auto itr = cpuArchNames.find({cpuType, cpuSubType});
+ if (itr != cpuArchNames.end())
+ return itr->second;
+
+ itr = cpuArchNames.find({cpuType, 0});
+ if (itr != cpuArchNames.end())
+ return itr->second;
+
+ return "Unknown";
+}
+
+
+bool UniversalTransform::ParseHeaders(Ref<BinaryView> data, FatHeader& fatHeader, vector<FatArch64>& fatArchEntries, bool& isFat64, string& errorMsg)
+{
+ if (data->GetLength() < 8)
+ {
+ errorMsg = "Universal (Fat Mach-O): file too small";
+ return false;
+ }
+
+ uint8_t header[8];
+ if (data->Read(header, 0, 8) < 8)
+ {
+ errorMsg = "Universal (Fat Mach-O): failed to read header";
+ return false;
+ }
+
+ uint32_t magic = ToBE32(*(uint32_t*)header);
+ if ((magic != FAT_MAGIC) && (magic != FAT_MAGIC_64))
+ {
+ errorMsg = "Universal (Fat Mach-O): invalid signature";
+ return false;
+ }
+
+ BinaryReader reader(data);
+ reader.SetEndianness(BigEndian);
+ fatHeader.magic = reader.Read32();
+ fatHeader.nfat_arch = reader.Read32();
+
+ isFat64 = (fatHeader.magic == FAT_MAGIC_64);
+ size_t requiredFatHeaderSize = fatHeader.nfat_arch * (isFat64 ? 32 : 20) + 8;
+ if (requiredFatHeaderSize > data->GetLength())
+ {
+ errorMsg = "Universal (Fat Mach-O): header truncated";
+ return false;
+ }
+
+ for (size_t i = 0; i < fatHeader.nfat_arch; i++)
+ {
+ FatArch64 fatArch;
+ if (isFat64)
+ {
+ fatArch.cputype = reader.Read32();
+ fatArch.cpusubtype = reader.Read32();
+ fatArch.offset = reader.Read64();
+ fatArch.size = reader.Read64();
+ fatArch.align = reader.Read32();
+ fatArch.reserved = reader.Read32();
+ }
+ else
+ {
+ fatArch.cputype = reader.Read32();
+ fatArch.cpusubtype = reader.Read32();
+ fatArch.offset = reader.Read32();
+ fatArch.size = reader.Read32();
+ fatArch.align = reader.Read32();
+ fatArch.reserved = 0;
+ }
+
+ // Mask away cpu subtype capability bits
+ fatArch.cpusubtype &= ~MACHO_CPU_SUBTYPE_MASK;
+ fatArchEntries.push_back(fatArch);
+ }
+
+ return true;
+}
+
+
+UniversalTransform::UniversalTransform() :
+ Transform(DecodeTransform, TransformCapabilities(TransformSupportsDetection | TransformSupportsContext), "Universal", "Universal (Fat Mach-O)", "Container")
+{
+}
+
+
+bool UniversalTransform::Decode(const DataBuffer& input, DataBuffer& output, const map<string, DataBuffer>& params)
+{
+ // Create a temporary BinaryView for parsing
+ auto fileMetadata = new FileMetadata();
+ auto rawView = new BinaryData(fileMetadata, input);
+
+ FatHeader fatHeader;
+ vector<FatArch64> fatArchEntries;
+ bool isFat64;
+ string errorMsg;
+
+ if (!ParseHeaders(rawView, fatHeader, fatArchEntries, isFat64, errorMsg))
+ {
+ LogError("Universal: %s", errorMsg.c_str());
+ return false;
+ }
+
+ if (fatArchEntries.empty())
+ {
+ LogError("Universal: no architectures found");
+ return false;
+ }
+
+ // Look for architecture parameter
+ string targetArch;
+ if (auto archParam = params.find("architecture"); archParam != params.end())
+ targetArch = string(reinterpret_cast<const char*>(archParam->second.GetData()), archParam->second.GetLength());
+
+ // If no target specified, extract first architecture
+ const FatArch64* targetEntry = nullptr;
+ if (targetArch.empty())
+ {
+ targetEntry = &fatArchEntries[0];
+ }
+ else
+ {
+ // Find matching architecture
+ for (const auto& entry : fatArchEntries)
+ {
+ bool is64Bit;
+ string archName = ArchitectureToString(entry.cputype, entry.cpusubtype, is64Bit);
+ if (archName == targetArch)
+ {
+ targetEntry = &entry;
+ break;
+ }
+ }
+
+ if (!targetEntry)
+ {
+ LogError("Universal: architecture '%s' not found", targetArch.c_str());
+ return false;
+ }
+ }
+
+ // Validate bounds
+ if (targetEntry->offset > input.GetLength() || targetEntry->size > input.GetLength() - targetEntry->offset)
+ {
+ LogError("Universal: architecture data extends beyond file bounds");
+ return false;
+ }
+
+ // Extract the Mach-O slice
+ output = DataBuffer(static_cast<const uint8_t*>(input.GetData()) + targetEntry->offset, static_cast<size_t>(targetEntry->size));
+ return true;
+}
+
+
+bool UniversalTransform::DecodeWithContext(Ref<TransformContext> context, const map<string, DataBuffer>& params)
+{
+ if (!context || !context->GetInput())
+ return false;
+
+ Ref<BinaryView> input = context->GetInput();
+ FatHeader fatHeader;
+ vector<FatArch64> fatArchEntries;
+ bool isFat64;
+ string errorMsg;
+
+ if (!ParseHeaders(input, fatHeader, fatArchEntries, isFat64, errorMsg))
+ {
+ LogError("Universal: %s", errorMsg.c_str());
+ return false;
+ }
+
+ if (fatArchEntries.empty())
+ {
+ LogError("Universal: no architectures found");
+ return false;
+ }
+
+ // Phase 1: Discovery - enumerate available architectures
+ if (!context->HasAvailableFiles())
+ {
+ vector<string> architectures;
+ for (const auto& entry : fatArchEntries)
+ {
+ bool is64Bit;
+ string archName = ArchitectureToString(entry.cputype, entry.cpusubtype, is64Bit);
+ architectures.push_back(archName);
+ }
+
+ if (!context->IsInteractive())
+ {
+ 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;
+ }
+
+ // Preserve original headless load behavior when no architecturePreference is specified
+ if (!BinaryNinja::IsUIEnabled() && archPref.empty() && architectures.size())
+ {
+ context->SetAvailableFiles({architectures[0]});
+ return false;
+ }
+ }
+
+ context->SetAvailableFiles(architectures);
+ return false;
+ }
+
+ // Phase 2: Extraction - extract requested architectures
+ vector<string> requestedFiles = context->GetRequestedFiles();
+ if (requestedFiles.empty())
+ return false;
+
+ // Build a map of architecture names to entries
+ map<string, const FatArch64*> archMap;
+ for (const auto& entry : fatArchEntries)
+ {
+ bool is64Bit;
+ string archName = ArchitectureToString(entry.cputype, entry.cpusubtype, is64Bit);
+ archMap[archName] = &entry;
+ }
+
+ bool complete = true;
+ for (const string& requestedArch : requestedFiles)
+ {
+ auto itr = archMap.find(requestedArch);
+ if (itr == archMap.end())
+ {
+ string msg = "Universal: requested architecture '" + requestedArch + "' not found";
+ context->SetChild(DataBuffer(), requestedArch, TransformFailure, msg);
+ complete = false;
+ continue;
+ }
+
+ const FatArch64* entry = itr->second;
+
+ // Validate bounds
+ if (entry->offset > input->GetLength() || entry->size > input->GetLength() - entry->offset)
+ {
+ string msg = "Universal: architecture data extends beyond file bounds";
+ context->SetChild(DataBuffer(), requestedArch, TransformFailure, msg);
+ complete = false;
+ continue;
+ }
+
+ // Extract the Mach-O slice
+ DataBuffer sliceData;
+ sliceData.SetSize(static_cast<size_t>(entry->size));
+ if (input->Read(sliceData.GetData(), entry->offset, static_cast<size_t>(entry->size)) < entry->size)
+ {
+ string msg = "Universal: failed to read architecture data";
+ context->SetChild(DataBuffer(), requestedArch, TransformFailure, msg);
+ complete = false;
+ continue;
+ }
+
+ // Create child context with the extracted slice
+ context->SetChild(sliceData, requestedArch, TransformSuccess, "", true);
+ }
+
+ return complete;
+}
+
+
+bool UniversalTransform::Encode(const DataBuffer& input, DataBuffer& output, const map<string, DataBuffer>& params)
+{
+ return false;
+}
+
+
+bool UniversalTransform::CanDecode(Ref<BinaryView> input) const
+{
+ if (input->GetLength() < 4)
+ return false;
+
+ uint8_t magic[4];
+ if (input->Read(magic, 0, 4) < 4)
+ return false;
+
+ uint32_t magicValue = ToBE32(*(uint32_t*)magic);
+ return (magicValue == FAT_MAGIC) || (magicValue == FAT_MAGIC_64);
+}
+
+
+void BinaryNinja::InitUniversalTransform()
+{
+ static UniversalTransform universalXform;
+ Transform::Register(&universalXform);
+}
diff --git a/view/macho/universaltransform.h b/view/macho/universaltransform.h
new file mode 100644
index 00000000..2d54d65e
--- /dev/null
+++ b/view/macho/universaltransform.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "binaryninjaapi.h"
+#include "machoview.h"
+
+namespace BinaryNinja
+{
+ void InitUniversalTransform();
+
+ class UniversalTransform : public Transform
+ {
+ public:
+ static const std::map<std::pair<cpu_type_t, cpu_subtype_t>, std::string>& GetArchitectures();
+ static std::string ArchitectureToString(cpu_type_t cpuType, cpu_subtype_t cpuSubType, bool& is64Bit);
+ static bool ParseHeaders(Ref<BinaryView> data, FatHeader& fatHeader, std::vector<FatArch64>& fatArchEntries, bool& isFat64, std::string& errorMsg);
+
+ UniversalTransform();
+
+ virtual bool Decode(const DataBuffer& input, DataBuffer& output, const std::map<std::string, DataBuffer>& params) override;
+ virtual bool DecodeWithContext(Ref<TransformContext> context, const std::map<std::string, DataBuffer>& params) override;
+ virtual bool Encode(const DataBuffer& input, DataBuffer& output, const std::map<std::string, DataBuffer>& params) override;
+ virtual bool CanDecode(Ref<BinaryView> input) const override;
+ };
+}
diff --git a/view/macho/universalview.cpp b/view/macho/universalview.cpp
index d11630a3..16e973f5 100644
--- a/view/macho/universalview.cpp
+++ b/view/macho/universalview.cpp
@@ -6,6 +6,7 @@
#include <cxxabi.h>
#endif
#include "universalview.h"
+#include "universaltransform.h"
#include "machoview.h"
#include "rapidjsonwrapper.h"
@@ -16,92 +17,6 @@ using namespace std;
static UniversalViewType* g_universalViewType = nullptr;
-const map<pair<cpu_type_t, cpu_subtype_t>, string>& UniversalViewType::GetArchitectures()
-{
- static map<pair<cpu_type_t, cpu_subtype_t>, string> g_cpuArchNames =
- {
- {{MACHO_CPU_TYPE_VAX, 0}, "vax"},
- {{MACHO_CPU_TYPE_MC680x0, 0}, "mc680x0"},
- {{MACHO_CPU_TYPE_X86, 0}, "x86"},
- {{MACHO_CPU_TYPE_X86, MACHO_CPU_SUBTYPE_X86_ALL}, "x86"},
- {{MACHO_CPU_TYPE_X86, MACHO_CPU_SUBTYPE_X86_ARCH1}, "x86 (Arch1)"},
- {{MACHO_CPU_TYPE_X86_64, 0}, "x86_64"},
- {{MACHO_CPU_TYPE_X86_64, MACHO_CPU_SUBTYPE_X86_64_ALL}, "x86_64"},
- {{MACHO_CPU_TYPE_X86_64, MACHO_CPU_SUBTYPE_X86_64_H}, "x86_64 (Haswell)"},
- {{MACHO_CPU_TYPE_MIPS, 0}, "mips"},
- {{MACHO_CPU_TYPE_MC98000, 0}, "mc98000"},
- {{MACHO_CPU_TYPE_HPPA, 0}, "hppa"},
- {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_ALL}, "arm"},
- {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V4T}, "armv4t"},
- {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V6}, "armv6"},
- {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V5TEJ}, "armv5tej"},
- {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_XSCALE}, "arm (XScale)"},
- {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V7}, "armv7"},
- {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V7F}, "armv7f"},
- {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V7S}, "armv7s"},
- {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V7K}, "armv7k"},
- {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V8}, "armv8"},
- {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V6M}, "armv6m"},
- {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V7M}, "armv7m"},
- {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V7EM}, "armv7em"},
- {{MACHO_CPU_TYPE_ARM64, MACHO_CPU_SUBTYPE_ARM64_ALL}, "arm64"},
- {{MACHO_CPU_TYPE_ARM64, MACHO_CPU_SUBTYPE_ARM64_V8}, "arm64v8"},
- {{MACHO_CPU_TYPE_ARM64, MACHO_CPU_SUBTYPE_ARM64E}, "arm64e"},
- {{MACHO_CPU_TYPE_ARM64_32, MACHO_CPU_SUBTYPE_ARM64_32_ALL}, "arm64_32"},
- {{MACHO_CPU_TYPE_ARM64_32, MACHO_CPU_SUBTYPE_ARM64_32_V8}, "arm64_32v8"},
- {{MACHO_CPU_TYPE_MC88000, 0}, "mc88000"},
- {{MACHO_CPU_TYPE_SPARC, 0}, "sparc"},
- {{MACHO_CPU_TYPE_I860, 0}, "i860"},
- {{MACHO_CPU_TYPE_ALPHA, 0}, "alpha"},
- {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_ALL}, "ppc"},
- {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_601}, "ppc601"},
- {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_602}, "ppc602"},
- {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_603}, "ppc603"},
- {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_603e}, "ppc603e"},
- {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_603ev}, "ppc603ev"},
- {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_604}, "ppc604"},
- {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_604e}, "ppc604e"},
- {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_620}, "ppc620"},
- {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_750}, "ppc750"},
- {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_7400}, "ppc7400"},
- {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_7450}, "ppc7450"},
- {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_970}, "ppc970"},
- {{MACHO_CPU_TYPE_POWERPC64, 0}, "ppc64"}
- };
-
- return g_cpuArchNames;
-}
-
-
-string UniversalViewType::ArchitectureToString(cpu_type_t cpuType, cpu_subtype_t cpuSubType, bool& is64Bit)
-{
- const map<pair<cpu_type_t, cpu_subtype_t>, string>& cpuArchNames = GetArchitectures();
-
- switch(cpuType)
- {
- case MACHO_CPU_TYPE_X86_64:
- case MACHO_CPU_TYPE_ARM64:
- case MACHO_CPU_TYPE_ARM64_32:
- case MACHO_CPU_TYPE_POWERPC64:
- is64Bit = true;
- break;
- default:
- is64Bit = false;
- break;
- }
-
- auto itr = cpuArchNames.find({cpuType, cpuSubType});
- if (itr != cpuArchNames.end())
- return itr->second;
-
- itr = cpuArchNames.find({cpuType, 0});
- if (itr != cpuArchNames.end())
- return itr->second;
-
- return "Unknown";
-}
-
-
void BinaryNinja::InitUniversalViewType()
{
static UniversalViewType type;
@@ -115,11 +30,11 @@ void BinaryNinja::InitUniversalViewType()
"type" : "array",
"sorted" : false,
"default" : [],
- "description" : "The preferred Mach-O architecture to select from Universal archives. On its own, changes the default Mach-O selected at load to match the specified architecture. To automatically open that slice without an extra dialog appearing, remove 'Universal' from ui.files.detection.openWithOptions",
+ "description" : "Specify an architecture preference for automatic loading of a Mach-O file from a Universal archive. When unset, headless operation defaults to the first available architecture, while interactive operation presents all available architectures for selection.",
"ignore" : ["SettingsProjectScope", "SettingsResourceScope"]
})");
- const map<pair<cpu_type_t, cpu_subtype_t>, string>& cpuArchNames = UniversalViewType::GetArchitectures();
+ const map<pair<cpu_type_t, cpu_subtype_t>, string>& cpuArchNames = UniversalTransform::GetArchitectures();
set<string> names;
for (const auto& [key, name] : cpuArchNames)
names.insert(name);
@@ -157,52 +72,7 @@ bool UniversalViewType::IsTypeValidForData(BinaryView* data)
bool UniversalViewType::ParseHeaders(BinaryView* data, FatHeader& fatHeader, vector<FatArch64>& fatArchEntries, bool& isFat64, string& errorMsg)
{
- if (!IsTypeValidForData(data))
- {
- errorMsg = "Universal (Fat Mach-O): invalid signature";
- return false;
- }
-
- BinaryReader reader(data);
- reader.SetEndianness(BigEndian);
- fatHeader.magic = reader.Read32();
- fatHeader.nfat_arch = reader.Read32();
-
- isFat64 = (fatHeader.magic == FAT_MAGIC_64);
- size_t requiredFatHeaderSize = fatHeader.nfat_arch * (isFat64 ? 32 : 20) + 8;
- if (requiredFatHeaderSize > data->GetLength())
- {
- errorMsg = "Universal (Fat Mach-O): header truncated";
- return false;
- }
-
- for (size_t i = 0; i < fatHeader.nfat_arch; i++)
- {
- FatArch64 fatArch;
- if (isFat64)
- {
- fatArch.cputype = reader.Read32();
- fatArch.cpusubtype = reader.Read32();
- fatArch.offset = reader.Read64();
- fatArch.size = reader.Read64();
- fatArch.align = reader.Read32();
- fatArch.reserved = reader.Read32();
- }
- else
- {
- fatArch.cputype = reader.Read32();
- fatArch.cpusubtype = reader.Read32();
- fatArch.offset = reader.Read32();
- fatArch.size = reader.Read32();
- fatArch.align = reader.Read32();
- }
-
- // Mask away cpu subtype capability bits
- fatArch.cpusubtype &= ~MACHO_CPU_SUBTYPE_MASK;
- fatArchEntries.push_back(fatArch);
- }
-
- return true;
+ return UniversalTransform::ParseHeaders(data, fatHeader, fatArchEntries, isFat64, errorMsg);
}
@@ -248,7 +118,7 @@ Ref<Settings> UniversalViewType::GetLoadSettingsForData(BinaryView* data)
{
rapidjson::Value result(rapidjson::kObjectType);
bool is64Bit;
- string archDesc = ArchitectureToString(entry.cputype, entry.cpusubtype, is64Bit);
+ string archDesc = UniversalTransform::ArchitectureToString(entry.cputype, entry.cpusubtype, is64Bit);
string bitDesc = is64Bit ? "64-bit" : "32-bit";
result.AddMember("id", count++, entries.GetAllocator());
result.AddMember("architecture", archDesc, entries.GetAllocator());
diff --git a/view/macho/universalview.h b/view/macho/universalview.h
index aaf483cc..bccea6a1 100644
--- a/view/macho/universalview.h
+++ b/view/macho/universalview.h
@@ -16,9 +16,6 @@ namespace BinaryNinja
class UniversalViewType: public BinaryViewType
{
public:
- static const std::map<std::pair<cpu_type_t, cpu_subtype_t>, std::string>& GetArchitectures();
- static std::string ArchitectureToString(cpu_type_t cpuType, cpu_subtype_t cpuSubType, bool& is64Bit);
-
UniversalViewType(): BinaryViewType("Universal", "Universal") { }
virtual Ref<BinaryView> Create(BinaryView* data) override;
virtual bool IsTypeValidForData(BinaryView* data) override;