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(§.sectname); 754 } 755 756 pub fn segName(sect: section_64) []const u8 { 757 return parseName(§.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(§)); 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