1 use metagoblin::error;
2 use metagoblin::mach;
3 use metagoblin::mach::exports::Export;
4 use metagoblin::mach::header;
5 use metagoblin::mach::load_command;
6 
7 use crate::Opt;
8 
9 use atty;
10 use prettytable::Cell;
11 use prettytable::Row;
12 use std::io::{self, Write};
13 use termcolor::Color::*;
14 use termcolor::*;
15 
16 use crate::format::*;
17 
18 pub struct Mach<'a>(pub mach::MachO<'a>, pub Opt);
19 
20 impl<'a> Mach<'a> {
print(&self) -> error::Result<()>21     pub fn print(&self) -> error::Result<()> {
22         let mach = &self.0;
23         let args = &self.1;
24 
25         let cc = if args.color || atty::is(atty::Stream::Stdout) {
26             ColorChoice::Auto
27         } else {
28             ColorChoice::Never
29         };
30         let writer = BufferWriter::stdout(cc);
31         let fmt = &mut writer.buffer();
32 
33         let header = &mach.header;
34         let endianness = if mach.little_endian {
35             "little-endian"
36         } else {
37             "big-endian"
38         };
39         let machine = header.cputype;
40         let kind = |fmt: &mut Buffer, header: &header::Header| {
41             let typ_cell = header.filetype;
42             let kind_str = header::filetype_to_str(typ_cell);
43             match typ_cell {
44                 header::MH_OBJECT => fmt.set_color(
45                     ::termcolor::ColorSpec::new()
46                         .set_intense(true)
47                         .set_bg(Some(Yellow))
48                         .set_fg(Some(Black)),
49                 )?,
50                 header::MH_EXECUTE => fmt.set_color(
51                     ::termcolor::ColorSpec::new()
52                         .set_intense(true)
53                         .set_bg(Some(Red))
54                         .set_fg(Some(Black)),
55                 )?,
56                 header::MH_DYLIB => fmt.set_color(
57                     ::termcolor::ColorSpec::new()
58                         .set_intense(true)
59                         .set_bg(Some(Yellow))
60                         .set_fg(Some(Black)),
61                 )?,
62                 header::MH_DYLINKER => fmt.set_color(
63                     ::termcolor::ColorSpec::new()
64                         .set_intense(true)
65                         .set_bg(Some(Yellow))
66                         .set_fg(Some(Black)),
67                 )?,
68                 header::MH_DYLIB_STUB => fmt.set_color(
69                     ::termcolor::ColorSpec::new()
70                         .set_intense(true)
71                         .set_bg(Some(Blue))
72                         .set_fg(Some(Black)),
73                 )?,
74                 header::MH_DSYM => fmt.set_color(
75                     ::termcolor::ColorSpec::new()
76                         .set_intense(true)
77                         .set_bg(Some(Green))
78                         .set_fg(Some(Black)),
79                 )?,
80                 header::MH_CORE => fmt.set_color(
81                     ::termcolor::ColorSpec::new()
82                         .set_intense(true)
83                         .set_bg(Some(White))
84                         .set_fg(Some(Black)),
85                 )?,
86                 _ => (),
87             }
88             write!(fmt, "{}", kind_str)?;
89             fmt.reset()
90         };
91         fmt_hdr(fmt, "Mach-o ")?;
92         kind(fmt, &mach.header)?;
93         write!(fmt, " ")?;
94         fmt_name_bold(
95             fmt,
96             mach::constants::cputype::get_arch_name_from_types(machine, header.cpusubtype)
97                 .unwrap_or("None"),
98         )?;
99         write!(fmt, "-{} @ ", endianness)?;
100         fmt_addrx(fmt, mach.entry as u64)?;
101         writeln!(fmt, ":")?;
102         writeln!(fmt, "")?;
103 
104         let lcs = &mach.load_commands;
105         fmt_header(fmt, "LoadCommands", lcs.len())?;
106         for (i, lc) in lcs.into_iter().enumerate() {
107             fmt_idx(fmt, i)?;
108             write!(fmt, " ")?;
109             let name = load_command::cmd_to_str(lc.command.cmd());
110             let name = &format!("{:.27}", name);
111             match lc.command {
112                 load_command::CommandVariant::Segment32(_command) => {
113                     fmt_name_color(fmt, name, Red)?
114                 }
115                 load_command::CommandVariant::Segment64(_command) => {
116                     fmt_name_color(fmt, name, Red)?
117                 }
118                 load_command::CommandVariant::Symtab(_command) => {
119                     fmt_name_color(fmt, name, Yellow)?
120                 }
121                 load_command::CommandVariant::Dysymtab(_command) => {
122                     fmt_name_color(fmt, name, Green)?
123                 }
124                 load_command::CommandVariant::LoadDylinker(_command) => {
125                     fmt_name_color(fmt, name, Yellow)?
126                 }
127                 load_command::CommandVariant::LoadDylib(_command)
128                 | load_command::CommandVariant::LoadUpwardDylib(_command)
129                 | load_command::CommandVariant::ReexportDylib(_command)
130                 | load_command::CommandVariant::LazyLoadDylib(_command) => {
131                     fmt_name_color(fmt, name, Blue)?
132                 }
133                 load_command::CommandVariant::DyldInfo(_command)
134                 | load_command::CommandVariant::DyldInfoOnly(_command) => {
135                     fmt_name_color(fmt, name, Cyan)?
136                 }
137                 load_command::CommandVariant::Unixthread(_command) => {
138                     fmt_name_color(fmt, name, Red)?
139                 }
140                 load_command::CommandVariant::Main(_command) => fmt_name_color(fmt, name, Red)?,
141                 _ => fmt_name_bold(fmt, name)?,
142             }
143             writeln!(fmt, "")?;
144         }
145 
146         writeln!(fmt, "")?;
147 
148         let segments = &mach.segments;
149         fmt_header(fmt, "Segments", segments.len())?;
150         for (ref _i, ref segment) in segments.into_iter().enumerate() {
151             let name = segment.name().unwrap();
152             let sections = &segment.sections().unwrap();
153             let mut segment_table = new_table(row![b->"Segment", b->"# Sections"]);
154             segment_table.add_row(Row::new(vec![
155                 str_cell(&name),
156                 Cell::new(&sections.len().to_string()),
157             ]));
158             flush(fmt, &writer, segment_table, args.color)?;
159 
160             let mut section_table = new_table(
161                 row![b->"", b->"Idx", b->"Name", b->"Addr", b->"Size", b->"Offset", b->"Align", b->"Reloff", b->"Nrelocs", b->"Flags"],
162             );
163             for (i, &(ref section, _)) in sections.into_iter().enumerate() {
164                 if let Ok(name) = section.name() {
165                     section_table.add_row(Row::new(vec![
166                         Cell::new(&format!("{:4}", "")), // filler
167                         Cell::new(&i.to_string()),
168                         Cell::new(name).style_spec("Fyb"),
169                         addrx_cell(section.addr),
170                         sz_cell(section.size),
171                         offsetx_cell(section.offset as u64),
172                         Cell::new(&format!("{}", section.align)),
173                         offsetx_cell(section.reloff as u64),
174                         Cell::new(&format!("{}", section.nreloc)),
175                         Cell::new(&format!("{:#x}", section.flags)),
176                     ]));
177                 } else {
178                     section_table.add_row(Row::new(vec![
179                         Cell::new(&i.to_string()),
180                         Cell::new("BAD SECTION NAME"),
181                     ]));
182                 }
183             }
184             flush(fmt, &writer, section_table, true)?;
185             writeln!(fmt, "")?;
186         }
187         writeln!(fmt, "")?;
188 
189         let mut relocations: Vec<_> = Vec::new();
190         let mut nrelocs = 0;
191         let r = mach.relocations().unwrap();
192         for (_i, relocs, section) in r.into_iter() {
193             let section_name = section.name().unwrap();
194             let segment_name = section.segname().unwrap();
195             let mut rs = Vec::new();
196             for reloc in relocs {
197                 let reloc = reloc.unwrap();
198                 nrelocs += 1;
199                 rs.push(reloc);
200             }
201             if !rs.is_empty() {
202                 relocations.push((segment_name.to_owned(), section_name.to_owned(), rs))
203             };
204         }
205         // need this to print relocation references
206         let symbols = mach.symbols().collect::<Vec<_>>();
207         let sections = mach
208             .segments
209             .sections()
210             .flat_map(|x| x)
211             .map(|s| s.unwrap().0)
212             .collect::<Vec<_>>();
213 
214         fmt_header(fmt, "Relocations", nrelocs)?;
215         for (n1, n2, relocs) in relocations {
216             let mut section_table = new_table(row![b->"Segment", b->"Section", b->"Count"]);
217             section_table.add_row(Row::new(vec![
218                 str_cell(&n1),
219                 str_cell(&n2),
220                 Cell::new(&relocs.len().to_string()),
221             ]));
222             flush(fmt, &writer, section_table, args.color)?;
223             let mut reloc_table = new_table(
224                 row![b->"", b->"Type", b->"Offset", b->"Length", b->"PIC", b->"Extern", b->"SymbolNum", b->"Symbol"],
225             );
226             for reloc in relocs {
227                 let idx = reloc.r_symbolnum();
228                 let name_cell = {
229                     if reloc.is_extern() {
230                         // FIXME: i cannot currently get this to compile without iterating and doing all this nonsense, otherwise move errors...
231                         let mut maybe_name = None;
232                         for (i, symbol) in symbols.iter().enumerate() {
233                             match symbol {
234                                 &Ok((ref name, _)) => {
235                                     let name: &str = name;
236                                     if i == idx {
237                                         maybe_name = Some(name);
238                                     }
239                                 }
240                                 &Err(_) => (),
241                             }
242                         }
243                         match maybe_name {
244                             Some(name) => string_cell(&args, name),
245                             None => cell("None").style_spec("b"),
246                         }
247                     // not extern so the symbol num should reference a section
248                     } else {
249                         let section = &sections[idx - 1 as usize];
250                         let sectname = section.name()?;
251                         let segname = section.segname()?;
252                         cell(format!("{}.{}", segname, sectname)).style_spec("bi")
253                     }
254                 };
255                 reloc_table.add_row(Row::new(vec![
256                     Cell::new(&format!("{:4}", "")),
257                     cell(reloc.to_str(machine)),
258                     addrx_cell(reloc.r_address as u64),
259                     cell(reloc.r_length()),
260                     bool_cell(reloc.is_pic()),
261                     bool_cell(reloc.is_extern()),
262                     offsetx_cell(idx as u64),
263                     name_cell,
264                 ]));
265             }
266 
267             flush(fmt, &writer, reloc_table, args.color)?;
268             writeln!(fmt, "")?;
269         }
270         writeln!(fmt, "")?;
271 
272         fmt_header(fmt, "Symbols", symbols.len())?;
273         let mut symbol_table =
274             new_table(row![b->"Offset", b->"Name", b->"Section", b->"Global", b->"Undefined"]);
275         for (i, symbol) in symbols.into_iter().enumerate() {
276             match symbol {
277                 Ok((name, symbol)) => {
278                     let section_cell = if symbol.get_type() == mach::symbols::N_SECT {
279                         // we subtract 1 because when N_SECT it is an ordinal, and hence indexing starts from 1
280                         let section = &sections[symbol.n_sect - 1 as usize];
281                         let sectname = section.name()?;
282                         let segname = section.segname()?;
283                         cell(format!("{}.{}", segname, sectname)).style_spec("b")
284                     } else {
285                         cell("None").style_spec("i")
286                     };
287                     symbol_table.add_row(Row::new(vec![
288                         addrx_cell(symbol.n_value as u64),
289                         string_cell(&args, name),
290                         section_cell,
291                         bool_cell(symbol.is_global()),
292                         bool_cell(symbol.is_undefined()),
293                     ]));
294                 }
295                 Err(e) => {
296                     writeln!(fmt, "  {}: {}", i, e)?;
297                 }
298             }
299         }
300         flush(fmt, &writer, symbol_table, args.color)?;
301         writeln!(fmt)?;
302 
303         let fmt_exports = |fmt: &mut Buffer, name: &str, syms: &[Export]| -> io::Result<()> {
304             fmt_header(fmt, name, syms.len())?;
305             for sym in syms {
306                 fmt_addr_right(fmt, sym.offset)?;
307                 write!(fmt, " ")?;
308                 fmt_string(fmt, args, &sym.name)?;
309                 write!(fmt, " (")?;
310                 fmt_sz(fmt, sym.size as u64)?;
311                 writeln!(fmt, ")")?;
312             }
313             writeln!(fmt, "")
314         };
315 
316         let exports = match mach.exports() {
317             Ok(exports) => exports,
318             Err(_) => Vec::new(),
319         };
320         fmt_exports(fmt, "Exports", &exports)?;
321 
322         let imports = match mach.imports() {
323             Ok(imports) => imports,
324             Err(_) => Vec::new(),
325         };
326         fmt_header(fmt, "Imports", imports.len())?;
327         for sym in imports {
328             fmt_addr_right(fmt, sym.offset)?;
329             write!(fmt, " ")?;
330             fmt_string(fmt, args, &sym.name)?;
331             write!(fmt, " (")?;
332             fmt_sz(fmt, sym.size as u64)?;
333             write!(fmt, ")")?;
334             write!(fmt, " -> ")?;
335             fmt_lib(fmt, sym.dylib)?;
336             writeln!(fmt, "")?;
337         }
338         writeln!(fmt, "")?;
339 
340         fmt_header(fmt, "Libraries", mach.libs.len() - 1)?;
341         for lib in &mach.libs[1..] {
342             fmt_lib_right(fmt, lib)?;
343             writeln!(fmt, "")?;
344         }
345         writeln!(fmt, "")?;
346 
347         write!(fmt, "Name: ")?;
348         fmt_str_option(fmt, &mach.name)?;
349         writeln!(fmt, "")?;
350         write!(fmt, "is_64: ")?;
351         fmt_bool(fmt, mach.is_64)?;
352         writeln!(fmt, "")?;
353         write!(fmt, "is_lib: ")?;
354         fmt_bool(fmt, mach.header.filetype == header::MH_DYLIB)?;
355         writeln!(fmt, "")?;
356         write!(fmt, "little_endian: ")?;
357         fmt_bool(fmt, mach.little_endian)?;
358         writeln!(fmt, "")?;
359         write!(fmt, "entry: ")?;
360         fmt_addr(fmt, mach.entry as u64)?;
361         writeln!(fmt, "")?;
362 
363         writer.print(fmt)?;
364         Ok(())
365     }
366 }
367