Skip to content

Commit

Permalink
Merge pull request #15 from madsmtm/static-msg-send
Browse files Browse the repository at this point in the history
Statically find `msgSend` functions on apple devices
  • Loading branch information
madsmtm authored Sep 2, 2021
2 parents d857e90 + 5f3fa07 commit de78ed4
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 104 deletions.
55 changes: 26 additions & 29 deletions objc/src/message/apple/arm.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use core::any::{Any, TypeId};
use core::mem;

use super::MsgSendFn;
use crate::runtime::Imp;
use crate::{Encode, Encoding};

// TODO: C-unwind
extern "C" {
fn objc_msgSend();
fn objc_msgSend_stret();
Expand All @@ -11,32 +13,27 @@ extern "C" {
fn objc_msgSendSuper_stret();
}

pub fn msg_send_fn<R: Any>() -> Imp {
// Double-word sized fundamental data types don't use stret,
// but any composite type larger than 4 bytes does.
// <http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042e/IHI0042E_aapcs.pdf>

let type_id = TypeId::of::<R>();
if mem::size_of::<R>() <= 4
|| type_id == TypeId::of::<i64>()
|| type_id == TypeId::of::<u64>()
|| type_id == TypeId::of::<f64>()
{
objc_msgSend
} else {
objc_msgSend_stret
}
}

pub fn msg_send_super_fn<R: Any>() -> Imp {
let type_id = TypeId::of::<R>();
if mem::size_of::<R>() <= 4
|| type_id == TypeId::of::<i64>()
|| type_id == TypeId::of::<u64>()
|| type_id == TypeId::of::<f64>()
{
objc_msgSendSuper
} else {
objc_msgSendSuper_stret
}
/// Double-word sized fundamental data types don't use stret, but any
/// composite type larger than 4 bytes does.
///
/// <http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042e/IHI0042E_aapcs.pdf>
impl<T: Encode> MsgSendFn for T {
const MSG_SEND: Imp = {
if let Encoding::LongLong | Encoding::ULongLong | Encoding::Double = T::ENCODING {
objc_msgSend
} else if mem::size_of::<T>() <= 4 {
objc_msgSend
} else {
objc_msgSend_stret
}
};
const MSG_SEND_SUPER: Imp = {
if let Encoding::LongLong | Encoding::ULongLong | Encoding::Double = T::ENCODING {
objc_msgSendSuper
} else if mem::size_of::<T>() <= 4 {
objc_msgSendSuper
} else {
objc_msgSendSuper_stret
}
};
}
18 changes: 9 additions & 9 deletions objc/src/message/apple/arm64.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
use super::MsgSendFn;
use crate::runtime::Imp;
use crate::Encode;

// TODO: C-unwind
extern "C" {
fn objc_msgSend();

fn objc_msgSendSuper();
}

pub fn msg_send_fn<R>() -> Imp {
// stret is not even available in arm64.
// <https://twitter.com/gparker/status/378079715824660480>

objc_msgSend
}

pub fn msg_send_super_fn<R>() -> Imp {
objc_msgSendSuper
/// `objc_msgSend_stret` is not even available in arm64.
///
/// <https://twitter.com/gparker/status/378079715824660480>
impl<T: Encode> MsgSendFn for T {
const MSG_SEND: Imp = objc_msgSend;
const MSG_SEND_SUPER: Imp = objc_msgSendSuper;
}
21 changes: 12 additions & 9 deletions objc/src/message/apple/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
use core::any::Any;

use super::{Message, MessageArguments, MessageError, Super};
use crate::runtime::{Class, Object, Sel};
use super::{Encode, Message, MessageArguments, MessageError, Super};
use crate::runtime::{Class, Imp, Object, Sel};

#[cfg(target_arch = "x86")]
#[path = "x86.rs"]
Expand All @@ -16,16 +14,21 @@ mod arch;
#[path = "arm64.rs"]
mod arch;

use self::arch::{msg_send_fn, msg_send_super_fn};
/// On the above architectures we can statically find the correct method to
/// call from the return type, by looking at it's `Encode` implementation.
trait MsgSendFn: Encode {
const MSG_SEND: Imp;
const MSG_SEND_SUPER: Imp;
}

pub unsafe fn send_unverified<T, A, R>(obj: *const T, sel: Sel, args: A) -> Result<R, MessageError>
where
T: Message,
A: MessageArguments,
R: Any,
R: Encode,
{
let receiver = obj as *mut T as *mut Object;
let msg_send_fn = msg_send_fn::<R>();
let msg_send_fn = R::MSG_SEND;
objc_try!({ A::invoke(msg_send_fn, receiver, sel, args) })
}

Expand All @@ -38,13 +41,13 @@ pub unsafe fn send_super_unverified<T, A, R>(
where
T: Message,
A: MessageArguments,
R: Any,
R: Encode,
{
let sup = Super {
receiver: obj as *mut T as *mut Object,
superclass,
};
let receiver = &sup as *const Super as *mut Object;
let msg_send_fn = msg_send_super_fn::<R>();
let msg_send_fn = R::MSG_SEND_SUPER;
objc_try!({ A::invoke(msg_send_fn, receiver, sel, args) })
}
50 changes: 25 additions & 25 deletions objc/src/message/apple/x86.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use core::any::{Any, TypeId};
use core::mem;

use super::MsgSendFn;
use crate::runtime::Imp;
use crate::{Encode, Encoding};

// TODO: C-unwind
extern "C" {
fn objc_msgSend();
fn objc_msgSend_fpret();
Expand All @@ -12,28 +14,26 @@ extern "C" {
fn objc_msgSendSuper_stret();
}

pub fn msg_send_fn<R: Any>() -> Imp {
// Structures 1 or 2 bytes in size are placed in EAX.
// Structures 4 or 8 bytes in size are placed in: EAX and EDX.
// Structures of other sizes are placed at the address supplied by the caller.
// <https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/LowLevelABI/130-IA-32_Function_Calling_Conventions/IA32.html>

let type_id = TypeId::of::<R>();
let size = mem::size_of::<R>();
if type_id == TypeId::of::<f32>() || type_id == TypeId::of::<f64>() {
objc_msgSend_fpret
} else if size == 0 || size == 1 || size == 2 || size == 4 || size == 8 {
objc_msgSend
} else {
objc_msgSend_stret
}
}

pub fn msg_send_super_fn<R: Any>() -> Imp {
let size = mem::size_of::<R>();
if size == 0 || size == 1 || size == 2 || size == 4 || size == 8 {
objc_msgSendSuper
} else {
objc_msgSendSuper_stret
}
/// Structures 1 or 2 bytes in size are placed in EAX.
/// Structures 4 or 8 bytes in size are placed in: EAX and EDX.
/// Structures of other sizes are placed at the address supplied by the caller.
///
/// <https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/LowLevelABI/130-IA-32_Function_Calling_Conventions/IA32.html>
impl<T: Encode> MsgSendFn for T {
const MSG_SEND: Imp = {
if let Encoding::Float | Encoding::Double = T::ENCODING {
objc_msgSend_fpret
} else if let 0 | 1 | 2 | 4 | 8 = mem::size_of::<T>() {
objc_msgSend
} else {
objc_msgSend_stret
}
};
const MSG_SEND_SUPER: Imp = {
if let 0 | 1 | 2 | 4 | 8 = mem::size_of::<T>() {
objc_msgSendSuper
} else {
objc_msgSendSuper_stret
}
};
}
42 changes: 23 additions & 19 deletions objc/src/message/apple/x86_64.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use core::mem;

use super::MsgSendFn;
use crate::runtime::Imp;
use crate::Encode;

// TODO: C-unwind
extern "C" {
fn objc_msgSend();
fn objc_msgSend_stret();
Expand All @@ -10,23 +13,24 @@ extern "C" {
fn objc_msgSendSuper_stret();
}

pub fn msg_send_fn<R>() -> Imp {
// If the size of an object is larger than two eightbytes, it has class MEMORY.
// If the type has class MEMORY, then the caller provides space for the return
// value and passes the address of this storage.
// <http://people.freebsd.org/~obrien/amd64-elf-abi.pdf>

if mem::size_of::<R>() <= 16 {
objc_msgSend
} else {
objc_msgSend_stret
}
}

pub fn msg_send_super_fn<R>() -> Imp {
if mem::size_of::<R>() <= 16 {
objc_msgSendSuper
} else {
objc_msgSendSuper_stret
}
/// If the size of an object is larger than two eightbytes, it has class
/// MEMORY. If the type has class MEMORY, then the caller provides space for
/// the return value and passes the address of this storage.
///
/// <http://people.freebsd.org/~obrien/amd64-elf-abi.pdf>
impl<T: Encode> MsgSendFn for T {
const MSG_SEND: Imp = {
if mem::size_of::<T>() <= 16 {
objc_msgSend
} else {
objc_msgSend_stret
}
};
const MSG_SEND_SUPER: Imp = {
if mem::size_of::<T>() <= 16 {
objc_msgSendSuper
} else {
objc_msgSendSuper_stret
}
};
}
7 changes: 3 additions & 4 deletions objc/src/message/gnustep.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use core::any::Any;
use core::mem;

use super::{Message, MessageArguments, MessageError, Super};
use super::{Encode, Message, MessageArguments, MessageError, Super};
use crate::runtime::{Class, Imp, Object, Sel};

extern "C" {
Expand All @@ -13,7 +12,7 @@ pub unsafe fn send_unverified<T, A, R>(obj: *const T, sel: Sel, args: A) -> Resu
where
T: Message,
A: MessageArguments,
R: Any,
R: Encode,
{
if obj.is_null() {
return mem::zeroed();
Expand All @@ -33,7 +32,7 @@ pub unsafe fn send_super_unverified<T, A, R>(
where
T: Message,
A: MessageArguments,
R: Any,
R: Encode,
{
let receiver = obj as *mut T as *mut Object;
let sup = Super {
Expand Down
14 changes: 5 additions & 9 deletions objc/src/message/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use alloc::string::{String, ToString};
use core::any::Any;
use core::fmt;
use core::mem;
use std::error::Error;
Expand Down Expand Up @@ -80,7 +79,7 @@ pub unsafe trait Message: RefEncode {
where
Self: Sized,
A: MessageArguments + EncodeArguments,
R: Any + Encode,
R: Encode,
{
send_message(self, sel, args)
}
Expand Down Expand Up @@ -128,16 +127,13 @@ pub trait MessageArguments: Sized {
/// This method is the primitive used when sending messages and should not
/// be called directly; instead, use the `msg_send!` macro or, in cases
/// with a dynamic selector, the [`Message::send_message`] method.
unsafe fn invoke<R>(imp: Imp, obj: *mut Object, sel: Sel, args: Self) -> R
where
R: Any;
unsafe fn invoke<R>(imp: Imp, obj: *mut Object, sel: Sel, args: Self) -> R;
}

macro_rules! message_args_impl {
($($a:ident : $t:ident),*) => (
impl<$($t),*> MessageArguments for ($($t,)*) {
unsafe fn invoke<R>(imp: Imp, obj: *mut Object, sel: Sel, ($($a,)*): Self) -> R
where R: Any {
unsafe fn invoke<R>(imp: Imp, obj: *mut Object, sel: Sel, ($($a,)*): Self) -> R {
let imp: unsafe extern fn(*mut Object, Sel $(, $t)*) -> R =
mem::transmute(imp);
imp(obj, sel $(, $a)*)
Expand Down Expand Up @@ -221,7 +217,7 @@ pub unsafe fn send_message<T, A, R>(obj: *const T, sel: Sel, args: A) -> Result<
where
T: Message,
A: MessageArguments + EncodeArguments,
R: Any + Encode,
R: Encode,
{
#[cfg(feature = "verify_message")]
{
Expand All @@ -247,7 +243,7 @@ pub unsafe fn send_super_message<T, A, R>(
where
T: Message,
A: MessageArguments + EncodeArguments,
R: Any + Encode,
R: Encode,
{
#[cfg(feature = "verify_message")]
{
Expand Down

0 comments on commit de78ed4

Please sign in to comment.