Editing Mach-O files (hint: you can't, really)


Older: v1

You can't feasibly edit Mach-O object files. Bummer.

Why would anyone want to do this?

I want to sign a bit of code, which is acting as a web server, so that OS X will be willing to let it through its application firewall, without me having to click 'yes, OK' whenever I boot the machine.

That's easy: Apple have instructions for that. These include the instruction to create a suitable section in the binary to be signed, by adding arguments to the linker options:

-sectcreate __TEXT __info_plist Info.plist_path

But: The executable I'm trying to sign is produced using Racket (a fine Scheme implementation, with a nifty built-in web server), which assembles a standalone executable from Racket/scheme code by cloning the 'real' racket executable and editing the Mach-O file directly. That means that the executable I want to sign is a pre-existing one, and not one I'm linking for myself. That means in turn that, if I want to follow Apple's instructions here, I'm going to have to edit in the required __TEXT.__info_plist section myself.

A brief discussion on the Racket mailing list didn't come up with anything (one good suggestion included building Racket from source, and including the new section in that binary, but that's hammer-and-nut territory, which I'd prefer to steer clear of if I can).

So, nothing for it (especially with some pressing obligations to be urgently procrastinated), but to try to write such an editor myself. How hard can it be?

Actually, um..., quite hard.

The structure of Mach-O files

There appear to be no stock commands which edit a Mach-O object file (which includes executables). The otool command allows you to view the structure of such a file (use otool -l), but that's about it.

The structure of a Mach-O object file is documented on the Apple reference site. In summary, a Mach-O object file has the following structure:

The segments contain zero or more sections. The header and load commands are deemed to be in the first segment of the file, before any of that segment's sections. There are a couple of dozen load commands documented in the Apple documentation, but there are further commands defined in the relevant header file, and therefore clearly private (in the sense of do not monkey with, or may by redefined without warning, or do yourself a favour and stay well clear if you know what's good for you).

Adding a section would imply changing the length of a segment. Unless the section were extremely small, this would require pushing the following segment further into the file. That's the hard bit, because a lot of the load commands refer to data within the file with absolute offsets from the beginning of the file (as opposed to, say, the beginning of the segment or section which contains them), so that relocating the segments in a putative Mach-O editor would involve patching up a large number of offsets. That doesn't look easy, because there's a lot of offsets knocking around, all of which would have to be correct, but not all of which are even in publicly documented structures.

There are one or two Mach-O file viewers on the web, but none which do much that's different from otool, as far as I can see (it's not particularly hard: I wrote most of one this afternoon whilst trying to understand the format). There's at least one Mach-O editor, but it didn't seem to do anything that lipo didn't do (called 'moatool', but the source appears to have disappeared from google code).

Oh well. Time to find a Plan B, I suppose.

But wait!

I said Racket does something like this, doesn't it, when it compiles files into a standalone binary! So Racket clearly knows how to edit its own binary. Gasp!

So we rush to collects/compiler/private/mach-o.rkt to grub through the pearls. But no such luck. That compilation stage does involve some direct Mach-O editing, but 'only' to the extent of adding a new segment, at almost the end of the file, where there's no further need for offset patching – that's a lot easier than adding a section to an existing segment. Still pretty damn funky; but not what I'm needing right now.

So, we're still in search of a Plan B, then.

Norman, 2010 December 11