1

Given the type

struct thin_header {
 uint32_t offset;
 uint32_t size;
 struct mach_header header;
};

and the following function signature

struct thin_header *headersFromBinary(struct thin_header *headers, NSData *binary, uint32_t *amount);

I would like to convert the following code into Swift

struct thin_header headers[4];
uint32_t numHeaders = 0;
headersFromBinary(headers, binary, &numHeaders);

So far, I have done the following

var headers = thin_header()
var numHeaders = UInt32()
headersFromBinary(&headers, binary, &numHeaders)

However, I do not believe this makes sense because headers should really be an array of thin_headers. This code does compile and causes no errors at runtime, but how would I be able to loop through the array of thin_headers?

asked Mar 25, 2021 at 1:47
3
  • I've updated my answer for two reasons. The first is that I didn't actually answer the question of how to call headersFromBinary. The second is that I did a quick refresher on Mach-O headers, so my answer needed some small revisions. Commented Mar 25, 2021 at 10:58
  • @ChipJarred thank you very much for the very detailed answer!! Commented Mar 25, 2021 at 14:04
  • You're welcome. In the iteration loop toward the end of my answer, I'm assuming that thin_header.size is the number of bytes that includes thin_header itself. If only the size of the actual mach_header, then you'll need to change the MemoryLayout reference to use mach_header and mach_header_64. Commented Mar 25, 2021 at 14:11

2 Answers 2

2

Doing Mach-O parsing are we?

Basically you're not going to be able to do something as simple as making an array of thin headers. It's been a while since I've looked at the Mach-O format specs but I think they follow the fairly common C technique of defining structs that have some common information at the start, but differing length information thereafter, so where you have mach_header it could actually be a mach_header_64 and you have to use the size field to determine which.

So you basically you need to be able to advance by byte counts but then look at any given location as your thin header. You'll also need to define different structs for the different header types if you want to look at anything other than size and offset.

struct ThinHeader
{
 let offset: UInt32
 let size: UInt32
}
struct FatHeader32
{
 let thinHder: ThinHeader
 let restOfHder: mach_header
}
struct FatHeader64
{
 let thinHder: ThinHeader
 let restOfHder: mach_header_64
}

So to use the C API you give, you have to do a bit of fakery:


var machoHeaderData = Data(repeating: 0, count: MemoryLayout<fat_header64>.stride * maxHeaders)
var amount: UInt32 = dhrData.count // I assume that's what this is supposed to be
machoHeaderData.withUnsafeMutableBytes
{
 0ドル.withMemoryRebound(to: thin_header.self)
 {
 _ = headersFromBinary(&0,ドル ptrToBinary, &amount)
 }
}

Now machoHeaderData will contain the bytes of the array returned by headersFromBinary.

machoHeaderData.withUnsafeBytes
{
 var rawHdrPtr = 0ドル.baseAddress
 for _ in 0..<numHeaders
 {
 let size = rawHdrPtr!.bindMemory(to: ThinHeader.self, capacity: 1).pointee.size
 if size == MemoryLayout<FatHeader64>.size
 {
 let hdrPtr = rawHdrPtr!.bindMemory(to: FatHeader64.self, capacity: 1)
 doSomethingWithHeader64(hdrPtr.pointee)
 }
 else
 {
 assert(size == MemoryLayout<FatHeader32>.size)
 let hdrPtr = rawHdrPtr!.bindMemory(to: FatHeader32.self, capacity: 1)
 doSomethingWithHeader32(hdrPtr.pointee)
 }
 
 rawHdrPtr = rawHdrPtr!.advanced(by: size)
 }
}

Alternatively you could just assume it's a FatHeader32 type and rebind to FatHeader64 when size indicates. You save a bind to thin_header first, not that it's an expensive call. Just a matter of preference, I suppose.

The code above assumes that the size field in thin_header is the number of bytes in the actual header data, but the actual correct way to determine the size of a Mach-O header is by checking its magic field. It's value indicates the size

  • MH_MAGIC indicates mach_header in native byte-order
  • MH_CIGAM indicates mach_header in swapped byte-order
  • MH_MAGIC_64 indicates mach_header_64 in native byte-order
  • MH_CIGAM_64 indicates mach_header_64 in swapped byte-order

Pointers in Swift kind of suck to use... that's actually on purpose, to discourage their use, while acknowledging that they are sometimes necessary. It also means Swift will never displace C for systems programming, especially at the kernel level.

answered Mar 25, 2021 at 2:11
Sign up to request clarification or add additional context in comments.

Comments

1

To convert the C code directly into Swift, I would write something like this:

(Assuming you have prepared binary as Data.)

var headers: [thin_header] = Array(repeating: thin_header(), count: 4)
var numHeaders: UInt32 = 0
headersFromBinary(&headers, binary, &numHeaders)

but how would I be able to loop through the array of thin_headers?

Making headers an Array as above, you can loop through it.

answered Mar 25, 2021 at 11:22

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.