schnauzer 0.3.5

Library for parsing Mach-O files
Documentation
use super::common;
use super::common::Format;
use super::common::ObjectFilter;
use super::common::options::AddToOptions;
use super::handler::*;
use super::Printer;
use super::Result;
use crate::auto_enum_fields::*;
use crate::*;
use colored::*;
use getopts::*;

static SUBCOMM_NAME: &str = "syms";

pub(super) struct SymsHandler {
    pub(super) printer: Printer,
}

impl SymsHandler {
    pub(super) fn new(printer: Printer) -> Self {
        SymsHandler { printer }
    }
}

impl Handler for SymsHandler {
    fn command_name(&self) -> String {
        SUBCOMM_NAME.to_string()
    }

    fn description(&self) -> String {
        "Prints all symbols".to_string()
    }

    fn can_handle_with_name(&self, name: &str) -> bool {
        SUBCOMM_NAME == name
    }

    fn handle_object(&self, object: ObjectType, other_args: Vec<String>) -> Result<()> {
        let mut opts = Options::new();
        self.accepted_option_items().add_to_opts(&mut opts);

        let format = &Format::build(&mut opts, &other_args)?;
        let filter = ObjectFilter::build(&mut opts, &other_args)?;

        let objects = &filter.get_objects(object);
        let out_arch = objects.len() > 1;
        for (idx, obj) in objects.iter().enumerate() {
            if out_arch {
                common::out_single_arch_title(&self.printer, &obj.header(), idx, format.short);
            }
            self.handle_load_commands(obj.load_commands_iterator(), format);
        }

        Ok(())
    }

    fn accepted_option_items(&self) -> Vec<common::options::OptionItem> {
        let mut result = default_option_items();
        result.append(&mut Format::option_items());
        result
    }
}

impl SymsHandler {
    fn handle_load_commands(&self, commands: LoadCommandIterator, format: &Format) {
        let commands = commands.flat_map(|cmd| match cmd.variant {
            LcVariant::Symtab(symtab) => Some(symtab),
            _ => None,
        });
        for cmd in commands {
            self.handle_symtab_command(cmd, format);
        }
    }

    fn handle_symtab_command(&self, symtab: LcSymtab, format: &Format) {
        for (index, nlist) in symtab.nlist_iterator().enumerate() {
            self.handle_nlist(nlist, index, format);
        }
    }

    fn handle_nlist(&self, nlist: Nlist, index: usize, format: &Format) {
        if format.show_indices {
            self.printer.out_list_item_dash(0, index);
        }

        let title = self.type_title(&nlist);
        let name: String = match &nlist.name {
            Some(name) => match name.load_string() {
                Ok(s) => s,
                Err(err) => {
                    eprintln!("{:?}", err);
                    "".to_string()
                }
            },
            None => "".to_string(),
        };

        let name = if name.len() > 0 {
            name.yellow()
        } else {
            "[No name]".dimmed()
        };

        let label = format!("{title} {name}");

        self.printer.print_line(label);

        if !format.short {
            let other_fields = self.other_symbol_fields(&nlist);
            if other_fields.len() > 0 {
                self.printer.out_default_colored_fields(other_fields, "\n");
            }
        }
    }

    fn type_title(&self, nlist: &Nlist) -> ColoredString {
        let ntype = &nlist.n_type;
        if let Some(stab) = ntype.stab_type() {
            format!("Stab({:?})", stab)
        } else if ntype.is_private_external() {
            "Private".to_string()
        } else if ntype.is_external() {
            "External".to_string()
        } else if ntype.is_undefined() {
            "Undefined".to_string()
        } else if ntype.is_absolute() {
            "Absolute".to_string()
        } else if ntype.is_defined_in_n_sect() {
            "Symbol".to_string()
        } else if ntype.is_prebound() {
            "Prebound".to_string()
        } else if ntype.is_indirect() {
            "Same as".to_string()
        } else {
            "".to_string()
        }
        .blue()
    }

    fn other_symbol_fields(&self, nlist: &Nlist) -> Vec<Field> {
        self.fields_with_options(nlist, nlist.n_type.options())
    }

    fn fields_with_options(&self, nlist: &Nlist, opts: SymbolOptions) -> Vec<Field> {
        const SECTION_TITLE: &str = "Section";
        const N_SECT_TITLE: &str = "n_sect";
        const N_DESC_TITLE: &str = "n_desc";
        const N_VALUE_TITLE: &str = "n_value";

        let mut fields = vec![];

        let n_sect: Option<Field> = match opts.n_sect {
            NsectOption::None | NsectOption::Unknown => None,
            NsectOption::Some => Some(Field::new(SECTION_TITLE.to_string(), nlist.n_sect.to_string())),
            NsectOption::Zero => Some(Field::new(SECTION_TITLE.to_string(), 0.to_string())),
            NsectOption::Raw => Some(Field::new(N_SECT_TITLE.to_string(), nlist.n_sect.to_string())),
        };
        if let Some(n_sect) = n_sect {
            fields.push(n_sect);
        }

        let n_desc: Option<Field> = match opts.n_desc {
            NdescOption::None | NdescOption::Unknown => None,
            NdescOption::GlobalSymbolType
            | NdescOption::StaticSymbolType
            | NdescOption::LocalCommonSymbolType
            | NdescOption::RegisterType
            | NdescOption::StructureEltType
            | NdescOption::SymbolType
            | NdescOption::ParameterType => {
                Some(Field::new("Type".to_string(), nlist.n_desc.to_string()))
            }
            NdescOption::LineNumber => {
                Some(Field::new("Line".to_string(), nlist.n_desc.to_string()))
            }
            NdescOption::NestingLevel => {
                Some(Field::new("Nesting".to_string(), nlist.n_desc.to_string()))
            }
            NdescOption::Raw => {
                Some(Field::new(N_DESC_TITLE.to_string(), nlist.n_desc.to_string()))
            },
        };
        if let Some(n_desc) = n_desc {
            fields.push(n_desc);
        }

        let n_value: Option<Field> = match opts.n_value {
            NvalueOption::None | NvalueOption::Unknown => None,
            NvalueOption::Address => Some(Field::new(
                "Address".to_string(),
                nlist.n_value.to_string(),
            )),
            NvalueOption::Register => Some(Field::new(
                "Register".to_string(),
                nlist.n_value.to_string(),
            )),
            NvalueOption::StructOffset | NvalueOption::Offset => {
                Some(Field::new("Offset".to_string(), nlist.n_value.to_string()))
            }
            NvalueOption::LastModTime => Some(Field::new(
                "Last mod".to_string(),
                nlist.n_value.to_string(),
            )),
            NvalueOption::Sum => Some(Field::new("Sum".to_string(), nlist.n_value.to_string())),
            NvalueOption::Length => {
                Some(Field::new("Length".to_string(), nlist.n_value.to_string()))
            }
            NvalueOption::Raw => {
                Some(Field::new(N_VALUE_TITLE.to_string(), nlist.n_value.to_string()))
            },
        };
        if let Some(n_value) = n_value {
            fields.push(n_value)
        }

        fields
    }
}