You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 1b9d7cf
Browse filesBrowse the repository at this point in the historyBrowse files
The readme listed Mach-O among the formats left for contributors, and the manual did not mention it. Adds it to both, following how the other formats are covered: a feature list in the readme and a section in the manual with an overview, reading, writing, editing and signing.
The overview states the relationship a caller has to know about, that a section's address is its segment's address plus the section's distance from the segment's file offset, since that is what decides which parts of an image a layout may move and why adding a load command is bounded by the padding the linker left.
- readme.md: Mach-O added to the supported formats and dropped from the longer term plan
- doc/readme.md: the manual section
-[PE and COFF Specification](https://docs.microsoft.com/en-us/windows/win32/debug/pe-format)
549
+
550
+
## Mach-O Object File Format
551
+
552
+
### Overview
553
+
554
+
The main entry-point for reading/writing a Mach-O file is the [`MachOFile`](https://github.com/xoofx/LibObjectFile/blob/master/src/LibObjectFile/MachO/MachOFile.cs) class.
555
+
556
+
This class is the equivalent of the Mach-O header and contains the load commands, which describe everything else in the image, along with the content those commands point at.
557
+
558
+
#### Content
559
+
560
+
Everything in a Mach-O image is a `MachOContent` in the ordered `MachOFile.Content` list: the header, the load command table, the padding after it, the bytes of each section, each table in `__LINKEDIT`, and any alignment padding in between. Every byte of the file belongs to one of them, so writing the list back out reproduces the file.
561
+
562
+
Padding is kept as the bytes that were read rather than regenerated, because a linker pads executable sections with `nop` rather than zeros.
563
+
564
+
#### Addresses and what a layout may move
565
+
566
+
A section's address is its segment's address plus the section's distance from the segment's file offset:
Moving a section in the file therefore moves it in memory, and every instruction and relocation referring to it becomes wrong. Content carrying an address is `IsPositionPinned` and a layout leaves it where it is. Only what nothing addresses, which in practice is `__LINKEDIT` and the file tail, is free to move.
573
+
574
+
This is why a `MachOSegment` describes a mapping rather than owning bytes, and why adding a load command is bounded by the padding the linker left after the command table. `MachOFile.AvailableLoadCommandSpace` reports how much of it is left.
575
+
576
+
#### Universal binaries
577
+
578
+
A universal binary holds one image per architecture and is read with [`MachOFatFile`](https://github.com/xoofx/LibObjectFile/blob/master/src/LibObjectFile/MachO/MachOFatFile.cs). Use `MachOFatFile.IsFat` to tell the two apart:
Its header and slice table are stored big-endian whatever the architectures inside are, which is the one place the format departs from the image's own byte order.
592
+
593
+
### Reading a Mach-O File
594
+
595
+
The Mach-O API allows to read from a `System.IO.Stream` via the method `MachOFile.Read`:
596
+
597
+
```csharp
598
+
MachOFilemachO=MachOFile.Read(inputStream);
599
+
foreach (varsegmentinmachO.Segments)
600
+
{
601
+
Console.WriteLine($"{segment.Name} at 0x{segment.VmAddress:X}");
602
+
}
603
+
```
604
+
605
+
Load commands this library does not model are kept as a `MachOUnknownLoadCommand` and written back verbatim, so an image built by a newer linker still round-trips.
606
+
607
+
The symbol table, the indirect symbol table and the relocations of a section are decoded on demand:
A `MachOFile` is written back with `MachOFile.Write`:
619
+
620
+
```csharp
621
+
machO.Write(outputStream);
622
+
```
623
+
624
+
Content is written at the offset recorded for it, so an image that has not been edited comes back byte for byte. Use `MachOFile.Verify` to check an image before writing it.
625
+
626
+
### Editing load commands
627
+
628
+
`MachOFile` offers the operations `install_name_tool` provides, appending to the padding the linker left so that no content moves:
A dependency is appended rather than inserted, because dyld identifies a library by the position of its command among the others and the symbol table binds against that number. Removing a dependency is not offered for the same reason.
637
+
638
+
When the padding runs out these throw, naming the shortfall, rather than moving content and invalidating the image. There is no way around that short of relinking with `-headerpad_max_install_names`, which is also why `install_name_tool` fails in the same situation.
639
+
640
+
### Code signing
641
+
642
+
Apple Silicon refuses to execute an unsigned image, so an edited `arm64` binary has to be signed again to stay runnable:
643
+
644
+
```csharp
645
+
machO.AddRPath("@executable_path/../Frameworks");
646
+
machO.AdHocSign("mytool");
647
+
machO.Write(outputStream);
648
+
```
649
+
650
+
An ad-hoc signature carries no certificate. It states only that the image hashes to what its code directory says, which is what lets the kernel give the process a stable identity.
651
+
652
+
Signing has to be the last thing done before writing. Editing afterwards leaves the signature covering bytes that are no longer there, so `MachOFile.IsCodeSignatureStale` records it and writing fails until the image is signed again.
653
+
654
+
### Printing a Mach-O File
655
+
656
+
`MachOFile.Print` writes the header and every load command in a form close to `otool -h -l`:
657
+
658
+
```csharp
659
+
machO.Print(Console.Out);
660
+
```
661
+
662
+
### Links
663
+
664
+
-[OS X ABI Mach-O File Format Reference](https://github.com/aidansteele/osx-abi-macho-file-format-reference)
Copy file name to clipboardExpand all lines: readme.md
+12-1Lines changed: 12 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -8,10 +8,11 @@ LibObjectFile is a .NET library to read, manipulate and write linker and executa
8
8
>
9
9
> -**PE** image file format (Portable Executable / DLL)
10
10
> -**ELF** object-file format
11
+
> -**Mach-O** object-file format, including universal binaries
11
12
> -**DWARF** debugging format (version 4)
12
13
> -**Archive `ar`** file format (Common, GNU and BSD variants)
13
14
>
14
-
> There is a longer term plan to support other file formats (e.g COFF, MACH-O, .lib) but as I don't
15
+
> There is a longer term plan to support other file formats (e.g COFF, .lib) but as I don't
15
16
> have a need for them right now, it is left as an exercise for PR contributors! ;)
16
17
17
18
## Usage
@@ -54,6 +55,16 @@ elf.Write(outStream);
54
55
- Program headers with or without sections
55
56
-`ElfFile.AddNeededLibrary` injects a `DT_NEEDED` dependency into an existing image without moving any section (address-preserving, `patchelf`-style)
56
57
- Print with `readelf` similar output
58
+
- Good support for the **Mach-O file format**:
59
+
- Support byte-to-byte roundtrip
60
+
- Read and write from/to a `System.IO.Stream`
61
+
- 32 and 64 bit, `i386`, `x86_64` and `arm64`
62
+
- Universal (fat) binaries via `MachOFatFile`
63
+
- All load commands are decoded, and commands this library does not model round-trip verbatim
64
+
- Symbol table, indirect symbol table and relocations
65
+
-`MachOFile.AddLoadDylib`, `AddRPath` and `ChangeDylibName` edit an existing image without moving any section (address-preserving, `install_name_tool`-style)
66
+
-`MachOFile.AdHocSign` writes an ad-hoc code signature, which Apple Silicon requires in order to execute an image
67
+
-`MachOFile.Print` to print the content of a Mach-O file with `otool` similar output
57
68
- Support for **DWARF debugging format**:
58
69
- Partial support of Version 4 (currently still the default for GCC)
59
70
- Support for the sections: `.debug_info`, `.debug_line`, `.debug_aranges`, `.debug_abbrev` and `.debug_str`
0 commit comments