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(§ions.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 = §ions[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 = §ions[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