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_header
s. This code does compile and causes no errors at runtime, but how would I be able to loop through the array of thin_headers?
2 Answers 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
indicatesmach_header
in native byte-orderMH_CIGAM
indicatesmach_header
in swapped byte-orderMH_MAGIC_64
indicatesmach_header_64
in native byte-orderMH_CIGAM_64
indicatesmach_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.
Comments
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.
headersFromBinary
. The second is that I did a quick refresher on Mach-O headers, so my answer needed some small revisions.thin_header.size
is the number of bytes that includesthin_header
itself. If only the size of the actualmach_header
, then you'll need to change theMemoryLayout
reference to usemach_header
andmach_header_64
.