mach_object/
display.rs

1use std::fmt;
2
3use time::macros::format_description;
4
5use crate::commands::{LcString, LoadCommand, Section};
6use crate::consts::*;
7use crate::loader::{ArHeader, FatHeader, MachCommand, MachHeader};
8use crate::symbol::Symbol;
9
10impl fmt::Display for MachHeader {
11    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
12        writeln!(f, "Mach header")?;
13        writeln!(
14            f,
15            "      magic cputype cpusubtype  caps    filetype ncmds sizeofcmds      \
16             flags"
17        )?;
18        writeln!(
19            f,
20            " 0x{:08x} {:7} {:10}  0x{:02x}  {:10} {:5} {:10} 0x{:08x}",
21            self.magic,
22            self.cputype,
23            get_cpu_subtype_type(self.cpusubtype),
24            get_cpu_subtype_feature(self.cpusubtype),
25            self.filetype,
26            self.ncmds,
27            self.sizeofcmds,
28            self.flags
29        )
30    }
31}
32
33impl fmt::Display for FatHeader {
34    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
35        writeln!(f, "Fat headers")?;
36        writeln!(f, "fat_magic 0x{:08x}", self.magic)?;
37        writeln!(f, "nfat_arch {}", self.archs.len())?;
38
39        for (i, arch) in self.archs.iter().enumerate() {
40            writeln!(f, "architecture {}", i)?;
41            writeln!(f, "    cputype {}", arch.cputype)?;
42            writeln!(f, "    cpusubtype {}", get_cpu_subtype_type(arch.cpusubtype))?;
43            writeln!(f, "    capabilities 0x{:x}", get_cpu_subtype_feature(arch.cpusubtype))?;
44            writeln!(f, "    offset {}", arch.offset)?;
45            writeln!(f, "    size {}", arch.size)?;
46            writeln!(f, "    align 2^{} ({})", arch.align, 1 << arch.align)?;
47        }
48
49        Ok(())
50    }
51}
52
53impl fmt::Display for ArHeader {
54    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
55        writeln!(
56            f,
57            "0{:o} {:3}/{:<3} {:5} {} {}",
58            self.ar_mode, self.ar_uid, self.ar_gid, self.ar_size, self.ar_date, self.ar_name
59        )
60    }
61}
62
63impl MachCommand {
64    fn print_segment_command(&self, f: &mut fmt::Formatter) -> fmt::Result {
65        let MachCommand(ref cmd, cmdsize) = *self;
66
67        match *cmd {
68            LoadCommand::Segment {
69                ref segname,
70                vmaddr,
71                vmsize,
72                fileoff,
73                filesize,
74                maxprot,
75                initprot,
76                flags,
77                ref sections,
78            }
79            | LoadCommand::Segment64 {
80                ref segname,
81                vmaddr,
82                vmsize,
83                fileoff,
84                filesize,
85                maxprot,
86                initprot,
87                flags,
88                ref sections,
89            } => {
90                let is_64bit = cmd.cmd() == LC_SEGMENT_64;
91
92                writeln!(f, "      cmd {}", cmd.name())?;
93                writeln!(f, "  cmdsize {}", cmdsize)?;
94                writeln!(f, "  segname {}", segname)?;
95                if is_64bit {
96                    writeln!(f, "   vmaddr 0x{:016x}", vmaddr)?;
97                    writeln!(f, "   vmsize 0x{:016x}", vmsize)?;
98                } else {
99                    writeln!(f, "   vmaddr 0x{:08x}", vmaddr)?;
100                    writeln!(f, "   vmsize 0x{:08x}", vmsize)?;
101                }
102                writeln!(f, "  fileoff {}", fileoff)?;
103                writeln!(f, " filesize {}", filesize)?;
104                writeln!(f, "  maxprot 0x{:08x}", maxprot)?;
105                writeln!(f, " initprot 0x{:08x}", initprot)?;
106                writeln!(f, "   nsects {}", sections.len())?;
107                writeln!(f, "    flags 0x{:x}", flags.bits())?;
108
109                for section in sections {
110                    writeln!(f, "Section")?;
111                    writeln!(f, "  sectname {}", section.sectname)?;
112                    writeln!(
113                        f,
114                        "   segname {}{}",
115                        section.segname,
116                        if *segname != section.segname {
117                            " (does not match segment)"
118                        } else {
119                            ""
120                        }
121                    )?;
122                    if is_64bit {
123                        writeln!(f, "      addr 0x{:016x}", section.addr)?;
124                        writeln!(f, "      size 0x{:016x}", section.size)?;
125                    } else {
126                        writeln!(f, "      addr 0x{:08x}", section.addr)?;
127                        writeln!(f, "      size 0x{:08x}", section.size)?;
128                    }
129                    writeln!(f, "    offset {}", section.offset)?;
130                    writeln!(f, "     align 2^{} ({})", section.align, 1 << section.align)?;
131                    writeln!(f, "    reloff {}", section.reloff)?;
132                    writeln!(f, "    nreloc {}", section.nreloc)?;
133                    let flags: u32 = section.flags.into();
134                    writeln!(f, "     flags 0x{:08x}", flags)?;
135                    writeln!(
136                        f,
137                        " reserved1 {}{}",
138                        section.reserved1,
139                        match section.flags.sect_type() {
140                            S_SYMBOL_STUBS
141                            | S_LAZY_SYMBOL_POINTERS
142                            | S_LAZY_DYLIB_SYMBOL_POINTERS
143                            | S_NON_LAZY_SYMBOL_POINTERS => " (index into indirect symbol table)",
144                            _ => "",
145                        }
146                    )?;
147                    writeln!(
148                        f,
149                        " reserved2 {}{}",
150                        section.reserved2,
151                        if section.flags.sect_type() == S_SYMBOL_STUBS {
152                            " (size of stubs)"
153                        } else {
154                            ""
155                        }
156                    )?;
157                }
158
159                Ok(())
160            }
161            _ => {
162                unreachable!();
163            }
164        }
165    }
166
167    fn print_dyld_info_command(&self, f: &mut fmt::Formatter) -> fmt::Result {
168        let MachCommand(ref cmd, cmdsize) = *self;
169
170        if let LoadCommand::DyldInfo {
171            rebase_off,
172            rebase_size,
173            bind_off,
174            bind_size,
175            weak_bind_off,
176            weak_bind_size,
177            lazy_bind_off,
178            lazy_bind_size,
179            export_off,
180            export_size,
181        } = *cmd
182        {
183            writeln!(f, "            cmd {}", cmd.name())?;
184            writeln!(f, "        cmdsize {}", cmdsize)?;
185            writeln!(f, "     rebase_off 0x{:08x}", rebase_off)?;
186            writeln!(f, "    rebase_size {}", rebase_size)?;
187            writeln!(f, "       bind_off 0x{:08x}", bind_off)?;
188            writeln!(f, "      bind_size {}", bind_size)?;
189            writeln!(f, "  weak_bind_off 0x{:08x}", weak_bind_off)?;
190            writeln!(f, " weak_bind_size {}", weak_bind_size)?;
191            writeln!(f, "  lazy_bind_off 0x{:08x}", lazy_bind_off)?;
192            writeln!(f, " lazy_bind_size {}", lazy_bind_size)?;
193            writeln!(f, "     export_off 0x{:08x}", export_off)?;
194            writeln!(f, "    export_size {}", export_size)?;
195
196            Ok(())
197        } else {
198            unreachable!();
199        }
200    }
201
202    fn print_symtab_command(&self, f: &mut fmt::Formatter) -> fmt::Result {
203        let MachCommand(ref cmd, cmdsize) = *self;
204
205        if let LoadCommand::SymTab {
206            symoff,
207            nsyms,
208            stroff,
209            strsize,
210        } = *cmd
211        {
212            writeln!(f, "     cmd {}", cmd.name())?;
213            writeln!(f, " cmdsize {}", cmdsize)?;
214            writeln!(f, "  symoff {}", symoff)?;
215            writeln!(f, "   nsyms {}", nsyms)?;
216            writeln!(f, "  stroff {}", stroff)?;
217            writeln!(f, " strsize {}", strsize)?;
218
219            Ok(())
220        } else {
221            unreachable!();
222        }
223    }
224
225    fn print_dysymtab_command(&self, f: &mut fmt::Formatter) -> fmt::Result {
226        let MachCommand(ref cmd, cmdsize) = *self;
227
228        if let LoadCommand::DySymTab {
229            ilocalsym,
230            nlocalsym,
231            iextdefsym,
232            nextdefsym,
233            iundefsym,
234            nundefsym,
235            tocoff,
236            ntoc,
237            modtaboff,
238            nmodtab,
239            extrefsymoff,
240            nextrefsyms,
241            indirectsymoff,
242            nindirectsyms,
243            extreloff,
244            nextrel,
245            locreloff,
246            nlocrel,
247        } = *cmd
248        {
249            writeln!(f, "            cmd {}", cmd.name())?;
250            writeln!(f, "        cmdsize {}", cmdsize)?;
251            writeln!(f, "      ilocalsym {}", ilocalsym)?;
252            writeln!(f, "      nlocalsym {}", nlocalsym)?;
253            writeln!(f, "     iextdefsym {}", iextdefsym)?;
254            writeln!(f, "     nextdefsym {}", nextdefsym)?;
255            writeln!(f, "      iundefsym {}", iundefsym)?;
256            writeln!(f, "      nundefsym {}", nundefsym)?;
257            writeln!(f, "         tocoff {}", tocoff)?;
258            writeln!(f, "           ntoc {}", ntoc)?;
259            writeln!(f, "      modtaboff {}", modtaboff)?;
260            writeln!(f, "        nmodtab {}", nmodtab)?;
261            writeln!(f, "   extrefsymoff {}", extrefsymoff)?;
262            writeln!(f, "    nextrefsyms {}", nextrefsyms)?;
263            writeln!(f, " indirectsymoff {}", indirectsymoff)?;
264            writeln!(f, "  nindirectsyms {}", nindirectsyms)?;
265            writeln!(f, "      extreloff {}", extreloff)?;
266            writeln!(f, "        nextrel {}", nextrel)?;
267            writeln!(f, "      locreloff {}", locreloff)?;
268            writeln!(f, "        nlocrel {}", nlocrel)?;
269
270            Ok(())
271        } else {
272            unreachable!();
273        }
274    }
275
276    fn print_dylinker_command(&self, f: &mut fmt::Formatter) -> fmt::Result {
277        let MachCommand(ref cmd, cmdsize) = *self;
278
279        match *cmd {
280            LoadCommand::IdDyLinker(LcString(off, ref name))
281            | LoadCommand::LoadDyLinker(LcString(off, ref name))
282            | LoadCommand::DyLdEnv(LcString(off, ref name)) => {
283                writeln!(f, "          cmd {}", cmd.name())?;
284                writeln!(f, "      cmdsize {}", cmdsize)?;
285                writeln!(f, "         name {} (offset {})", name, off)?;
286
287                Ok(())
288            }
289            _ => {
290                unreachable!();
291            }
292        }
293    }
294
295    fn print_fvmlib_command(&self, f: &mut fmt::Formatter) -> fmt::Result {
296        let MachCommand(ref cmd, cmdsize) = *self;
297
298        match *cmd {
299            LoadCommand::IdFvmLib(ref fvmlib) | LoadCommand::LoadFvmLib(ref fvmlib) => {
300                writeln!(f, "           cmd {}", cmd.name())?;
301                writeln!(f, "       cmdsize {}", cmdsize)?;
302                writeln!(f, " minor version {}", fvmlib.minor_version)?;
303                writeln!(f, "   header addr 0x{:08x}", fvmlib.header_addr)?;
304
305                Ok(())
306            }
307            _ => {
308                unreachable!();
309            }
310        }
311    }
312
313    fn print_dylib_command(&self, f: &mut fmt::Formatter) -> fmt::Result {
314        let time_format = format_description!(
315            "[weekday repr:short] [month repr:short] [day padding:space] [hour]:[minute]:[second] [year] UTC"
316        );
317
318        let MachCommand(ref cmd, cmdsize) = *self;
319
320        match *cmd {
321            LoadCommand::IdDyLib(ref dylib)
322            | LoadCommand::LoadDyLib(ref dylib)
323            | LoadCommand::LoadWeakDyLib(ref dylib)
324            | LoadCommand::ReexportDyLib(ref dylib)
325            | LoadCommand::LoadUpwardDylib(ref dylib)
326            | LoadCommand::LazyLoadDylib(ref dylib) => {
327                writeln!(f, "          cmd {}", cmd.name())?;
328                writeln!(f, "      cmdsize {}", cmdsize)?;
329                writeln!(f, "         name {} (offset {})", dylib.name, dylib.name.0)?;
330                let ts = time::OffsetDateTime::from_unix_timestamp(i64::from(dylib.timestamp)).unwrap();
331                writeln!(
332                    f,
333                    "   time stamp {} {}",
334                    dylib.timestamp,
335                    ts.format(&time_format).unwrap()
336                )?;
337                writeln!(
338                    f,
339                    "      current version {}.{}.{}",
340                    dylib.current_version.major(),
341                    dylib.current_version.minor(),
342                    dylib.current_version.release()
343                )?;
344                writeln!(
345                    f,
346                    "compatibility version {}.{}.{}",
347                    dylib.compatibility_version.major(),
348                    dylib.compatibility_version.minor(),
349                    dylib.compatibility_version.release()
350                )?;
351
352                Ok(())
353            }
354            _ => {
355                unreachable!();
356            }
357        }
358    }
359
360    fn print_rpath_command(&self, f: &mut fmt::Formatter) -> fmt::Result {
361        let MachCommand(ref cmd, cmdsize) = *self;
362
363        if let LoadCommand::Rpath(ref path) = *cmd {
364            writeln!(f, "      cmd {}", cmd.name())?;
365            writeln!(f, "  cmdsize {}", cmdsize)?;
366            writeln!(f, "     path {}", path)?;
367
368            Ok(())
369        } else {
370            unreachable!();
371        }
372    }
373
374    fn print_version_min_command(&self, f: &mut fmt::Formatter) -> fmt::Result {
375        let MachCommand(ref cmd, cmdsize) = *self;
376
377        if let LoadCommand::VersionMin { version, sdk, .. } = *cmd {
378            writeln!(f, "      cmd {}", cmd.name())?;
379            writeln!(f, "  cmdsize {}", cmdsize)?;
380            writeln!(f, "  version {}", version)?;
381            writeln!(f, "      sdk {}", sdk)?;
382
383            Ok(())
384        } else {
385            unreachable!();
386        }
387    }
388
389    fn print_source_version_command(&self, f: &mut fmt::Formatter) -> fmt::Result {
390        let MachCommand(ref cmd, cmdsize) = *self;
391
392        if let LoadCommand::SourceVersion(version) = *cmd {
393            writeln!(f, "      cmd {}", cmd.name())?;
394            writeln!(f, "  cmdsize {}", cmdsize)?;
395            writeln!(f, "  version {}", version)?;
396
397            Ok(())
398        } else {
399            unreachable!();
400        }
401    }
402
403    fn print_uuid_command(&self, f: &mut fmt::Formatter) -> fmt::Result {
404        let MachCommand(ref cmd, cmdsize) = *self;
405
406        if let LoadCommand::Uuid(ref uuid) = *cmd {
407            writeln!(f, "     cmd {}", cmd.name())?;
408            writeln!(f, " cmdsize {}", cmdsize)?;
409            writeln!(f, "    uuid {}", uuid.to_string().to_uppercase())?;
410
411            Ok(())
412        } else {
413            unreachable!();
414        }
415    }
416
417    fn print_entry_point_command(&self, f: &mut fmt::Formatter) -> fmt::Result {
418        let MachCommand(ref cmd, cmdsize) = *self;
419
420        if let LoadCommand::EntryPoint { entryoff, stacksize } = *cmd {
421            writeln!(f, "       cmd {}", cmd.name())?;
422            writeln!(f, "   cmdsize {}", cmdsize)?;
423            writeln!(f, "  entryoff {}", entryoff)?;
424            writeln!(f, " stacksize {}", stacksize)?;
425
426            Ok(())
427        } else {
428            unreachable!();
429        }
430    }
431
432    fn print_linkedit_data_command(&self, f: &mut fmt::Formatter) -> fmt::Result {
433        let MachCommand(ref cmd, cmdsize) = *self;
434
435        match *cmd {
436            LoadCommand::CodeSignature(ref data)
437            | LoadCommand::SegmentSplitInfo(ref data)
438            | LoadCommand::FunctionStarts(ref data)
439            | LoadCommand::DataInCode(ref data)
440            | LoadCommand::DylibCodeSignDrs(ref data)
441            | LoadCommand::LinkerOptimizationHint(ref data) => {
442                writeln!(f, "      cmd {}", cmd.name())?;
443                writeln!(f, "  cmdsize {}", cmdsize)?;
444                writeln!(f, "  dataoff {}", data.off)?;
445                writeln!(f, " datasize {}", data.size)?;
446
447                Ok(())
448            }
449            _ => {
450                unreachable!();
451            }
452        }
453    }
454
455    fn print_build_version(&self, f: &mut fmt::Formatter) -> fmt::Result {
456        let MachCommand(ref cmd, cmdsize) = *self;
457
458        match *cmd {
459            LoadCommand::BuildVersion(ref version) => {
460                writeln!(f, "      cmd {}", cmd.name())?;
461                writeln!(f, "  cmdsize {}", cmdsize)?;
462                writeln!(f, " platform {:?}", version.platform())?;
463                writeln!(f, "    minos {}", version.minos)?;
464                writeln!(f, "      sdk {}", version.sdk)?;
465                writeln!(f, "    tools")?;
466
467                for tool in &version.build_tools {
468                    writeln!(f, "          {:?} {}", tool.tool(), tool.version)?;
469                }
470
471                Ok(())
472            }
473            _ => unreachable!(),
474        }
475    }
476}
477
478impl fmt::Display for MachCommand {
479    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
480        match self.0 {
481            LoadCommand::Segment { .. } | LoadCommand::Segment64 { .. } => self.print_segment_command(f),
482            LoadCommand::DyldInfo { .. } => self.print_dyld_info_command(f),
483            LoadCommand::SymTab { .. } => self.print_symtab_command(f),
484            LoadCommand::DySymTab { .. } => self.print_dysymtab_command(f),
485            LoadCommand::IdDyLinker(_) | LoadCommand::LoadDyLinker(_) | LoadCommand::DyLdEnv(_) => {
486                self.print_dylinker_command(f)
487            }
488            LoadCommand::IdFvmLib(_) | LoadCommand::LoadFvmLib(_) => self.print_fvmlib_command(f),
489            LoadCommand::IdDyLib(_)
490            | LoadCommand::LoadDyLib(_)
491            | LoadCommand::LoadWeakDyLib(_)
492            | LoadCommand::ReexportDyLib(_)
493            | LoadCommand::LoadUpwardDylib(_)
494            | LoadCommand::LazyLoadDylib(_) => self.print_dylib_command(f),
495            LoadCommand::Rpath(_) => self.print_rpath_command(f),
496            LoadCommand::VersionMin { .. } => self.print_version_min_command(f),
497            LoadCommand::SourceVersion(_) => self.print_source_version_command(f),
498            LoadCommand::Uuid(_) => self.print_uuid_command(f),
499            LoadCommand::EntryPoint { .. } => self.print_entry_point_command(f),
500            LoadCommand::CodeSignature(_)
501            | LoadCommand::SegmentSplitInfo(_)
502            | LoadCommand::FunctionStarts(_)
503            | LoadCommand::DataInCode(_)
504            | LoadCommand::DylibCodeSignDrs(_)
505            | LoadCommand::LinkerOptimizationHint(_) => self.print_linkedit_data_command(f),
506            LoadCommand::BuildVersion { .. } => self.print_build_version(f),
507            _ => {
508                warn!("ignore command: {:?}", self);
509
510                Ok(())
511            }
512        }
513    }
514}
515
516impl<'a> fmt::Display for Symbol<'a> {
517    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
518        match *self {
519            Symbol::Undefined { ref name, external, .. } => write!(
520                f,
521                "                 {} {}",
522                if external { "U" } else { "u" },
523                name.unwrap_or("")
524            ),
525            Symbol::Absolute {
526                ref name,
527                external,
528                entry,
529                ..
530            } => write!(
531                f,
532                "{:016x} {} {}",
533                entry,
534                if external { "A" } else { "a" },
535                name.unwrap_or("")
536            ),
537            Symbol::Defined {
538                ref name,
539                external,
540                ref section,
541                entry,
542                ..
543            } => {
544                let mut symtype = "s";
545
546                if let Some(ref section) = *section {
547                    let Section {
548                        ref sectname,
549                        ref segname,
550                        ..
551                    } = **section;
552
553                    if segname == SEG_TEXT && sectname == SECT_TEXT {
554                        symtype = "t"
555                    } else if segname == SEG_DATA {
556                        if sectname == SECT_DATA {
557                            symtype = "d"
558                        } else if sectname == SECT_BSS {
559                            symtype = "b"
560                        } else if sectname == SECT_COMMON {
561                            symtype = "c"
562                        }
563                    }
564                }
565
566                write!(
567                    f,
568                    "{:016x} {} {}",
569                    entry,
570                    if external {
571                        symtype.to_uppercase()
572                    } else {
573                        symtype.to_lowercase()
574                    },
575                    name.unwrap_or("")
576                )
577            }
578            Symbol::Prebound { ref name, external, .. } => write!(
579                f,
580                "                 {} {}",
581                if external { "P" } else { "p" },
582                name.unwrap_or("")
583            ),
584            Symbol::Indirect { ref name, external, .. } => write!(
585                f,
586                "                 {} {}",
587                if external { "I" } else { "i" },
588                name.unwrap_or("")
589            ),
590            Symbol::Debug { ref name, addr, .. } => {
591                if addr == 0 {
592                    write!(f, "                 d {}", name.unwrap_or(""))
593                } else {
594                    write!(f, "{:016x} d {}", addr, name.unwrap_or(""))
595                }
596            }
597        }
598    }
599}
600
601#[cfg(test)]
602pub mod tests {
603    use std::io::Write;
604    use std::str;
605
606    use diff;
607
608    use crate::loader::OFile;
609
610    static HELLO_WORLD_BIN: &'static [u8] = include_bytes!("../tests/helloworld");
611    static HELLO_WORLD_LC: &'static str = include_str!("../tests/helloworld.lc");
612    static HELLO_UNIVERSAL_BIN: &'static [u8] = include_bytes!("../tests/helloworld.universal");
613    static HELLO_UNIVERSAL_I386_LC: &'static str = include_str!(
614        "../tests/helloworld.universal.\
615         i386.lc"
616    );
617    static HELLO_UNIVERSAL_X86_64_LC: &'static str = include_str!(
618        "../tests/helloworld.universal.\
619         x86_64.lc"
620    );
621    static HELLO_OBJC_BIN: &'static [u8] = include_bytes!("../tests/helloobjc");
622    static HELLO_OBJC_LC: &'static str = include_str!("../tests/helloobjc.lc");
623    static HELLO_RUST_BIN: &'static [u8] = include_bytes!("../tests/hellorust");
624    static HELLO_RUST_LC: &'static str = include_str!("../tests/hellorust.lc");
625
626    macro_rules! parse_test_file {
627        ($buf:expr) => {{
628            let _ = ::pretty_env_logger::try_init();
629
630            let mut cursor = ::std::io::Cursor::new($buf);
631
632            $crate::OFile::parse(&mut cursor).unwrap()
633        }};
634    }
635
636    macro_rules! assert_nodiff {
637        ($left:expr, $right:expr) => {{
638            let mut w = Vec::new();
639            let mut diffs = 0;
640            let left = $left.replace("\r\n", "\n").replace("Jan 1 0:", "Jan  1 00:"); // FIXME
641            let right = $right.replace("\r\n", "\n");
642
643            for diff in diff::lines(&left, &right) {
644                match diff {
645                    diff::Result::Left(l) => {
646                        diffs += 1;
647                        writeln!(w, "-{}", l).unwrap()
648                    }
649                    diff::Result::Both(_, _) => {}
650                    diff::Result::Right(r) => {
651                        diffs += 1;
652                        writeln!(w, "+{}", r).unwrap()
653                    }
654                }
655            }
656
657            if diffs > 0 {
658                info!("found {} diffs:\n{}", diffs / 2, String::from_utf8(w).unwrap());
659            }
660
661            assert_eq!(&left, &right);
662        }};
663    }
664
665    #[test]
666    fn test_parse_hello_bin() {
667        if let OFile::MachFile { commands, .. } = parse_test_file!(HELLO_WORLD_BIN) {
668            let mut w = Vec::<u8>::new();
669
670            writeln!(w, "helloworld:").unwrap();
671
672            for (i, ref cmd) in commands.iter().enumerate() {
673                writeln!(w, "Load command {}", i).unwrap();
674                write!(w, "{}", cmd).unwrap();
675            }
676
677            let dump = str::from_utf8(w.as_slice()).unwrap();
678
679            assert_nodiff!(dump, HELLO_WORLD_LC);
680        } else {
681            panic!();
682        }
683    }
684
685    #[test]
686    fn test_parse_hello_objc() {
687        if let OFile::MachFile { commands, .. } = parse_test_file!(HELLO_OBJC_BIN) {
688            let mut w = Vec::<u8>::new();
689
690            writeln!(w, "helloobjc:").unwrap();
691
692            for (i, ref cmd) in commands.iter().enumerate() {
693                writeln!(w, "Load command {}", i).unwrap();
694                write!(w, "{}", cmd).unwrap();
695            }
696
697            let dump = str::from_utf8(w.as_slice()).unwrap();
698
699            assert_nodiff!(dump, HELLO_OBJC_LC);
700        } else {
701            panic!();
702        }
703    }
704
705    #[test]
706    fn test_parse_hello_rust() {
707        if let OFile::MachFile { commands, .. } = parse_test_file!(HELLO_RUST_BIN) {
708            let mut w = Vec::<u8>::new();
709
710            writeln!(w, "hellorust:").unwrap();
711
712            for (i, ref cmd) in commands.iter().enumerate() {
713                writeln!(w, "Load command {}", i).unwrap();
714                write!(w, "{}", cmd).unwrap();
715            }
716
717            let dump = str::from_utf8(w.as_slice()).unwrap();
718
719            assert_nodiff!(dump, HELLO_RUST_LC);
720        } else {
721            panic!();
722        }
723    }
724
725    #[test]
726    fn test_parse_hello_universal() {
727        if let OFile::FatFile { ref files, .. } = parse_test_file!(HELLO_UNIVERSAL_BIN) {
728            assert_eq!(files.len(), 2);
729
730            for (i, arch_dump) in [HELLO_UNIVERSAL_I386_LC, HELLO_UNIVERSAL_X86_64_LC].iter().enumerate() {
731                let mut w = Vec::<u8>::new();
732
733                writeln!(w, "helloworld.universal:").unwrap();
734
735                if let (_, OFile::MachFile { ref commands, .. }) = files[i] {
736                    for (i, ref cmd) in commands.iter().enumerate() {
737                        writeln!(w, "Load command {}", i).unwrap();
738                        write!(w, "{}", cmd).unwrap();
739                    }
740
741                    let dump = str::from_utf8(w.as_slice()).unwrap();
742
743                    assert_nodiff!(dump, *arch_dump);
744                } else {
745                    panic!();
746                }
747            }
748        } else {
749            panic!();
750        }
751    }
752}