summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm64/arch_arm64.cpp123
-rw-r--r--plugins/workflow_swift/src/demangler/function_type.rs65
-rw-r--r--rust/src/architecture.rs14
3 files changed, 199 insertions, 3 deletions
diff --git a/arch/arm64/arch_arm64.cpp b/arch/arm64/arch_arm64.cpp
index b71e79e8..a56ed35b 100644
--- a/arch/arm64/arch_arm64.cpp
+++ b/arch/arm64/arch_arm64.cpp
@@ -2765,6 +2765,115 @@ class AppleArm64SystemCallConvention : public CallingConvention
virtual bool IsEligibleForHeuristics() override { return false; }
};
+
+// Swift calling convention for ARM64.
+//
+// The Swift ABI repurposes three callee-saved registers for implicit parameters:
+// x20 - self (swiftself): context/self parameter, passed as the last integer argument
+// x21 - error (swifterror): caller initializes to zero, callee sets to error pointer on throw
+// x22 - async context (swiftasync): implicit async context for async functions
+//
+// Each combination of these three properties requires a distinct calling convention,
+// since it changes which registers are arguments, callee-saved, or implicitly defined.
+// The naming scheme is "swift" with optional suffixes: "-self", "-throws", "-async".
+class SwiftArm64CallingConvention : public CallingConvention
+{
+ bool m_hasSelf;
+ bool m_throws;
+ bool m_isAsync;
+
+public:
+ SwiftArm64CallingConvention(Architecture* arch, const string& name, bool hasSelf, bool throws, bool isAsync)
+ : CallingConvention(arch, name), m_hasSelf(hasSelf), m_throws(throws), m_isAsync(isAsync)
+ {
+ }
+
+ virtual vector<uint32_t> GetIntegerArgumentRegisters() override
+ {
+ // Self goes first because our function types list self as the first
+ // parameter. Parameters are assigned to registers sequentially, so
+ // this ensures self -> x20 and remaining args -> x0-x7.
+ // Async context and error go last as implicit trailing registers.
+ vector<uint32_t> regs;
+ if (m_hasSelf)
+ regs.push_back(REG_X20);
+ regs.insert(regs.end(), {REG_X0, REG_X1, REG_X2, REG_X3, REG_X4, REG_X5, REG_X6, REG_X7});
+ if (m_isAsync)
+ regs.push_back(REG_X22);
+ if (m_throws)
+ regs.push_back(REG_X21);
+ return regs;
+ }
+
+ virtual vector<uint32_t> GetFloatArgumentRegisters() override
+ {
+ return vector<uint32_t> {REG_V0, REG_V1, REG_V2, REG_V3, REG_V4, REG_V5, REG_V6, REG_V7};
+ }
+
+ virtual vector<uint32_t> GetCallerSavedRegisters() override
+ {
+ vector<uint32_t> regs {REG_X0, REG_X1, REG_X2, REG_X3, REG_X4, REG_X5, REG_X6, REG_X7, REG_X8,
+ REG_X9, REG_X10, REG_X11, REG_X12, REG_X13, REG_X14, REG_X15, REG_X16, REG_X17, REG_X18,
+ REG_X30, REG_V0, REG_V1, REG_V2, REG_V3, REG_V4, REG_V5, REG_V6, REG_V7, REG_V16, REG_V17,
+ REG_V18, REG_V19, REG_V20, REG_V21, REG_V22, REG_V23, REG_V24, REG_V25, REG_V26, REG_V27,
+ REG_V28, REG_V29, REG_V30, REG_V31};
+ // When used as special registers, they are no longer callee-saved.
+ if (m_hasSelf)
+ regs.push_back(REG_X20);
+ if (m_throws)
+ regs.push_back(REG_X21);
+ if (m_isAsync)
+ regs.push_back(REG_X22);
+ return regs;
+ }
+
+ virtual vector<uint32_t> GetCalleeSavedRegisters() override
+ {
+ vector<uint32_t> regs {REG_X19, REG_X23, REG_X24, REG_X25, REG_X26, REG_X27, REG_X28, REG_X29};
+ // Only include x20/x21/x22 as callee-saved when they are NOT repurposed.
+ if (!m_hasSelf)
+ regs.push_back(REG_X20);
+ if (!m_throws)
+ regs.push_back(REG_X21);
+ if (!m_isAsync)
+ regs.push_back(REG_X22);
+ return regs;
+ }
+
+ virtual vector<uint32_t> GetImplicitlyDefinedRegisters() override
+ {
+ vector<uint32_t> regs;
+ // Throwing functions implicitly define x21 (the error register) on return.
+ if (m_throws)
+ regs.push_back(REG_X21);
+ return regs;
+ }
+
+ virtual uint32_t GetIntegerReturnValueRegister() override { return REG_X0; }
+
+ virtual uint32_t GetFloatReturnValueRegister() override { return REG_V0; }
+
+ virtual vector<uint32_t> GetRequiredArgumentRegisters() override
+ {
+ vector<uint32_t> regs;
+ if (m_hasSelf)
+ regs.push_back(REG_X20);
+ if (m_throws)
+ regs.push_back(REG_X21);
+ if (m_isAsync)
+ regs.push_back(REG_X22);
+ return regs;
+ }
+
+ virtual bool AreArgumentRegistersUsedForVarArgs() override { return false; }
+
+ virtual bool IsEligibleForHeuristics() override
+ {
+ return m_hasSelf || m_throws || m_isAsync;
+ }
+};
+
+
#define PAGE(x) (uint32_t)((x) >> 12)
#define PAGE_OFF(x) (uint32_t)((x)&0xfff)
#define PAGE_NO_OFF(x) (uint32_t)((x)&0xFFFFF000)
@@ -3550,6 +3659,20 @@ extern "C"
conv = new AppleArm64CallingConvention(arm64);
arm64->RegisterCallingConvention(conv);
+ // Register Swift calling conventions (all combinations of self/throws/async).
+ for (int swiftFlags = 0; swiftFlags < 8; swiftFlags++)
+ {
+ bool hasSelf = (swiftFlags & 1) != 0;
+ bool throws = (swiftFlags & 2) != 0;
+ bool isAsync = (swiftFlags & 4) != 0;
+ string name = "swift";
+ if (hasSelf) name += "-self";
+ if (throws) name += "-throws";
+ if (isAsync) name += "-async";
+ conv = new SwiftArm64CallingConvention(arm64, name, hasSelf, throws, isAsync);
+ arm64->RegisterCallingConvention(conv);
+ }
+
for (uint32_t i = REG_X0; i <= REG_X28; i++)
{
if (i == REG_X16 || i == REG_X17 || i == REG_X18) // reserved by os.
diff --git a/plugins/workflow_swift/src/demangler/function_type.rs b/plugins/workflow_swift/src/demangler/function_type.rs
index 3313143e..908f5940 100644
--- a/plugins/workflow_swift/src/demangler/function_type.rs
+++ b/plugins/workflow_swift/src/demangler/function_type.rs
@@ -10,6 +10,43 @@ use swift_demangler::{
use super::type_reconstruction::{make_named_type_ref, TypeRefExt};
+/// Architecture-specific register assignments for Swift implicit parameters.
+struct PlatformAbi {
+ arch: CoreArchitecture,
+ error_reg: &'static str,
+ async_context_reg: &'static str,
+}
+
+impl PlatformAbi {
+ fn for_arch(arch: &CoreArchitecture) -> Option<Self> {
+ match arch.name().as_ref() {
+ "aarch64" => Some(Self {
+ arch: *arch,
+ error_reg: "x21",
+ async_context_reg: "x22",
+ }),
+ _ => None,
+ }
+ }
+
+ fn error_location(&self) -> Option<Variable> {
+ self.register_variable(self.error_reg)
+ }
+
+ fn async_context_location(&self) -> Option<Variable> {
+ self.register_variable(self.async_context_reg)
+ }
+
+ fn register_variable(&self, name: &str) -> Option<Variable> {
+ let reg = self.arch.register_by_name(name)?;
+ Some(Variable::new(
+ VariableSourceType::RegisterVariableSourceType,
+ 0,
+ reg.id().0 as i64,
+ ))
+ }
+}
+
/// Swift calling convention builder.
///
/// Tracks which implicit parameters (self, error, async context) are present,
@@ -17,6 +54,7 @@ use super::type_reconstruction::{make_named_type_ref, TypeRefExt};
/// architecture-specific calling convention when building the final function type.
struct CallingConvention {
arch: CoreArchitecture,
+ abi: Option<PlatformAbi>,
flags: u8,
leading_params: Vec<FunctionParameter>,
trailing_params: Vec<FunctionParameter>,
@@ -30,6 +68,7 @@ impl CallingConvention {
fn for_arch(arch: &CoreArchitecture) -> Self {
Self {
arch: *arch,
+ abi: PlatformAbi::for_arch(arch),
flags: 0,
leading_params: Vec::new(),
trailing_params: Vec::new(),
@@ -94,6 +133,8 @@ impl CallingConvention {
/// Prepends `self` before `params` and appends error/async context after,
/// then looks up the appropriate calling convention by name on the architecture.
fn build_type(self, ret_type: &Type, params: Vec<FunctionParameter>) -> Ref<Type> {
+ let cc_name = self.cc_name();
+
let mut all_params = self.leading_params;
all_params.extend(params);
all_params.extend(self.trailing_params);
@@ -103,7 +144,7 @@ impl CallingConvention {
all_params.push(FunctionParameter {
ty: error_ty.into(),
name: "error".to_string(),
- location: None,
+ location: self.abi.as_ref().and_then(|a| a.error_location()),
});
}
@@ -112,11 +153,29 @@ impl CallingConvention {
all_params.push(FunctionParameter {
ty: ptr_ty.into(),
name: "asyncContext".to_string(),
- location: None,
+ location: self.abi.as_ref().and_then(|a| a.async_context_location()),
});
}
- Type::function(ret_type, all_params, false)
+ if let Some(cc) = self.arch.calling_convention_by_name(&cc_name) {
+ Type::function_with_opts(ret_type, &all_params, false, cc, Conf::new(0, 0))
+ } else {
+ Type::function(ret_type, all_params, false)
+ }
+ }
+
+ fn cc_name(&self) -> String {
+ let mut name = String::from("swift");
+ if self.flags & Self::SELF != 0 {
+ name.push_str("-self");
+ }
+ if self.flags & Self::THROWS != 0 {
+ name.push_str("-throws");
+ }
+ if self.flags & Self::ASYNC != 0 {
+ name.push_str("-async");
+ }
+ name
}
}
diff --git a/rust/src/architecture.rs b/rust/src/architecture.rs
index 6b644e88..e854d53b 100644
--- a/rust/src/architecture.rs
+++ b/rust/src/architecture.rs
@@ -1262,6 +1262,20 @@ pub trait ArchitectureExt: Architecture {
}
}
+ fn calling_convention_by_name(&self, name: &str) -> Option<Ref<CoreCallingConvention>> {
+ let name = name.to_cstr();
+ unsafe {
+ let result = NonNull::new(BNGetArchitectureCallingConventionByName(
+ self.as_ref().handle,
+ name.as_ptr(),
+ ))?;
+ Some(CoreCallingConvention::ref_from_raw(
+ result.as_ptr(),
+ self.as_ref().handle(),
+ ))
+ }
+ }
+
fn calling_conventions(&self) -> Array<CoreCallingConvention> {
unsafe {
let mut count = 0;