1const std = @import("std");
2const builtin = @import("builtin");
3const assert = std.debug.assert;
4const io = std.io;
5const mem = std.mem;
6const meta = std.meta;
7const testing = std.testing;
8
9const Allocator = mem.Allocator;
10
11pub const mach_header = extern struct {
12    magic: u32,
13    cputype: cpu_type_t,
14    cpusubtype: cpu_subtype_t,
15    filetype: u32,
16    ncmds: u32,
17    sizeofcmds: u32,
18    flags: u32,
19};
20
21pub const mach_header_64 = extern struct {
22    magic: u32 = MH_MAGIC_64,
23    cputype: cpu_type_t = 0,
24    cpusubtype: cpu_subtype_t = 0,
25    filetype: u32 = 0,
26    ncmds: u32 = 0,
27    sizeofcmds: u32 = 0,
28    flags: u32 = 0,
29    reserved: u32 = 0,
30};
31
32pub const fat_header = extern struct {
33    magic: u32,
34    nfat_arch: u32,
35};
36
37pub const fat_arch = extern struct {
38    cputype: cpu_type_t,
39    cpusubtype: cpu_subtype_t,
40    offset: u32,
41    size: u32,
42    @"align": u32,
43};
44
45pub const load_command = extern struct {
46    cmd: LC,
47    cmdsize: u32,
48};
49
50/// The uuid load command contains a single 128-bit unique random number that
51/// identifies an object produced by the static link editor.
52pub const uuid_command = extern struct {
53    /// LC_UUID
54    cmd: LC = .UUID,
55
56    /// sizeof(struct uuid_command)
57    cmdsize: u32,
58
59    /// the 128-bit uuid
60    uuid: [16]u8,
61};
62
63/// The version_min_command contains the min OS version on which this
64/// binary was built to run.
65pub const version_min_command = extern struct {
66    /// LC_VERSION_MIN_MACOSX or LC_VERSION_MIN_IPHONEOS or LC_VERSION_MIN_WATCHOS or LC_VERSION_MIN_TVOS
67    cmd: LC,
68
69    /// sizeof(struct version_min_command)
70    cmdsize: u32,
71
72    /// X.Y.Z is encoded in nibbles xxxx.yy.zz
73    version: u32,
74
75    /// X.Y.Z is encoded in nibbles xxxx.yy.zz
76    sdk: u32,
77};
78
79/// The source_version_command is an optional load command containing
80/// the version of the sources used to build the binary.
81pub const source_version_command = extern struct {
82    /// LC_SOURCE_VERSION
83    cmd: LC = .SOURCE_VERSION,
84
85    /// sizeof(source_version_command)
86    cmdsize: u32,
87
88    /// A.B.C.D.E packed as a24.b10.c10.d10.e10
89    version: u64,
90};
91
92/// The build_version_command contains the min OS version on which this
93/// binary was built to run for its platform. The list of known platforms and
94/// tool values following it.
95pub const build_version_command = extern struct {
96    /// LC_BUILD_VERSION
97    cmd: LC = .BUILD_VERSION,
98
99    /// sizeof(struct build_version_command) plus
100    /// ntools * sizeof(struct build_version_command)
101    cmdsize: u32,
102
103    /// platform
104    platform: PLATFORM,
105
106    /// X.Y.Z is encoded in nibbles xxxx.yy.zz
107    minos: u32,
108
109    /// X.Y.Z is encoded in nibbles xxxx.yy.zz
110    sdk: u32,
111
112    /// number of tool entries following this
113    ntools: u32,
114};
115
116pub const build_tool_version = extern struct {
117    /// enum for the tool
118    tool: TOOL,
119
120    /// version number of the tool
121    version: u32,
122};
123
124pub const PLATFORM = enum(u32) {
125    MACOS = 0x1,
126    IOS = 0x2,
127    TVOS = 0x3,
128    WATCHOS = 0x4,
129    BRIDGEOS = 0x5,
130    MACCATALYST = 0x6,
131    IOSSIMULATOR = 0x7,
132    TVOSSIMULATOR = 0x8,
133    WATCHOSSIMULATOR = 0x9,
134    DRIVERKIT = 0x10,
135    _,
136};
137
138pub const TOOL = enum(u32) {
139    CLANG = 0x1,
140    SWIFT = 0x2,
141    LD = 0x3,
142    _,
143};
144
145/// The entry_point_command is a replacement for thread_command.
146/// It is used for main executables to specify the location (file offset)
147/// of main(). If -stack_size was used at link time, the stacksize
148/// field will contain the stack size needed for the main thread.
149pub const entry_point_command = extern struct {
150    /// LC_MAIN only used in MH_EXECUTE filetypes
151    cmd: LC = .MAIN,
152
153    /// sizeof(struct entry_point_command)
154    cmdsize: u32,
155
156    /// file (__TEXT) offset of main()
157    entryoff: u64,
158
159    /// if not zero, initial stack size
160    stacksize: u64,
161};
162
163/// The symtab_command contains the offsets and sizes of the link-edit 4.3BSD
164/// "stab" style symbol table information as described in the header files
165/// <nlist.h> and <stab.h>.
166pub const symtab_command = extern struct {
167    /// LC_SYMTAB
168    cmd: LC = .SYMTAB,
169
170    /// sizeof(struct symtab_command)
171    cmdsize: u32,
172
173    /// symbol table offset
174    symoff: u32,
175
176    /// number of symbol table entries
177    nsyms: u32,
178
179    /// string table offset
180    stroff: u32,
181
182    /// string table size in bytes
183    strsize: u32,
184};
185
186/// This is the second set of the symbolic information which is used to support
187/// the data structures for the dynamically link editor.
188///
189/// The original set of symbolic information in the symtab_command which contains
190/// the symbol and string tables must also be present when this load command is
191/// present.  When this load command is present the symbol table is organized
192/// into three groups of symbols:
193///  local symbols (static and debugging symbols) - grouped by module
194///  defined external symbols - grouped by module (sorted by name if not lib)
195///  undefined external symbols (sorted by name if MH_BINDATLOAD is not set,
196///       			    and in order the were seen by the static
197///  			    linker if MH_BINDATLOAD is set)
198/// In this load command there are offsets and counts to each of the three groups
199/// of symbols.
200///
201/// This load command contains a the offsets and sizes of the following new
202/// symbolic information tables:
203///  table of contents
204///  module table
205///  reference symbol table
206///  indirect symbol table
207/// The first three tables above (the table of contents, module table and
208/// reference symbol table) are only present if the file is a dynamically linked
209/// shared library.  For executable and object modules, which are files
210/// containing only one module, the information that would be in these three
211/// tables is determined as follows:
212/// 	table of contents - the defined external symbols are sorted by name
213///  module table - the file contains only one module so everything in the
214///  	       file is part of the module.
215///  reference symbol table - is the defined and undefined external symbols
216///
217/// For dynamically linked shared library files this load command also contains
218/// offsets and sizes to the pool of relocation entries for all sections
219/// separated into two groups:
220///  external relocation entries
221///  local relocation entries
222/// For executable and object modules the relocation entries continue to hang
223/// off the section structures.
224pub const dysymtab_command = extern struct {
225    /// LC_DYSYMTAB
226    cmd: LC = .DYSYMTAB,
227
228    /// sizeof(struct dysymtab_command)
229    cmdsize: u32,
230
231    // The symbols indicated by symoff and nsyms of the LC_SYMTAB load command
232    // are grouped into the following three groups:
233    //    local symbols (further grouped by the module they are from)
234    //    defined external symbols (further grouped by the module they are from)
235    //    undefined symbols
236    //
237    // The local symbols are used only for debugging.  The dynamic binding
238    // process may have to use them to indicate to the debugger the local
239    // symbols for a module that is being bound.
240    //
241    // The last two groups are used by the dynamic binding process to do the
242    // binding (indirectly through the module table and the reference symbol
243    // table when this is a dynamically linked shared library file).
244
245    /// index of local symbols
246    ilocalsym: u32,
247
248    /// number of local symbols
249    nlocalsym: u32,
250
251    /// index to externally defined symbols
252    iextdefsym: u32,
253
254    /// number of externally defined symbols
255    nextdefsym: u32,
256
257    /// index to undefined symbols
258    iundefsym: u32,
259
260    /// number of undefined symbols
261    nundefsym: u32,
262
263    // For the for the dynamic binding process to find which module a symbol
264    // is defined in the table of contents is used (analogous to the ranlib
265    // structure in an archive) which maps defined external symbols to modules
266    // they are defined in.  This exists only in a dynamically linked shared
267    // library file.  For executable and object modules the defined external
268    // symbols are sorted by name and is use as the table of contents.
269
270    /// file offset to table of contents
271    tocoff: u32,
272
273    /// number of entries in table of contents
274    ntoc: u32,
275
276    // To support dynamic binding of "modules" (whole object files) the symbol
277    // table must reflect the modules that the file was created from.  This is
278    // done by having a module table that has indexes and counts into the merged
279    // tables for each module.  The module structure that these two entries
280    // refer to is described below.  This exists only in a dynamically linked
281    // shared library file.  For executable and object modules the file only
282    // contains one module so everything in the file belongs to the module.
283
284    /// file offset to module table
285    modtaboff: u32,
286
287    /// number of module table entries
288    nmodtab: u32,
289
290    // To support dynamic module binding the module structure for each module
291    // indicates the external references (defined and undefined) each module
292    // makes.  For each module there is an offset and a count into the
293    // reference symbol table for the symbols that the module references.
294    // This exists only in a dynamically linked shared library file.  For
295    // executable and object modules the defined external symbols and the
296    // undefined external symbols indicates the external references.
297
298    /// offset to referenced symbol table
299    extrefsymoff: u32,
300
301    /// number of referenced symbol table entries
302    nextrefsyms: u32,
303
304    // The sections that contain "symbol pointers" and "routine stubs" have
305    // indexes and (implied counts based on the size of the section and fixed
306    // size of the entry) into the "indirect symbol" table for each pointer
307    // and stub.  For every section of these two types the index into the
308    // indirect symbol table is stored in the section header in the field
309    // reserved1.  An indirect symbol table entry is simply a 32bit index into
310    // the symbol table to the symbol that the pointer or stub is referring to.
311    // The indirect symbol table is ordered to match the entries in the section.
312
313    /// file offset to the indirect symbol table
314    indirectsymoff: u32,
315
316    /// number of indirect symbol table entries
317    nindirectsyms: u32,
318
319    // To support relocating an individual module in a library file quickly the
320    // external relocation entries for each module in the library need to be
321    // accessed efficiently.  Since the relocation entries can't be accessed
322    // through the section headers for a library file they are separated into
323    // groups of local and external entries further grouped by module.  In this
324    // case the presents of this load command who's extreloff, nextrel,
325    // locreloff and nlocrel fields are non-zero indicates that the relocation
326    // entries of non-merged sections are not referenced through the section
327    // structures (and the reloff and nreloc fields in the section headers are
328    // set to zero).
329    //
330    // Since the relocation entries are not accessed through the section headers
331    // this requires the r_address field to be something other than a section
332    // offset to identify the item to be relocated.  In this case r_address is
333    // set to the offset from the vmaddr of the first LC_SEGMENT command.
334    // For MH_SPLIT_SEGS images r_address is set to the the offset from the
335    // vmaddr of the first read-write LC_SEGMENT command.
336    //
337    // The relocation entries are grouped by module and the module table
338    // entries have indexes and counts into them for the group of external
339    // relocation entries for that the module.
340    //
341    // For sections that are merged across modules there must not be any
342    // remaining external relocation entries for them (for merged sections
343    // remaining relocation entries must be local).
344
345    /// offset to external relocation entries
346    extreloff: u32,
347
348    /// number of external relocation entries
349    nextrel: u32,
350
351    // All the local relocation entries are grouped together (they are not
352    // grouped by their module since they are only used if the object is moved
353    // from it staticly link edited address).
354
355    /// offset to local relocation entries
356    locreloff: u32,
357
358    /// number of local relocation entries
359    nlocrel: u32,
360};
361
362/// The linkedit_data_command contains the offsets and sizes of a blob
363/// of data in the __LINKEDIT segment.
364pub const linkedit_data_command = extern struct {
365    /// LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO, LC_FUNCTION_STARTS, LC_DATA_IN_CODE, LC_DYLIB_CODE_SIGN_DRS or LC_LINKER_OPTIMIZATION_HINT.
366    cmd: LC,
367
368    /// sizeof(struct linkedit_data_command)
369    cmdsize: u32,
370
371    /// file offset of data in __LINKEDIT segment
372    dataoff: u32,
373
374    /// file size of data in __LINKEDIT segment
375    datasize: u32,
376};
377
378/// The dyld_info_command contains the file offsets and sizes of
379/// the new compressed form of the information dyld needs to
380/// load the image.  This information is used by dyld on Mac OS X
381/// 10.6 and later.  All information pointed to by this command
382/// is encoded using byte streams, so no endian swapping is needed
383/// to interpret it.
384pub const dyld_info_command = extern struct {
385    /// LC_DYLD_INFO or LC_DYLD_INFO_ONLY
386    cmd: LC,
387
388    /// sizeof(struct dyld_info_command)
389    cmdsize: u32,
390
391    // Dyld rebases an image whenever dyld loads it at an address different
392    // from its preferred address.  The rebase information is a stream
393    // of byte sized opcodes whose symbolic names start with REBASE_OPCODE_.
394    // Conceptually the rebase information is a table of tuples:
395    //    <seg-index, seg-offset, type>
396    // The opcodes are a compressed way to encode the table by only
397    // encoding when a column changes.  In addition simple patterns
398    // like "every n'th offset for m times" can be encoded in a few
399    // bytes.
400
401    /// file offset to rebase info
402    rebase_off: u32,
403
404    /// size of rebase info
405    rebase_size: u32,
406
407    // Dyld binds an image during the loading process, if the image
408    // requires any pointers to be initialized to symbols in other images.
409    // The bind information is a stream of byte sized
410    // opcodes whose symbolic names start with BIND_OPCODE_.
411    // Conceptually the bind information is a table of tuples:
412    //    <seg-index, seg-offset, type, symbol-library-ordinal, symbol-name, addend>
413    // The opcodes are a compressed way to encode the table by only
414    // encoding when a column changes.  In addition simple patterns
415    // like for runs of pointers initialzed to the same value can be
416    // encoded in a few bytes.
417
418    /// file offset to binding info
419    bind_off: u32,
420
421    /// size of binding info
422    bind_size: u32,
423
424    // Some C++ programs require dyld to unique symbols so that all
425    // images in the process use the same copy of some code/data.
426    // This step is done after binding. The content of the weak_bind
427    // info is an opcode stream like the bind_info.  But it is sorted
428    // alphabetically by symbol name.  This enable dyld to walk
429    // all images with weak binding information in order and look
430    // for collisions.  If there are no collisions, dyld does
431    // no updating.  That means that some fixups are also encoded
432    // in the bind_info.  For instance, all calls to "operator new"
433    // are first bound to libstdc++.dylib using the information
434    // in bind_info.  Then if some image overrides operator new
435    // that is detected when the weak_bind information is processed
436    // and the call to operator new is then rebound.
437
438    /// file offset to weak binding info
439    weak_bind_off: u32,
440
441    /// size of weak binding info
442    weak_bind_size: u32,
443
444    // Some uses of external symbols do not need to be bound immediately.
445    // Instead they can be lazily bound on first use.  The lazy_bind
446    // are contains a stream of BIND opcodes to bind all lazy symbols.
447    // Normal use is that dyld ignores the lazy_bind section when
448    // loading an image.  Instead the static linker arranged for the
449    // lazy pointer to initially point to a helper function which
450    // pushes the offset into the lazy_bind area for the symbol
451    // needing to be bound, then jumps to dyld which simply adds
452    // the offset to lazy_bind_off to get the information on what
453    // to bind.
454
455    /// file offset to lazy binding info
456    lazy_bind_off: u32,
457
458    /// size of lazy binding info
459    lazy_bind_size: u32,
460
461    // The symbols exported by a dylib are encoded in a trie.  This
462    // is a compact representation that factors out common prefixes.
463    // It also reduces LINKEDIT pages in RAM because it encodes all
464    // information (name, address, flags) in one small, contiguous range.
465    // The export area is a stream of nodes.  The first node sequentially
466    // is the start node for the trie.
467    //
468    // Nodes for a symbol start with a uleb128 that is the length of
469    // the exported symbol information for the string so far.
470    // If there is no exported symbol, the node starts with a zero byte.
471    // If there is exported info, it follows the length.
472    //
473    // First is a uleb128 containing flags. Normally, it is followed by
474    // a uleb128 encoded offset which is location of the content named
475    // by the symbol from the mach_header for the image.  If the flags
476    // is EXPORT_SYMBOL_FLAGS_REEXPORT, then following the flags is
477    // a uleb128 encoded library ordinal, then a zero terminated
478    // UTF8 string.  If the string is zero length, then the symbol
479    // is re-export from the specified dylib with the same name.
480    // If the flags is EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER, then following
481    // the flags is two uleb128s: the stub offset and the resolver offset.
482    // The stub is used by non-lazy pointers.  The resolver is used
483    // by lazy pointers and must be called to get the actual address to use.
484    //
485    // After the optional exported symbol information is a byte of
486    // how many edges (0-255) that this node has leaving it,
487    // followed by each edge.
488    // Each edge is a zero terminated UTF8 of the addition chars
489    // in the symbol, followed by a uleb128 offset for the node that
490    // edge points to.
491
492    /// file offset to lazy binding info
493    export_off: u32,
494
495    /// size of lazy binding info
496    export_size: u32,
497};
498
499/// A program that uses a dynamic linker contains a dylinker_command to identify
500/// the name of the dynamic linker (LC_LOAD_DYLINKER). And a dynamic linker
501/// contains a dylinker_command to identify the dynamic linker (LC_ID_DYLINKER).
502/// A file can have at most one of these.
503/// This struct is also used for the LC_DYLD_ENVIRONMENT load command and contains
504/// string for dyld to treat like an environment variable.
505pub const dylinker_command = extern struct {
506    /// LC_ID_DYLINKER, LC_LOAD_DYLINKER, or LC_DYLD_ENVIRONMENT
507    cmd: LC,
508
509    /// includes pathname string
510    cmdsize: u32,
511
512    /// A variable length string in a load command is represented by an lc_str
513    /// union.  The strings are stored just after the load command structure and
514    /// the offset is from the start of the load command structure.  The size
515    /// of the string is reflected in the cmdsize field of the load command.
516    /// Once again any padded bytes to bring the cmdsize field to a multiple
517    /// of 4 bytes must be zero.
518    name: u32,
519};
520
521/// A dynamically linked shared library (filetype == MH_DYLIB in the mach header)
522/// contains a dylib_command (cmd == LC_ID_DYLIB) to identify the library.
523/// An object that uses a dynamically linked shared library also contains a
524/// dylib_command (cmd == LC_LOAD_DYLIB, LC_LOAD_WEAK_DYLIB, or
525/// LC_REEXPORT_DYLIB) for each library it uses.
526pub const dylib_command = extern struct {
527    /// LC_ID_DYLIB, LC_LOAD_WEAK_DYLIB, LC_LOAD_DYLIB, LC_REEXPORT_DYLIB
528    cmd: LC,
529
530    /// includes pathname string
531    cmdsize: u32,
532
533    /// the library identification
534    dylib: dylib,
535};
536
537/// Dynamicaly linked shared libraries are identified by two things.  The
538/// pathname (the name of the library as found for execution), and the
539/// compatibility version number.  The pathname must match and the compatibility
540/// number in the user of the library must be greater than or equal to the
541/// library being used.  The time stamp is used to record the time a library was
542/// built and copied into user so it can be use to determined if the library used
543/// at runtime is exactly the same as used to built the program.
544pub const dylib = extern struct {
545    /// library's pathname (offset pointing at the end of dylib_command)
546    name: u32,
547
548    /// library's build timestamp
549    timestamp: u32,
550
551    /// library's current version number
552    current_version: u32,
553
554    /// library's compatibility version number
555    compatibility_version: u32,
556};
557
558/// The rpath_command contains a path which at runtime should be added to the current
559/// run path used to find @rpath prefixed dylibs.
560pub const rpath_command = extern struct {
561    /// LC_RPATH
562    cmd: LC = .RPATH,
563
564    /// includes string
565    cmdsize: u32,
566
567    /// path to add to run path
568    path: u32,
569};
570
571/// The segment load command indicates that a part of this file is to be
572/// mapped into the task's address space.  The size of this segment in memory,
573/// vmsize, maybe equal to or larger than the amount to map from this file,
574/// filesize.  The file is mapped starting at fileoff to the beginning of
575/// the segment in memory, vmaddr.  The rest of the memory of the segment,
576/// if any, is allocated zero fill on demand.  The segment's maximum virtual
577/// memory protection and initial virtual memory protection are specified
578/// by the maxprot and initprot fields.  If the segment has sections then the
579/// section structures directly follow the segment command and their size is
580/// reflected in cmdsize.
581pub const segment_command = extern struct {
582    /// LC_SEGMENT
583    cmd: LC = .SEGMENT,
584
585    /// includes sizeof section structs
586    cmdsize: u32,
587
588    /// segment name
589    segname: [16]u8,
590
591    /// memory address of this segment
592    vmaddr: u32,
593
594    /// memory size of this segment
595    vmsize: u32,
596
597    /// file offset of this segment
598    fileoff: u32,
599
600    /// amount to map from the file
601    filesize: u32,
602
603    /// maximum VM protection
604    maxprot: vm_prot_t,
605
606    /// initial VM protection
607    initprot: vm_prot_t,
608
609    /// number of sections in segment
610    nsects: u32,
611    flags: u32,
612};
613
614/// The 64-bit segment load command indicates that a part of this file is to be
615/// mapped into a 64-bit task's address space.  If the 64-bit segment has
616/// sections then section_64 structures directly follow the 64-bit segment
617/// command and their size is reflected in cmdsize.
618pub const segment_command_64 = extern struct {
619    /// LC_SEGMENT_64
620    cmd: LC = .SEGMENT_64,
621
622    /// includes sizeof section_64 structs
623    cmdsize: u32 = @sizeOf(segment_command_64),
624
625    /// segment name
626    segname: [16]u8,
627
628    /// memory address of this segment
629    vmaddr: u64 = 0,
630
631    /// memory size of this segment
632    vmsize: u64 = 0,
633
634    /// file offset of this segment
635    fileoff: u64 = 0,
636
637    /// amount to map from the file
638    filesize: u64 = 0,
639
640    /// maximum VM protection
641    maxprot: vm_prot_t = VM_PROT_NONE,
642
643    /// initial VM protection
644    initprot: vm_prot_t = VM_PROT_NONE,
645
646    /// number of sections in segment
647    nsects: u32 = 0,
648    flags: u32 = 0,
649
650    pub fn segName(seg: segment_command_64) []const u8 {
651        return parseName(&seg.segname);
652    }
653};
654
655/// A segment is made up of zero or more sections.  Non-MH_OBJECT files have
656/// all of their segments with the proper sections in each, and padded to the
657/// specified segment alignment when produced by the link editor.  The first
658/// segment of a MH_EXECUTE and MH_FVMLIB format file contains the mach_header
659/// and load commands of the object file before its first section.  The zero
660/// fill sections are always last in their segment (in all formats).  This
661/// allows the zeroed segment padding to be mapped into memory where zero fill
662/// sections might be. The gigabyte zero fill sections, those with the section
663/// type S_GB_ZEROFILL, can only be in a segment with sections of this type.
664/// These segments are then placed after all other segments.
665///
666/// The MH_OBJECT format has all of its sections in one segment for
667/// compactness.  There is no padding to a specified segment boundary and the
668/// mach_header and load commands are not part of the segment.
669///
670/// Sections with the same section name, sectname, going into the same segment,
671/// segname, are combined by the link editor.  The resulting section is aligned
672/// to the maximum alignment of the combined sections and is the new section's
673/// alignment.  The combined sections are aligned to their original alignment in
674/// the combined section.  Any padded bytes to get the specified alignment are
675/// zeroed.
676///
677/// The format of the relocation entries referenced by the reloff and nreloc
678/// fields of the section structure for mach object files is described in the
679/// header file <reloc.h>.
680pub const @"section" = extern struct {
681    /// name of this section
682    sectname: [16]u8,
683
684    /// segment this section goes in
685    segname: [16]u8,
686
687    /// memory address of this section
688    addr: u32,
689
690    /// size in bytes of this section
691    size: u32,
692
693    /// file offset of this section
694    offset: u32,
695
696    /// section alignment (power of 2)
697    @"align": u32,
698
699    /// file offset of relocation entries
700    reloff: u32,
701
702    /// number of relocation entries
703    nreloc: u32,
704
705    /// flags (section type and attributes
706    flags: u32,
707
708    /// reserved (for offset or index)
709    reserved1: u32,
710
711    /// reserved (for count or sizeof)
712    reserved2: u32,
713};
714
715pub const section_64 = extern struct {
716    /// name of this section
717    sectname: [16]u8,
718
719    /// segment this section goes in
720    segname: [16]u8,
721
722    /// memory address of this section
723    addr: u64 = 0,
724
725    /// size in bytes of this section
726    size: u64 = 0,
727
728    /// file offset of this section
729    offset: u32 = 0,
730
731    /// section alignment (power of 2)
732    @"align": u32 = 0,
733
734    /// file offset of relocation entries
735    reloff: u32 = 0,
736
737    /// number of relocation entries
738    nreloc: u32 = 0,
739
740    /// flags (section type and attributes
741    flags: u32 = S_REGULAR,
742
743    /// reserved (for offset or index)
744    reserved1: u32 = 0,
745
746    /// reserved (for count or sizeof)
747    reserved2: u32 = 0,
748
749    /// reserved
750    reserved3: u32 = 0,
751
752    pub fn sectName(sect: section_64) []const u8 {
753        return parseName(&sect.sectname);
754    }
755
756    pub fn segName(sect: section_64) []const u8 {
757        return parseName(&sect.segname);
758    }
759
760    pub fn type_(sect: section_64) u8 {
761        return @truncate(u8, sect.flags & 0xff);
762    }
763
764    pub fn attrs(sect: section_64) u32 {
765        return sect.flags & 0xffffff00;
766    }
767
768    pub fn isCode(sect: section_64) bool {
769        const attr = sect.attrs();
770        return attr & S_ATTR_PURE_INSTRUCTIONS != 0 or attr & S_ATTR_SOME_INSTRUCTIONS != 0;
771    }
772
773    pub fn isDebug(sect: section_64) bool {
774        return sect.attrs() & S_ATTR_DEBUG != 0;
775    }
776
777    pub fn isDontDeadStrip(sect: section_64) bool {
778        return sect.attrs() & S_ATTR_NO_DEAD_STRIP != 0;
779    }
780
781    pub fn isDontDeadStripIfReferencesLive(sect: section_64) bool {
782        return sect.attrs() & S_ATTR_LIVE_SUPPORT != 0;
783    }
784};
785
786fn parseName(name: *const [16]u8) []const u8 {
787    const len = mem.indexOfScalar(u8, name, @as(u8, 0)) orelse name.len;
788    return name[0..len];
789}
790
791pub const nlist = extern struct {
792    n_strx: u32,
793    n_type: u8,
794    n_sect: u8,
795    n_desc: i16,
796    n_value: u32,
797};
798
799pub const nlist_64 = extern struct {
800    n_strx: u32,
801    n_type: u8,
802    n_sect: u8,
803    n_desc: u16,
804    n_value: u64,
805
806    pub fn stab(sym: nlist_64) bool {
807        return (N_STAB & sym.n_type) != 0;
808    }
809
810    pub fn pext(sym: nlist_64) bool {
811        return (N_PEXT & sym.n_type) != 0;
812    }
813
814    pub fn ext(sym: nlist_64) bool {
815        return (N_EXT & sym.n_type) != 0;
816    }
817
818    pub fn sect(sym: nlist_64) bool {
819        const type_ = N_TYPE & sym.n_type;
820        return type_ == N_SECT;
821    }
822
823    pub fn undf(sym: nlist_64) bool {
824        const type_ = N_TYPE & sym.n_type;
825        return type_ == N_UNDF;
826    }
827
828    pub fn indr(sym: nlist_64) bool {
829        const type_ = N_TYPE & sym.n_type;
830        return type_ == N_INDR;
831    }
832
833    pub fn abs(sym: nlist_64) bool {
834        const type_ = N_TYPE & sym.n_type;
835        return type_ == N_ABS;
836    }
837
838    pub fn weakDef(sym: nlist_64) bool {
839        return (sym.n_desc & N_WEAK_DEF) != 0;
840    }
841
842    pub fn weakRef(sym: nlist_64) bool {
843        return (sym.n_desc & N_WEAK_REF) != 0;
844    }
845
846    pub fn discarded(sym: nlist_64) bool {
847        return (sym.n_desc & N_DESC_DISCARDED) != 0;
848    }
849
850    pub fn tentative(sym: nlist_64) bool {
851        if (!sym.undf()) return false;
852        return sym.n_value != 0;
853    }
854};
855
856/// Format of a relocation entry of a Mach-O file.  Modified from the 4.3BSD
857/// format.  The modifications from the original format were changing the value
858/// of the r_symbolnum field for "local" (r_extern == 0) relocation entries.
859/// This modification is required to support symbols in an arbitrary number of
860/// sections not just the three sections (text, data and bss) in a 4.3BSD file.
861/// Also the last 4 bits have had the r_type tag added to them.
862pub const relocation_info = packed struct {
863    /// offset in the section to what is being relocated
864    r_address: i32,
865
866    /// symbol index if r_extern == 1 or section ordinal if r_extern == 0
867    r_symbolnum: u24,
868
869    /// was relocated pc relative already
870    r_pcrel: u1,
871
872    /// 0=byte, 1=word, 2=long, 3=quad
873    r_length: u2,
874
875    /// does not include value of sym referenced
876    r_extern: u1,
877
878    /// if not 0, machine specific relocation type
879    r_type: u4,
880};
881
882/// After MacOS X 10.1 when a new load command is added that is required to be
883/// understood by the dynamic linker for the image to execute properly the
884/// LC_REQ_DYLD bit will be or'ed into the load command constant.  If the dynamic
885/// linker sees such a load command it it does not understand will issue a
886/// "unknown load command required for execution" error and refuse to use the
887/// image.  Other load commands without this bit that are not understood will
888/// simply be ignored.
889pub const LC_REQ_DYLD = 0x80000000;
890
891pub const LC = enum(u32) {
892    /// segment of this file to be mapped
893    SEGMENT = 0x1,
894
895    /// link-edit stab symbol table info
896    SYMTAB = 0x2,
897
898    /// link-edit gdb symbol table info (obsolete)
899    SYMSEG = 0x3,
900
901    /// thread
902    THREAD = 0x4,
903
904    /// unix thread (includes a stack)
905    UNIXTHREAD = 0x5,
906
907    /// load a specified fixed VM shared library
908    LOADFVMLIB = 0x6,
909
910    /// fixed VM shared library identification
911    IDFVMLIB = 0x7,
912
913    /// object identification info (obsolete)
914    IDENT = 0x8,
915
916    /// fixed VM file inclusion (internal use)
917    FVMFILE = 0x9,
918
919    /// prepage command (internal use)
920    PREPAGE = 0xa,
921
922    /// dynamic link-edit symbol table info
923    DYSYMTAB = 0xb,
924
925    /// load a dynamically linked shared library
926    LOAD_DYLIB = 0xc,
927
928    /// dynamically linked shared lib ident
929    ID_DYLIB = 0xd,
930
931    /// load a dynamic linker
932    LOAD_DYLINKER = 0xe,
933
934    /// dynamic linker identification
935    ID_DYLINKER = 0xf,
936
937    /// modules prebound for a dynamically
938    PREBOUND_DYLIB = 0x10,
939
940    /// image routines
941    ROUTINES = 0x11,
942
943    /// sub framework
944    SUB_FRAMEWORK = 0x12,
945
946    /// sub umbrella
947    SUB_UMBRELLA = 0x13,
948
949    /// sub client
950    SUB_CLIENT = 0x14,
951
952    /// sub library
953    SUB_LIBRARY = 0x15,
954
955    /// two-level namespace lookup hints
956    TWOLEVEL_HINTS = 0x16,
957
958    /// prebind checksum
959    PREBIND_CKSUM = 0x17,
960
961    /// load a dynamically linked shared library that is allowed to be missing
962    /// (all symbols are weak imported).
963    LOAD_WEAK_DYLIB = (0x18 | LC_REQ_DYLD),
964
965    /// 64-bit segment of this file to be mapped
966    SEGMENT_64 = 0x19,
967
968    /// 64-bit image routines
969    ROUTINES_64 = 0x1a,
970
971    /// the uuid
972    UUID = 0x1b,
973
974    /// runpath additions
975    RPATH = (0x1c | LC_REQ_DYLD),
976
977    /// local of code signature
978    CODE_SIGNATURE = 0x1d,
979
980    /// local of info to split segments
981    SEGMENT_SPLIT_INFO = 0x1e,
982
983    /// load and re-export dylib
984    REEXPORT_DYLIB = (0x1f | LC_REQ_DYLD),
985
986    /// delay load of dylib until first use
987    LAZY_LOAD_DYLIB = 0x20,
988
989    /// encrypted segment information
990    ENCRYPTION_INFO = 0x21,
991
992    /// compressed dyld information
993    DYLD_INFO = 0x22,
994
995    /// compressed dyld information only
996    DYLD_INFO_ONLY = (0x22 | LC_REQ_DYLD),
997
998    /// load upward dylib
999    LOAD_UPWARD_DYLIB = (0x23 | LC_REQ_DYLD),
1000
1001    /// build for MacOSX min OS version
1002    VERSION_MIN_MACOSX = 0x24,
1003
1004    /// build for iPhoneOS min OS version
1005    VERSION_MIN_IPHONEOS = 0x25,
1006
1007    /// compressed table of function start addresses
1008    FUNCTION_STARTS = 0x26,
1009
1010    /// string for dyld to treat like environment variable
1011    DYLD_ENVIRONMENT = 0x27,
1012
1013    /// replacement for LC_UNIXTHREAD
1014    MAIN = (0x28 | LC_REQ_DYLD),
1015
1016    /// table of non-instructions in __text
1017    DATA_IN_CODE = 0x29,
1018
1019    /// source version used to build binary
1020    SOURCE_VERSION = 0x2A,
1021
1022    /// Code signing DRs copied from linked dylibs
1023    DYLIB_CODE_SIGN_DRS = 0x2B,
1024
1025    /// 64-bit encrypted segment information
1026    ENCRYPTION_INFO_64 = 0x2C,
1027
1028    /// linker options in MH_OBJECT files
1029    LINKER_OPTION = 0x2D,
1030
1031    /// optimization hints in MH_OBJECT files
1032    LINKER_OPTIMIZATION_HINT = 0x2E,
1033
1034    /// build for AppleTV min OS version
1035    VERSION_MIN_TVOS = 0x2F,
1036
1037    /// build for Watch min OS version
1038    VERSION_MIN_WATCHOS = 0x30,
1039
1040    /// arbitrary data included within a Mach-O file
1041    NOTE = 0x31,
1042
1043    /// build for platform min OS version
1044    BUILD_VERSION = 0x32,
1045
1046    _,
1047};
1048
1049/// the mach magic number
1050pub const MH_MAGIC = 0xfeedface;
1051
1052/// NXSwapInt(MH_MAGIC)
1053pub const MH_CIGAM = 0xcefaedfe;
1054
1055/// the 64-bit mach magic number
1056pub const MH_MAGIC_64 = 0xfeedfacf;
1057
1058/// NXSwapInt(MH_MAGIC_64)
1059pub const MH_CIGAM_64 = 0xcffaedfe;
1060
1061/// relocatable object file
1062pub const MH_OBJECT = 0x1;
1063
1064/// demand paged executable file
1065pub const MH_EXECUTE = 0x2;
1066
1067/// fixed VM shared library file
1068pub const MH_FVMLIB = 0x3;
1069
1070/// core file
1071pub const MH_CORE = 0x4;
1072
1073/// preloaded executable file
1074pub const MH_PRELOAD = 0x5;
1075
1076/// dynamically bound shared library
1077pub const MH_DYLIB = 0x6;
1078
1079/// dynamic link editor
1080pub const MH_DYLINKER = 0x7;
1081
1082/// dynamically bound bundle file
1083pub const MH_BUNDLE = 0x8;
1084
1085/// shared library stub for static linking only, no section contents
1086pub const MH_DYLIB_STUB = 0x9;
1087
1088/// companion file with only debug sections
1089pub const MH_DSYM = 0xa;
1090
1091/// x86_64 kexts
1092pub const MH_KEXT_BUNDLE = 0xb;
1093
1094// Constants for the flags field of the mach_header
1095
1096/// the object file has no undefined references
1097pub const MH_NOUNDEFS = 0x1;
1098
1099/// the object file is the output of an incremental link against a base file and can't be link edited again
1100pub const MH_INCRLINK = 0x2;
1101
1102/// the object file is input for the dynamic linker and can't be staticly link edited again
1103pub const MH_DYLDLINK = 0x4;
1104
1105/// the object file's undefined references are bound by the dynamic linker when loaded.
1106pub const MH_BINDATLOAD = 0x8;
1107
1108/// the file has its dynamic undefined references prebound.
1109pub const MH_PREBOUND = 0x10;
1110
1111/// the file has its read-only and read-write segments split
1112pub const MH_SPLIT_SEGS = 0x20;
1113
1114/// the shared library init routine is to be run lazily via catching memory faults to its writeable segments (obsolete)
1115pub const MH_LAZY_INIT = 0x40;
1116
1117/// the image is using two-level name space bindings
1118pub const MH_TWOLEVEL = 0x80;
1119
1120/// the executable is forcing all images to use flat name space bindings
1121pub const MH_FORCE_FLAT = 0x100;
1122
1123/// this umbrella guarantees no multiple defintions of symbols in its sub-images so the two-level namespace hints can always be used.
1124pub const MH_NOMULTIDEFS = 0x200;
1125
1126/// do not have dyld notify the prebinding agent about this executable
1127pub const MH_NOFIXPREBINDING = 0x400;
1128
1129/// the binary is not prebound but can have its prebinding redone. only used when MH_PREBOUND is not set.
1130pub const MH_PREBINDABLE = 0x800;
1131
1132/// indicates that this binary binds to all two-level namespace modules of its dependent libraries. only used when MH_PREBINDABLE and MH_TWOLEVEL are both set.
1133pub const MH_ALLMODSBOUND = 0x1000;
1134
1135/// safe to divide up the sections into sub-sections via symbols for dead code stripping
1136pub const MH_SUBSECTIONS_VIA_SYMBOLS = 0x2000;
1137
1138/// the binary has been canonicalized via the unprebind operation
1139pub const MH_CANONICAL = 0x4000;
1140
1141/// the final linked image contains external weak symbols
1142pub const MH_WEAK_DEFINES = 0x8000;
1143
1144/// the final linked image uses weak symbols
1145pub const MH_BINDS_TO_WEAK = 0x10000;
1146
1147/// When this bit is set, all stacks in the task will be given stack execution privilege.  Only used in MH_EXECUTE filetypes.
1148pub const MH_ALLOW_STACK_EXECUTION = 0x20000;
1149
1150/// When this bit is set, the binary declares it is safe for use in processes with uid zero
1151pub const MH_ROOT_SAFE = 0x40000;
1152
1153/// When this bit is set, the binary declares it is safe for use in processes when issetugid() is true
1154pub const MH_SETUID_SAFE = 0x80000;
1155
1156/// When this bit is set on a dylib, the static linker does not need to examine dependent dylibs to see if any are re-exported
1157pub const MH_NO_REEXPORTED_DYLIBS = 0x100000;
1158
1159/// When this bit is set, the OS will load the main executable at a random address.  Only used in MH_EXECUTE filetypes.
1160pub const MH_PIE = 0x200000;
1161
1162/// Only for use on dylibs.  When linking against a dylib that has this bit set, the static linker will automatically not create a LC_LOAD_DYLIB load command to the dylib if no symbols are being referenced from the dylib.
1163pub const MH_DEAD_STRIPPABLE_DYLIB = 0x400000;
1164
1165/// Contains a section of type S_THREAD_LOCAL_VARIABLES
1166pub const MH_HAS_TLV_DESCRIPTORS = 0x800000;
1167
1168/// When this bit is set, the OS will run the main executable with a non-executable heap even on platforms (e.g. i386) that don't require it. Only used in MH_EXECUTE filetypes.
1169pub const MH_NO_HEAP_EXECUTION = 0x1000000;
1170
1171/// The code was linked for use in an application extension.
1172pub const MH_APP_EXTENSION_SAFE = 0x02000000;
1173
1174/// The external symbols listed in the nlist symbol table do not include all the symbols listed in the dyld info.
1175pub const MH_NLIST_OUTOFSYNC_WITH_DYLDINFO = 0x04000000;
1176
1177// Constants for the flags field of the fat_header
1178
1179/// the fat magic number
1180pub const FAT_MAGIC = 0xcafebabe;
1181
1182/// NXSwapLong(FAT_MAGIC)
1183pub const FAT_CIGAM = 0xbebafeca;
1184
1185/// the 64-bit fat magic number
1186pub const FAT_MAGIC_64 = 0xcafebabf;
1187
1188/// NXSwapLong(FAT_MAGIC_64)
1189pub const FAT_CIGAM_64 = 0xbfbafeca;
1190
1191/// The flags field of a section structure is separated into two parts a section
1192/// type and section attributes.  The section types are mutually exclusive (it
1193/// can only have one type) but the section attributes are not (it may have more
1194/// than one attribute).
1195/// 256 section types
1196pub const SECTION_TYPE = 0x000000ff;
1197
1198///  24 section attributes
1199pub const SECTION_ATTRIBUTES = 0xffffff00;
1200
1201/// regular section
1202pub const S_REGULAR = 0x0;
1203
1204/// zero fill on demand section
1205pub const S_ZEROFILL = 0x1;
1206
1207/// section with only literal C string
1208pub const S_CSTRING_LITERALS = 0x2;
1209
1210/// section with only 4 byte literals
1211pub const S_4BYTE_LITERALS = 0x3;
1212
1213/// section with only 8 byte literals
1214pub const S_8BYTE_LITERALS = 0x4;
1215
1216/// section with only pointers to
1217pub const S_LITERAL_POINTERS = 0x5;
1218
1219/// if any of these bits set, a symbolic debugging entry
1220pub const N_STAB = 0xe0;
1221
1222/// private external symbol bit
1223pub const N_PEXT = 0x10;
1224
1225/// mask for the type bits
1226pub const N_TYPE = 0x0e;
1227
1228/// external symbol bit, set for external symbols
1229pub const N_EXT = 0x01;
1230
1231/// symbol is undefined
1232pub const N_UNDF = 0x0;
1233
1234/// symbol is absolute
1235pub const N_ABS = 0x2;
1236
1237/// symbol is defined in the section number given in n_sect
1238pub const N_SECT = 0xe;
1239
1240/// symbol is undefined  and the image is using a prebound
1241/// value  for the symbol
1242pub const N_PBUD = 0xc;
1243
1244/// symbol is defined to be the same as another symbol; the n_value
1245/// field is an index into the string table specifying the name of the
1246/// other symbol
1247pub const N_INDR = 0xa;
1248
1249/// global symbol: name,,NO_SECT,type,0
1250pub const N_GSYM = 0x20;
1251
1252/// procedure name (f77 kludge): name,,NO_SECT,0,0
1253pub const N_FNAME = 0x22;
1254
1255/// procedure: name,,n_sect,linenumber,address
1256pub const N_FUN = 0x24;
1257
1258/// static symbol: name,,n_sect,type,address
1259pub const N_STSYM = 0x26;
1260
1261/// .lcomm symbol: name,,n_sect,type,address
1262pub const N_LCSYM = 0x28;
1263
1264/// begin nsect sym: 0,,n_sect,0,address
1265pub const N_BNSYM = 0x2e;
1266
1267/// AST file path: name,,NO_SECT,0,0
1268pub const N_AST = 0x32;
1269
1270/// emitted with gcc2_compiled and in gcc source
1271pub const N_OPT = 0x3c;
1272
1273/// register sym: name,,NO_SECT,type,register
1274pub const N_RSYM = 0x40;
1275
1276/// src line: 0,,n_sect,linenumber,address
1277pub const N_SLINE = 0x44;
1278
1279/// end nsect sym: 0,,n_sect,0,address
1280pub const N_ENSYM = 0x4e;
1281
1282/// structure elt: name,,NO_SECT,type,struct_offset
1283pub const N_SSYM = 0x60;
1284
1285/// source file name: name,,n_sect,0,address
1286pub const N_SO = 0x64;
1287
1288/// object file name: name,,0,0,st_mtime
1289pub const N_OSO = 0x66;
1290
1291/// local sym: name,,NO_SECT,type,offset
1292pub const N_LSYM = 0x80;
1293
1294/// include file beginning: name,,NO_SECT,0,sum
1295pub const N_BINCL = 0x82;
1296
1297/// #included file name: name,,n_sect,0,address
1298pub const N_SOL = 0x84;
1299
1300/// compiler parameters: name,,NO_SECT,0,0
1301pub const N_PARAMS = 0x86;
1302
1303/// compiler version: name,,NO_SECT,0,0
1304pub const N_VERSION = 0x88;
1305
1306/// compiler -O level: name,,NO_SECT,0,0
1307pub const N_OLEVEL = 0x8A;
1308
1309/// parameter: name,,NO_SECT,type,offset
1310pub const N_PSYM = 0xa0;
1311
1312/// include file end: name,,NO_SECT,0,0
1313pub const N_EINCL = 0xa2;
1314
1315/// alternate entry: name,,n_sect,linenumber,address
1316pub const N_ENTRY = 0xa4;
1317
1318/// left bracket: 0,,NO_SECT,nesting level,address
1319pub const N_LBRAC = 0xc0;
1320
1321/// deleted include file: name,,NO_SECT,0,sum
1322pub const N_EXCL = 0xc2;
1323
1324/// right bracket: 0,,NO_SECT,nesting level,address
1325pub const N_RBRAC = 0xe0;
1326
1327/// begin common: name,,NO_SECT,0,0
1328pub const N_BCOMM = 0xe2;
1329
1330/// end common: name,,n_sect,0,0
1331pub const N_ECOMM = 0xe4;
1332
1333/// end common (local name): 0,,n_sect,0,address
1334pub const N_ECOML = 0xe8;
1335
1336/// second stab entry with length information
1337pub const N_LENG = 0xfe;
1338
1339// For the two types of symbol pointers sections and the symbol stubs section
1340// they have indirect symbol table entries.  For each of the entries in the
1341// section the indirect symbol table entries, in corresponding order in the
1342// indirect symbol table, start at the index stored in the reserved1 field
1343// of the section structure.  Since the indirect symbol table entries
1344// correspond to the entries in the section the number of indirect symbol table
1345// entries is inferred from the size of the section divided by the size of the
1346// entries in the section.  For symbol pointers sections the size of the entries
1347// in the section is 4 bytes and for symbol stubs sections the byte size of the
1348// stubs is stored in the reserved2 field of the section structure.
1349
1350/// section with only non-lazy symbol pointers
1351pub const S_NON_LAZY_SYMBOL_POINTERS = 0x6;
1352
1353/// section with only lazy symbol pointers
1354pub const S_LAZY_SYMBOL_POINTERS = 0x7;
1355
1356/// section with only symbol stubs, byte size of stub in the reserved2 field
1357pub const S_SYMBOL_STUBS = 0x8;
1358
1359/// section with only function pointers for initialization
1360pub const S_MOD_INIT_FUNC_POINTERS = 0x9;
1361
1362/// section with only function pointers for termination
1363pub const S_MOD_TERM_FUNC_POINTERS = 0xa;
1364
1365/// section contains symbols that are to be coalesced
1366pub const S_COALESCED = 0xb;
1367
1368/// zero fill on demand section (that can be larger than 4 gigabytes)
1369pub const S_GB_ZEROFILL = 0xc;
1370
1371/// section with only pairs of function pointers for interposing
1372pub const S_INTERPOSING = 0xd;
1373
1374/// section with only 16 byte literals
1375pub const S_16BYTE_LITERALS = 0xe;
1376
1377/// section contains DTrace Object Format
1378pub const S_DTRACE_DOF = 0xf;
1379
1380/// section with only lazy symbol pointers to lazy loaded dylibs
1381pub const S_LAZY_DYLIB_SYMBOL_POINTERS = 0x10;
1382
1383// If a segment contains any sections marked with S_ATTR_DEBUG then all
1384// sections in that segment must have this attribute.  No section other than
1385// a section marked with this attribute may reference the contents of this
1386// section.  A section with this attribute may contain no symbols and must have
1387// a section type S_REGULAR.  The static linker will not copy section contents
1388// from sections with this attribute into its output file.  These sections
1389// generally contain DWARF debugging info.
1390
1391/// a debug section
1392pub const S_ATTR_DEBUG = 0x02000000;
1393
1394/// section contains only true machine instructions
1395pub const S_ATTR_PURE_INSTRUCTIONS = 0x80000000;
1396
1397/// section contains coalesced symbols that are not to be in a ranlib
1398/// table of contents
1399pub const S_ATTR_NO_TOC = 0x40000000;
1400
1401/// ok to strip static symbols in this section in files with the
1402/// MH_DYLDLINK flag
1403pub const S_ATTR_STRIP_STATIC_SYMS = 0x20000000;
1404
1405/// no dead stripping
1406pub const S_ATTR_NO_DEAD_STRIP = 0x10000000;
1407
1408/// blocks are live if they reference live blocks
1409pub const S_ATTR_LIVE_SUPPORT = 0x8000000;
1410
1411/// used with i386 code stubs written on by dyld
1412pub const S_ATTR_SELF_MODIFYING_CODE = 0x4000000;
1413
1414/// section contains some machine instructions
1415pub const S_ATTR_SOME_INSTRUCTIONS = 0x400;
1416
1417/// section has external relocation entries
1418pub const S_ATTR_EXT_RELOC = 0x200;
1419
1420/// section has local relocation entries
1421pub const S_ATTR_LOC_RELOC = 0x100;
1422
1423/// template of initial values for TLVs
1424pub const S_THREAD_LOCAL_REGULAR = 0x11;
1425
1426/// template of initial values for TLVs
1427pub const S_THREAD_LOCAL_ZEROFILL = 0x12;
1428
1429/// TLV descriptors
1430pub const S_THREAD_LOCAL_VARIABLES = 0x13;
1431
1432/// pointers to TLV descriptors
1433pub const S_THREAD_LOCAL_VARIABLE_POINTERS = 0x14;
1434
1435/// functions to call to initialize TLV values
1436pub const S_THREAD_LOCAL_INIT_FUNCTION_POINTERS = 0x15;
1437
1438/// 32-bit offsets to initializers
1439pub const S_INIT_FUNC_OFFSETS = 0x16;
1440
1441pub const cpu_type_t = integer_t;
1442pub const cpu_subtype_t = integer_t;
1443pub const integer_t = c_int;
1444pub const vm_prot_t = c_int;
1445
1446/// CPU type targeting 64-bit Intel-based Macs
1447pub const CPU_TYPE_X86_64: cpu_type_t = 0x01000007;
1448
1449/// CPU type targeting 64-bit ARM-based Macs
1450pub const CPU_TYPE_ARM64: cpu_type_t = 0x0100000C;
1451
1452/// All Intel-based Macs
1453pub const CPU_SUBTYPE_X86_64_ALL: cpu_subtype_t = 0x3;
1454
1455/// All ARM-based Macs
1456pub const CPU_SUBTYPE_ARM_ALL: cpu_subtype_t = 0x0;
1457
1458// Protection values defined as bits within the vm_prot_t type
1459/// No VM protection
1460pub const VM_PROT_NONE: vm_prot_t = 0x0;
1461
1462/// VM read permission
1463pub const VM_PROT_READ: vm_prot_t = 0x1;
1464
1465/// VM write permission
1466pub const VM_PROT_WRITE: vm_prot_t = 0x2;
1467
1468/// VM execute permission
1469pub const VM_PROT_EXECUTE: vm_prot_t = 0x4;
1470
1471// The following are used to encode rebasing information
1472pub const REBASE_TYPE_POINTER: u8 = 1;
1473pub const REBASE_TYPE_TEXT_ABSOLUTE32: u8 = 2;
1474pub const REBASE_TYPE_TEXT_PCREL32: u8 = 3;
1475
1476pub const REBASE_OPCODE_MASK: u8 = 0xF0;
1477pub const REBASE_IMMEDIATE_MASK: u8 = 0x0F;
1478pub const REBASE_OPCODE_DONE: u8 = 0x00;
1479pub const REBASE_OPCODE_SET_TYPE_IMM: u8 = 0x10;
1480pub const REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: u8 = 0x20;
1481pub const REBASE_OPCODE_ADD_ADDR_ULEB: u8 = 0x30;
1482pub const REBASE_OPCODE_ADD_ADDR_IMM_SCALED: u8 = 0x40;
1483pub const REBASE_OPCODE_DO_REBASE_IMM_TIMES: u8 = 0x50;
1484pub const REBASE_OPCODE_DO_REBASE_ULEB_TIMES: u8 = 0x60;
1485pub const REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: u8 = 0x70;
1486pub const REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: u8 = 0x80;
1487
1488// The following are used to encode binding information
1489pub const BIND_TYPE_POINTER: u8 = 1;
1490pub const BIND_TYPE_TEXT_ABSOLUTE32: u8 = 2;
1491pub const BIND_TYPE_TEXT_PCREL32: u8 = 3;
1492
1493pub const BIND_SPECIAL_DYLIB_SELF: i8 = 0;
1494pub const BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE: i8 = -1;
1495pub const BIND_SPECIAL_DYLIB_FLAT_LOOKUP: i8 = -2;
1496
1497pub const BIND_SYMBOL_FLAGS_WEAK_IMPORT: u8 = 0x1;
1498pub const BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION: u8 = 0x8;
1499
1500pub const BIND_OPCODE_MASK: u8 = 0xf0;
1501pub const BIND_IMMEDIATE_MASK: u8 = 0x0f;
1502pub const BIND_OPCODE_DONE: u8 = 0x00;
1503pub const BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: u8 = 0x10;
1504pub const BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: u8 = 0x20;
1505pub const BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: u8 = 0x30;
1506pub const BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: u8 = 0x40;
1507pub const BIND_OPCODE_SET_TYPE_IMM: u8 = 0x50;
1508pub const BIND_OPCODE_SET_ADDEND_SLEB: u8 = 0x60;
1509pub const BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: u8 = 0x70;
1510pub const BIND_OPCODE_ADD_ADDR_ULEB: u8 = 0x80;
1511pub const BIND_OPCODE_DO_BIND: u8 = 0x90;
1512pub const BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: u8 = 0xa0;
1513pub const BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: u8 = 0xb0;
1514pub const BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: u8 = 0xc0;
1515
1516pub const reloc_type_x86_64 = enum(u4) {
1517    /// for absolute addresses
1518    X86_64_RELOC_UNSIGNED = 0,
1519
1520    /// for signed 32-bit displacement
1521    X86_64_RELOC_SIGNED,
1522
1523    /// a CALL/JMP instruction with 32-bit displacement
1524    X86_64_RELOC_BRANCH,
1525
1526    /// a MOVQ load of a GOT entry
1527    X86_64_RELOC_GOT_LOAD,
1528
1529    /// other GOT references
1530    X86_64_RELOC_GOT,
1531
1532    /// must be followed by a X86_64_RELOC_UNSIGNED
1533    X86_64_RELOC_SUBTRACTOR,
1534
1535    /// for signed 32-bit displacement with a -1 addend
1536    X86_64_RELOC_SIGNED_1,
1537
1538    /// for signed 32-bit displacement with a -2 addend
1539    X86_64_RELOC_SIGNED_2,
1540
1541    /// for signed 32-bit displacement with a -4 addend
1542    X86_64_RELOC_SIGNED_4,
1543
1544    /// for thread local variables
1545    X86_64_RELOC_TLV,
1546};
1547
1548pub const reloc_type_arm64 = enum(u4) {
1549    /// For pointers.
1550    ARM64_RELOC_UNSIGNED = 0,
1551
1552    /// Must be followed by a ARM64_RELOC_UNSIGNED.
1553    ARM64_RELOC_SUBTRACTOR,
1554
1555    /// A B/BL instruction with 26-bit displacement.
1556    ARM64_RELOC_BRANCH26,
1557
1558    /// Pc-rel distance to page of target.
1559    ARM64_RELOC_PAGE21,
1560
1561    /// Offset within page, scaled by r_length.
1562    ARM64_RELOC_PAGEOFF12,
1563
1564    /// Pc-rel distance to page of GOT slot.
1565    ARM64_RELOC_GOT_LOAD_PAGE21,
1566
1567    /// Offset within page of GOT slot, scaled by r_length.
1568    ARM64_RELOC_GOT_LOAD_PAGEOFF12,
1569
1570    /// For pointers to GOT slots.
1571    ARM64_RELOC_POINTER_TO_GOT,
1572
1573    /// Pc-rel distance to page of TLVP slot.
1574    ARM64_RELOC_TLVP_LOAD_PAGE21,
1575
1576    /// Offset within page of TLVP slot, scaled by r_length.
1577    ARM64_RELOC_TLVP_LOAD_PAGEOFF12,
1578
1579    /// Must be followed by PAGE21 or PAGEOFF12.
1580    ARM64_RELOC_ADDEND,
1581};
1582
1583/// This symbol is a reference to an external non-lazy (data) symbol.
1584pub const REFERENCE_FLAG_UNDEFINED_NON_LAZY: u16 = 0x0;
1585
1586/// This symbol is a reference to an external lazy symbol—that is, to a function call.
1587pub const REFERENCE_FLAG_UNDEFINED_LAZY: u16 = 0x1;
1588
1589/// This symbol is defined in this module.
1590pub const REFERENCE_FLAG_DEFINED: u16 = 0x2;
1591
1592/// This symbol is defined in this module and is visible only to modules within this shared library.
1593pub const REFERENCE_FLAG_PRIVATE_DEFINED: u16 = 3;
1594
1595/// This symbol is defined in another module in this file, is a non-lazy (data) symbol, and is visible
1596/// only to modules within this shared library.
1597pub const REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY: u16 = 4;
1598
1599/// This symbol is defined in another module in this file, is a lazy (function) symbol, and is visible
1600/// only to modules within this shared library.
1601pub const REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY: u16 = 5;
1602
1603/// Must be set for any defined symbol that is referenced by dynamic-loader APIs (such as dlsym and
1604/// NSLookupSymbolInImage) and not ordinary undefined symbol references. The strip tool uses this bit
1605/// to avoid removing symbols that must exist: If the symbol has this bit set, strip does not strip it.
1606pub const REFERENCED_DYNAMICALLY: u16 = 0x10;
1607
1608/// Used by the dynamic linker at runtime. Do not set this bit.
1609pub const N_DESC_DISCARDED: u16 = 0x20;
1610
1611/// Indicates that this symbol is a weak reference. If the dynamic linker cannot find a definition
1612/// for this symbol, it sets the address of this symbol to 0. The static linker sets this symbol given
1613/// the appropriate weak-linking flags.
1614pub const N_WEAK_REF: u16 = 0x40;
1615
1616/// Indicates that this symbol is a weak definition. If the static linker or the dynamic linker finds
1617/// another (non-weak) definition for this symbol, the weak definition is ignored. Only symbols in a
1618/// coalesced section (page 23) can be marked as a weak definition.
1619pub const N_WEAK_DEF: u16 = 0x80;
1620
1621/// The N_SYMBOL_RESOLVER bit of the n_desc field indicates that the
1622/// that the function is actually a resolver function and should
1623/// be called to get the address of the real function to use.
1624/// This bit is only available in .o files (MH_OBJECT filetype)
1625pub const N_SYMBOL_RESOLVER: u16 = 0x100;
1626
1627// The following are used on the flags byte of a terminal node in the export information.
1628pub const EXPORT_SYMBOL_FLAGS_KIND_MASK: u8 = 0x03;
1629pub const EXPORT_SYMBOL_FLAGS_KIND_REGULAR: u8 = 0x00;
1630pub const EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL: u8 = 0x01;
1631pub const EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE: u8 = 0x02;
1632pub const EXPORT_SYMBOL_FLAGS_KIND_WEAK_DEFINITION: u8 = 0x04;
1633pub const EXPORT_SYMBOL_FLAGS_REEXPORT: u8 = 0x08;
1634pub const EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER: u8 = 0x10;
1635
1636// An indirect symbol table entry is simply a 32bit index into the symbol table
1637// to the symbol that the pointer or stub is refering to.  Unless it is for a
1638// non-lazy symbol pointer section for a defined symbol which strip(1) as
1639// removed.  In which case it has the value INDIRECT_SYMBOL_LOCAL.  If the
1640// symbol was also absolute INDIRECT_SYMBOL_ABS is or'ed with that.
1641pub const INDIRECT_SYMBOL_LOCAL: u32 = 0x80000000;
1642pub const INDIRECT_SYMBOL_ABS: u32 = 0x40000000;
1643
1644// Codesign consts and structs taken from:
1645// https://opensource.apple.com/source/xnu/xnu-6153.81.5/osfmk/kern/cs_blobs.h.auto.html
1646
1647/// Single Requirement blob
1648pub const CSMAGIC_REQUIREMENT: u32 = 0xfade0c00;
1649/// Requirements vector (internal requirements)
1650pub const CSMAGIC_REQUIREMENTS: u32 = 0xfade0c01;
1651/// CodeDirectory blob
1652pub const CSMAGIC_CODEDIRECTORY: u32 = 0xfade0c02;
1653/// embedded form of signature data
1654pub const CSMAGIC_EMBEDDED_SIGNATURE: u32 = 0xfade0cc0;
1655/// XXX
1656pub const CSMAGIC_EMBEDDED_SIGNATURE_OLD: u32 = 0xfade0b02;
1657/// Embedded entitlements
1658pub const CSMAGIC_EMBEDDED_ENTITLEMENTS: u32 = 0xfade7171;
1659/// Multi-arch collection of embedded signatures
1660pub const CSMAGIC_DETACHED_SIGNATURE: u32 = 0xfade0cc1;
1661/// CMS Signature, among other things
1662pub const CSMAGIC_BLOBWRAPPER: u32 = 0xfade0b01;
1663
1664pub const CS_SUPPORTSSCATTER: u32 = 0x20100;
1665pub const CS_SUPPORTSTEAMID: u32 = 0x20200;
1666pub const CS_SUPPORTSCODELIMIT64: u32 = 0x20300;
1667pub const CS_SUPPORTSEXECSEG: u32 = 0x20400;
1668
1669/// Slot index for CodeDirectory
1670pub const CSSLOT_CODEDIRECTORY: u32 = 0;
1671pub const CSSLOT_INFOSLOT: u32 = 1;
1672pub const CSSLOT_REQUIREMENTS: u32 = 2;
1673pub const CSSLOT_RESOURCEDIR: u32 = 3;
1674pub const CSSLOT_APPLICATION: u32 = 4;
1675pub const CSSLOT_ENTITLEMENTS: u32 = 5;
1676
1677/// first alternate CodeDirectory, if any
1678pub const CSSLOT_ALTERNATE_CODEDIRECTORIES: u32 = 0x1000;
1679/// Max number of alternate CD slots
1680pub const CSSLOT_ALTERNATE_CODEDIRECTORY_MAX: u32 = 5;
1681/// One past the last
1682pub const CSSLOT_ALTERNATE_CODEDIRECTORY_LIMIT: u32 = CSSLOT_ALTERNATE_CODEDIRECTORIES + CSSLOT_ALTERNATE_CODEDIRECTORY_MAX;
1683
1684/// CMS Signature
1685pub const CSSLOT_SIGNATURESLOT: u32 = 0x10000;
1686pub const CSSLOT_IDENTIFICATIONSLOT: u32 = 0x10001;
1687pub const CSSLOT_TICKETSLOT: u32 = 0x10002;
1688
1689/// Compat with amfi
1690pub const CSTYPE_INDEX_REQUIREMENTS: u32 = 0x00000002;
1691/// Compat with amfi
1692pub const CSTYPE_INDEX_ENTITLEMENTS: u32 = 0x00000005;
1693
1694pub const CS_HASHTYPE_SHA1: u8 = 1;
1695pub const CS_HASHTYPE_SHA256: u8 = 2;
1696pub const CS_HASHTYPE_SHA256_TRUNCATED: u8 = 3;
1697pub const CS_HASHTYPE_SHA384: u8 = 4;
1698
1699pub const CS_SHA1_LEN: u32 = 20;
1700pub const CS_SHA256_LEN: u32 = 32;
1701pub const CS_SHA256_TRUNCATED_LEN: u32 = 20;
1702
1703/// Always - larger hashes are truncated
1704pub const CS_CDHASH_LEN: u32 = 20;
1705/// Max size of the hash we'll support
1706pub const CS_HASH_MAX_SIZE: u32 = 48;
1707
1708pub const CS_SIGNER_TYPE_UNKNOWN: u32 = 0;
1709pub const CS_SIGNER_TYPE_LEGACYVPN: u32 = 5;
1710pub const CS_SIGNER_TYPE_MAC_APP_STORE: u32 = 6;
1711
1712pub const CS_ADHOC: u32 = 0x2;
1713
1714pub const CS_EXECSEG_MAIN_BINARY: u32 = 0x1;
1715
1716/// This CodeDirectory is tailored specfically at version 0x20400.
1717pub const CodeDirectory = extern struct {
1718    /// Magic number (CSMAGIC_CODEDIRECTORY)
1719    magic: u32,
1720
1721    /// Total length of CodeDirectory blob
1722    length: u32,
1723
1724    /// Compatibility version
1725    version: u32,
1726
1727    /// Setup and mode flags
1728    flags: u32,
1729
1730    /// Offset of hash slot element at index zero
1731    hashOffset: u32,
1732
1733    /// Offset of identifier string
1734    identOffset: u32,
1735
1736    /// Number of special hash slots
1737    nSpecialSlots: u32,
1738
1739    /// Number of ordinary (code) hash slots
1740    nCodeSlots: u32,
1741
1742    /// Limit to main image signature range
1743    codeLimit: u32,
1744
1745    /// Size of each hash in bytes
1746    hashSize: u8,
1747
1748    /// Type of hash (cdHashType* constants)
1749    hashType: u8,
1750
1751    /// Platform identifier; zero if not platform binary
1752    platform: u8,
1753
1754    /// log2(page size in bytes); 0 => infinite
1755    pageSize: u8,
1756
1757    /// Unused (must be zero)
1758    spare2: u32,
1759
1760    ///
1761    scatterOffset: u32,
1762
1763    ///
1764    teamOffset: u32,
1765
1766    ///
1767    spare3: u32,
1768
1769    ///
1770    codeLimit64: u64,
1771
1772    /// Offset of executable segment
1773    execSegBase: u64,
1774
1775    /// Limit of executable segment
1776    execSegLimit: u64,
1777
1778    /// Executable segment flags
1779    execSegFlags: u64,
1780};
1781
1782/// Structure of an embedded-signature SuperBlob
1783pub const BlobIndex = extern struct {
1784    /// Type of entry
1785    @"type": u32,
1786
1787    /// Offset of entry
1788    offset: u32,
1789};
1790
1791/// This structure is followed by GenericBlobs in no particular
1792/// order as indicated by offsets in index
1793pub const SuperBlob = extern struct {
1794    /// Magic number
1795    magic: u32,
1796
1797    /// Total length of SuperBlob
1798    length: u32,
1799
1800    /// Number of index BlobIndex entries following this struct
1801    count: u32,
1802};
1803
1804pub const GenericBlob = extern struct {
1805    /// Magic number
1806    magic: u32,
1807
1808    /// Total length of blob
1809    length: u32,
1810};
1811
1812/// The LC_DATA_IN_CODE load commands uses a linkedit_data_command
1813/// to point to an array of data_in_code_entry entries. Each entry
1814/// describes a range of data in a code section.
1815pub const data_in_code_entry = extern struct {
1816    /// From mach_header to start of data range.
1817    offset: u32,
1818
1819    /// Number of bytes in data range.
1820    length: u16,
1821
1822    /// A DICE_KIND value.
1823    kind: u16,
1824};
1825
1826/// A Zig wrapper for all known MachO load commands.
1827/// Provides interface to read and write the load command data to a buffer.
1828pub const LoadCommand = union(enum) {
1829    segment: SegmentCommand,
1830    dyld_info_only: dyld_info_command,
1831    symtab: symtab_command,
1832    dysymtab: dysymtab_command,
1833    dylinker: GenericCommandWithData(dylinker_command),
1834    dylib: GenericCommandWithData(dylib_command),
1835    main: entry_point_command,
1836    version_min: version_min_command,
1837    source_version: source_version_command,
1838    build_version: GenericCommandWithData(build_version_command),
1839    uuid: uuid_command,
1840    linkedit_data: linkedit_data_command,
1841    rpath: GenericCommandWithData(rpath_command),
1842    unknown: GenericCommandWithData(load_command),
1843
1844    pub fn read(allocator: Allocator, reader: anytype) !LoadCommand {
1845        const header = try reader.readStruct(load_command);
1846        var buffer = try allocator.alloc(u8, header.cmdsize);
1847        defer allocator.free(buffer);
1848        mem.copy(u8, buffer, mem.asBytes(&header));
1849        try reader.readNoEof(buffer[@sizeOf(load_command)..]);
1850        var stream = io.fixedBufferStream(buffer);
1851
1852        return switch (header.cmd) {
1853            .SEGMENT_64 => LoadCommand{
1854                .segment = try SegmentCommand.read(allocator, stream.reader()),
1855            },
1856            .DYLD_INFO, .DYLD_INFO_ONLY => LoadCommand{
1857                .dyld_info_only = try stream.reader().readStruct(dyld_info_command),
1858            },
1859            .SYMTAB => LoadCommand{
1860                .symtab = try stream.reader().readStruct(symtab_command),
1861            },
1862            .DYSYMTAB => LoadCommand{
1863                .dysymtab = try stream.reader().readStruct(dysymtab_command),
1864            },
1865            .ID_DYLINKER, .LOAD_DYLINKER, .DYLD_ENVIRONMENT => LoadCommand{
1866                .dylinker = try GenericCommandWithData(dylinker_command).read(allocator, stream.reader()),
1867            },
1868            .ID_DYLIB, .LOAD_WEAK_DYLIB, .LOAD_DYLIB, .REEXPORT_DYLIB => LoadCommand{
1869                .dylib = try GenericCommandWithData(dylib_command).read(allocator, stream.reader()),
1870            },
1871            .MAIN => LoadCommand{
1872                .main = try stream.reader().readStruct(entry_point_command),
1873            },
1874            .VERSION_MIN_MACOSX, .VERSION_MIN_IPHONEOS, .VERSION_MIN_WATCHOS, .VERSION_MIN_TVOS => LoadCommand{
1875                .version_min = try stream.reader().readStruct(version_min_command),
1876            },
1877            .SOURCE_VERSION => LoadCommand{
1878                .source_version = try stream.reader().readStruct(source_version_command),
1879            },
1880            .BUILD_VERSION => LoadCommand{
1881                .build_version = try GenericCommandWithData(build_version_command).read(allocator, stream.reader()),
1882            },
1883            .UUID => LoadCommand{
1884                .uuid = try stream.reader().readStruct(uuid_command),
1885            },
1886            .FUNCTION_STARTS, .DATA_IN_CODE, .CODE_SIGNATURE => LoadCommand{
1887                .linkedit_data = try stream.reader().readStruct(linkedit_data_command),
1888            },
1889            .RPATH => LoadCommand{
1890                .rpath = try GenericCommandWithData(rpath_command).read(allocator, stream.reader()),
1891            },
1892            else => LoadCommand{
1893                .unknown = try GenericCommandWithData(load_command).read(allocator, stream.reader()),
1894            },
1895        };
1896    }
1897
1898    pub fn write(self: LoadCommand, writer: anytype) !void {
1899        return switch (self) {
1900            .dyld_info_only => |x| writeStruct(x, writer),
1901            .symtab => |x| writeStruct(x, writer),
1902            .dysymtab => |x| writeStruct(x, writer),
1903            .main => |x| writeStruct(x, writer),
1904            .version_min => |x| writeStruct(x, writer),
1905            .source_version => |x| writeStruct(x, writer),
1906            .uuid => |x| writeStruct(x, writer),
1907            .linkedit_data => |x| writeStruct(x, writer),
1908            .segment => |x| x.write(writer),
1909            .dylinker => |x| x.write(writer),
1910            .dylib => |x| x.write(writer),
1911            .rpath => |x| x.write(writer),
1912            .build_version => |x| x.write(writer),
1913            .unknown => |x| x.write(writer),
1914        };
1915    }
1916
1917    pub fn cmd(self: LoadCommand) LC {
1918        return switch (self) {
1919            .dyld_info_only => |x| x.cmd,
1920            .symtab => |x| x.cmd,
1921            .dysymtab => |x| x.cmd,
1922            .main => |x| x.cmd,
1923            .version_min => |x| x.cmd,
1924            .source_version => |x| x.cmd,
1925            .uuid => |x| x.cmd,
1926            .linkedit_data => |x| x.cmd,
1927            .segment => |x| x.inner.cmd,
1928            .dylinker => |x| x.inner.cmd,
1929            .dylib => |x| x.inner.cmd,
1930            .rpath => |x| x.inner.cmd,
1931            .build_version => |x| x.inner.cmd,
1932            .unknown => |x| x.inner.cmd,
1933        };
1934    }
1935
1936    pub fn cmdsize(self: LoadCommand) u32 {
1937        return switch (self) {
1938            .dyld_info_only => |x| x.cmdsize,
1939            .symtab => |x| x.cmdsize,
1940            .dysymtab => |x| x.cmdsize,
1941            .main => |x| x.cmdsize,
1942            .version_min => |x| x.cmdsize,
1943            .source_version => |x| x.cmdsize,
1944            .linkedit_data => |x| x.cmdsize,
1945            .uuid => |x| x.cmdsize,
1946            .segment => |x| x.inner.cmdsize,
1947            .dylinker => |x| x.inner.cmdsize,
1948            .dylib => |x| x.inner.cmdsize,
1949            .rpath => |x| x.inner.cmdsize,
1950            .build_version => |x| x.inner.cmdsize,
1951            .unknown => |x| x.inner.cmdsize,
1952        };
1953    }
1954
1955    pub fn deinit(self: *LoadCommand, allocator: Allocator) void {
1956        return switch (self.*) {
1957            .segment => |*x| x.deinit(allocator),
1958            .dylinker => |*x| x.deinit(allocator),
1959            .dylib => |*x| x.deinit(allocator),
1960            .rpath => |*x| x.deinit(allocator),
1961            .build_version => |*x| x.deinit(allocator),
1962            .unknown => |*x| x.deinit(allocator),
1963            else => {},
1964        };
1965    }
1966
1967    fn writeStruct(command: anytype, writer: anytype) !void {
1968        return writer.writeAll(mem.asBytes(&command));
1969    }
1970
1971    pub fn eql(self: LoadCommand, other: LoadCommand) bool {
1972        if (@as(meta.Tag(LoadCommand), self) != @as(meta.Tag(LoadCommand), other)) return false;
1973        return switch (self) {
1974            .dyld_info_only => |x| meta.eql(x, other.dyld_info_only),
1975            .symtab => |x| meta.eql(x, other.symtab),
1976            .dysymtab => |x| meta.eql(x, other.dysymtab),
1977            .main => |x| meta.eql(x, other.main),
1978            .version_min => |x| meta.eql(x, other.version_min),
1979            .source_version => |x| meta.eql(x, other.source_version),
1980            .build_version => |x| x.eql(other.build_version),
1981            .uuid => |x| meta.eql(x, other.uuid),
1982            .linkedit_data => |x| meta.eql(x, other.linkedit_data),
1983            .segment => |x| x.eql(other.segment),
1984            .dylinker => |x| x.eql(other.dylinker),
1985            .dylib => |x| x.eql(other.dylib),
1986            .rpath => |x| x.eql(other.rpath),
1987            .unknown => |x| x.eql(other.unknown),
1988        };
1989    }
1990};
1991
1992/// A Zig wrapper for segment_command_64.
1993/// Encloses the extern struct together with a list of sections for this segment.
1994pub const SegmentCommand = struct {
1995    inner: segment_command_64,
1996    sections: std.ArrayListUnmanaged(section_64) = .{},
1997
1998    pub fn read(allocator: Allocator, reader: anytype) !SegmentCommand {
1999        const inner = try reader.readStruct(segment_command_64);
2000        var segment = SegmentCommand{
2001            .inner = inner,
2002        };
2003        try segment.sections.ensureTotalCapacityPrecise(allocator, inner.nsects);
2004
2005        var i: usize = 0;
2006        while (i < inner.nsects) : (i += 1) {
2007            const sect = try reader.readStruct(section_64);
2008            segment.sections.appendAssumeCapacity(sect);
2009        }
2010
2011        return segment;
2012    }
2013
2014    pub fn write(self: SegmentCommand, writer: anytype) !void {
2015        try writer.writeAll(mem.asBytes(&self.inner));
2016        for (self.sections.items) |sect| {
2017            try writer.writeAll(mem.asBytes(&sect));
2018        }
2019    }
2020
2021    pub fn deinit(self: *SegmentCommand, allocator: Allocator) void {
2022        self.sections.deinit(allocator);
2023    }
2024
2025    pub fn eql(self: SegmentCommand, other: SegmentCommand) bool {
2026        if (!meta.eql(self.inner, other.inner)) return false;
2027        const lhs = self.sections.items;
2028        const rhs = other.sections.items;
2029        var i: usize = 0;
2030        while (i < self.inner.nsects) : (i += 1) {
2031            if (!meta.eql(lhs[i], rhs[i])) return false;
2032        }
2033        return true;
2034    }
2035};
2036
2037pub fn emptyGenericCommandWithData(cmd: anytype) GenericCommandWithData(@TypeOf(cmd)) {
2038    return .{ .inner = cmd };
2039}
2040
2041/// A Zig wrapper for a generic load command with variable-length data.
2042pub fn GenericCommandWithData(comptime Cmd: type) type {
2043    return struct {
2044        inner: Cmd,
2045        /// This field remains undefined until `read` is called.
2046        data: []u8 = undefined,
2047
2048        const Self = @This();
2049
2050        pub fn read(allocator: Allocator, reader: anytype) !Self {
2051            const inner = try reader.readStruct(Cmd);
2052            var data = try allocator.alloc(u8, inner.cmdsize - @sizeOf(Cmd));
2053            errdefer allocator.free(data);
2054            try reader.readNoEof(data);
2055            return Self{
2056                .inner = inner,
2057                .data = data,
2058            };
2059        }
2060
2061        pub fn write(self: Self, writer: anytype) !void {
2062            try writer.writeAll(mem.asBytes(&self.inner));
2063            try writer.writeAll(self.data);
2064        }
2065
2066        pub fn deinit(self: *Self, allocator: Allocator) void {
2067            allocator.free(self.data);
2068        }
2069
2070        pub fn eql(self: Self, other: Self) bool {
2071            if (!meta.eql(self.inner, other.inner)) return false;
2072            return mem.eql(u8, self.data, other.data);
2073        }
2074    };
2075}
2076
2077pub fn createLoadDylibCommand(
2078    allocator: Allocator,
2079    name: []const u8,
2080    timestamp: u32,
2081    current_version: u32,
2082    compatibility_version: u32,
2083) !GenericCommandWithData(dylib_command) {
2084    const cmdsize = @intCast(u32, mem.alignForwardGeneric(
2085        u64,
2086        @sizeOf(dylib_command) + name.len + 1, // +1 for nul
2087        @sizeOf(u64),
2088    ));
2089
2090    var dylib_cmd = emptyGenericCommandWithData(dylib_command{
2091        .cmd = .LOAD_DYLIB,
2092        .cmdsize = cmdsize,
2093        .dylib = .{
2094            .name = @sizeOf(dylib_command),
2095            .timestamp = timestamp,
2096            .current_version = current_version,
2097            .compatibility_version = compatibility_version,
2098        },
2099    });
2100    dylib_cmd.data = try allocator.alloc(u8, cmdsize - dylib_cmd.inner.dylib.name);
2101
2102    mem.set(u8, dylib_cmd.data, 0);
2103    mem.copy(u8, dylib_cmd.data, name);
2104
2105    return dylib_cmd;
2106}
2107
2108fn testRead(allocator: Allocator, buffer: []const u8, expected: anytype) !void {
2109    var stream = io.fixedBufferStream(buffer);
2110    var given = try LoadCommand.read(allocator, stream.reader());
2111    defer given.deinit(allocator);
2112    try testing.expect(expected.eql(given));
2113}
2114
2115fn testWrite(buffer: []u8, cmd: LoadCommand, expected: []const u8) !void {
2116    var stream = io.fixedBufferStream(buffer);
2117    try cmd.write(stream.writer());
2118    try testing.expect(mem.eql(u8, expected, buffer[0..expected.len]));
2119}
2120
2121fn makeStaticString(bytes: []const u8) [16]u8 {
2122    var buf = [_]u8{0} ** 16;
2123    assert(bytes.len <= buf.len);
2124    mem.copy(u8, &buf, bytes);
2125    return buf;
2126}
2127
2128test "read-write segment command" {
2129    // TODO compiling for macOS from big-endian arch
2130    if (builtin.target.cpu.arch.endian() != .Little) return error.SkipZigTest;
2131
2132    var gpa = testing.allocator;
2133    const in_buffer = &[_]u8{
2134        0x19, 0x00, 0x00, 0x00, // cmd
2135        0x98, 0x00, 0x00, 0x00, // cmdsize
2136        0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // segname
2137        0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // vmaddr
2138        0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // vmsize
2139        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // fileoff
2140        0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // filesize
2141        0x07, 0x00, 0x00, 0x00, // maxprot
2142        0x05, 0x00, 0x00, 0x00, // initprot
2143        0x01, 0x00, 0x00, 0x00, // nsects
2144        0x00, 0x00, 0x00, 0x00, // flags
2145        0x5f, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sectname
2146        0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // segname
2147        0x00, 0x40, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // address
2148        0xc0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // size
2149        0x00, 0x40, 0x00, 0x00, // offset
2150        0x02, 0x00, 0x00, 0x00, // alignment
2151        0x00, 0x00, 0x00, 0x00, // reloff
2152        0x00, 0x00, 0x00, 0x00, // nreloc
2153        0x00, 0x04, 0x00, 0x80, // flags
2154        0x00, 0x00, 0x00, 0x00, // reserved1
2155        0x00, 0x00, 0x00, 0x00, // reserved2
2156        0x00, 0x00, 0x00, 0x00, // reserved3
2157    };
2158    var cmd = SegmentCommand{
2159        .inner = .{
2160            .cmdsize = 152,
2161            .segname = makeStaticString("__TEXT"),
2162            .vmaddr = 4294967296,
2163            .vmsize = 294912,
2164            .filesize = 294912,
2165            .maxprot = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE,
2166            .initprot = VM_PROT_EXECUTE | VM_PROT_READ,
2167            .nsects = 1,
2168        },
2169    };
2170    try cmd.sections.append(gpa, .{
2171        .sectname = makeStaticString("__text"),
2172        .segname = makeStaticString("__TEXT"),
2173        .addr = 4294983680,
2174        .size = 448,
2175        .offset = 16384,
2176        .@"align" = 2,
2177        .flags = S_REGULAR | S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SOME_INSTRUCTIONS,
2178    });
2179    defer cmd.deinit(gpa);
2180    try testRead(gpa, in_buffer, LoadCommand{ .segment = cmd });
2181
2182    var out_buffer: [in_buffer.len]u8 = undefined;
2183    try testWrite(&out_buffer, LoadCommand{ .segment = cmd }, in_buffer);
2184}
2185
2186test "read-write generic command with data" {
2187    // TODO compiling for macOS from big-endian arch
2188    if (builtin.target.cpu.arch.endian() != .Little) return error.SkipZigTest;
2189
2190    var gpa = testing.allocator;
2191    const in_buffer = &[_]u8{
2192        0x0c, 0x00, 0x00, 0x00, // cmd
2193        0x20, 0x00, 0x00, 0x00, // cmdsize
2194        0x18, 0x00, 0x00, 0x00, // name
2195        0x02, 0x00, 0x00, 0x00, // timestamp
2196        0x00, 0x00, 0x00, 0x00, // current_version
2197        0x00, 0x00, 0x00, 0x00, // compatibility_version
2198        0x2f, 0x75, 0x73, 0x72, 0x00, 0x00, 0x00, 0x00, // data
2199    };
2200    var cmd = GenericCommandWithData(dylib_command){
2201        .inner = .{
2202            .cmd = .LOAD_DYLIB,
2203            .cmdsize = 32,
2204            .dylib = .{
2205                .name = 24,
2206                .timestamp = 2,
2207                .current_version = 0,
2208                .compatibility_version = 0,
2209            },
2210        },
2211    };
2212    cmd.data = try gpa.alloc(u8, 8);
2213    defer gpa.free(cmd.data);
2214    cmd.data[0] = 0x2f;
2215    cmd.data[1] = 0x75;
2216    cmd.data[2] = 0x73;
2217    cmd.data[3] = 0x72;
2218    cmd.data[4] = 0x0;
2219    cmd.data[5] = 0x0;
2220    cmd.data[6] = 0x0;
2221    cmd.data[7] = 0x0;
2222    try testRead(gpa, in_buffer, LoadCommand{ .dylib = cmd });
2223
2224    var out_buffer: [in_buffer.len]u8 = undefined;
2225    try testWrite(&out_buffer, LoadCommand{ .dylib = cmd }, in_buffer);
2226}
2227
2228test "read-write C struct command" {
2229    // TODO compiling for macOS from big-endian arch
2230    if (builtin.target.cpu.arch.endian() != .Little) return error.SkipZigTest;
2231
2232    var gpa = testing.allocator;
2233    const in_buffer = &[_]u8{
2234        0x28, 0x00, 0x00, 0x80, // cmd
2235        0x18, 0x00, 0x00, 0x00, // cmdsize
2236        0x04, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // entryoff
2237        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // stacksize
2238    };
2239    const cmd = .{
2240        .cmd = .MAIN,
2241        .cmdsize = 24,
2242        .entryoff = 16644,
2243        .stacksize = 0,
2244    };
2245    try testRead(gpa, in_buffer, LoadCommand{ .main = cmd });
2246
2247    var out_buffer: [in_buffer.len]u8 = undefined;
2248    try testWrite(&out_buffer, LoadCommand{ .main = cmd }, in_buffer);
2249}
2250