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.
headersFromBinary. The second is that I did a quick refresher on Mach-O headers, so my answer needed some small revisions.thin_header.sizeis the number of bytes that includesthin_headeritself. If only the size of the actualmach_header, then you'll need to change theMemoryLayoutreference to usemach_headerandmach_header_64.