Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ members = [
"plugins/workflow_objc/demo",
"plugins/bntl_utils",
"plugins/bntl_utils/cli",
"plugins/workflow_swift",
"plugins/workflow_swift/demo",
]

[workspace.dependencies]
Expand Down
123 changes: 123 additions & 0 deletions arch/arm64/arch_arm64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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.
Expand Down
171 changes: 171 additions & 0 deletions plugins/workflow_swift/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
cmake_minimum_required(VERSION 3.15 FATAL_ERROR)

project(workflow_swift)

if(NOT BN_API_BUILD_EXAMPLES AND NOT BN_INTERNAL_BUILD)
if(NOT BN_API_PATH)
# If we have not already defined the API source directory try and find it.
find_path(
BN_API_PATH
NAMES binaryninjaapi.h
# List of paths to search for the clone of the api
HINTS ../../.. ../../binaryninja/api/ binaryninjaapi binaryninja-api $ENV{BN_API_PATH}
REQUIRED
)
endif()
set(CARGO_STABLE_VERSION 1.91.1)
add_subdirectory(${BN_API_PATH} binaryninjaapi)
endif()

file(GLOB_RECURSE PLUGIN_SOURCES CONFIGURE_DEPENDS
${PROJECT_SOURCE_DIR}/Cargo.toml
${PROJECT_SOURCE_DIR}/src/*.rs)

if(CMAKE_BUILD_TYPE MATCHES Debug)
if(DEMO)
set(TARGET_DIR ${PROJECT_BINARY_DIR}/target/dev-demo)
set(CARGO_OPTS --target-dir=${PROJECT_BINARY_DIR}/target --profile=dev-demo)
else()
set(TARGET_DIR ${PROJECT_BINARY_DIR}/target/debug)
set(CARGO_OPTS --target-dir=${PROJECT_BINARY_DIR}/target)
endif()
else()
if(DEMO)
set(TARGET_DIR ${PROJECT_BINARY_DIR}/target/release-demo)
set(CARGO_OPTS --target-dir=${PROJECT_BINARY_DIR}/target --profile=release-demo)
else()
set(TARGET_DIR ${PROJECT_BINARY_DIR}/target/release)
set(CARGO_OPTS --target-dir=${PROJECT_BINARY_DIR}/target --release)
endif()
endif()

if(FORCE_COLORED_OUTPUT)
set(CARGO_OPTS ${CARGO_OPTS} --color always)
endif()

if(DEMO)
set(CARGO_FEATURES --features demo --manifest-path ${PROJECT_SOURCE_DIR}/demo/Cargo.toml)

set(OUTPUT_FILE_NAME ${CMAKE_STATIC_LIBRARY_PREFIX}${PROJECT_NAME}_static${CMAKE_STATIC_LIBRARY_SUFFIX})
set(OUTPUT_PDB_NAME ${CMAKE_STATIC_LIBRARY_PREFIX}${PROJECT_NAME}.pdb)
set(OUTPUT_FILE_PATH ${CMAKE_BINARY_DIR}/${OUTPUT_FILE_NAME})
set(OUTPUT_PDB_PATH ${CMAKE_BINARY_DIR}/${OUTPUT_PDB_NAME})

set(BINJA_LIB_DIR $<TARGET_FILE_DIR:binaryninjacore>)
else()
# NOTE: --no-default-features is set to disable building artifacts used for testing
# NOTE: the linker is looking in the target dir and linking on it apparently.
set(CARGO_FEATURES "--no-default-features")

set(OUTPUT_FILE_NAME ${CMAKE_SHARED_LIBRARY_PREFIX}${PROJECT_NAME}${CMAKE_SHARED_LIBRARY_SUFFIX})
set(OUTPUT_PDB_NAME ${CMAKE_SHARED_LIBRARY_PREFIX}${PROJECT_NAME}.pdb)
set(OUTPUT_FILE_PATH ${BN_CORE_PLUGIN_DIR}/${OUTPUT_FILE_NAME})
set(OUTPUT_PDB_PATH ${BN_CORE_PLUGIN_DIR}/${OUTPUT_PDB_NAME})

set(BINJA_LIB_DIR ${BN_INSTALL_BIN_DIR})
endif()


add_custom_target(${PROJECT_NAME} ALL DEPENDS ${OUTPUT_FILE_PATH})
add_dependencies(${PROJECT_NAME} binaryninjaapi)
get_target_property(BN_API_SOURCE_DIR binaryninjaapi SOURCE_DIR)
list(APPEND CMAKE_MODULE_PATH "${BN_API_SOURCE_DIR}/cmake")
find_package(BinaryNinjaCore REQUIRED)

set_property(TARGET ${PROJECT_NAME} PROPERTY OUTPUT_FILE_PATH ${OUTPUT_FILE_PATH})

# Add the whole api to the depends too
file(GLOB_RECURSE API_SOURCES CONFIGURE_DEPENDS
${PROJECT_SOURCE_DIR}/../../binaryninjacore.h
${PROJECT_SOURCE_DIR}/../../rust/binaryninjacore-sys/build.rs
${PROJECT_SOURCE_DIR}/../../rust/binaryninjacore-sys/Cargo.toml
${PROJECT_SOURCE_DIR}/../../rust/binaryninjacore-sys/src/*.rs
${PROJECT_SOURCE_DIR}/../../rust/Cargo.toml
${PROJECT_SOURCE_DIR}/../../rust/src/*/*.rs)

find_program(RUSTUP_PATH rustup REQUIRED HINTS ~/.cargo/bin)
set(RUSTUP_COMMAND ${RUSTUP_PATH} run ${CARGO_STABLE_VERSION} cargo)

if(APPLE)
if(UNIVERSAL)
if(CMAKE_BUILD_TYPE MATCHES Debug)
if(DEMO)
set(AARCH64_LIB_PATH ${PROJECT_BINARY_DIR}/target/aarch64-apple-darwin/dev-demo/${OUTPUT_FILE_NAME})
set(X86_64_LIB_PATH ${PROJECT_BINARY_DIR}/target/x86_64-apple-darwin/dev-demo/${OUTPUT_FILE_NAME})
else()
set(AARCH64_LIB_PATH ${PROJECT_BINARY_DIR}/target/aarch64-apple-darwin/debug/${OUTPUT_FILE_NAME})
set(X86_64_LIB_PATH ${PROJECT_BINARY_DIR}/target/x86_64-apple-darwin/debug/${OUTPUT_FILE_NAME})
endif()
else()
if(DEMO)
set(AARCH64_LIB_PATH ${PROJECT_BINARY_DIR}/target/aarch64-apple-darwin/release-demo/${OUTPUT_FILE_NAME})
set(X86_64_LIB_PATH ${PROJECT_BINARY_DIR}/target/x86_64-apple-darwin/release-demo/${OUTPUT_FILE_NAME})
else()
set(AARCH64_LIB_PATH ${PROJECT_BINARY_DIR}/target/aarch64-apple-darwin/release/${OUTPUT_FILE_NAME})
set(X86_64_LIB_PATH ${PROJECT_BINARY_DIR}/target/x86_64-apple-darwin/release/${OUTPUT_FILE_NAME})
endif()
endif()

add_custom_command(
OUTPUT ${OUTPUT_FILE_PATH}
COMMAND ${CMAKE_COMMAND} -E env
MACOSX_DEPLOYMENT_TARGET=10.14 BINARYNINJADIR=${BINJA_LIB_DIR}
${RUSTUP_COMMAND} clean --target=aarch64-apple-darwin ${CARGO_OPTS} --package binaryninjacore-sys
COMMAND ${CMAKE_COMMAND} -E env
MACOSX_DEPLOYMENT_TARGET=10.14 BINARYNINJADIR=${BINJA_LIB_DIR}
${RUSTUP_COMMAND} clean --target=x86_64-apple-darwin ${CARGO_OPTS} --package binaryninjacore-sys
COMMAND ${CMAKE_COMMAND} -E env
MACOSX_DEPLOYMENT_TARGET=10.14 BINARYNINJADIR=${BINJA_LIB_DIR}
${RUSTUP_COMMAND} build --target=aarch64-apple-darwin ${CARGO_OPTS} ${CARGO_FEATURES}
COMMAND ${CMAKE_COMMAND} -E env
MACOSX_DEPLOYMENT_TARGET=10.14 BINARYNINJADIR=${BINJA_LIB_DIR}
${RUSTUP_COMMAND} build --target=x86_64-apple-darwin ${CARGO_OPTS} ${CARGO_FEATURES}
COMMAND lipo -create ${AARCH64_LIB_PATH} ${X86_64_LIB_PATH} -output ${OUTPUT_FILE_PATH}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES}
)
else()
add_custom_command(
OUTPUT ${OUTPUT_FILE_PATH}
COMMAND ${CMAKE_COMMAND} -E env
MACOSX_DEPLOYMENT_TARGET=10.14 BINARYNINJADIR=${BINJA_LIB_DIR}
${RUSTUP_COMMAND} clean ${CARGO_OPTS} --package binaryninjacore-sys
COMMAND ${CMAKE_COMMAND} -E env
MACOSX_DEPLOYMENT_TARGET=10.14 BINARYNINJADIR=${BINJA_LIB_DIR}
${RUSTUP_COMMAND} build ${CARGO_OPTS} ${CARGO_FEATURES}
COMMAND ${CMAKE_COMMAND} -E copy ${TARGET_DIR}/${OUTPUT_FILE_NAME} ${OUTPUT_FILE_PATH}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES}
)
endif()
elseif(WIN32)
if(DEMO)
add_custom_command(
OUTPUT ${OUTPUT_FILE_PATH}
COMMAND ${CMAKE_COMMAND} -E env BINARYNINJADIR=${BINJA_LIB_DIR} ${RUSTUP_COMMAND} clean ${CARGO_OPTS} --package binaryninjacore-sys
COMMAND ${CMAKE_COMMAND} -E env BINARYNINJADIR=${BINJA_LIB_DIR} ${RUSTUP_COMMAND} build ${CARGO_OPTS} ${CARGO_FEATURES}
COMMAND ${CMAKE_COMMAND} -E copy ${TARGET_DIR}/${OUTPUT_FILE_NAME} ${OUTPUT_FILE_PATH}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES}
)
else()
add_custom_command(
OUTPUT ${OUTPUT_FILE_PATH}
COMMAND ${CMAKE_COMMAND} -E env BINARYNINJADIR=${BINJA_LIB_DIR} ${RUSTUP_COMMAND} clean ${CARGO_OPTS} --package binaryninjacore-sys
COMMAND ${CMAKE_COMMAND} -E env BINARYNINJADIR=${BINJA_LIB_DIR} ${RUSTUP_COMMAND} build ${CARGO_OPTS} ${CARGO_FEATURES}
COMMAND ${CMAKE_COMMAND} -E copy ${TARGET_DIR}/${OUTPUT_FILE_NAME} ${OUTPUT_FILE_PATH}
COMMAND ${CMAKE_COMMAND} -E copy ${TARGET_DIR}/${OUTPUT_PDB_NAME} ${OUTPUT_PDB_PATH}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES}
)
endif()
else()
add_custom_command(
OUTPUT ${OUTPUT_FILE_PATH}
COMMAND ${CMAKE_COMMAND} -E env BINARYNINJADIR=${BINJA_LIB_DIR} ${RUSTUP_COMMAND} clean ${CARGO_OPTS} --package binaryninjacore-sys
COMMAND ${CMAKE_COMMAND} -E env BINARYNINJADIR=${BINJA_LIB_DIR} ${RUSTUP_COMMAND} build ${CARGO_OPTS} ${CARGO_FEATURES}
COMMAND ${CMAKE_COMMAND} -E copy ${TARGET_DIR}/${OUTPUT_FILE_NAME} ${OUTPUT_FILE_PATH}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES}
)
endif()
19 changes: 19 additions & 0 deletions plugins/workflow_swift/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "workflow_swift"
version = "0.1.0"
edition = "2021"
license = "BSD-3-Clause"
publish = false

[lib]
crate-type = ["staticlib", "cdylib"]

[features]
demo = []

[dependencies]
binaryninja = { workspace = true }
binaryninjacore-sys.workspace = true
tracing = "0.1"
thiserror = "2.0"
swift-demangler = { git = "https://github.com/Vector35/swift-demangler.git" }
Loading
Loading