Mach-O and DWARF inspection in pure MoonBit: segments, symbols, code signature entitlements and crash-address symbolication, with no Apple toolchain required.
moon run cmd/main -- internal/fixtures/ls.x86_64.machoarch x86_64:
header x86_64 MH_MAGIC_64 (little-endian), subtype 3, filetype 2, 18 commands, 1736 bytes
cpu type 16777223, subtype 3, capabilities 0x0
filetype 2
flags 0x200085$ moon run cmd/main -- --sections internal/fixtures/ls.x86_64.macho
__PAGEZERO vmaddr 0x0 vmsize 0x100000000 fileoff 0 filesize 0
__TEXT vmaddr 0x100000000 vmsize 0x5000 fileoff 0 filesize 20480
__text addr 0x100000718 size 15095 offset 1816
__stubs addr 0x10000420f size 504 offset 16911$ moon run cmd/main -- --signature internal/fixtures/ls.x86_64.macho
identifier com.apple.ls
version 0x20400
flags 0x0
hash 2
slots 8| Capability | Status |
|---|---|
| Thin Mach-O, 32/64-bit, both byte orders | done |
| Universal (fat) binaries, fat_arch and fat_arch_64 | done |
| Load-command framing with full bounds checking | done |
| Type-level cpusubtype / capability-bit split | done |
| Segment and section table, address ↔ file offset | done |
| Symbol table with names resolved through the string table | done |
| Embedded code signature: superblob, CodeDirectory | done |
| Entitlements, reached through the PKCS#7 DER wrapper | done |
| DWARF 5 line tables, address → file:line:column | done |
| CLI: header, --commands, --symbols, --sections, --signature, --exports, --fixups, --lookup | done |
| --json output for every mode | done |
| dyld export trie | done |
| Chained fixups (PTR_64, PTR_64_OFFSET) | done |
| Chained fixups for arm64e authenticated pointers | planned |
| .debug_info variable and type lookup | planned |
machscope.mbt errors, byte order, the cursor type
reader.mbt bounds-checked reads of every primitive
macho.mbt magic numbers, fat tables, mach_header, command framing
commands.mbt LC_SYMTAB and the symbol table
segments.mbt LC_SEGMENT_64, sections, address ↔ offset
exports.mbt dyld export trie
fixups.mbt chained fixups: imports, segment chains, rebase/bind
signature.mbt superblob index, CodeDirectory, entitlements
der.mbt the DER subset needed to reach a plist
dwarf.mbt line program prologues, LEB128
dwarf_line.mbt the line-number state machine, address lookup
json_report.mbt Json values for every structure
cmd/main/ the CLI
bench/ timing harness$ machscope 0x100000490 internal/fixtures/sample.dsym
t.c:3:33$ machscope --json --exports sample.bin
{
"architectures": [
{
"header": { "magic": "MH_MAGIC_64", ... },
"commands": [ ... ],
"exports": [
{ "name": "_main", "offset": "0x478", "flags": 0, "weak": false,
"reexport": false }
]
}
]
}let file = @machscope.parse(bytes)
match file {
Thin(slice) => {
println(slice.header.kind) // MH_MAGIC_64
println(slice.header.cpu_type) // 16777228 (arm64)
println(slice.header.cpu_subtype_base())
println(slice.header.cpu_capabilities()) // 0x80 for arm64e
for cmd in slice.commands {
println(cmd.cmd) // e.g. LC_SEGMENT_64
}
}
Fat(slices) => {
for slice in slices {
println("\{slice.cpu_type} @ \{slice.offset}+ \{slice.size}")
}
}
}fixture operation mean
ls.x86_64.macho parse (header+commands) 2.7 us
ls.x86_64.macho segments 14.7 us
ls.x86_64.macho symbols 47.6 us
ls.fat.macho parse both slices 6.4 us
ls.fat.macho exports, both slices 10.3 us
sample.dsym line tables 39.9 us
sample.dsym address lookup 40.1 usmoon testpub(all) suberror MachscopeError {
UnexpectedEnd(offset~ : Int64, needed~ : Int64, available~ : Int64)
BadMagic(magic~ : UInt)
BadValue(field~ : String, value~ : Int64)
OutOfBounds(what~ : String, offset~ : Int64, size~ : Int64, limit~ : Int64)
BadName(offset~ : Int64)
Inconsistent(what~ : String, detail~ : String)
LimitExceeded(what~ : String, limit~ : Int64)
Unsupported(what~ : String)
}impl Show for MachscopeErrorpub(all) struct Bounds {
bytes : Bytes
start : Int64
limit : Int64
}fn Bounds::fixed_name(self : Bounds, offset : Int64, width : Int64, what : String) -> String raise MachscopeErrorfn Bounds::require(self : Bounds, offset : Int64, size : Int64, what : String) -> Unit raise MachscopeErrorfn Bounds::slice(self : Bounds, offset : Int64, n : Int64, what : String) -> Bytes raise MachscopeErrorpub(all) struct ChainedFixups {
version : UInt
imports : Array[ChainedImport]
segments : Array[SegmentChains]
}pub(all) struct ChainedImport {
name_offset : UInt
name : String
library_ordinal : UInt
weak : Bool
}pub(all) struct CodeDirectory {
magic : UInt
length : Int64
version : UInt
flags : UInt
identifier : String
team_id : String
hash_type : UInt
hash_size : UInt
page_size : UInt
code_slots : UInt
}pub(all) struct DerElement {
tag : UInt
content_start : Int64
content_end : Int64
next : Int64
}pub(all) enum Endian {
Little
Big
}pub(all) struct Export {
name : String
address : UInt64
flags : UInt64
original_name : String
}pub(all) struct ExportTrieRegion {
offset : Int64
size : Int64
}pub(all) struct FatSlice {
cpu_type : Int64
cpu_subtype : Int64
offset : Int64
size : Int64
align : UInt
image : MachSlice
}pub(all) struct Fixup {
address : Int64
target : Int64
import_index : Int64?
symbol : String?
library_ordinal : UInt
}pub(all) struct LineRow {
address : Int64
line : UInt
column : UInt
file : UInt
is_stmt : Bool
end_sequence : Bool
op_index : UInt
basic_block : Bool
prologue_end : Bool
epilogue_begin : Bool
isa : UInt
discriminator : UInt
}pub(all) struct MachHeader {
kind : MachKind
cpu_type : Int64
cpu_subtype : Int64
file_type : UInt
ncmds : UInt
sizeofcmds : UInt
flags : UInt
reserved : UInt
}pub(all) enum MachKind {
Mach64
Mach32
Mach64Be
Mach32Be
}pub(all) struct RawCommand {
cmd : UInt
size : UInt
offset : Int64
}pub(all) struct Section {
name : String
segment_name : String
addr : Int64
size : Int64
file_offset : UInt
align : UInt
reloc_offset : UInt
nreloc : UInt
flags : UInt
}pub(all) struct SegmentChains {
segment_index : Int64
page_size : UInt
pointer_format : UInt
page_starts : Array[UInt]
}pub(all) struct SignatureBlobRef {
slot : UInt
offset : Int64
magic : UInt
length : Int64
}pub(all) struct SignatureRegion {
offset : Int64
size : Int64
}pub(all) struct SourceLocation {
file : String
line : UInt
column : UInt
}pub(all) struct Symbol {
name : String
n_type : UInt
n_sect : UInt
n_desc : UInt
n_value : Int64
}pub(all) struct SymtabCommand {
symoff : Int64
nsyms : UInt
stroff : Int64
strsize : UInt
}let CHAINED_IMPORT_FORMAT : UIntlet CHAINED_PTR_64 : UIntlet CHAINED_PTR_64_OFFSET : UIntlet CHAINED_PTR_ARM64E_USERLAND : UIntlet CPU_SUBTYPE_MASK : Int64let CSSLOT_ENTITLEMENTS : UIntlet EXPORT_FLAG_REEXPORT : UInt64let EXPORT_FLAG_STUB_AND_RESOLVER : UInt64let EXPORT_FLAG_WEAK : UInt64let EXPORT_KIND_ABSOLUTE : UInt64let EXPORT_KIND_MASK : UInt64let EXPORT_KIND_REGULAR : UInt64let EXPORT_KIND_THREAD_LOCAL : UInt64let LC_DYLD_EXPORTS_TRIE : UIntlet LC_SEGMENT : UIntlet N_STAB : UIntlet S_ZEROFILL : UIntlet TAG_CTX_0 : UIntlet TAG_CTX_1 : UIntfn code_directory(slice : MachSlice, region : SignatureRegion) -> CodeDirectory? raise MachscopeErrorfn file_report(file : MachFile, segments_? : Bool, symbols_? : Bool, exports_? : Bool, fixups_? : Bool) -> Json raise MachscopeErrorfn hex(v : Int64) -> Stringfn signature_blobs(slice : MachSlice, region : SignatureRegion) -> Array[SignatureBlobRef] raise MachscopeErrorfn slice_report(slice : MachSlice, segments_? : Bool, symbols_? : Bool, exports_? : Bool, fixups_? : Bool) -> Json raise MachscopeErrorfn string_table_entry(slice : MachSlice, tab : SymtabCommand, offset : UInt) -> String raise MachscopeErrorfn subtype_base(raw : Int64) -> Int64fn subtype_capabilities(raw : Int64) -> Int64Install
Download zipMach-O and DWARF inspection in pure MoonBit: segments, symbols, code signature entitlements and crash-address symbolication, with no Apple toolchain required.