Why XNU?

Perhaps you know that OSX and iOS are based on XNU. So technically Mach Messages will be created on XNU layer. Mach IPC is derived from the Mach microkernel.

There is some concept that knowing them will help you to continue understanding how Mach Message works: Tasks, Threads, Port. I recommend you to have a look into apple documents for Mach Overview.

How is a Mach Message looks like?

To see Mach Message’s structure lets take a look into a document:

There are three important parts that we have to take a look into them.

typedef struct

{

mach_msg_bits_t msgh_bits;

mach_msg_size_t msgh_size;

mach_port_t msgh_remote_port;

mach_port_t msgh_local_port;

mach_msg_size_t msgh_reserved;

mach_msg_id_t msgh_id;

} mach_msg_header_t; typedef struct

{

mach_msg_size_t msgh_descriptor_count;

} mach_msg_body_t; typedef struct

{

mach_msg_header_t header;

mach_msg_body_t body;

} mach_msg_base_t;

Let’s begin header:

msgh_bits : It’s a bitmap who specifies some properties of the message like the message is simple or complex ( will talk about it later )

: It’s a bitmap who specifies some properties of the message like the message is simple or complex ( will talk about it later ) msgh_size : Size of the message which includes header and body

: Size of the message which includes header and body msgh_remote_port : Port right of where we are going to send the message.

: Port right of where we are going to send the message. msgh_local_port : Specifies an auxiliary port right, which is conventionally used as a reply port by the recipient of the message

: Specifies an auxiliary port right, which is conventionally used as a reply port by the recipient of the message msgh_reserved -> msgh_voucher_port : The msgh_reserved field has become msgh_voucher_port with the introduction of vouchers. Vouchers are used to pass arbitrary data in messages over key-value pairs

: The msgh_reserved field has become msgh_voucher_port with the introduction of vouchers. Vouchers are used to pass arbitrary data in messages over key-value pairs msgh_id: 32-bit field message-id.

In the message body, we see something about “descriptor”

typedef union

{

mach_msg_port_descriptor_t port;

mach_msg_ool_descriptor_t out_of_line;

mach_msg_ool_ports_descriptor_t ool_ports;

mach_msg_type_descriptor_t type;

} mach_msg_descriptor_t;

mach_msg_port_descriptor_t : Embedding a port into the message

: Embedding a port into the message mach_msg_ool_descriptor_t: Attaching ool data to the message

Attaching ool data to the message mach_msg_ool_ports_descriptor_t: Adding OOL ports array in a message

Adding OOL ports array in a message mach_msg_type_descriptor_t: is gonna be of these types.

#define MACH_MSG_PORT_DESCRIPTOR 0

#define MACH_MSG_OOL_DESCRIPTOR 1

#define MACH_MSG_OOL_PORTS_DESCRIPTOR 2

#define MACH_MSG_OOL_VOLATILE_DESCRIPTOR 3

Based on names and former descriptions we the names almost clear about what they are doing except MACH_MSG_OOL_VOLATILE_DESCRIPTOR which is used for Sending volatile data in a message. Could be very handy.

If the message is complex, it contains count of type descriptors and the type descriptors themselves (mach_msg_descriptor_t). Then the size of the message must be specified in bytes, and includes the message header, descriptor count, descriptors,and inline data.

now let’s make our hands dirty. I going to create a very simple application to test sending a very simple Mach Message. basically, it’s just a header without content.

In our test app, at the beginning we create a port to establish communication, Then simply change message id for each message that we sending and listening to messages.